Implantación de aplicaciones web PHP en Docker
- Francisco Javier Huete
- Implantacion
- February 24, 2025
Índice
Creación de una imagen docker a partir de una aplicación PHP
Para implantar aplicaciones PHP usando docker hay que crear una imagen Docker a partir de la aplicación para que se pueda implantar. En este caso se implanta una aplicación PHP llamada Biblioteca que almacena su información en un contenedor Docker MariaDB con un volumen para almacenar los datos de manera persistente.
El punto de partida para crear esta imagen Docker es el siguiente árbol de directorios:
biblioteca
├── build
│ ├── Dockerfile
│ ├── app
│ │ ├── ...
│ │ ├── biblioteca.sql
│ │ └── index.php
│ └── docker-entrypoint.sh
└── docker-compose.yml
Esta aplicación no está desarrollada pensando en desplegarse en contenedores Docker, de manera que define una serie de constantes en uno de sus ficheros. Para poder dockerizar la aplicación es necesario que reciba estos valores desde variables de entorno. Por eso, hay que modificar el contenido de este fichero llamado Config/Config.php.
<?php
define('base_url',getenv('BASE_URL'));
define('host',getenv('HOST'));
define('user',getenv('USER'));
define('pass',getenv('PASS'));
define('db',getenv('DB'));
define('charset',getenv('CHARSET'));
?>
Al inicializar el contenedor, se ejecuta un script que verifica que la base de datos es accesible, vuelva la información a la base de datos y ejecuta el servidor web en segundo plano.
#!/bin/bash
while ! mysql -u ${USER} -p${PASS} -h ${HOST} -e ";" ; do
sleep 1
done
mysql -u ${USER} -p${PASS} -h ${HOST} ${DB} < /opt/biblioteca.sql
apache2ctl -D FOREGROUND
En el Dockerfile se deben indicar, junto a este script, las variables de entorno necesarias para crear tanto el contenedor de la aplicación como la base de datos, así como los comandos que se deben ejecutar para la construcción de la imagen. Para su correcto funcionamiento, esta aplicación necesita que se instalen los módulos pdo
y pdo_mysql
en el contenedor php.
FROM php:8.4.3-apache
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh \
&& a2enmod rewrite \
&& apt-get update && apt-get install -y mariadb-client \
&& docker-php-ext-install pdo pdo_mysql\
&& docker-php-ext-enable pdo pdo_mysql
COPY app /var/www/html/
EXPOSE 80
ENV BASE_URL=http://localhost/biblio/
ENV HOST=localhost
ENV USER=usuario
ENV PASS=asdasd
ENV DB=biblioteca
ENV CHARSET="charset=utf8"
COPY app/biblioteca.sql /opt
CMD /usr/local/bin/docker-entrypoint.sh
Este fichero se puede usar para crear la imagen Docker de la aplicación.
debian@docker:~/biblioteca/build$ docker build -t fjhuete/biblioteca:v1 .
[+] Building 51.8s (11/11) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 623B 0.0s
=> [internal] load metadata for docker.io/library/php:8.4.3-apache 1.5s
=> [auth] library/php:pull token for registry-1.docker.io 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> CACHED [1/5] FROM docker.io/library/php:8.4.3-apache@sha256:da2c37150 0.0s
=> [internal] load build context 0.4s
=> => transferring context: 10.83MB 0.4s
=> [2/5] COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh 0.3s
=> [3/5] RUN chmod +x /usr/local/bin/docker-entrypoint.sh && apt-get up 46.1s
=> [4/5] COPY app /var/www/html/ 0.9s
=> [5/5] COPY app/biblioteca.sql /opt 0.1s
=> exporting to image 2.0s
=> => exporting layers 1.9s
=> => writing image sha256:050c2a2b10b6830af919e33c92433b489ff062fe85d41 0.0s
=> => naming to docker.io/fjhuete/biblioteca:v1 0.0s
1 warning found (use docker --debug to expand):
- JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintended behavior related to OS signals (line 20)
Además, en el fichero docker-compose se debe crear el contenedor de la aplicación con sus variables de entorno correspondientes, así como el contenedor de la base de datos con sus propias variables de entorno y los volúmenes asociados a cada uno. Para que la aplicación funcione correctamente, es necesario especificar la URL base desde la que se va a acceder a la aplicación en el docker-compose.
services:
app:
container_name: biblioteca
image: fjhuete/biblioteca:v1
restart: always
environment:
BASE_URL: http://172.22.200.203:8080/
HOST: biblioteca_db
USER: usuario
PASS: asdasd
DB: biblioteca
CHARSET: "charset=utf8"
ports:
- 8080:80
volumes:
- biblioteca_logs:/var/log/apache2
depends_on:
- db
db:
container_name: biblioteca_db
image: mariadb
restart: always
environment:
MYSQL_DATABASE: biblioteca
MYSQL_USER: usuario
MYSQL_PASSWORD: asdasd
MYSQL_ROOT_PASSWORD: asdasd
volumes:
- biblioteca_data:/var/lib/mysql
volumes:
biblioteca_data:
biblioteca_logs:
Se puede desplegar esta aplicación a partir de la imagen creada usando el fichero docker-compose.
docker compose up -d
Así, se crea la red propia del escenario, junto a los volúmenes indicados en el fichero y los dos contenedores que sirven la aplicación.
debian@docker:~/biblioteca$ docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
biblioteca fjhuete/biblioteca:v1 "docker-php-entrypoi…" app 3 minutes ago Up 3 minutes 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
biblioteca_db mariadb "docker-entrypoint.s…" db 3 minutes ago Up 3 minutes 3306/tcp
Implantación de una aplicación PHP usando Docker en el entorno de producción
Para poder implantar la aplicación usando la imagen creada, ésta debe ser accesible desde otros equipos. Para ello, se puede publicar en un repositorio de imágenes de Docker en Internet como DockerHub.
docker push fjhuete/biblioteca:v1
Para hacer que sea más fácil reutilizar el fichero docker-compose se puede parametrizar.
services:
app:
container_name: biblioteca
image: fjhuete/biblioteca:${VERSION_BIBLIOTECA}
restart: always
environment:
BASE_URL: ${BASE_URL}
HOST: ${HOST}
USER: ${USER}
PASS: ${PASS}
DB: ${DB}
CHARSET: "charset=utf8"
ports:
- ${PUERTO}:80
volumes:
- biblioteca_logs:/var/log/apache2
depends_on:
- db
db:
container_name: biblioteca_db
image: mariadb:${VERSION_MARIADB}
restart: always
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
volumes:
- biblioteca_data:/var/lib/mysql
volumes:
biblioteca_data:
biblioteca_logs:
Para pasar las variables al fichero docker-compose se usa un fichero oculto llamado .env. El contenide de este fichero, en cambio, será diferente en el entorno de desarrollo y en el entorno de producción. Por ejemplo, en el entorno de desarrollo el contenido del fichero .env es el siguiente:
BASE_URL=http://172.22.200.203:8080/
VERSION_BIBLIOTECA=v1
VERSION_MARIADB=latest
PUERTO=8080
HOST=biblioteca_db
USER=usuario
PASS=asdasd
DB=biblioteca
MYSQL_DATABASE=biblioteca
MYSQL_USER=usuario
MYSQL_PASSWORD=asdasd
MYSQL_ROOT_PASSWORD=asdasd
Mientras que en el entorno de producción, el fichero .env contendrá las siguientes variables:
BASE_URL=https://biblioteca.javihuete.site
VERSION_BIBLIOTECA=v1
VERSION_MARIADB=latest
PUERTO=8086
HOST=biblioteca_db
USER=user_biblioteca
PASS=qj6LSdAWKQke1w
DB=biblioteca
MYSQL_DATABASE=biblioteca
MYSQL_USER=user_biblioteca
MYSQL_PASSWORD=qj6LSdAWKQke1w
MYSQL_ROOT_PASSWORD=TrgbaPMfOYsBpA
Los ficheros necesarios para la implantación de la aplicación se pueden subir a un repositorio en GitHub para compartirlos entre el entorno de desarrollo y de producción. En este repositorio, el fichero .env debe estar incluido dentro del fichero .gitignore para que no se publique.
En el entorno de producción se clona el repositorio.
git clone https://github.com/fjhuete/biblioteca.git
En este directorio, se puede desplegar la aplicación usando el docker-compose.
docker compose up -d
Para poder acceder a la aplicación, el servidor web, en este caso Nginx, debe tener un Virtual Host configurado que funcione como proxy inverso para acceder a la aplicación.
nano /etc/nginx/sites-available/biblioteca
En este fichero se configura el acceso por HTTPS al servidor web, así como la configuración del proxy inverso.
server {
listen 443 ssl;
server_name biblioteca.javihuete.site;
ssl_certificate /etc/letsencrypt/live/javihuete.site/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/javihuete.site/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://localhost:8086;
#include proxy_params;
}
error_log /var/log/nginx/biblioteca_error.log;
access_log /var/log/nginx/biblioteca_access.log;
}
Para habilitar el nuevo Virtual Host se crea un enlace simbólico a este fichero en el directorio sites-available.
cd ../sites-available
sudo ln -s ../sites-available/biblioteca
Por último, para aplicar los cambios, se reinicia el servidor web.
sudo systemctl restart nginx
Actualización de la aplicación
Si en el entorno de desarrollo se hace alguna modificación de la aplicación, se debe crear una nueva imagen con esta versión y subirla al repositorio de imágenes, en este caso DockerHub.
docker build -t fjhuete/biblioteca:v2 .
docker push fjhuete/biblioteca:v2
Para desplegarla en el entorno de producción, se modifica la el valor de la variable de la versión en el fichero .env.
VERSION_BIBLIOTECA=v2
Se paran los contenedores del docker-compose sin usar la opción -v para que la información se mantenga en los volúmenes que estaban usando y se levantan los contenedores con la nueva versión.
docker compose down
docker compose rm
docker compose up -d