Обзор задачи
В статье рассматриваются различные варианты настройки CI/CD с использованием GitLab CI/CD и werf.
Конечный pipeline состоит из следующего набора стадий:
build-and-publish— стадия сборки и публикации образов приложения;deploy— стадия деплоя приложения для одного из контуров кластера;dismiss— стадия удаления приложения для review окружения;cleanup— стадия очистки хранилища стадий и Docker registry.
Набор контуров (а равно — окружений GitLab) в кластере Kubernetes может варьироваться в зависимости от многих факторов. В статье будут приведены различные варианты организации окружений для следующих:
Далее последовательно рассматриваются стадии pipeline и различные варианты их организации. Изложение построено от общего к частному. В конце статьи приведены окончательные версии .gitlab-ci.yml для готовых workflow.
Независимо от workflow, все версии конфигурации подчиняются следующим правилам:
- Сборка и публикация выполняется при каждом push в репозитории.
 - Выкат/удаление review окружений:
    
- Выкат на review окружение возможен только в рамках Merge Request (MR).
 - Review окружения удаляются с помощью инструментария GitHub (по кнопке в разделе Environment), автоматически при удалении ветки или отсутствии активности в MR в течение суток.
 
 - Очистка запускается один раз в день по расписанию на master.
 
Для выкатов review окружения и staging и production окружений предложены самые популярные варианты по организации. Каждый вариант для staging и production окружений сопровождается всевозможными способами отката релиза в production.
С общей информацией по организации CI/CD с помощью werf, а также информацией по конструированию своего workflow, можно ознакомиться в общей статье
Требования
- Кластер Kubernetes и настроенный для работы с ним 
kubectl. - GitLab-сервер версии выше 10.x либо учетная запись на gitlab.com.
 - Docker registry, встроенный в GitLab или выделенный.
 - Приложение, которое успешно собирается и деплоится с werf.
 - Понимание основных концептов GitLab CI/CD.
 
Инфраструктура

- Кластер Kubernetes.
 - GitLab со встроенным Docker registry.
 - Узел или группа узлов, с предустановленным werf и зависимостями.
 
Организовать работу werf внутри Docker-контейнера можно, но мы не поддерживаем данный способ. Найти информацию по этому вопросу и обсудить можно в issue. В данном примере и в целом мы рекомендуем использовать shell executor.
Процесс деплоя требует наличия доступа к кластеру через kubectl, поэтому необходимо установить и настроить kubectl на узле, с которого будет запускаться werf.
Если не указывать конкретный контекст опцией --kube-context или переменной окружения WERF_KUBE_CONTEXT, то werf будет использовать контекст kubectl по умолчанию.
В конечном счете werf требует наличия доступа на используемых узлах:
- к Git-репозиторию кода приложения;
 - к Docker registry;
 - к кластеру Kubernetes.
 
Настройка runner
На узле, где предполагается запуск werf, установим и настроим GitLab-runner:
- Создадим проект в GitLab и добавим push кода приложения.
 - Получим токен регистрации GitLab-runner’а:
    
- заходим в проекте в GitLab 
Settings—>CI/CD; - во вкладке 
Runnersнеобходимый токен находится в секцииSetup a specific Runner manually. 
 - заходим в проекте в GitLab 
 - Установим GitLab-runner по инструкции.
 - Зарегистрируем 
gitlab-runner, выполнив шаги за исключением следующих моментов:- используем 
werfв качестве тега runner’а; - используем 
shellв качестве executor для runner’а. 
 - используем 
 - 
    
Добавим пользователя
gitlab-runnerв группуdocker.sudo usermod -aG docker gitlab-runner - Установим Docker и настроим 
kubectl, если они не были установлены ранее. - Установим зависимости werf.
 - 
    
Установим multiwerf пользователем
gitlab-runner:# переключение пользователя sudo su gitlab-runner # добавление ~/bin в PATH export PATH=$PATH:$HOME/bin echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc # установка multiwerf в директорию ~/bin mkdir -p ~/bin cd ~/bin curl -L https://raw.githubusercontent.com/werf/multiwerf/master/get.sh | bash - Скопируем файл конфигурации 
kubectlв домашнюю папку пользователяgitlab-runner.mkdir -p /home/gitlab-runner/.kube && sudo cp -i /etc/kubernetes/admin.conf /home/gitlab-runner/.kube/config && sudo chown -R gitlab-runner:gitlab-runner /home/gitlab-runner/.kube 
После того, как GitLab-runner настроен, можно переходить к настройке pipeline.
Сборка и публикация образов приложения
Build and Publish:
  stage: build-and-publish
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf build-and-publish
  except: [schedules]
  tags: [werf]
