Docker-Componer proyectos con nombres de servicio idénticos
Utilice el nombre de servicio Docker-Compose sólo si el servicio se encuentra únicamente en la red interna del proyecto Docker-Compose.
Si tenemos proyectos Docker-Compose idénticos con nombres de servicio idénticos, conectados por una red Docker , debemos asegurarnos de que accedemos al servicio adecuado. En una red Docker hay dos formas de acceder a un servicio:
- Por nombre de servicio
- Por nombre de contenedor
Tengo múltiples proyectos Docker-Compose que son casi idénticos, cada proyecto está en su propio directorio y tiene su propio entorno.
Supuse que un servicio dentro de un proyecto Docker-Compose utilizaría siempre otro servicio dentro del mismo proyecto Docker-Compose al utilizar el nombre del servicio (no el nombre del contenedor). Y que produciría un error, si no estaba disponible. Estaba equivocado. Lo que realmente ocurrirá es que podrás acceder al servicio con el mismo nombre en otro proyecto Docker-Compose.
Como siempre, estoy ejecutando esto en Ubuntu 22.04.
Configuración del proyecto
Tenemos dos directorios con nuestros proyectos (casi) idénticos.
├── my_app1
│ ├── docker-compose.yml
│ └── .env
├── my_app2
│ ├── docker-compose.yml
│ └── .env
Los dos archivos 'docker-compose.yml' son idénticos. Los archivos '.env' son diferentes, contienen sólo una variable de entorno, la 'COMPOSE_PROJECT_NAME'. Esta variable no sólo se utiliza para crear los nombres de los contenedores, también la utilizamos dentro de nuestros contenedores para identificar el proyecto Docker-Compose.
Estamos utilizando el 'nicolaka/netshoot' Docker image aquí. Tiene un montón de utilidades útiles, incluyendo un 'netcat', 'nc', que utilizamos para crear daemons.
Estos son los archivos
# my_app1/.env
COMPOSE_PROJECT_NAME=my_app1
y
# my_app2/.env
COMPOSE_PROJECT_NAME=my_app2
y
# docker-compose.yml
version: '3.7'
x-service_defaults: &service_defaults
env_file:
- ./.env
restart: always
services:
app:
<< : *service_defaults
image: nicolaka/netshoot
command: bash -c "echo \"From ${COMPOSE_PROJECT_NAME} - app:\" | /usr/bin/nc -l 80"
networks:
- internal_network
- app_network
web:
<< : *service_defaults
image: nicolaka/netshoot
command: bash -c "echo \"From ${COMPOSE_PROJECT_NAME} - web:\" | /usr/bin/nc -l 81"
networks:
- internal_network
- app_network
networks:
internal_network:
app_network:
external:
name: my_app_network
Antes de empezar, creamos la red externa Docker :
docker network create my_app_network
Ejecutando algunas comprobaciones
Abre algunos terminales e inicia ambos proyectos ejecutando este comando en cada directorio:
docker-compose up
Comprueba que se han creado y están en ejecución los siguientes contenedores:
my_app1_app_1
my_app1_web_1
my_app2_app_1
my_app2_web_1
Ahora en otra terminal ejecuta:
docker run -it --network=my_app_network --rm busybox
Accede a un servicio de nuestros contenedores utilizando el nombre del contenedor:
telnet my_app_1_app_1 80
La respuesta será:
Connected to my_app1_app_1
From my_app1 - app:
Esto funciona como esperábamos. Escribe algún texto. Se emitirá un eco. También puedes comprobarlo en el terminal del proyecto 'my_app1' Docker-Compose.
Generación del error
Primero entramos en el servicio 'app' del proyecto 'my_app1' :
docker exec -it my_app1_app_1 bash
Ahora accedamos al servicio 'web' del mismo proyecto Docker-Compose, utilizando el nombre del servicio:
telnet web 81
La respuesta es:
Connected to web
From my_app1 - web:
Perfecto, esto es lo que esperábamos.
Ahora vamos a hacer desaparecer (temporalmente) el servicio 'web' del proyecto 'my_app1' . Para ello, lo eliminamos.
En otro terminal, vamos al directorio 'my_app1' y tecleamos:
docker-compose stop web
Ahora accedemos de nuevo al servicio 'web' del mismo proyecto:
telnet web 81
La respuesta es:
Connected to web
From my_app2 - web:
Uh-oh. Ahora estamos accediendo al servicio 'web' en 'my_app2' mientras que queríamos acceder al servicio 'web' en 'my_app1'. Ten en cuenta que hay formas (mucho) mejores, pero también mucho más complejas, de simular esto.
Soluciones
Hay dos posibles soluciones aquí, dependiendo de su situación:
Solución 1. Si el servicio 'web' no necesita ser externo, lo eliminamos de la red externa.
Hacemos esto para ambos proyectos (recuerda que los ficheros 'docker-compose.yml' son idénticos):
web:
<< : *service_defaults
image: nicolaka/netshoot
command: bash -c "echo \"From ${COMPOSE_PROJECT_NAME} - web:\" | /usr/bin/nc -l 81"
networks:
- internal_network
#- app_network
Bajamos los contenedores y los volvemos a subir, y volvemos a parar el servicio 'web' del proyecto 'my_app1' .
Ahora, si intentas acceder al servicio 'web' , ver arriba:
telnet web 81
la respuesta será, después de algún tiempo:
telnet: bad address 'web'
Bien. Recibimos un mensaje de error cuando el servicio no está disponible.
Solución 2. Si el servicio 'web' también debe ser externo, usamos siempre el nombre del contenedor del servicio 'web' cuando nos refiramos a él, aunque estemos en el mismo proyecto Docker-Compose
No es una buena solución, pero no queda más remedio. Si el servicio 'app' quiere acceder al servicio 'web' del mismo proyecto Docker-Compose, podemos construir el nombre del contenedor utilizando la variable 'COMPOSE_PROJECT_NAME' :
container_name = <COMPOSE_PROJECT_NAME>_<service name>_1
Para el servicio 'web' en 'my_app1':
web_container_name = my_app1_web_1
Resumen
Me encontré con esto, al pasar de un contenedor a dos contenedores. Empezaron a pasar cosas raras, y los datos se mezclaban entre los contenedores. Después de localizar el problema, lo simulé con el código de arriba. Luego lo arreglé para mi proyecto. Lección aprendida: Nunca supongas.
Enlaces / créditos
Docker - Change pre-defined environment variables in Docker Compose
https://docs.docker.com/compose/environment-variables/envvars
nicolaka / netshoot
https://github.com/nicolaka/netshoot
Leer más
Docker Docker-compose
Recientes
- Uso de Ingress para acceder a RabbitMQ en un clúster Microk8s
- Galería de vídeo simple con Flask, Jinja, Bootstrap y JQuery
- Programación básica de trabajos con APScheduler
- Un conmutador de base de datos con HAProxy y el HAProxy Runtime API
- Docker Swarm rolling updates
- Cómo ocultar las claves primarias de la base de datos UUID de su aplicación web
Más vistos
- Usando PyInstaller y Cython para crear un ejecutable de Python
- Reducir los tiempos de respuesta de las páginas de un sitio Flask SQLAlchemy web
- Usando Python's pyOpenSSL para verificar los certificados SSL descargados de un host
- Conectarse a un servicio en un host Docker desde un contenedor Docker
- Usando UUIDs en lugar de Integer Autoincrement Primary Keys con SQLAlchemy y MariaDb
- SQLAlchemy: Uso de Cascade Deletes para eliminar objetos relacionados