Este artículo no es una solución de descargar, pegar y a funcionar.
Es más bien, el proceso que sigo para montar las imágenes docker que necesito. En este caso un lamp con vhosts. Me sirve para aprender, practicar y coger soltura a la hora de encontrarme y solucionar errores. Prefiero encontrarme problemas mientras estoy “jugando”. Cuando te pasa trabajando es más estresante.
Dejo la mayoría de errores, para analizarlos y ver como llegar a la solución.
Si lo que quieres el el código para hacerlo funcionar y no liarte lo puedes ver aquí: montar-docker-lamp-vhosts.
Empezamos
En este artículo vamos a montar desde cero una imagen de docker, que soporte virtual hosts. No es el concepto que hay detras de contenedores docker, pero nos puede servir para practicar.
Vamos a partir de una imagen ubuntu:latest “bindeando” el puerto 8080 de nuestra máquina al puerto 80 del contenedor. También montaremos un directorio de nuestro host en el contenedor
athos@athos-Z97P-D3:~/dockerVirtualHosts$ docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2:/etc/apache2 ubuntu:latest bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
5bed26d33875: Pull complete
f11b29a9c730: Pull complete
930bda195c84: Pull complete
78bf9a5ad49e: Pull complete
Digest: sha256:bec5a2727be7fff3d308193cfde3491f8fba1a2ba392b7546b43a051853a341d
Status: Downloaded newer image for ubuntu:latest
root@53b269a2f09d:/#
Ahora, vamos a instalar a ir instalando los paquetes necesarios poco a poco, y haciendo las comprobaciones pertinentes. Cada paso que demos lo vamos a ir añadiendo a nuestro archivo Dockerfile, para posteriormente, poder construir nuestra imagen.
apt update && apt install apache2 php -y
Dockerfile
FROM ubuntu:latest
RUN apt update && apt install apache2 php -y
Levantamos el apache
/etc/init.d/apache2 start
Y podremos comprobar que el apache funciona
El siguiente paso que vamos a realizar, es crear un archivo php en /var/www/html para comprobar si php esta funcionando, para esto ejecutaremos lo siguiente:
echo "<?php echo 'Hola mundo' ?>" > /var/www/html/index.php
Y como podemos ver, no funciona
Esto es porque no hemos instalado el módulo de apache. Vamos a instalarlo, y además actualizaremos nuestro Dockerfile
apt install -y libapache2-mod-php && /etc/init.d/apache2 restart
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt update && apt install -y apache2 php libapache2-mod-php
En este momento ya podremos ver nuestro “Hola Mundo”
Una vez hecho esto, vamos a desechar el contenedor con el que estamos trabajando, buildearemos nuestra primera versión de la imagen y la levantaremos
Hacemos el build
docker build -t vhosts .
Levantamos nuestra nueva imagen. En este caso, vamos a montar en el contenedor también el directorio donde irán las webs.
docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2:/etc/apache2 -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts vhosts bash
Una vez aquí, para crear un host virtual, tendremos que añadir un fichero en conf.d/apache2/sites-available/nuestrovirtualhost.conf, pero si lo hacemos veremos que tenemos un error de permisos
athos@athos-Z97P-D3:~/dockerVirtualHosts$ touch conf.d/apache2/sites-available/nuestrovirtualhost.conf
touch: cannot touch 'conf.d/apache2/sites-available/nuestrovirtualhost.conf': Permission denied
Para solucionar esto hay varias opciones. Si tenemos en cuenta que vamos a crear varios hosts virtuales a lo largo del tiempo, igual lo más cómodo es crear un script que nos automatice la tarea.
El script es el siguiente:
#!/bin/bash
cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/$1.conf
sed -i "s@#ServerName www.example.com@ServerName $2@g" /etc/apache2/sites-available/$1.conf
sed -i "s@DocumentRoot /var/www/html@DocumentRoot /var/www/html/vhosts/$1@g" /etc/apache2/sites-available/$1.conf
mkdir -p /var/www/html/vhosts/$1
echo "Virtual host $2" > /var/www/html/vhosts/$1/index.php
a2ensite $1
/etc/init.d/apache2 reload
Este script lo guardaremos a la par que nuestro Dockerfile, tendremos que copiarlo dentro de nuestro contenedor, así que tendremos que modificar nuestro Dockerfile para que quede de la siguiente forma:
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt update && apt install -y apache2 php libapache2-mod-php
COPY ./newVhost /
Volvemos a generar la imagen
docker build -t vhosts .
Y creamos un nuevo contenedor con esa imagen
docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2:/etc/apache2 -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts vhosts bash
Una vez dentro comprobamos que se ha compiado nuestro script
root@d2ccca5bcca9:/# ls -l /newVhost
-rw-rw-r-- 1 root root 437 Mar 21 12:33 /newVhost
Levantamos el apache, probamos a entrar en el virtual host que vamos a crear posteriormente para comprobar que no funciona.
/etc/init.d/apache2 start
Intentamos acceder a la web (no olvidar añadir el dominio al /etc/hosts)
sudo bash -c 'echo "127.0.0.1 miweb.com" >> /etc/hosts'
Podemos comprobar que no funciona.
Ejecutamos nuestro script desde el contenedor:
bash newVhost miweb miweb.com
Enabling site miweb.
To activate the new configuration, you need to run:
service apache2 reload
* Reloading Apache httpd web server apache2*
Y en esta ocasión, podremos comprobar que si funciona
Vamos a hacer una segunda prueba con tuweb.com (añadir tuweb.com a /etc/hosts como hicimos anteriormente con miweb.com)
bash newVhost tuweb tuweb.com
Enabling site tuweb.
To activate the new configuration, you need to run:
service apache2 reload
* Reloading Apache httpd web server apache2*
Hasta aquí perfecto, ya podemos crear host virtuales básicos. Pero ahora nos encontramos otro problema. Si desde nuestro host, intentamos crear un archivo por ejemplo en vhosts/miweb nos dice que no tenemos permisos.
athos@athos-Z97P-D3:~/dockerVirtualHosts$ touch vhosts/miweb/index2.php
touch: cannot touch 'vhosts/miweb/index2.php': Permission denied
Si miramos dentro del contenedor, los permisos de ese directorio podemos ver el problema
root@055e5806495f:/# ls -l /var/www/html/vhosts/
total 1
drwxr-xr-x 2 root root 3 Mar 21 12:31 miweb
drwxr-xr-x 2 root root 3 Mar 21 12:49 tuweb
El directorio miweb es del usuario root y del grupo root, y según esos permisos, sólo puede escribir dentro de ese directorio el usuario root. Para resolver este problema, tenemos varias opciones.
Primero, podemos dar permisos de escritura al usuario “otros” que es como ve el contenedor a nuestro host cuando creamos un archivo. Pero esto es un problema de seguridad.
La segunda opción que tenemos es cambiar los permisos del grupo del directorio, y hacer que el grupo root pueda escribir, y meter al usuario con el que creamos los archivos en ese grupo.
La tercera opcion es cambiar el grupo del directorio para que coincida con el grupo del usuario que crea el archivo y darle permisos de escritura.
Por otro lado, hay que tener en cuenta, que es posible que se requiera que el directorio tenga permisos para el usuario que levantó el servicio apache2, que normalmente suele ser www-data
Como podemos ver, hay muchas posibles soluciones, cada una con sus problemas.
Lo primero que necesitamos, es saber el usuario que crea los ficheros cuando desde fuera del contenedor intentamos crearlo. Para saber esto, temporalmente vamos a dar permisos 777 a miweb, crearemos un archivo, y luego volveremos a dejar los permisos como estaban
root@055e5806495f:/# chmod 777 /var/www/html/vhosts/miweb/
root@055e5806495f:/#
athos@athos-Z97P-D3:~/dockerVirtualHosts$ touch vhosts/miweb/index2.php
athos@athos-Z97P-D3:~/dockerVirtualHosts$
root@055e5806495f:/# chmod 755 /var/www/html/vhosts/miweb/
root@055e5806495f:/#
root@055e5806495f:/# ls -l /var/www/html/vhosts/miweb/
total 2
-rw-r--r-- 1 root root 23 Mar 21 12:46 index.php
-rw-rw-r-- 1 1000 1000 0 Mar 21 13:01 index2.php
root@055e5806495f:/#
Como podemos ver, el usuario y el grupo que crea el archivo es el usuario 1000. Si miramos el archivo /etc/password vemos que ese usuario no existe.
root@055e5806495f:/# cat /etc/passwd
root❌0:0:root:/root:/bin/bash
daemon❌1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin❌2:2:bin:/bin:/usr/sbin/nologin
sys❌3:3:sys:/dev:/usr/sbin/nologin
sync❌4:65534:sync:/bin:/bin/sync
games❌5:60:games:/usr/games:/usr/sbin/nologin
man❌6:12👨/var/cache/man:/usr/sbin/nologin
lp❌7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail❌8:8:mail:/var/mail:/usr/sbin/nologin
news❌9:9:news:/var/spool/news:/usr/sbin/nologin
uucp❌10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy❌13:13:proxy:/bin:/usr/sbin/nologin
www-data❌33:33:www-data:/var/www:/usr/sbin/nologin
backup❌34:34:backup:/var/backups:/usr/sbin/nologin
list❌38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc❌39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats❌41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody❌65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt❌100:65534::/nonexistent:/usr/sbin/nologin
En linux, cuando creamos un usuario, se le asigna el siguiente id disponible a partir del 1000, es decir, el primer usuario será el usuario 1000, el segundo usuario que creemos sera el 1001 y así sucesivamente
Por otro lado, vamos a lanzar un ps aux para cerciorarnos del usuario que levanta el apache2
root@055e5806495f:/# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 18684 3576 pts/0 Ss 12:45 0:00 bash
root 31 0.0 0.0 327188 18576 ? Ss 12:46 0:00 /usr/sbin/apache2 -k start
www-data 115 0.0 0.0 327456 10668 ? S 12:49 0:00 /usr/sbin/apache2 -k start
www-data 116 0.0 0.0 327212 6320 ? S 12:49 0:00 /usr/sbin/apache2 -k start
www-data 117 0.0 0.0 327212 6320 ? S 12:49 0:00 /usr/sbin/apache2 -k start
www-data 118 0.0 0.0 327212 6320 ? S 12:49 0:00 /usr/sbin/apache2 -k start
www-data 119 0.0 0.0 327212 6320 ? S 12:49 0:00 /usr/sbin/apache2 -k start
www-data 120 0.0 0.0 327212 6320 ? S 12:49 0:00 /usr/sbin/apache2 -k start
root 126 0.0 0.0 34404 2924 pts/0 R+ 13:03 0:00 ps aux
Como podemos ver es www-data.
Llegados a este punto, sacamos como conclusiones que el directorio miweb, debería tener permisos de escritura para www-data (id 33) y para el usuario con id 1000.
La opción que vamos a probar es la siguiente. Vamos a hacer que el propietario del directorio sea www-data y el grupo sea también www-data. Posteriormente vamos a añadir al usuario 1000 al grupo www-data. Espero que con esto solucionemos los problemas que tenemos.
Para esto, tenemos que modificar el script que hicimos anteriormente quedando de la siguiente manera:
#!/bin/bash
cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/$1.conf
sed -i "s@#ServerName www.example.com@ServerName $2@g" /etc/apache2/sites-available/$1.conf
sed -i "s@DocumentRoot /var/www/html@DocumentRoot /var/www/html/vhosts/$1@g" /etc/apache2/sites-available/$1.conf
mkdir -p /var/www/html/vhosts/$1
chown www-data:www-data /var/www/html/vhosts/$1 #añadimos esta linea
chmod g+s /var/www/html/vhosts/$1 #añadimos esta linea
chmod g+w /var/www/html/vhosts/$1 #añadimos esta linea
echo "Virtual host $2" > /var/www/html/vhosts/$1/index.php
a2ensite $1
/etc/init.d/apache2 reload
Deberemos añadir a nuestro usuario al grupo www-data de nuestro hosts y reiniciar el equipo
adduser $USER www-data
Ahora, y gracias a la segunda linea que añadimos (chmod g+s /var/www/html/vhosts/$1) todos los archivos que creemos se crearan con el grupo www-data independientemente del usuario que lo cree, con lo que el usuario que levanta el servicio apache (www-data) podra escribir en ellos.
Vamos a probar ahora a hacer un vhost con un wordpress dentro. El dominio será miwordpress.com, lo añadimos a /etc/hosts
root@3967149bf7d4:/# bash newVhost miwordpress miwordpress.com
Enabling site miwordpress.
To activate the new configuration, you need to run:
service apache2 reload
* Reloading Apache httpd web server apache2 *
root@3967149bf7d4:/#
Ahora vamos al navegador y deberíamos poder ver el vhost creado.
Descargamos un wordpress y lo copiamos en su correspondiente directorio. También eliminaremos el archivo index.php que crea nuestro script.
athos@athos-Z97P-D3:~/dockerVirtualHosts$ rm vhosts/miwordpress/index.php && rsync -rtl ~/Desktop/wp5.3.2/ vhosts/miwordpress/
rm: remove write-protected regular file 'vhosts/miwordpress/index.php'? y
rsync: failed to set times on "/home/athos/dockerVirtualHosts/vhosts/miwordpress/.": Operation not permitted (1)
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1183) [sender=3.1.1]
athos@athos-Z97P-D3:~/dockerVirtualHosts$ ls vhosts/miwordpress/
index.php readme.html wp-blog-header.php wp-content wp-links-opml.php wp-mail.php wp-trackback.php
licencia.txt wp-activate.php wp-comments-post.php wp-cron.php wp-load.php wp-settings.php xmlrpc.php
license.txt wp-admin wp-config-sample.php wp-includes wp-login.php wp-signup.php
athos@athos-Z97P-D3:~/dockerVirtualHosts$
Y nos encontramos lo siguiente:
Nuestro contenedor no tiene la extensión mysql necesaria para funcionar, así, que vamos a instalarla, añadiendola al Dockerfile y reconstruyendo la imagen.
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt update && apt install -y apache2 php libapache2-mod-php php-mysql
COPY ./newVhost /
COPY ./limpiar /
athos@athos-Z97P-D3:~/dockerVirtualHosts$ docker build -t vhosts .
athos@athos-Z97P-D3:~/dockerVirtualHosts$ docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2/:/etc/apache2/ -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts vhosts bash
root@bbe3266f54e5:/# /etc/init.d/apache2 start
* Starting Apache httpd web server apache2 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
*
root@bbe3266f54e5:/#
Una vez instalado el módulo, recargamos la página y esta vez si vemos el wordpress.
Bien, llegamos al paso de la base de datos, aquí tenemos que tomar una decisión, instalar mysql en nuestro contenedor (lo que iría en contra de la filosofía de docker) o levantar un contenedor mysql atacarlo desde el nuestro.
En este caso, lo vamos a hacer mal, y lo vamos a instalar en el mismo contenedor.
Así que tendremos que volver a modificar el Dockerfile quedando así:
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt update && apt install -y apache2 php libapache2-mod-php php-mysql mariadb-server
COPY ./newVhost /
Levantamos un nuevo contenedor
docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2/:/etc/apache2/ -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts vhosts bash
Levantamos el servicio mysql y probamos si nos podemos conectar.
root@d19185ab39b2:/# /etc/init.d/mysql start
* Starting MariaDB database server mysqld [ OK ]
root@d19185ab39b2:/# mysql -u root
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 43
Server version: 10.1.44-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
MariaDB [(none)]>
Ahora tendremos que crear la base de datos, el usuario de la base de datos, y concederle permisos.
MariaDB [(none)]> CREATE DATABASE miwordpress;
Query OK, 1 row affected (0.01 sec)
MariaDB [(none)]> create user miusuario IDENTIFIED BY 'mipassword';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> GRANT USAGE ON miwordpress.* TO 'miusuario'@localhost IDENTIF
IED BY 'mipassword';
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]> GRANT ALL privileges ON `miwordpress`.* TO 'miusuario'@localhost;
Query OK, 0 rows affected (0.00 sec)
MariaDB [(none)]>
Comprobamos que el usuario se ha creado bien y que podemos acceder a la base de datos que creamos.
root@d19185ab39b2:/# mysql -u miusuario -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 64
Server version: 10.1.44-MariaDB-0ubuntu0.18.04.1 Ubuntu 18.04
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| miwordpress |
+--------------------+
2 rows in set (0.00 sec)
MariaDB [(none)]>
Ahora, vamos a continuar con el asistente de instalación de wordpress.
Y ya tendremos instalado el wordpress en el host virtual.
Hasta aquí perfecto, pero… ¿no estaría bien poner un phpmyadmin en cada host virtual?
Vamos a añadir a nuestro Dockerfile el phpmyadmin, y en nuestro script newVhost vamos a añadir un enlace a cada host virtual.
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt update && apt install -y apache2 php libapache2-mod-php php-mysql mariadb-server phpmyadmin
RUN chown -R mysql:mysql /var/lib/mysql /var/run/mysqld
COPY ./newVhost /
Volvemos a crear la imagen…
Una vez recreada la imagen y lanzado el contenedor, nos damos cuenta que miwordpres.com:8080/phpmyadmin funciona, pero el wordpress en si nos da un error
¿Por qué?
El motivo es que no hemos montado un directorio externo al contenedor para la base de datos, por lo que una vez que el contenedor se destruye perdemos los datos.
Esta vez montaremos el contenedor de la siguiente forma:
athos@athos-Z97P-D3:~/dockerVirtualHosts$ docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2/:/etc/apache2/ -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts -v ~/dockerVirtualHosts/db:/var/lib/mysql vhosts bash
En esta parte he tenido unos cuantos problemas para levantar la base de datos. Para que no se haga el post más largo todavía, voy a hacer un resumen.
Mariadb (en mysql no se si será igual), cuando instalamos el servicio, crea unas tablas para el funcionamiento de la base de datos en si. Como vamos a montar un directorio vacío en la ubicación donde van los archivos de datos de mariadb, necesitamos, en caso de que sea la primera vez y el directorio esté vacío, generar estas tablas.
Las tablas se crean con el siguiente comando:
mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
Ya que una vez entramos al contenedor, tenemos que realizar varias acciones (levantar el apache, levantar mariadb, comprobar si el directorio de mariadb está vacío o no…) vamos a crear un archivo llamado entrypoint.sh
/etc/init.d/rsyslog start
aux=`ls -l /var/lib/mysql|wc -l`
if [ "$aux" == "1" ]
then
mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
/etc/init.d/mysql start &&\
/usr/bin/mysqladmin -u root password 'example'
pma=`cat /etc/phpmyadmin/config-db.php |grep dbpass | cut -d"'" -f2`
echo "CREATE USER 'phpmyadmin'@'localhost' IDENTIFIED BY '$pma';
GRANT ALL PRIVILEGES ON *.* TO 'phpmyadmin'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;" | mysql -u root -pexample
fi
/etc/init.d/mysql start && apache2ctl -D FOREGROUND
Vamos a explicar un poco el script.
Lo primero, podemos darnos cuenta que iniciamos un servicio del que no hemos hablado hasta ahora: rsyslog. Esto hace que se genere el archivo /var/logs/syslog y en el se vaya registrando lo que ocurre en la máquina.
Lo instale y lo añadí al Dockerfile.
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt update && apt install -y rsyslog apache2 php libapache2-mod-php php-mysql mariadb-server phpmyadmin
COPY ./newVhost /
Lo siguiente que hacemos es comprobar el numero de archivos que hay dentro de /var/lib/mysql
aux=`ls -l /var/lib/mysql|wc -l`
Y en el if, comprobamos que si el numero de archivos es 1, esto quiere decir que el directorio está vacío.
En ese caso, tenemos que realizar varias tareas:
Generamos las tablas de mysql
mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
Iniciamos la base de datos
/etc/init.d/mysql start &&\
Establecemos la contraseña del usuario root de la base de datos (esta contraseña estaría mejor si la cogiesemos de una variable de entorno)
/usr/bin/mysqladmin -u root password 'example'
Cuando instalamos phpmyadmin, este genera una contraseña aleatoria, con el siguiente comando, lo guardamos en la variable pma, y la utilizaremos mas adelante para crear el usuario phpmyadmin en la base de datos
pma=`cat /etc/phpmyadmin/config-db.php |grep dbpass | cut -d"'" -f2`
Ahora creamos el usuario phpmyadmin en la base de datos
echo "CREATE USER 'phpmyadmin'@'localhost' IDENTIFIED BY '$pma';GRANT ALL PRIVILEGES ON *.* TO 'phpmyadmin'@'localhost' WITH GRANT OPTION;FLUSH PRIVILEGES;" | mysql -u root -pexample
Finalmente, tanto si entró en el if como si no, levantamos la base de datos (si se levantó previamente en el if no pasa nada) y ejecutamos el apache.
/etc/init.d/mysql start && apache2ctl -D FOREGROUND
Una vez tenemos el entrypoint.sh, vamos a modificar de nuevo el Dockerfile añadiendo tres cosas.
- Copiamos entrypoint.sh dentro del contenedor.
- Le damos permisos de ejecucion.
- Esto es muy importante. Le decimos al contenedor mediante CMD cual es el comando que se va a ejecutar cuando el contenedor se inicie. Al haber puesto el entrypoint.sh, el contenedor ejecutara todas las tareas que estén en este archivo.
FROM ubuntu:latest
ENV DEBIAN_FRONTEND noninteractive
RUN apt update && apt install -y rsyslog apache2 php libapache2-mod-php php-mysql mariadb-server phpmyadmin
COPY ./newVhost /
COPY ./entrypoint.sh /
RUN chmod +x /entrypoint.sh
CMD bash /entrypoint.sh
Ahora vamos a ejecutar el contenedor de nuevo, pero esta vez no sobre-escribiremos el comando (CMD) por defecto de la imagen
Si os habéis fijado, en el comando siempre hemos estado poniendo bash al final. Esto sobre-escribe el CMD.
docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2/:/etc/apache2/ -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts -v ~/dockerVirtualHosts/db:/var/lib/mysql vhosts bash
En este caso, vamos a ejecutarlo pero sin el “bash” final, para que se ejecute el comando.
docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2/:/etc/apache2/ -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts -v ~/dockerVirtualHosts/db:/var/lib/mysql vhosts
Obtenemos el siguiente resultado:
* Starting enhanced syslogd rsyslogd [ OK ]
* Stopping MariaDB database server mysqld [ OK ]
* Starting MariaDB database server mysqld [ OK ]
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
Como se puede observar, nos saltan dos errores, por un lado, nos dice que apache no puede determinar el fqdn, pero es algo que no nos importa demasiado.
Por otro lado, vemos un error al iniciar mysql, diciendo que no se puede acceder con el usuario root sin password.
Despues de investigarlo un poco, me doy cuenta que el problema es al checkear el estado del servicio y no al arrancarlo como pensaba al principio.
root@13d5ca923d48:/# /etc/init.d/mysql status
/usr/bin/mysqladmin: connect to server at 'localhost' failed
error: 'Access denied for user 'root'@'localhost' (using password: NO)'
Voy a investigar un poco ese archivo (/etc/init.d/mysql)
## Checks if there is a server running and if so if it is accessible.
#
# check_alive insists on a pingable server
# check_dead also fails if there is a lost mysqld in the process list
#
# Usage: boolean mysqld_status [check_alive|check_dead] [warn|nowarn]
mysqld_status () {
ping_output=`$MYADMIN ping 2>&1`; ping_alive=$(( ! $? ))
ps_alive=0
pidfile=`mysqld_get_param pid-file`
if [ -f "$pidfile" ] && ps `cat $pidfile` >/dev/null 2>&1; then ps_alive=1; fi
if [ "$1" = "check_alive" -a $ping_alive = 1 ] ||
[ "$1" = "check_dead" -a $ping_alive = 0 -a $ps_alive = 0 ]; then
return 0 # EXIT_SUCCESS
else
if [ "$2" = "warn" ]; then
echo -e "$ps_alive processes alive and '$MYADMIN ping' resulted in\n$ping_output\n" | $ERR_LOGGER -p daemon.debug
fi
return 1 # EXIT_FAILURE
fi
}
En esta sección del archivo, imprimo por pantalla el valor de $MYADMIN y obtengo el siguiente resultado.
root@13d5ca923d48:/# /etc/init.d/mysql status
El contenido de MYADMIN ES: /usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf
/usr/bin/mysqladmin: connect to server at 'localhost' failed
error: 'Access denied for user 'root'@'localhost' (using password: NO)'
Me dirijo al archivo /etc/mysql/debian.cnf a ver su contenido
root@13d5ca923d48:/# cat /etc/mysql/debian.cnf
# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host = localhost
user = root
password =
socket = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
host = localhost
user = root
password =
socket = /var/run/mysqld/mysqld.sock
basedir = /usr
root@13d5ca923d48:/#
Vemos claramente que el campo password está vacío, y claro, tiene su lógica. Por defecto cuando instalas mariadb la contraseña de root es una cadena vacía. Nosotros hemos cambiado la contraseña de root, por ende, nos da error.
Vamos a probar a meter ahí el password de root a ver que pasa…
root@13d5ca923d48:/# /etc/init.d/mysql status
El contenido de MYADMIN ES: /usr/bin/mysqladmin --defaults-file=/etc/mysql/debian.cnf
* /usr/bin/mysqladmin Ver 9.1 Distrib 10.1.44-MariaDB, for debian-linux-gnu on x86_64
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Server version 10.1.44-MariaDB-0ubuntu0.18.04.1
Protocol version 10
Connection Localhost via UNIX socket
UNIX socket /var/run/mysqld/mysqld.sock
Uptime: 6 min 26 sec
Threads: 1 Questions: 2 Slow queries: 0 Opens: 17 Flush tables: 1 Open tables: 11 Queries per second avg: 0.005
root@13d5ca923d48:/#
Se fué el error!!! Muy bien, ahora tenemos que hacer, que cuando se cambiamos la contraseña de root, se actualice también en ese archivo.
Los permisos del archivo son los siguientes:
root@13d5ca923d48:/# ls -l /etc/mysql/debian.cnf
-rw------- 1 root root 292 Mar 21 18:16 /etc/mysql/debian.cnf
Nos aseguramos de que ese archivo solo lo pueda leer el usuario root.
Para solucionarlo, vamos a modificar nuestro entrypoint.sh, añadiremos lo siguiente
sed -i 's@password =.*@password = example@g' /etc/mysql/debian.cnf
Quedando el archivo de la siguiente manera:
/etc/init.d/rsyslog start
aux=`ls -l /var/lib/mysql|wc -l`
if [ "$aux" == "1" ]
then
mysql_install_db --user=mysql --basedir=/usr --datadir=/var/lib/mysql
/etc/init.d/mysql start &&\
/usr/bin/mysqladmin -u root password 'example' &&\
pma=`cat /etc/phpmyadmin/config-db.php |grep dbpass | cut -d"'" -f2`
echo "CREATE USER 'phpmyadmin'@'localhost' IDENTIFIED BY '$pma';
GRANT ALL PRIVILEGES ON *.* TO 'phpmyadmin'@'localhost' WITH GRANT OPTION;
FLUSH PRIVILEGES;" | mysql -u root -pexample
fi
sed -i 's@password =.*@password = example@g' /etc/mysql/debian.cnf
/etc/init.d/mysql restart && apache2ctl -D FOREGROUND
Ahora, recostruimos la imagen y la volvemos a lanzar.
docker build -t vhosts .
docker run -it -p 8080:80 -v ~/dockerVirtualHosts/conf.d/apache2/:/etc/apache2/ -v ~/dockerVirtualHosts/vhosts:/var/www/html/vhosts -v ~/dockerVirtualHosts/db:/var/lib/mysql vhosts
Y como podemos ver al final de la salida del comando, esta vez no nos da el error:
* Starting MariaDB database server mysqld [ OK ]
* Starting MariaDB database server mysqld [ OK ]
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
Estaría bien poder crear nuevos hosts virtuales sin tener que entrar dentro del contenedor, para esto, lo único que tenemos que hacer es lo siguiente:
- Lanzamos nuestro contenedor
- Identificamos el id de nuestro contenedor con docker ps
- Desde otra terminal ejecutamos lo siguiente:
athos@athos-Z97P-D3:~/dockerVirtualHosts$ docker exec 80a0f2e84607 bash -c "bash /newVhost tuweb tuweb.com" Enabling site tuweb. To activate the new configuration, you need to run: service apache2 reload * Reloading Apache httpd web server apache2
Y como último paso, vamos a crear un docker-compose.yml, esto no es imprescindible pero nos hará mas cómodo levantar y parar el contenedor.
version: '3'
services:
vhosts:
build: .
volumes:
- ./conf.d/apache2/:/etc/apache2/
- ./vhosts:/var/www/html/vhosts
- ./db:/var/lib/mysql
ports:
- 8080:80
Repositorio: