9.2.2.2.2.3. Composición¶
docker-compose es una aplicación que facilita la definición y ejecución de aplicaciones multicontenedor, esto es, aplicaciones que se dividen en aplicaciones más simples cada una de las cuales se ejecuta en un contenedor distinto. En particular proporciona un método declarativo para describir cómo construir varios contenedores y cómo ponerlos a funcionar simultáneamente.
Requiere una instalación independiente:
# apt install docker-compose
9.2.2.2.2.3.1. Fundamentos¶
El uso de docker-compose se basa en la creación, dentro de un
directorio de trabajo, de un fichero docker-compose.yaml
(o
docker-compose.yml
) donde se declaran cuáles son los contenedores, cómo
se construyen sus imágenes (si es que hay que construirlas previamente), cuáles
son las relaciones existentes entre ellos y cómo deben arrancarse.
Para comocer la sintaxis de estos ficheros YAML puede recurrse a la documentación oficial sobre docker-compose. Básicamente consiste en ir declarando entidades: contenedores (que denomina services), volúmenes, etc.
La puesta en marcha de los declarado resultará en la ejecución de varios contenedores incluidos dentro de una misma red[1].
9.2.2.2.2.3.2. Ejemplo ilustrativo¶
Supongamos que deseamos levantar un servicio web con nginx que sea capaz de generar páginas escritas en PHP. Para ello dispondremos dos contenedores:
Uno que ejecute nginx`, que parta de una imagen de Alpine e instale nginx[2].
Otro que sea capaz de ejecutar FPM y que tomaremos directamente de una de las imágenes oficiales de PHP
Para llevar a cabo este propósito crearemos un directorio de trabajo con el siguiente contenido:
+ /tmp/nginx+php
|
+-- nginx
| +-- default.conf
| +-- Dockerfile
|
+-- webapp
| +-- index.php
|
+-- docker-compose.yaml
donde Dockerfile
permite construir una imagen con nginx:
FROM alpine
RUN apk update && apk add nginx && \
ln -s /dev/stdout /var/log/nginx/access.log;\
ln -s /dev/stderr /var/log/nginx/error.log;\
mkdir /run/nginx
EXPOSE 80/tcp
CMD ["nginx", "-g", "daemon off;"]
y una configuración del servidor que nos permita ejecutar PHP:
server {
listen 80;
try_files $uri $uri/ =404;
index index.php;
root /srv/www;
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
include fastcgi.conf;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php:9000;
}
}
Nota
Obsérvese que para conectar el servidor web con el intérprete de PHP, se utiliza como nombre de máquina php. Esto es debido a que la dirección IP de cada máquina es resoluble utilizando el nombre de servicio que se le ha asignado.
El directorio webapp
es el directorio donde se almacenará la aplicación
PHP. Nos limitaremos a utilizar el típico ejemplo:
# echo '<?php phpinfo(); ?>' > webapp/index.php
Y, por último, el docker-compose.yaml
:
version: "3.7"
services:
php:
image: php:fpm-alpine
volumes:
- ./webapp:/srv/www
web:
image: alpine:nginx
build: ./nginx
ports:
- "80:80"
volumes:
- ./webapp:/srv/www
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- php
Nota
Nótese que la primera imagen se obtiene directamente del repositorio gracias a la directiva image. Al indicar sólo su nombre y entiqueta, se obtendrá del registro predefinido. No obstante, puede usarse:
image: docker.io/php:fpm-alpine
La segunda, en cambio, añade la directiva build. Esto significa que la imagen no se descarga sino que se construye a partir de la configuración que se encuentre en el directorio especificado. Además de un directorio local se puede especificar la URL de un repositorio de GitHub:
build: https://github.com/miusuario/repositorio.git
En este caso, image sirve únicamente para darle nombre a la imagen construida.
Para poner en funcionamiento ambos contenedores basta con encontrarse en el
directorio nginx+php
y ejecutar[3]:
# docker-compose up -d
Podemos parar la ejecución de ambos contenedores con:
# docker-compose stop
los cuales quedará listos para una ejecución posterior con:
# docker-compose start
En cambio, si queremos deshacernos de los contenedores tenemos que hacer:
# docker-compose down
lo cual, además, eliminará la red bridge asociada y los propios contenedores.
No así, las imágenes que se hayan generado, que de hecho no se volverán a
generar[4], aunque cambién los Dockerfile
correspondientes, a menos
que específicamente se indique al levantar los servicios:
# docker-compose up --build -d
Sólo hemos definido la sección services, pero si es necesario crear volúmenes de datos, podremos añadir otra sección:
volumes:
socket:
y haber hecho referencia a este volumen en uno de los servicios:
services:
php:
image: php:fpm-alpine
volumes:
- ./webapp:/srv/www
- socket: /var/run/php-fpm
Por otro lado, no hemos establecido ninguna regla sobre la red de ambos contenedores. Por defecto, docker-compose crea una red de usuario de tipo bridge (de ahí que los nombres de las máquinas sean resolubles).
Sin embargo podríamos haber redefinido la red que se crea por defecto añadiendo otra sección principal (al nivel de services o volumes):
networks:
default:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.22.0.0/16
La red es la red predeterminada porque la hemos llamado «default». Podríamos haberle dado otro nombre (p.e. «mired») y haber especificado en los servicios que esa era la red que usan:
services:
php:
image: php:fpm-alpine
volumes:
- ./webapp:/srv/www
- socket: /var/run/php-fpm
networks:
- mired
Notas al pie