8. Despliegue avanzado en Servidor

En el tema anterior vimos lo básico para empezar a usar Docker. En este tema vamos a ver aspectos mas avanzados de Docker y trucos a tener en cuenta para usarlo.

Mas información:

An Updated Performance Comparison of Virtual Machines and Linux Containers

Build your own images

Docker for dummies

Logs en Docker

10 Examples of how to get Docker Container IP Address

Creación de imágenes

Podemos crear nuestras propias imágenes si ninguna de las que hay hace lo que nosotros necesitamos. PAra ello usaremos el comando docker build.

El comando docker build se utiliza para construir imágenes de contenedor Docker a partir de un Dockerfile. A continuación se presentan las principales características y uso de este comando.

<shx>

docker build -t nombre_imagen:etiqueta ruta_del_dockerfile

</shx>

Donde:

-t nombre_imagen:etiqueta: Nombre y tag de la imagen que se va a crear

ruta_del_dockerfile: Ruta al directorio que contiene el Dockerfile. Lo normal es estar en el directorio donde está el fichero Dockerfile por lo que sea pondrá un punto indicando que es el directorio actual.

Ejemplo de uso: Crea la imagen llamada mi_aplicacion_web:1.0 sabiendo que el fichero Dockerfile está en la carpeta actual.

<shx> docker build -t mi_aplicacion_web:1.0 . </shx>

Estructura Básica del Dockerfile

Un Dockerfile contiene instrucciones para la construcción de una imagen. Aquí se muestra una estructura básica:

- FROM: Indica la imagen base desde la cual se construirá la nueva imagen. La versión es opcional.

- RUN: Ejecuta comandos en una nueva capa del contenedor. Se utiliza para instalar paquetes, configurar el entorno y realizar otras acciones durante la construcción de la imagen.

- COPY: Copia archivos desde la ruta de origen en la máquina host hacia el contenedor en la ruta de destino.

- EXPOSE: Informa a Docker que el contenedor escuchará en el puerto especificado en tiempo de ejecución. No publica el puerto en el host.

- ENV: Define variables de entorno dentro del contenedor.

- CMD: Especifica el comando predeterminado que se ejecutará cuando el contenedor se inicie.

Las diferencias entre RUN y CMD es que RUN se ejecuta cuando se crea la imagen con el comando docker build ,es decir configurándola mientras que CMD se ejecuta cuando se crea el contenedor con el comando docker container run

Gestión de Redes

Docker permite crear redes aisladas entre los distintos contenedores. De esa forma , si un contenedor es atacado, no se tendrá acceso por red al resto de los contenedores sino únicamente a los de su propia red.

Orden	Explicación
docker network create	Crea una red
docker network rm	Borrar una red
docker network ls	Ver las redes que hay
docker network connect	Conecta un contenedor a una red
docker network disconnect	Desconecta un contenedor de una red

Para trabajar con redes, lo que debemos hacer es

Primero crear la red:

docker network create nombreDeLaRed

Conectar un contenedor a esa red

docker network connect nombreDeLaRed nombreDelContenedor

También podemos conectar la red al contenedor al usar docker container run con la opción --network=nombreDeLaRed

docker container run \
-dit \
-v /opt/mariadb:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-p 4000:3306 \
--network=nombreDeLaRed \
--name prueba_mariadb \
mariadb:10.1

Docker Compose

Docker Compose es una herramienta que permite definir y administrar aplicaciones Docker de múltiples contenedores. Utiliza un archivo YAML para configurar los servicios, las redes y los volúmenes, facilitando la definición y el despliegue de aplicaciones complejas.

Estructura Básica de un archivo docker-compose.yml

Un archivo define la configuración de una aplicación multi-contenedor. Aquí se presentan las secciones principales:

Comandos Básicos de Docker Compose

Iniciar la aplicación basada en la configuración de .

Detener y eliminar los contenedores definidos en el archivo .

- Muestra el estado de los contenedores definidos en el archivo .

Ejemplo de Uso

Supongamos un archivo para una aplicación web con un servicio de frontend y otro de base de datos:

Para iniciar esta aplicación, ejecutamos:

Esto levantará los contenedores de frontend y base de datos con la configuración especificada.

Docker Compose simplifica la gestión de aplicaciones Docker complejas, permitiendo definir y coordinar múltiples servicios con facilidad.

