Конфиг docker-compose в зависимости от переменных окружения
Есть приложение в docker, в нём есть контейнер nginx, который его и отдаёт.
Приложение не знает об окружении в котором будет использоваться. Возможно, ему нужно пробросить свой порт на хост, чтобы им можно было пользоваться. Или же им будут пользоваться из другого контейнера и можно обойтись без задействования хост-порта.
Хочется в docker-compose.yml иметь возможность:
- Указывать порт в
.env. Здесь всё просто:ports: ${EXTERNAL_PORT}:80 - Либо не указать вообще - тогда порт и не должен прокидываться. Список
portsдолжен быть пустым.
Можно ли как-то это реализовать?
Ответы (2 шт):
Единственное, что придумал, это использовать docker-compose.override.yml.
- В основном docker-compose.yml не указываем вообще порт
- docker-compose.override.yml добавляем в игнор в репе
- В том окружении, где нужно создаём его и прописываем что-то вроде:
version: "3.8"
services:
www:
ports:
- "${HOST_PORT}:80"
UPDATE
Второе решение - генерировать docker-composer.yml. У меня была идея сделать для этого простой шаблонизатор на PHP (так как проект на нём), в комментариях предлагают Makefile.
Проблема в том, что, раз мы используем Docker, то хотелось бы максимально разгрузить хост. Здесь же нам на хосте нужны языки/библиотеки для генерации конфига.
Так что второе решение (на любителя) - создать ещё один docker-compose-проект, предназначенный для генерации конфига для основного проекта :)
В docker-compose.yml нет поддержки условий, поэтому вариант с docker-compose.override.yml, который вы сами
предложили - практически единственный способ добавления гибкости.
Другой вариант - использовать несколько yml-файлов. При запуске docker-compose можно указать несколько yml-файлов,
они будут слиты в единую конфигурацию: docker-compose -f файл1.yml -f файл2.yml ... up ...
Кастомная конфигурация через Makefile
Нужно написать Makefile, который будет создавать кастомную конфигурацию. Таким способом можно реализовать любую логику.
Выглядит это следующим образом:
- есть файл конфигурации
.env, который влияет на конфигурацию - вы делаете
make up, и утилита make запускает цельupиз файлаMakefile, в котором собирается собственный кастомный вариантdocker-compose-dev.yml
Ниже - пример, в котором проверяется .env файл на наличие HOST_PORT и опционально добавляется порт.
Сначала устанавливаем утилиту yq, которая может сливать yaml-файлы:
sudo add-apt-repository ppa:rmescandon/yq
sudo apt-get install yq
Makefile:
####################################
# Список сервисов для запуска
####################################
SERVICES=www mysql nginx
# Запуск всех контейнеров
.PHONY: up
up: docker-compose-dev.yml
# для запуска с кастомной конфигурацией нужно раскомментировать следующую строку, она должна начинаться с Tab
# @docker-compose -f docker-compose-dev.yml up -d ${SERVICES}
# для примера тут запускается diff, который покажет разницу между 2 файлами
@diff docker-compose.yml docker-compose-dev.yml || true
# файл-цель зависит от других файлов и будет пересобираться при их изменении
docker-compose-dev.yml: docker-compose.yml with-port.yml .env
@/usr/bin/bash build-yml.sh
build-yml.sh:
#!/usr/bin/bash
grep -qP '^HOST_PORT=' .env
[ $? -eq 0 ] && \
yq merge docker-compose.yml with-port.yml > docker-compose-dev.yml || \
cp docker-compose.yml docker-compose-dev.yml
with-port.yml:
services:
www:
ports:
- "${HOST_PORT}:80"
.env:
# Порт приложения
HOST_PORT=8080
Проверка: запускаем make up, diff выдает такую разницу:
> ports:
> - "${HOST_PORT}:80"
Если удалить HOST_PORT, порт из yml-файла исчезает.
Я использовал связку bash + yq, но в более сложных кейсах лучше написать скрипт на более функциональном языке.
На enSO есть аналогичный вопрос.