Забегая вперед, очистка хранилища стадий и Docker registry предполагает запуск соответствующего задания по расписанию.
Так как при очистке не требуется выполнять сборку образов, то указываем except: [schedules], чтобы стадия сборки не запускалась в случае работы pipeline по расписанию.
Конфигурация задания достаточно проста, поэтому хочется сделать акцент на том, чего в ней нет — явной авторизации в Docker registry, вызова docker login.
В простейшем случае, при использовании встроенного Docker registry, авторизация выполняется автоматически при вызове команды werf ci-env. В качестве необходимых аргументов используются переменные окружения GitLab CI_JOB_TOKEN (подробнее про модель разграничения доступа при выполнении заданий в GitLab можно прочитать здесь) и CI_REGISTRY_IMAGE.
При вызове werf ci-env создаётся временный docker config, который используется всеми командами в shell-сессии (в том числе docker). Таким образов, параллельные задания никак не пересекаются при использовании docker и временный токен в конфигурации не перетирается.
Если необходимо выполнить авторизацию с произвольными учётными данными, docker login должен выполняться после вызова werf ci-env (подробнее про авторизацию в отдельной статье).
Выкат приложения
Прежде всего необходимо описать шаблон, общую часть для деплоя в любой контур, что позволит уменьшить размер файла .gitlab-ci.yml, улучшит его читаемость, а также позволит далее сосредоточиться на workflow.
.base_deploy: &base_deploy
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf deploy --set "global.env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
  dependencies:
    - Build and Publish
  except: [schedules]
  tags: [werf]
При использовании шаблона base_deploy для каждого контура будет определяться своё окружение GitLab:
Example:
  <<: *base_deploy
  environment:
    name: <environment name>
    url: <url>
    ...
  ...
При выполнении задания, werf ci-env устанавливает переменную WERF_ENV в соответствии с именем окружения GitLab (CI_ENVIRONMENT_SLUG).
Для того, чтобы по-разному конфигурировать приложение для используемых контуров кластера в helm-шаблонах можно использовать Go-шаблоны и переменную .Values.global.env, что соответствует значению опции --env или переменной окружения WERF_ENV.
Также в шаблоне используется адрес окружения, URL для доступа к разворачиваемому в контуре приложению, который передаётся параметром global.env_url. 
Это значение может использоваться в helm-шаблонах, например, для конфигурации Ingress-ресурсов.
Далее будут представлены популярные стратегии и практики, на базе которых мы предлагаем выстраивать ваши процессы в GitLab.
Варианты организации review окружения
Как уже было сказано ранее, review окружение — это временный контур, поэтому помимо выката, у этого окружения также должна быть и очистка.
Рассмотрим базовые конфигурации Review и Stop Review заданий, которые лягут в основу всех предложенных вариантов.
Review:
  <<: *base_deploy
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}-${CI_MERGE_REQUEST_ID}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  only: [merge_requests]
Stop Review: 
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
В задание Review описывается выкат review-релиза в динамическое окружение, в основу имени которого закладывается уникальный идентификатор MR. Параметр auto_stop_in позволяет указать период отсутствия активности в MR, после которого окружение GitLab будет автоматически остановлено. Остановка окружения GitLab сама по себе никак не влияет на ресурсы в кластере, review-релиз, поэтому в дополнении необходимо определить задание, которое вызывается при остановке (on_stop). В нашем случае, это задание Stop Review.
Задание Stop Review выполняет удаление review-релиза, а также остановку окружения GitLab (action: stop): werf удаляет helm-релиз и namespace в Kubernetes со всем его содержимым (werf dismiss). Задание Stop Review может быть запущено вручную после деплоя на review контур, а также автоматически GitLab-сервером, например, при удалении соответствующей ветки в результате слияния ветки с master и указания соответствующей опции в интерфейсе GitLab.
Для выполнения werf dismiss требуется werf.yaml, так как в нём содержаться шаблоны имени релиза и namespace. При удалении ветки нет возможности использовать исходники из git, поэтому в задании Stop Review используется werf.yaml, сохранённый при выполнении задания Review, и отключено подтягивание изменений из git (GIT_STRATEGY: none).
Таким образом, по умолчанию закладываем следующие варианты удаления review окружения:
- вручную;
 - автоматически при отсутствии активности MR в течение суток и при удалении ветки.
 