Para pasar un script de docker al formato de docker compose se puede usar la herramienta: https://it-tools.tech/docker-run-to-docker-compose-converter

Proxy con Nginx

¿Recuerdas la cabecera Host de HTTP? Servía para indicar a que servidor web iba dirigida la petición ya que en una misma máquina se ponían varios servidores web.

Pues vamos ahora a configurar Docker para que todos los servidores compartan el mismo puerto (el 80). Para ello vamos a usar un servidor proxy llamado Nginx. Obviamente, Nginx va estar dentro de otro contenedor de Docker. La imagen a usar es jwilder/nginx-proxy con tag 0.7.0.

Para arrancar el servidor usamos la siguiente orden:

sudo docker container run \
-dit \
-p 80:80 \
--restart always \
-v /var/run/docker.sock:/tmp/docker.sock:ro \
--name nginx-proxy \
nginxproxy/nginx-proxy:1.1.0

Como vemos no tiene nada de especial excepto la línea -v /var/run/docker.sock:/tmp/docker.sock:ro pero solo decir que es para que Nginx pueda acceder al Docker real de la máquina real.

Y ahora , ¿Como arrancamos nuestro servidores web? Pues simplemente hay que añadir dos parámetros y quitar uno.

Y hay que quitar el parámetro de -p puertoExterno:puertoInterno ya que ahora el contenedor no se va a exponer al exterior.

Hay que añadir:

-e VIRTUAL_PORT=puertoInterno: Siendo puertoInterno donde está escuchando nuestro servidor Web. Esa información la sabremos por la documentación

-e VIRTUAL_HOST=nombreDominio: Siendo nombreDominio el nombre del dominio que queremos que gestione el servidor que estamos arrancando.

Veamos el siguiente ejemplo:

sudo docker container run \
-dit \
-v /home/alumno/lorenzo/httpd:/var/www/html \
-e VIRTUAL_PORT=80
-e VIRTUAL_HOST=lorenzo.daw2.pve3.fpmislata.com \
--name lorenzo_apache \
php:8.1-apache-bullseye

Vemos que el servidor está escuchando en el puerto 80 y va a gestionar las peticiones del dominio lorenzo.daw2.pve3.fpmislata.com que lleguen a Nginx.

Los servidores de Base de datos u otros contenedores que no son el servidor web no tienen que hacer nada especial por usar Nginx ya que no se accede a ellos a través del proxy sino directamente a ellos o únicamente de forma interna.

Mas información:

Running an NGINX Reverse Proxy with Docker and Let's Encrypt on Google Compute Engine

Automated Nginx Reverse Proxy for Docker

How to setup NGINX with automatic HTTPS in Docker

Host multiple websites with HTTPS on a single server

El fichero hosts

Ahora para que funcione el proxy con nginx ya no podemos acceder a la máquina real con la IP sino que habrá que usar el nombre de dominio. Si no tenemos acceso a un nombre de dominio real, podemos simular nosotros uno modificando el fichero hosts de la máquina donde vayamos a hacer la petición.

El fichero hosts simplemente contiene IPs y nombre e dominio que usará nuestro ordenador. De esa forma simularemos tener un dominio.

En los siguientes artículos se explica como modificar el fichero hosts

Archivo hosts

¿Cómo se usa el archivo “hosts”?

Ejercicios

Para hacer estos ejercicios debe crearse antes la red "prueba_red"
Para la página PHP que se conectará la MySQL se puede usar el siguiente código: ejemplo_php.zip

Ejercicio 1

Crea un contenedor llamado "maria_a" que sean de MariaDB:

Crea la table "info" con una columna llamada "nombre" y añade una fila con el valor "soy la base de datos A"

Su puerto externo es el 2010

Conectado a la red "prueba_red"

Que el nombre del host sea "maria_a"

Crea un contenedor llamado "maria_b" que sean de MariaDB:

Crea la table "info" con una columna llamada "nombre" y añade una fila con el valor "soy la base de datos B"

Su puerto externo es el 2020

Conectado a la red "prueba_red"

Que el nombre del host sea "maria_b"

Ejercicio 2

Crea un contenedores llamado "apache_a" que sean del servidor web Apache con PHP:

Crea una página llamada index.php que

Se conecte a la base de datos llamada "maria_a" ,que hemos creado antes y que muestre los datos de alguna fila de la tabla "peliculas"

Incluya un título llamado "Soy el servidor Web A"

