Docker-Композиция проектов с одинаковыми именами сервисов
Используйте имя сервиса Docker-Compose только в том случае, если сервис находится только во внутренней сети проекта Docker-Compose.
Если у нас есть идентичные проекты Docker-Compose с одинаковыми именами сервисов, соединенные сетью Docker , то мы должны убедиться, что обращаемся к нужному сервису. В сети Docker существует два способа доступа к сервису:
- По имени сервиса
- По имени контейнера
У меня есть несколько проектов Docker-Compose, которые практически идентичны, каждый проект находится в своей директории и имеет собственное окружение.
Я предполагал, что сервис внутри проекта Docker-Compose всегда будет использовать другой сервис внутри того же проекта Docker-Compose при использовании имени сервиса (не имени контейнера). И что при его отсутствии будет выдаваться ошибка. Я ошибся. На самом деле произойдет то, что вы сможете получить доступ к сервису с тем же именем в другом проекте Docker-Compose.
Как обычно, я работаю на Ubuntu 22.04.
Настройка проекта
У нас есть две директории с нашими (почти) идентичными проектами.
├── my_app1
│ ├── docker-compose.yml
│ └── .env
├── my_app2
│ ├── docker-compose.yml
│ └── .env
Два файла 'docker-compose.yml' идентичны. Файлы '.env' отличаются тем, что содержат только одну переменную окружения - 'COMPOSE_PROJECT_NAME'. Эта переменная используется не только для создания имен контейнеров, но и внутри наших контейнеров для идентификации проекта Docker-Compose.
Здесь мы используем 'nicolaka/netshoot' Docker image . В нем есть много полезных утилит, в том числе правильно работающий 'netcat', 'nc', который мы используем для создания слушателей daemon.
Вот эти файлы:
# my_app1/.env
COMPOSE_PROJECT_NAME=my_app1
and
# my_app2/.env
COMPOSE_PROJECT_NAME=my_app2
и
# 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
Перед началом работы создадим внешнюю сеть Docker :
docker network create my_app_network
Выполнение некоторых проверок
Откройте несколько терминалов и запустите оба проекта, выполнив в каждой директории следующую команду:
docker-compose up
Проверьте, что следующие контейнеры созданы и запущены:
my_app1_app_1
my_app1_web_1
my_app2_app_1
my_app2_web_1
Теперь в другом терминале выполните команду:
docker run -it --network=my_app_network --rm busybox
Получить доступ к сервису в наших контейнерах, используя имя контейнера:
telnet my_app_1_app_1 80
Ответ будет следующим:
Connected to my_app1_app_1
From my_app1 - app:
Все работает, как и ожидалось. Введите текст. Это будет передано эхом. Проверить это можно также в терминале проекта 'my_app1' Docker-Compose.
Генерация ошибки
Сначала войдем в сервис 'app' проекта 'my_app1' :
docker exec -it my_app1_app_1 bash
Теперь обратимся к сервису 'web' в том же проекте Docker-Compose, используя имя сервиса:
telnet web 81
Ответ:
Connected to web
From my_app1 - web:
Отлично, это то, что мы ожидали.
Теперь давайте сделаем так, чтобы сервис 'web' в проекте 'my_app1' (временно) исчез. Для этого мы его удалим.
В другом терминале переходим в каталог 'my_app1' и набираем:
docker-compose stop web
Теперь снова обращаемся к сервису 'web' в том же проекте:
telnet web 81
В ответ получаем:
Connected to web
From my_app2 - web:
О-о. Теперь мы обращаемся к сервису 'web' в 'my_app2' , тогда как хотели обратиться к сервису 'web' в 'my_app1'. Заметим, что существуют (гораздо) лучшие, но и гораздо более сложные способы моделирования.
Решения
Здесь возможны два варианта решения, в зависимости от ситуации:
Решение 1. Если служба 'web' не нуждается во внешней сети, то удаляем ее из внешней сети.
Делаем это для обоих проектов (помните, что файлы 'docker-compose.yml' идентичны):
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
Уберите контейнеры вниз, а затем снова вверх, и снова остановите службу 'web' проекта 'my_app1' .
Теперь, если попытаться обратиться к службе 'web' , см. выше:
telnet web 81
, то через некоторое время будет получен ответ:
telnet: bad address 'web'
Хорошо. При недоступности сервиса выдается сообщение об ошибке.
Решение 2. Если сервис 'web' также должен быть внешним, то при обращении к нему мы всегда используем имя контейнера сервиса 'web' , даже если мы находимся в одном проекте Docker-Compose.
Это не совсем удачное решение, но другого выхода нет. Если служба 'app' хочет получить доступ к службе 'web' в том же проекте Docker-Compose, мы можем сконструировать имя контейнера с помощью переменной 'COMPOSE_PROJECT_NAME' :
container_name = <COMPOSE_PROJECT_NAME>_<service name>_1
Для сервиса 'web' в проекте 'my_app1':
web_container_name = my_app1_web_1
Резюме
Я столкнулся с этим, когда переходил от одного контейнера к двум. Стали происходить странные вещи, данные между контейнерами перемешивались. Обнаружив проблему, я смоделировал ее с помощью приведенного выше кода. Затем я исправил ее для своего проекта. Извлеченный урок: Никогда не предполагайте.
Ссылки / кредиты
Docker - Change pre-defined environment variables in Docker Compose
https://docs.docker.com/compose/environment-variables/envvars
nicolaka / netshoot
https://github.com/nicolaka/netshoot
Подробнее
Docker Docker-compose
Недавний
- Скрытие первичных ключей базы данных UUID вашего веб-приложения
- Don't Repeat Yourself (DRY) с Jinja2
- SQLAlchemy, PostgreSQL, максимальное количество строк для user
- Показать значения в динамических фильтрах SQLAlchemy
- Безопасная передача данных с помощью шифрования Public Key и pyNaCl
- rqlite: альтернатива dist с высокой степенью готовности и SQLite
Большинство просмотренных
- Используя Python pyOpenSSL для проверки SSL-сертификатов, загруженных с хоста
- Использование UUID вместо Integer Autoincrement Primary Keys с SQLAlchemy и MariaDb
- Подключение к службе на хосте Docker из контейнера Docker
- Использование PyInstaller и Cython для создания исполняемого файла Python
- SQLAlchemy: Использование Cascade Deletes для удаления связанных объектов
- Flask Удовлетворительный запрос API проверка параметров запроса с помощью схем Маршмэллоу