Далее разберём основные стратегии при организации выката review окружения.
Мы не ограничиваем вас предложенными вариантами, даже напротив — рекомендуем комбинировать их и создавать конфигурацию workflow под нужды вашей команды
№1 Вручную
Данный вариант реализует подход описанный в разделе Выкат на review из pull request по кнопке
При таком подходе пользователь выкатывает и удаляет окружение по кнопке в pipeline.
Он самый простой и может быть удобен в случае, когда выкаты происходят редко и review окружение не используется при разработке. По сути, для проверки перед принятием MR.
Review:
  <<: *base_deploy
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}-${CI_MERGE_REQUEST_ID}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  only: [merge_requests]
  when: manual
Stop Review: 
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
№2 Автоматически по имени ветки
Данный вариант реализует подход описанный в разделе Выкат на review из ветки по шаблону автоматически
В предложенном ниже варианте автоматический релиз выполняется для каждого коммита в MR, в случае, если имя git-ветки имеет префикс review-.
Review:
  <<: *base_deploy
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}-${CI_MERGE_REQUEST_ID}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  rules:
    - if: $CI_MERGE_REQUEST_ID && $CI_COMMIT_REF_NAME =~ /^review-/
Stop Review:
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
№3 Полуавтоматический режим с лейблом (рекомендованный)
Данный вариант реализует подход описанный в разделе Выкат на review из pull request автоматически после ручной активации
Полуавтоматический режим с лейблом — это комплексное решение, объединяющие первые два варианта.
При проставлении специального лейбла, в примере ниже review, пользователь активирует автоматический выкат в review окружения для каждого коммита. 
При снятии лейбла происходит остановка окружения GitLab, удаление review-релиза.
Review:
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - >
      # do optional deploy/dismiss
      
      if [ -z "$PRIVATE_TOKEN" ]; then
        echo "\$PRIVATE_TOKEN is not defined" >&2
        exit 1
      fi
      api_url=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}
      if ! response_body=$(curl -sS --header "PRIVATE-TOKEN: ${PRIVATE_TOKEN}" ${api_url}); then
        echo "GET ${api_url}"
        echo ${response_body}
        exit 1
      fi
      if ! echo ${response_body} | jq .labels[] >/dev/null 2>&1; then
        echo "GET ${api_url}"
        echo ${response_body}
        exit 1
      fi
      if echo ${response_body} | jq .labels[] | grep -q '^"review"$'; then
        werf deploy --set "global.env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
      else
        if werf helm get $(werf helm get-release) 2>/dev/null; then
          werf dismiss --with-namespace
        fi
      fi
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}-${CI_MERGE_REQUEST_ID}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  dependencies:
    - Build and Publish
  only: [merge_requests]
  tags: [werf]
Stop Review:
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
Для проверки наличия лейбла у MR используется GitLab API. 
Так как токена CI_JOB_TOKEN недостаточно для работы с private репозиториями, необходимо сгенерировать специальный токен PRIVATE_TOKEN.
Варианты организации staging и production окружений
Предложенные далее варианты являются наиболее эффективными комбинациями правил выката staging и production окружений.
В нашем случае, данные окружения являются определяющими, поэтому названия вариантов соответствуют названиям окончательных готовых workflow, предложенных в конце статьи.
№1 Fast and Furious (рекомендованный)
Данный вариант реализует подходы описанные в разделах Выкат на production из master автоматически и Выкат на production-like из pull request по кнопке
Выкат в production происходит автоматически при любых изменениях в master. Выполнить выкат в staging можно по кнопке в MR.
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [merge_requests]
  when: manual
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only: [master]
Варианты отката изменений в production:
- revert изменений в master (рекомендованный);
 - выкат стабильного MR или воспользовавшись кнопкой Rollback.
 
№2 Push the Button
Данный вариант реализует подходы описанные в разделах Выкат на production из master по кнопке и Выкат на staging из master автоматически
Выкат production осуществляется по кнопке у коммита в master, а выкат в staging происходит автоматически при любых изменениях в master.
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [master]
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only: [master]
  when: manual  
Варианты отката изменений в production:
- по кнопке у стабильного коммита или воспользовавшись кнопкой Rollback (рекомендованный);
 - выкат стабильного MR и нажатии кнопки.
 
№3 Tag everything (рекомендованный)
Данный вариант реализует подходы описанные в разделах Выкат на production из тега автоматически и Выкат на staging из master по кнопке
Выкат в production выполняется при проставлении тега, а в staging по кнопке у коммита в master.
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [master]
  when: manual
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only:
    - tags