Su puerto externo es el 3010

Utiliza el fichero .env para guardar los datos de conexión a la base de datos

Conectado a la red "prueba_red"

En vez de crear tu el código PHP puedes usar el siguiente:ejemplo_php.zip

Crea un contenedores llamado "apache_b" que sean del servidor web Apache con PHP:

Crea una página llamada index.php que

Se conecte a una de las bases de datos "maria_b" que hemos creado antes y que muestre los datos de alguna fila de la tabla "peliculas"

Incluya un título llamado "Soy el servidor Web B"

Su puerto externo es el 3020

Utiliza el fichero .env para guardar los datos de conexión a la base de datos

Conectado a la red "prueba_red"

En vez de crear tu el código PHP puedes usar el siguiente:ejemplo_php.zip

Ejercicio 3

Modifica únicamente el fichero .env de cada servidor para que ahora se conecten a la otra base de datos. Deberá funcionar todo sin problemas.

Vuelve a dejar las conexiones como estaban antes del ejercicio.

Ejercicio 4

Crea ahora dos redes llamadas:

red_a

red_b

Vuelve a crear los 4 contenedores de antes pero de forma que cada uno esté asociado a una red distinta.

Contenedor	Red
apache_a	red_a
maria_a	red_a
apache_b	red_b
maria_b	red_b

Prueba que los 2 servidores web siguen funcionando y accediendo a sus bases de datos correspondientes.

Ejercicio 5

Modifica únicamente el fichero .env de cada servidor para que ahora se conecten a la otra base de datos. No deberán poder conectarse

Vuelve a dejar las conexiones como estaban antes del ejercicio, es decir todas conectadas a "prueba_red".

Ejercicio 6

Hasta ahora para acceder a cada servidor web usabas los puertos 3010 y 3020. Vamos ahora a usar el servidor Proxy Nginx para que a ambos se pueda acceder por nombre de dominio y todos por el puerto 80. Para ello ya no vamos a usar nuestra máquina sino que vamos a usar la máquina que nos proporciona el centro, a la que nos tenemos que conectar por VPN. En esa máquina ya hemos instalado Nginx como proxy.

En esa máquina haz lo siguiente

Crear un contenedor "apache_tuNombre"

Indicando que la variable de entorno VIRTUAL_HOST vale "tuNombre.daw2.pve2.fpmislata.com"

Indicando que la variable de entorno VIRTUAL_PORT vale "80"

Deberás incluir el proyecto de PHP que permitía acceder a la base de datos

Que la carpeta compartida esté en /opt/tuNombre/docker/apache

El código fuente de PHP lo copias en /opt/tuNombre/docker/apache

Crear un contenedor "mariadb_tuNombre"

En la base de datos crea la tabla y los datos que necesites.

Que la carpeta compartida esté en /opt/tuNombre/docker/mariadb

Prueba a navegar la siguientes url (y deberá funcionar):

http://tuNombre.daw2.pve2.fpmislata.com

Ejercicio 7

Crea un proyecto Web en GitHub llamado "ejemplodaw" en los que haya lo siguiente:

Código PHP

Código SASS

Muestre desde PHP información de la base de datos.

El proyecto es similar al anterior solo que ahora también hay código SASS.

Incluye en el proyecto un script de bash llamado deploy.sh para desplegar el proyecto en docker. El script se ejecutará con npm run deploy. El script hará lo siguiente:

Copiar el código de src a dist

Compilar en dist el código SASS

Borrar los contenedores de Docker que vaya a crear (por si ya existen), que son "apache_tuNombre" y "mariadb_tuNombre"

Copiar el contenido de la carpeta dist a /opt/tuNombre/docker/apache

Crear el contenedor de Docker del servidor Web "apache_tuNombre"

Crear el contenedor de Docker de la base de datos "mariadb_tuNombre"

En la máquina real ves a la carpeta /opt/tuNombre y haz un git clone del proyecto que acabas de crear en GitHub. Se creará la carpeta /opt/tuNombre/ejemplodaw.

Para probar que todo funciona haz cambios en el SASS en master , súbelos a GitHub y ejecuta dentro de la máquina Linux lo siguiente:

cd /opt/tuNombre/ejemplodaw
git fetch --prune --force
git checkout master
git merge --ff-only origin/master
npm run deploy

Prueba a navegar a http://tuNombre.daw2.pve2.fpmislata.com y se verán los cambios.