GitLab CI: deploy на docker swarm
Dec 4, 2017 10:39 · 793 words · 4 minute read
В цикле о настройке GitLab continuous integration мы подробно рассматривали этап деплоя docker-образов на ревью окружение; чуть позже скрипт деплоя был изменен для достижения zero downtime (избавления от простоя).
В данной статье пойдет речь о деплое сервисов в docker swarm — давайте разберемся!
Рассматривать вопрос настройки docker swarm кластера мы не будем — это задача тривиальная и подобных примеров полным-полно на просторах интернета. Обязательно стоит ознакомиться с двумя статьями — Docker Swarm: stack deploy и env-переменные и Docker Swarm: stack deploy и именованные тома (named volumes), так будет гораздо понятнее что мы делаем и почему именно так.
Как я уже упоминал в предыдущей статье, деплой docker-образов на ревью (или релиз) окружения выполняются задачей из конфигурационного файла .gitlab-ci.yml
, которая выглядит так:
...
deploy-to-review:
stage: deploy
before_script:
- eval $(ssh-agent -s)
- ssh-add <(echo "${SSH_PRIVATE_KEY}")
- BRANCH_NAME=$(echo $CI_COMMIT_REF_NAME | awk -F "/" '{print $1}')
- TAG=$(echo $CI_COMMIT_REF_NAME | awk -F "/" '{print $2}')
script:
- scp ./docker/${STAGING_ENV_FILE} ./docker/${STAGING_DC_STACK_FILE} provisioner@staging.lc:~/docker
- ssh provisioner@staging.lc VER=${CI_PIPELINE_ID} DC_FILE=${STAGING_DC_STACK_FILE} ENV_FILE=${STAGING_ENV_FILE} BRANCH_NAME=${BRANCH_NAME} TAG=${TAG:-latest} 'bash -s' < ./docker/stack_deploy.sh
environment:
name: review/develop
url: https://www.develop-labs.cf
only:
- develop
tags:
- deploy
...
Перед выполнением основных команд (секция before_script
) с помощью команды ssh-add добавляется приватная часть ssh-ключа пользователя, под которым осуществляется подключение на удаленный сервер (provisioner) и формируются значения переменных ${BRANCH_NAME}
и ${TAG}
.
Основные команды, описанные в секции script
с помощью утилиты scp
копируют на сервер staging.lc (в каталог ~/docker
) файл с переменными окружения и docker-compose-stack-staging.yml
(описание стека docker-контейнеров), после чего передают значения нескольких переменных и выполняют на удаленном сервере скрипт stack_deploy.sh
.
Содержимое скрипта stack_deploy.sh
, который и выполняет основные действия деплоя в docker swarm:
#!/bin/bash
DIRECTORY=~/docker
REGISTRY=registry.gitlab.lc:5000
cd $DIRECTORY;
SERVICES=$(docker service ls --filter name=ed_applications --quiet | wc -l)
docker system prune -f;
docker volume create --name code-$VER
if [[ "$SERVICES" -gt 0 ]]; then
# Update
docker service update \
--detach=false \
--mount-add type=volume,source=code-$VER,target=/var/www/${BRANCH_NAME} \
--constraint-add node.role==manager \
--with-registry-auth \
--image $REGISTRY/develop/ed/${BRANCH_NAME}.sources:${TAG} \
ed_applications
else
# Create
docker service create \
--name=ed_applications \
--mount type=volume,source=code-$VER,target=/var/www/${BRANCH_NAME} \
--constraint node.role==manager \
--with-registry-auth \
$REGISTRY/develop/ed/${BRANCH_NAME}.sources:${TAG}
fi
env $(cat $ENV_FILE | grep ^[A-Z] | xargs) docker stack deploy --with-registry-auth --compose-file $DC_FILE ed;
Как видим, здесь выполняется несколько простых действий:
- проверяем, запущен ли сервис с именем
ed_applications
(data-only контейнер с кодом); - выполняем очистку системы от неиспользуемых docker-контейнеров/образов/томов и т.д.;
- создаем новый именованный том, частью имени будет значение переменной переданной из CI;
- создаем (если его нет) или обновляем (если есть) сервис
ed_applications
с использованием «свежего» контейнера с кодом (имя/тег также получаем из CI — это дает возможность использовать один и тот же скрипт на разных окружениях); - применяем переменные окружения из файла (имя файла получаем из CI) и обновляем стек сервисов, описанных в файле имя которого получаем также из CI (в данном случае
docker-compose-stack-staging.yml
).
Конфигурационный файл docker-compose-stack-staging.yml
выглядит так:
version: '3.1'
services:
### PHP-FPM ############################################################################################################
php-fpm:
image: registry.gitlab.lc:5000/develop/ed/php-fpm-ed-sq:staging
volumes:
- developcode:/var/www/develop
- static:/var/www/static
deploy:
replicas: 4
update_config:
parallelism: 1
delay: 10s
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
- static:/var/www/static
ports:
- "80:80"
- "443:443"
deploy:
replicas: 4
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
### SDCV ##############################################################################################################
sdcv:
image: registry.gitlab.lc:5000/develop/ed/sdcv-ed-sq:latest
ports:
- "9095:9095"
deploy:
replicas: 1
update_config:
parallelism: 1
delay: 1s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "sdcv"
### Redis ##############################################################################################################
redis:
image: registry.gitlab.lc:5000/develop/ed/redis-ed-sq:latest
volumes:
- redis:/data
ports:
- "6379:6379"
deploy:
replicas: 1
update_config:
parallelism: 1
delay: 1s
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:
- "11211:11211"
deploy:
replicas: 1
update_config:
parallelism: 1
delay: 1s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "memcached"
### Websocket ##########################################################################################################
websocket:
image: registry.gitlab.lc:5000/develop/ed/websocket-ed-sq:latest
env_file: .env.staging.lc
ports:
- "8092:8092"
deploy:
replicas: 1
update_config:
parallelism: 1
delay: 1s
restart_policy:
condition: on-failure
placement:
constraints: [node.role == manager]
logging:
driver: gelf
options:
gelf-address: "udp://${GRAYLOG_ADDR}:12201"
tag: "websocket"
### Monitoring: node-exporter ##########################################################################################
nodeexporter:
image: prom/node-exporter:v0.14.0
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '-collector.procfs=/host/proc'
- '-collector.sysfs=/host/sys'
- '-collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
ports:
- "9100:9100"
deploy:
mode: global
restart_policy:
condition: on-failure
labels:
org.label-schema.group: "monitoring"
### Monitoring: cAdvisor ###############################################################################################
cadvisor:
image: google/cadvisor:v0.26.1
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "9200:8080"
deploy:
mode: global
restart_policy:
condition: on-failure
labels:
org.label-schema.group: "monitoring"
### Volumes Setup ######################################################################################################
volumes:
developcode:
external:
name: code-${VER}
memcached:
driver: "local"
redis:
driver: "local"
static:
driver: "local"
driver_opts:
type: nfs
o: addr=192.168.0.218,rw
device: ":/srv/static"
О переходе на docker-compose.yml
третьей версии мы уже упоминали в этой статье, стоит отметить только что в стек сервисов добавились два контейнера для мониторинга (node-exporter
и cadvisor
), которые запускаются в режиме mode: global
(по одному экземпляру на каждой ноде кластера), собирают метрики и отправляют их в Prometheus.