Варианты отката изменений в production:
- нажатие кнопки на другом теге (рекомендованный);
 - создание нового тега на старый коммит (так делать не надо).
 
№4 Branch, branch, branch!
Данный вариант реализует подходы описанные в разделах Выкат на production из ветки автоматически и Выкат на production-like из ветки автоматически
Выкат в production происходит автоматически при любых изменениях в ветке production, а в staging при любых изменениях в ветке master.
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [master]
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only: [production]
Варианты отката изменений в production:
- воспользовавшись кнопкой Rollback;
 - revert изменений в ветке production;
 - revert изменений в master и fast-forward merge в ветку production;
 - удаление коммита из ветки production и push-force.
 
Очистка образов
Cleanup:
  stage: cleanup
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - docker login -u nobody -p ${WERF_IMAGES_CLEANUP_PASSWORD} ${WERF_IMAGES_REPO}
    - werf cleanup
  only: [schedules]
  tags: [werf]
В werf встроен эффективный механизм очистки, который позволяет избежать переполнения Docker registry и диска сборочного узла от устаревших и неиспользуемых образов. Более подробно ознакомиться с функционалом очистки, встроенным в werf, можно здесь.
Чтобы использовать очистку, необходимо создать Personal Access Token в GitLab с необходимыми правами. С помощью данного токена будет осуществляться авторизация в Docker registry перед очисткой.
Для вашего тестового проекта вы можете просто создать Personal Access Token а вашей учетной записи GitLab. Для этого откройте страницу Settings в GitLab (настройки вашего профиля), затем откройте раздел Access Token. Укажите имя токена, в разделе Scope отметьте api и нажмите Create personal access token — вы получите Personal Access Token.
Чтобы передать Personal Access Token в переменную окружения GitLab откройте ваш проект, затем откройте Settings —> CI/CD и разверните Variables. Создайте новую переменную окружения WERF_IMAGES_CLEANUP_PASSWORD и в качестве ее значения укажите содержимое Personal Access Token. Для безопасности отметьте созданную переменную как protected.
Стадия очистки запускается только по расписанию, которое вы можете определить открыв раздел CI/CD —> Schedules настроек проекта в GitLab. Нажмите кнопку New schedule, заполните описание задания и определите шаблон запуска в обычном cron-формате. В качестве ветки оставьте master (название ветки не влияет на процесс очистки), отметьте Active и сохраните pipeline.
Полный .gitlab-ci.yml для готовых workflow
Детали workflow
Подробнее про workflow можно почитать в отдельной статье
- Сборка и публикация.
 - Выкат на review контур по стратегии №3 Полуавтоматический режим с лейблом (рекомендованный).
 - Выкат на staging и production контуры осуществляется по стратегии №1 Fast and Furious (рекомендованный).
 - Очистка стадий выполняется по расписанию раз в сутки.
 
.gitlab-ci.yml
stages:
  - build-and-publish
  - deploy
  - dismiss
  - cleanup
Build and Publish:
  stage: build-and-publish
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf build-and-publish
  except: [schedules]
  tags: [werf]
.base_deploy: &base_deploy
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf deploy --set "global.env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
  dependencies:
    - Build and Publish
  tags: [werf]
Review:
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - >
      # do optional deploy/dismiss
      
      if [ -z "$PRIVATE_TOKEN" ]; then
        echo "\$PRIVATE_TOKEN is not defined" >&2
        exit 1
      fi
      api_url=${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}
      if ! response_body=$(curl -sS --header "PRIVATE-TOKEN: ${PRIVATE_TOKEN}" ${api_url}); then
        echo "GET ${api_url}"
        echo ${response_body}
        exit 1
      fi
      if ! echo ${response_body} | jq .labels[] >/dev/null 2>&1; then
        echo "GET ${api_url}"
        echo ${response_body}
        exit 1
      fi
      if echo ${response_body} | jq .labels[] | grep -q '^"review"$'; then
        werf deploy --set "global.env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
      else
        if werf helm get $(werf helm get-release) 2>/dev/null; then
          werf dismiss --with-namespace
        fi
      fi
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}-${CI_MERGE_REQUEST_ID}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  dependencies:
    - Build and Publish
  only: [merge_requests]
  tags: [werf]
Stop Review:
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [merge_requests]
  when: manual
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only: [master]
Cleanup:
  stage: cleanup
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - docker login -u nobody -p ${WERF_IMAGES_CLEANUP_PASSWORD} ${WERF_IMAGES_REPO}
    - werf cleanup
  only: [schedules]
  tags: [werf]
