GitLab CI: Чем проще .gitlab-ci.yml, тем лучше
Nov 27, 2017 10:38 · 873 words · 5 minute read
В статье, завершающей цикл о настройке Gitlab CI (continuous integration) была приведена в качестве примера финальная, полностью рабочая версия конфигурационного файла .gitlab-ci.yml
.
С момента ее публикации прошло чуть более четырех месяцев, теперь настройки CI в репозитории проекта немного изменились — давайте разберемся!
Целиком наш конфигурационный файл .gitlab-ci.yml
выглядит так:
image: registry.gitlab.lc:5000/develop/ed/tmaier-dc-ssh:latest
services:
- registry.gitlab.lc:5000/develop/ed/my-docker-dind:latest
variables:
DOCKER_DRIVER: overlay2
NODEIMAGE: registry.gitlab.lc:5000/develop/ed/node-npm-ed-sq:latest
NODEHOTIMAGE: registry.gitlab.lc:5000/develop/ed/node-hot-ed-sq:latest
WORKSPACEIMAGE: registry.gitlab.lc:5000/develop/ed/workspace-ed-sq:latest
DIR_IGNORED: node_modules,bin,data/logs,data/mail,data/migrations,vendor,build,tests,public,docker
STAGING_DC_STACK_FILE: docker-compose-stack-staging.yml
STAGING_ENV_FILE: .env.staging.lc
RELEASE_DC_STACK_FILE: docker-compose-stack-release.yml
RELEASE_ENV_FILE: .env.release
stages:
- build
- test
- release
- deploy
compile:
stage: build
before_script:
- docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
script:
- cp -u .env.develop .env
- docker run -v $(pwd):/var/www/example --rm --user www-data ${WORKSPACEIMAGE} sh -c "composer install --no-dev && ./zf routes compile && ./zf cache clean-modified-content && ./zf user-default-avatar"
- docker run -v $(pwd):/var/www/example --rm ${NODEIMAGE} sh -c "npm install --production --quiet && grunt install-build --no-dev"
- docker run -v $(pwd):/var/www/example --rm ${NODEHOTIMAGE} sh -c "npm run aglio-build"
cache:
key: ${CI_COMMIT_REF_NAME}
paths:
- node_modules/
- vendor/
artifacts:
paths:
- build/api.html
- vendor/
- public/dsd/js/utils/routes.js
- bin/
- .env
- public/default_img/user/en/
- public/default_img/user/ru/
- public/dsd/css/style.css
- public/dsd/css/style.css.map
- public/dsd/mix/tmp-pages-bundle/
- public/dsd/js/bundles/
- public/dsd/js/templates.hbs.js
- public/dsd/js/templates.html.js
- public/dsl/css/
- public/dsl/js/bundles/
- public/vendor/
when: on_success
expire_in: 1h
only:
- develop
- /^release\/.*$/
# - master
testing:
stage: test
dependencies:
- compile
script:
- docker run -v $(pwd):/project --rm -w /project jakzal/phpqa phpmetrics --exclude=${DIR_IGNORED} --report-html=./build/phpmetrics/ .
# - docker run -v $(pwd):/project --rm -w /project jakzal/phpqa phpmd . html codesize --reportfile ./build/phpmd.html --exclude ${DIR_IGNORED}
# - docker run -v $(pwd):/project --rm -w /project jakzal/phpqa phploc --exclude=${DIR_IGNORED} --log-xml=./build/phploc.xml .
# - docker run -v $(pwd):/project --rm -w /project jakzal/phpqa phpcpd --exclude=${DIR_IGNORED} --log-pmd=./build/phpcpd.xml .
artifacts:
paths:
# - build/phpcpd.xml
# - build/phploc.xml
# - build/phpmd.html
- build/phpmetrics/
when: on_success
expire_in: 1h
only:
- develop-disabled
# - /^release\/.*$/
# - master
release-image:
stage: release
dependencies:
- compile
- testing
before_script:
- docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
- BRANCH_NAME=$(echo $CI_COMMIT_REF_NAME | awk -F "/" '{print $1}')
- TAG=$(echo $CI_COMMIT_REF_NAME | awk -F "/" '{print $2}')
- CONTAINER_RELEASE_IMAGE=${CI_REGISTRY}/develop/ed/${BRANCH_NAME}.sources:${TAG:-latest}
script:
- docker build --squash -t ${CONTAINER_RELEASE_IMAGE} -f Dockerfile . --build-arg BRANCH_NAME=${BRANCH_NAME}
- docker push ${CONTAINER_RELEASE_IMAGE}
only:
- develop
- /^release\/.*$/
# - master
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
deploy-to-release:
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/${RELEASE_ENV_FILE} ./docker/${RELEASE_DC_STACK_FILE} provisioner@release.lc:~/docker
- ssh provisioner@release.lc VER=${CI_PIPELINE_ID} DC_FILE=${RELEASE_DC_STACK_FILE} ENV_FILE=${RELEASE_ENV_FILE} BRANCH_NAME=${BRANCH_NAME} TAG=${TAG:-latest} 'bash -s' < ./docker/stack_deploy.sh
environment:
name: review/release
url: https://release.eddev.cf/
only:
- /^release\/.*$/
when: manual
tags:
- deploy
deploy-to-prod:
stage: deploy
before_script:
- mkdir -p ~/.ssh
- echo "${SSH_PRIVATE_KEY}" | tr -d '\r' > ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh-keyscan -p 5022 -H 'example.com' >> ~/.ssh/known_hosts
script:
- git push ssh://git@example.com:/home/git/repo/ed.git HEAD:master
environment:
name: production
url: https://www.example.com/
only:
- master
when: manual
tags:
- deploy
Итак, что же изменилось за 4 месяца:
- на ревью-окружении (задача
deploy-to-review
) docker теперь работает в режиме swarm, в связи с чем полностью изменился скрипт деплоя (об этом отдельная статья); - было развернуто release-окружение (отдельный сервер также с docker в режиме swarm), в связи с чем появилась задача
deploy-to-release
выполняющаяся в ручном режиме; - добавилась задача деплоя на production-сервер (увы, docker пока не внедрен, поэтому
git push
и гитхук на стороне сервера) в ручном режиме; - самая ресурсоемкая и длинная задача (
testing
) по умолчанию отключена. При необходимости ее можно включить и выбрать какие именно тесты нужно запускать (не забываем про полученные на выходе артефакты). К слову, для запуска тестов теперь используется другой docker-контейнер с огромным набором статических тестов; - этапы
spawn
(когда в начале поднимаются контейнеры, описанные в специальном файлеdocker-compose-build.yml
) иcleanup
(когда останавливаем и удаляем запущенные вначале контейнеры) полностью удалены. Теперь сборка проекта (задачаcompile
) выполняется посредствомdocker run ...
, а неdocker exec -T ...
— функциональность та же, экономия времени 30-40 секунд; - добавлены новые переменные в секцию
variables
(и удалены больше неиспользуемые). Имена переменных «говорящие», в комментариях не нуждаются; - так как собираются docker-образы для разных веток, формируем их имена/теги «на лету».
Чуть подробнее стоит остановиться разве что на формировании имен/тегов docker-образов. В репозитории постоянно существует ветка develop (множество коммитов/мерж реквестов ежедневно, после каждого запускается CI и образ с изменениями выкатывается на ревью); релизные ветки создаются примерно раз в 2 недели и выглядят как release/2.35.
Для сборки образов с разными именами получаем имя ветки (все, что до первого символа «/», например develop или release):
BRANCH_NAME=$(echo $CI_COMMIT_REF_NAME | awk -F "/" '{print $1}')
Тегом будет все, что встретится после символа «/» (например, 2.35 или пустая строка):
TAG=$(echo $CI_COMMIT_REF_NAME | awk -F "/" '{print $2}')
Имя docker-образа формируется так:
CONTAINER_RELEASE_IMAGE=${CI_REGISTRY}/develop/ed/${BRANCH_NAME}.sources:${TAG:-latest}
Следует обратить внимание на конструкцию ${TAG:-latest}
, которая значит «использовать значение переменной TAG
, но если такого значения нет (пусто), то использовать latest
». Именно благодаря такому “костылю” решению можно получить правильные имена docker-образов.
Как всегда, буду рад любым дополнениям/замечаниям и постараюсь ответить на ваши вопросы. В следующей статье рассмотрим как изменился скрипт деплоя с переходом на docker в режиме swarm.