/

Montar docker lamp vhosts

Created 2020-03-21 Modifyed 2020-03-21
4477 Words

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.

Si eres capáz de llegar al final del artículo enhorabuena!!! Deja tu comentario.

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
Si te fijas bien en este último paso, para levantar apache he usado apache2ctl -D FOREGROUND. Esto tiene un motivo. Un contenedor docker, se para cuando la tarea que ejecuta termina. Si utilizamos /etc/init.d/apache2 restart, se reinicia el apache, y esa tarea, "reinicio de apache" en realidad termina, aunque deje un servicio corriendo en background. Si utilizamos apache2ctl -D FOREGROUND, la tarea no se ejecuta en segundo plano, y la tarea no finaliza, en principio, nunca, con lo que nuestro contenedor no se para, que es precisamente lo que queremos.

Una vez tenemos el entrypoint.sh, vamos a modificar de nuevo el Dockerfile añadiendo tres cosas.

  1. Copiamos entrypoint.sh dentro del contenedor.
  2. Le damos permisos de ejecucion.
  3. 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:

  1. Lanzamos nuestro contenedor
  2. Identificamos el id de nuestro contenedor con docker ps
  3. 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:

https://gitlab.com/athos.oc/montar-docker-lamp-vhosts