Implantación de aplicaciones web PHP en Docker

Í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
comments powered by Disqus

Relacionados

Ejemplos de uso de comandos básicos en Debian

En esta entrada se recogen algunos ejercicios básicos que demuestran el uso de los comandos más usados en Debian y otras distribuciones GNU/Linux.

Leer

Creación de una VPN sitio a sitio con WireGuard

En este post, el escenario está formado por dos servidores de VPN que actúan, a su vez, como clientes del otro servidor y un equipo en la red interna de cada servidor.

Leer

Guía básica de configuración de Nginx

En este post se recoge una guía básica del uso del servidor web Nginx a partir de un supuesto práctico.

Leer