Детали workflow
Подробнее про workflow можно почитать в отдельной статье
- Сборка и публикация.
 - Выкат на review контур по стратегии №1 Вручную.
 - Выкат на staging и production контуры осуществляется по стратегии №2 Push the Button.
 - Очистка стадий выполняется по расписанию раз в сутки.
 
.gitlab-ci.yml
stages:
  - build-and-publish
  - deploy
  - dismiss
  - cleanup
Build and Publish:
  stage: build-and-publish
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf build-and-publish
  except: [schedules]
  tags: [werf]
.base_deploy: &base_deploy
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf deploy --set "global.env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
  dependencies:
    - Build and Publish
  except: [schedules]
  tags: [werf]
Review:
  <<: *base_deploy
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}-${CI_MERGE_REQUEST_ID}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  only: [merge_requests]
  when: manual
Stop Review: 
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [master]
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only: [master]
  when: manual  
Cleanup:
  stage: cleanup
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - docker login -u nobody -p ${WERF_IMAGES_CLEANUP_PASSWORD} ${WERF_IMAGES_REPO}
    - werf cleanup
  only: [schedules]
  tags: [werf]
Детали workflow
Подробнее про workflow можно почитать в отдельной статье
- Сборка и публикация.
 - Выкат на review контур по стратегии №1 Вручную.
 - Выкат на staging и production контуры осуществляется по стратегии №3 Tag everything.
 - Очистка стадий выполняется по расписанию раз в сутки.
 
.gitlab-ci.yml
stages:
  - build-and-publish
  - deploy
  - dismiss
  - cleanup
Build and Publish:
  stage: build-and-publish
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf build-and-publish
  except: [schedules]
  tags: [werf]
.base_deploy: &base_deploy
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf deploy --set "global.env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
  dependencies:
    - Build and Publish
  except: [schedules]
  tags: [werf]
Review:
  <<: *base_deploy
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  only: [merge_requests]
  when: manual
Stop Review: 
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [master]
  when: manual
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only: [tags]
Cleanup:
  stage: cleanup
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - docker login -u nobody -p ${WERF_IMAGES_CLEANUP_PASSWORD} ${WERF_IMAGES_REPO}
    - werf cleanup
  only: [schedules]
  tags: [werf]
Детали workflow
Подробнее про workflow можно почитать в отдельной статье
- Сборка и публикация.
 - Выкат на review контур по стратегии №2 Автоматически по имени ветки.
 - Выкат на staging и production контуры осуществляется по стратегии №4 Branch, branch, branch!.
 - Очистка стадий выполняется по расписанию раз в сутки.
 
.gitlab-ci.yml
stages:
  - build-and-publish
  - deploy
  - dismiss
  - cleanup
Build and Publish:
  stage: build-and-publish
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf build-and-publish
  except: [schedules]
  tags: [werf]
.base_deploy: &base_deploy
  stage: deploy
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf deploy --set "global.env_url=$(echo ${CI_ENVIRONMENT_URL} | cut -d / -f 3)"
  dependencies:
    - Build and Publish
  except: [schedules]
  tags: [werf]
Review:
  <<: *base_deploy
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
    on_stop: Stop Review
    auto_stop_in: 1 day
  artifacts:
    paths:
      - werf.yaml
  rules:
    - if: $CI_MERGE_REQUEST_ID && $CI_COMMIT_REF_NAME =~ /^review-/
Stop Review: 
  stage: dismiss
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - werf dismiss --with-namespace
  environment:
    name: review-${CI_MERGE_REQUEST_ID}
    action: stop
  variables:
    GIT_STRATEGY: none
  dependencies:
    - Review
  only: [merge_requests]
  when: manual
  tags: [werf]
Deploy to Staging:
  <<: *base_deploy
  environment:
    name: staging
    url: http://${CI_PROJECT_NAME}.kube.DOMAIN
  only: [master]
Deploy to Production:
  <<: *base_deploy
  environment:
    name: production
    url: https://www.company.org
  only: [production]
    
Cleanup:
  stage: cleanup
  script:
    - type multiwerf && . $(multiwerf use 1.1 stable --as-file)
    - type werf && source $(werf ci-env gitlab --as-file)
    - docker login -u nobody -p ${WERF_IMAGES_CLEANUP_PASSWORD} ${WERF_IMAGES_REPO}
    - werf cleanup
  only: [schedules]
  tags: [werf]