Схема CI/CD процессов

Continuous Delivery могут называть также CDL, а Continuous Deployment - CDP.

Pipeline

Пример пайплайна сборки docker образа после тестов:

Как сохранять состояния между job-ами: Caching in GitLab CI/CD

Ручной запуск job-а

Задачи по типу docker build не всегда нужно повторять. Банально, не всегда есть смысл заново собирать образ, который уже собран и ждем в Container Registry.
Поэтому такие задачи можно оставить на ручной запуск в gitlab-ci.yml:

script:
	- echo test
when: manual

Predefined CI/CD variables

Предопределенные переменные генерируются при запуске пайплайна в рамках этого пайплайна и каждого job-а.

Predefined variable examples


Сервисы

Сервис - это дополнительный контейнер, поднимающийся вместе с основным в момент выполнения задачи.

Серсивы имеют доступ к переменным.
Сервисы можно конфигурировать:

services:
	- name: custom-postgres:11.7
	  alias: shmostgress
	  entrypoint: ["/usr/local/bin/db-postgres"]
	  command: ["start"]

Можно юзать разные версии одного сервиса, например, для тестирования.

docker:dind или “Docker in Docker”

Образ docker:dind (dind - docker in docker) нужен, если хочешь работать с docker и его командами, но не в хостовой системе.

dind share-ит свой сокет наружу, что позволяет, юзать команды docker из других контейнеров.

Самый частый use case - сборка образов, каждый из которых сохраняется локально и если каждый образ собирать на хостовой системе - там просто кончится место на диске, потому что образы заполнят всё. Поэтому берется контейнер с docker-ом, внутри которого уже собирается образ, который сохранится в рамках этого контейнера.

Вызывается как сервис:

...
	services:
		- docker:dind
...

GitLab Container Registry

Контейнеры это stateless и immutable инфраструктура:

  • stateless: контейнеры не сохраняют состояние между запусками (в этом же и идея docker-а, что все данные должны быть внешними / в виде volume-ов).
  • immutable: контейнеры неизменяемы после создания, то есть, для обновлений создаются новые образы, а старые удаляются.
    Это к тому, что всё, что произошло внутри контейнера будет потеряно, когда контейнер выключится. То есть, образ, собранный внутри контейнера, не сохранится никак в системе после выключения этого контейнера.

Поэтому нужен какой-нибудь Container Registry 👇.

GitLab Container Registry - доп. функция GitLab-а, позволяющая хранить docker-образы для проектов.

  • Для каждого проекта - свой реестр
  • Доступ по учетке в GitLab
  • Доступ через CI по токену

Для того, чтобы собрать и запушить образ в GitLab Container Registry нужно:

  1. docker login
  2. docker build
  3. docker push
Docker build:
	stage: build
	image: docker:stable
    services:
	    - docker:dind
    script:
        - docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
        - docker build -t ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:${CI_COMMIT_REF_SLUG} .
        - docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:${CI_COMMIT_REF_SLUG}
        - docker tag ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:${CI_COMMIT_REF_SLUG} ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:latest
        - docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:latest
    tags:
        - docker
login
docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}

логин, где gitlab-ci-token - автоматически созданный юзер для тебя с такими же правами, как у тебя.

build и push
docker build -t ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:${CI_COMMIT_REF_SLUG} .
docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:${CI_COMMIT_REF_SLUG}

Используются GitLab CI predefined variables, чтобы собрать и запушить образ, который после этого будет доступен по подобной ссылке: registry.gitlab.com/arut-plus/docker-cicd-django/docker-cicd-django:master.
Но нужен образ с тегом latest, поэтому следующий шаг это реализует.

tag и push
docker tag ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:${CI_COMMIT_REF_SLUG} ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:latest
docker push ${CI_REGISTRY}/${CI_PROJECT_PATH}/${CI_PROJECT_NAME}:latest

Заново тегируется образ и опять пушится. В итоге в registry запушится один образ с двумя тегами.
В итоге, можно будет юзать ссылку на образ с тегом latest: registry.gitlab.com/arut-plus/docker-cicd-django/docker-cicd-django:latest

Ссылку на образ можно скопировать в Deploy > Container Registry.

В docker-compose.yml это будет выглядеть, например, так:

services:
  web:
    image: registry.gitlab.com/arut-plus/docker-cicd-django/docker-cicd-django:latest

При чем, при деплое, а конкретнее, до docker-compose, можно заменить latest на имя ветки такой командой:

- sed s/:latest/:${CI_COMMIT_REF_SLUG}/g -i ./docker-compose.yml
- docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
- docker-compose pull
...

after_script не останавливает пайплайн

Ошибка при выполнении кода в блоке after_script не пометит пайплайн как failed. Это такое исключение, которое отличает after_script от before_script и script.


CI/CD

Code reuse in pipeline development или includes

В gitlab-ci.yml можно включать блоки кода из других yaml файлов, находящихся в current или других репах.

Репозиторий должен быть частью группы

Если собираешься включать файлы из других репозиториев - последние должны быть частью группы в GitLab. Связано это с отличиями в правах и доступе по сравнению с личными репозиториями.
Есть, конечно, обходные пути, типа:

include:
	- remote: 'https://raw.githubusercontent.com/user/repository/branch/.some.yml'

но с таким подходом можно столкнуться с проблемами доступа через HTTP/S соединение и ограничениями контроля версий. Поэтому юзай include:project:

include:
	- project: 'my-group/my-project'
	- ref: main
	- file: '/templates/.gitlab-ci-template.yml'

Сохранение результатов в пайплайне

Caching в Gitlab CI/CD

Кеш можно использовать для передачи данных между задачами в рамках одной ветки (key: ${CI_COMMIT_REF_SLUG}), одного коммита, в рамках всех пайплайнов для одного или нескольких (при некоторой настройке) раннеров.

Кеш хранится локально на хосте, где установлен раннер.

Resolve dependencies:
  script:
    - yarn install
  cache:
    key:
      files:
        - yarn.lock
    paths:
      - node_modules

В key, в данном примере, указывается файл, при изменении которого будет создан новый кеш. То есть, пока файл не трогают, используется старый кеш.

Artifacts

Используется для передачи состояния между этапами.
Артефакты можно скачать прямо в GitLab WebUI - поэтому их и юзают для получения, например, тест-репортов или apk-пакетов.
Артефакты сохраняет GitLab - runner здесь не при чем, ибо это зона ответственности самой платформы (GitLab).

pdf:
  script: xelatex mycv.tex
    artifacts:
	  paths:
	    - mycv.pdf
	  expired_in: 1 week

В тестах может понадобится еще одна конфигурация:

some test:
  artifacts:
    when: on_failure

CI/CD Variables

Работа с CI/CD variables в не protected ветках

Чтобы CI/CD переменные работали в не protected ветках, проверь, чтобы галочка не стояла на “Protect variable”, который Export variable to pipelines running on protected branches and tags only. Этот flag упомянается в доках здесь.


gitlab-runner