Docker Swarm: stack deploy и env-переменные
Sep 7, 2017 14:52 · 921 words · 5 minute read
Познакомившись с docker, рано или поздно вы начнете использовать оркестраторы Fleet/Nomad/Kubernetes/Aurora/Docker Swarm и т.д. На мой взгляд, самый простой из них — Docker Swarm, который «из коробки» дает возможность развернуть отдельный сервис или целый стек.
Однако при использовании команды docker stack deploy
для развертывания стека сервисов из файла docker-compose.yml
переменные окружения заменяются пустыми значениями, хотя при использовании команды docker-compose up -d
все работает, как ожидается. Давайте разберемся!
В данной статье не рассматривается установка и настройка полноценного кластера Docker Swarm — работаем только с одной нодой и решаем только вопрос проброса переменных окружения при деплое стека.
Считаем, что у вас уже установлен docker
и docker-compose
необходимых версий и включен режим роя (swarm
). В данном примере используются:
docker info
Containers: 16
Running: 16
Paused: 0
Stopped: 0
Images: 181
Server Version: 17.06.0-ce
Storage Driver: aufs
Root Dir: /var/lib/docker/aufs
Backing Filesystem: extfs
Dirs: 280
Dirperm1 Supported: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file logentries splunk syslog
Swarm: active
NodeID: udow4edogefqxcfs6gzkkgh6w
Is Manager: true
ClusterID: ut8uvfxdk1ldgguazc7snuc6u
Managers: 1
Nodes: 1
Orchestration:
Task History Retention Limit: 5
Raft:
Snapshot Interval: 10000
Number of Old Snapshots to Retain: 0
Heartbeat Tick: 1
Election Tick: 3
Dispatcher:
Heartbeat Period: 5 seconds
CA Configuration:
Expiry Duration: 3 months
Force Rotate: 0
Root Rotation In Progress: false
Node Address: 192.168.0.34
Manager Addresses:
192.168.0.34:2377
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: cfb82a876ecc11b5ca0977d1733adbe58599088a
runc version: 2d41c047c83e09a6d61d464906feb2a2f3c52aa4
init version: 949e6fa
Security Options:
apparmor
seccomp
Profile: default
Kernel Version: 4.4.0-91-generic
Operating System: Ubuntu 16.04.2 LTS
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 7.67GiB
Name: e-lebed.lc
ID: 7VNW:YUXN:EOH2:3JRQ:V7AX:O5OQ:EYJ3:UT7H:JNM3:SGTK:BRIP:DHKB
Docker Root Dir: /var/lib/docker
Debug Mode (client): false
Debug Mode (server): false
Username: ealebed
Registry: https://index.docker.io/v1/
Experimental: true
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
WARNING: No swap limit support
docker-compose version
docker-compose version 1.14.0, build c7bdf9e
docker-py version: 2.3.0
CPython version: 2.7.13
OpenSSL version: OpenSSL 1.0.1t 3 May 2016
После инициализации режима роя следует переписать файл docker-compose.yml
минимум на 3-ю версию для развертывания сервисов через команду deploy
. К примеру, ранее мы использовали файл второй версии:
version: '2'
services:
### Applications Code Container #############################
applications:
container_name: application
image: registry.gitlab.lc:5000/develop/ed/develop.sources:latest
### PHP-FPM Container #######################################
php-fpm:
container_name: php-fpm
image: registry.gitlab.lc:5000/develop/ed/php-fpm-ed-sq:latest
volumes_from:
- applications
expose:
- "9000"
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "php-fpm"
### Nginx Server Container ##################################
nginx:
container_name: nginx
image: registry.gitlab.lc:5000/develop/ed/nginx-ed-sq:latest
volumes_from:
- applications
depends_on:
- "php-fpm"
ports:
- "80:80"
- "443:443"
### Redis Container #########################################
redis:
container_name: redis
image: registry.gitlab.lc:5000/develop/ed/redis-ed-sq:latest
volumes:
- redis:/data
ports:
- "${REDIS_PORT}:6379"
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "redis"
### Memcached Container #####################################
memcached:
container_name: memcached
image: registry.gitlab.lc:5000/develop/ed/memcached-ed-sq:latest
volumes:
- memcached:/var/lib/memcached
ports:
- "${MEMCACHED_PORT}:11211"
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "memcached"
### Volumes Setup ###########################################
volumes:
memcached:
driver: "local"
redis:
driver: "local"
То теперь файл третьей версии будет выглядеть так:
version: '3.1'
services:
### Code from branch develop #############################################
applications:
image: registry.gitlab.lc:5000/develop/ed/develop.sources:latest
volumes:
- developcode:/var/www/develop
deploy:
replicas: 1
update_config:
parallelism: 1
delay: 5s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
### PHP-FPM ##############################################################
php-fpm:
image: registry.gitlab.lc:5000/develop/ed/php-fpm-ed-sq:latest
volumes:
- developcode:/var/www/develop
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 5s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "php-fpm"
### Nginx ################################################################
nginx:
image: registry.gitlab.lc:5000/develop/ed/nginx-ed-sq:staging
volumes:
- developcode:/var/www/develop
ports:
- "80:80"
- "443:443"
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 5s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
### Redis ################################################################
redis:
image: registry.gitlab.lc:5000/develop/ed/redis-ed-sq:latest
volumes:
- redis:/data
ports:
- "${REDIS_PORT}:6379"
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 5s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "redis"
### Memcached ############################################################
memcached:
image: registry.gitlab.lc:5000/develop/ed/memcached-ed-sq:latest
volumes:
- memcached:/var/lib/memcached
ports:
- "${MEMCACHED_PORT}:11211"
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 5s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "memcached"
### Volumes Setup ########################################################
volumes:
developcode:
memcached:
driver: "local"
redis:
driver: "local"
Из самого важного — мы больше не можем использовать конструкцию volumes_from
для монтирования данных из Data Only контейнеров, приходится выкручиваться с именованными томами (named volumes
), об этом отдельная подробная статья. Также в режиме роя больше не нужны имена контейнеров (они генерируются автоматически и выглядят примерно так: test-stack_nginx.1.kut1gjutr9zdbo1wdf1biuvti
), не нужно делать expose
для портов, доступных только между контейнерами и появилась большая и гибкая секция deploy
.
В каталоге с вышеприведенным файлом docker-compose.yml
находится также файл с переменными окружения .env
с таким содержимым:
### Graylog ##############################################################
GRAYLOG_ADDR=graylog.lc
### Redis ################################################################
REDIS_PORT=6379
### Memcached ############################################################
MEMCACHED_PORT=11211
При использовании команды docker-compose up -d
переменные читаются из файла и подставляются при старте контейнеров — можно в этом убедиться выполнив docker inspect <имя_контейнера>
, например (вывод сокращен):
...
"HostConfig": {
"Binds": [],
"ContainerIDFile": "",
"LogConfig": {
"Type": "gelf",
"Config": {
"gelf-address": "udp://graylog.lc:12201",
"tag": "redis"
...
"Env": [
"REDIS_PORT=6379",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"REDIS_VERSION=3.2.8",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.8.tar.gz",
"REDIS_DOWNLOAD_SHA1=6780d1abb66f33a97aad0edbe020403d0a15b67f"
],
...
Однако, если мы попробуем развернуть данный стек сервисов с помощью команды deploy
, например:
docker stack deploy --with-registry-auth --compose-file docker-compose.yml test-stack
то вместо ожидаемых значений переменных из файла .env
мы увидим пустоту:
...
"HostConfig": {
"Binds": [],
"ContainerIDFile": "",
"LogConfig": {
"Type": "gelf",
"Config": {
"gelf-address": "udp://:12201",
"tag": "redis"
...
"Env": [
"REDIS_PORT=",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"REDIS_VERSION=3.2.8",
"REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.8.tar.gz",
"REDIS_DOWNLOAD_SHA1=6780d1abb66f33a97aad0edbe020403d0a15b67f"
],
...
Точно такой же результат будет если передавать значения в контейнер с помощью секции environment
, о чем пишут здесь.
Один из вариантов решения — не использовать переменные и сразу захардкодить все в docker-compose.yml
, но иногда вследствие архитектурных решений обойтись без переменных нельзя. В таком случае предлагаю использовать следующий «велосипед» (надежный и проверенный):
env $(cat .env | grep ^[A-Z] | xargs) docker stack deploy --with-registry-auth --compose-file docker-compose.yml test-stack