Как стать автором
Обновить
Флант
DevOps-as-a-Service, Kubernetes, обслуживание 24×7

Организация распределенного CI/CD с помощью werf

Блог компании ФлантСистемы сборкиDevOpsKubernetes


werf — наша Open Source-утилита для сборки и деплоя приложений. Сегодня мы с радостью сообщаем, что werf научилась работать в распределенном режиме, начиная с версии v1.1.10 (доступна в каналах v1.1 alpha, beta, ea и stable). Для его подключения требуется минимум усилий.

Вот некоторые из примечательных особенностей нового режима:

  • хранение сборочных кэш-слоев (стадий) в реестре Docker-образов (stages storage);
  • продвинутое распределенное кэширование сборочных кэш-слоев (стадий) для сборщика stapel;
  • возможность использования произвольного количества runner'ов (постоянных или временных) для запуска werf;
  • эффективный и оптимизированный алгоритм выбора стадий и сборки;
  • подключение к любому экземпляру Kubernetes — единственная внешняя зависимость для этой функции (она необходима для синхронизации процессов werf, работающих в распределенном режиме, и для хранения внутренних кэшей).

Заметим, что сборщик Dockerfile также можно использовать в распределенном режиме, однако он пока не поддерживает продвинутое распределенное кэширование слоев.

Однако начнем с рассказа о том, что такое распределенный режим и какие компоненты необходимы для его реализации. Затем покажем, как включить этот режим и как перевести на него (с локального режима) существующие проекты, использующие werf. Наконец, рассмотрим демо-проект, который в полной мере задействует механизм распределенной сборки werf.

Общая информация


В werf имеется несколько ключевых концепций, связанных с процессом сборки: стадии, образы, хранилище стадий. В документации стадии и образы определяются следующим образом:

Мы предлагаем разделить сборочный процесс на этапы, каждый с четкими функциями и своим назначением. Каждый этап соответствует промежуточному образу, подобно слоям в Docker. В werf такой этап называется стадией, и конечный образ в итоге состоит из набора собранных стадий. Все стадии хранятся в хранилище стадий (stages storage).

Что за хранилище стадий? До версии v1.1.10 на этот вопрос можно было ответить так: «Это просто локальный сервер Docker». Однако начиная с текущего момента, werf позволяет хранить стадии в реестре Docker-образов. Более того, он поддерживает большинство реализаций Docker Registry, доступных сегодня.

Использование Docker Registry в качестве хранилища стадий позволяет проводить распределенную сборку образов на нескольких хостах. Для сборщика stapel, представляющего собой альтернативу Dockerfile со множеством полезных функций, доступно продвинутое кэширование слоев. Для него реализован эффективный и оптимизированный алгоритм выбора стадий и сборки:

  • Уже собранные стадии, имеющиеся в хранилище стадий, будут использованы при сборке новой стадии.
  • Стадия извлекается из хранилища стадий, когда это необходимо для сборки следующей стадии.
  • Стадия, извлеченная из хранилища стадий, останется в локальном кэше Docker-образов (автоматический сборщик мусора удалит наименее востребованные образы).
  • Публикация свежесобранных образов осуществляется гораздо быстрее, поскольку в момент, когда она происходит, Docker Registry (по совместительству хранящий и стадии) уже содержит все стадии образа. Они являются базовыми слоями для публикуемого образа.
  • Для сохранения свежесобранных стадий в хранилище алгоритм сборки использует оптимистическую блокировку: тем самым гарантируется, что только один сборщик сможет сохранить новую стадию, после чего она станет доступной для других процессов сборщика.

Для сборки образов на нескольких хостах можно также использовать и сборщик Dockerfile, однако на данный момент для него не реализовано продвинутое распределенное кэширование слоев (впрочем, это будет сделано).

С появлением распределенного режима werf предлагает два уровня кэширования Docker-образов:

  1. Стадии, хранящиеся в хранилище стадий (Docker-образы в Docker registry).
  2. Локальные стадии (Docker-образы), лежащие на локальных Docker-серверах каждого узла сборки.

Кроме того, имеются образы, опубликованные в репозитории образов, — назовем их разновидность (3).

Кэши нуждаются в периодической очистке. В werf для этого встроена команда werf cleanup, удаляющая Docker-образы (2)-го и (3)-го типов.

Локальные Docker-образы (1) пока приходится убирать вручную (только при использовании распределенного режима). Эти образы можно удалять с помощью любых инструментов (например, docker rmi). Будущие версии werf смогут автоматически удалять эти образы при выполнении связанных со сборкой команд (используя алгоритм на основе LRU с автоматической поддержкой заполненности места файловой системы сборочного узла на уровне 80%).

Также следует отметить, что распределенный режим werf использует стадии из хранилища стадий (Docker Registry) и извлекает только те образы, которые необходимы для сборки нового слоя (скачивает только базовый образ). При этом во время холостых сборок (когда образы в действительности не собираются) образы вообще не извлекаются.

Более подробная информация об алгоритме работы сборщика и архитектуре доступна в документации:


Требования к узлам


Для работы в распределенном режиме werf'у требуется подключение к какому-либо кластеру Kubernetes. Kubernetes будет использоваться для координации множества процессов werf при:

  • выборе и записи стадий в хранилище стадий;
  • публикации образов в репозиторий;
  • одновременном развертывании приложений с нескольких хостов.

Не имеет значения, используется ли данный кластер Kubernetes для деплоя приложения. Единственное требование — один и тот же экземпляр K8s должен использоваться для всего проекта.

Как именно он используется? werf создает ConfigMap cm/werf-PROJECT_NAME в пространстве имен werf-synchronization для каждого проекта. Этот ConfigMap используется для хранения так называемого кэша хранилища стадий и для распределенных блокировок. Для реализации распределенной блокировки в кластере Kubernetes используется Open Source-библиотека lockgate, созданная нами специально для werf.

Различные процессы werf, работающие с одним и тем же проектом, должны использовать единое хранилище стадий и один и тот же экземпляр кластера Kubernetes.

Дополнительную информацию о синхронизации можно найти в документации.

Новые команды для работы со стадиями


В werf реализованы новые команды для работы со стадиями:

  1. werf stages sync — копирует стадии между хранилищами.
  2. werf stages switch-from-local — помогает перевести существующий проект в распределенный режим (подробнее об этом процессе см. ниже).

Как включить распределенный режим


Для использования распределенного режима достаточно указать параметр --stages-storage=DOCKER_REPO_ADDRESS для всех команд werf.

Обратите внимание, что DOCKER_REPO_ADDRESS должен ссылаться на уникальный Docker-репозиторий для данного проекта. Этот репозиторий не может использоваться одновременно для нескольких проектов (хотя один и тот же Docker Registry, конечно, может использоваться несколькими проектами).

Команда werf ci-env, интегрирующая werf в процесс CI/CD, экспортирует переменную WERF_STAGES_STORAGE. Она содержит адрес Docker-репозитория, предназначенного для хранения стадий, и это хранилище по умолчанию будет использоваться при всех последующих вызовах werf. Вот пример этой переменной для GitLab CI/CD: WERF_STAGES_STORAGE=CI_REGISTRY_IMAGE/stages.

Если --stages-storage определён не :local, а как адрес Docker Registry (DOCKER_REPO_ADDRESS), werf автоматически задействует пространство имен Kubernetes werf-synchronization и текущий контекст из kubeconfig'а по умолчанию для подключения к кластеру. При этом пользователь может явно указать произвольное пространство имен с помощью опции --synchronization=kubernetes://NAMESPACE. Дополнительная информация доступна в документации.

Миграция существующего проекта


Проект можно переключить в распределенный режим, если он уже использует werf с локальным хранилищем стадий. Новая версия werf поставляется со специальными инструментами, упрощающими этот процесс. Подробная информация об этом доступна в специальном руководстве по миграции.

Демо-проект


Мы также подготовили демо-проект, чтобы продемонстрировать, как с помощью распределенного режима werf можно собрать приложение в публичном GitLab: symfony-demo.

Ниже приведены шаги по использованию распределенного режима werf:

1. Подготовьте werf.yaml:

Его содержимое
project: symfony-demo
configVersion: 1
---

image: ~
from: ubuntu:16.04
docker:
  WORKDIR: /app
  # Non-root user
  USER: app
  EXPOSE: "80"
  ENV:
    LC_ALL: en_US.UTF-8
ansible:
  beforeInstall:
  - name: "Install additional packages"
    apt:
      state: present
      update_cache: yes
      pkg:
      - locales
      - ca-certificates
  - name: "Generate en_US.UTF-8 default locale"
    locale_gen:
      name: en_US.UTF-8
      state: present
  - name: "Create non-root group for the main application"
    group:
      name: app
      state: present
      gid: 242
  - name: "Create non-root user for the main application"
    user:
      name: app
      comment: "Create non-root user for the main application"
      uid: 242
      group: app
      shell: /bin/bash
      home: /app
  - name: Add repository key
    apt_key:
      keyserver: keyserver.ubuntu.com
      id: E5267A6C
  - name: "Add PHP apt repository"
    apt_repository:
      repo: 'deb http://ppa.launchpad.net/ondrej/php/ubuntu xenial main'
      update_cache: yes
  - name: "Install PHP and modules"
    apt:
      name: "{{`{{packages}}`}}"
      state: present
      update_cache: yes
    vars:
      packages:
      - php7.2
      - php7.2-sqlite3
      - php7.2-xml
      - php7.2-zip
      - php7.2-mbstring
      - php7.2-intl
  - name: Install composer
    get_url:
      url: https://getcomposer.org/download/1.6.5/composer.phar
      dest: /usr/local/bin/composer
      mode: a+x
  install:
  - name: "Install app deps"
    # NOTICE: Always use `composer install` command in real world environment!
    shell: composer update
    become: yes
    become_user: app
    args:
      creates: /app/vendor/
      chdir: /app/
  setup:
  - name: "Create start script"
    copy:
      content: |
        #!/bin/bash
        php -S 0.0.0.0:8000 -t public/
      dest: /app/start.sh
      owner: app
      group: app
      mode: 0755
  - raw: echo `date` > /app/version.txt
  - raw: chown app:app /app/version.txt
git:
- add: /
  to: /app
  owner: app
  group: app

(файл в репозитории)

2. Для распределенного режима werf требуется экземпляр кластера Kubernetes — мы воспользуемся GKE. Подготовьте kube-config для кластера и установите секретную переменную BASE64_KUBECONFIG:

cat .kube/config | base64 -w0 > /tmp/base64_kubeconfig
# copy /tmp/base64_kubeconfig content and set BASE64_KUBECONFIG variable in CI/CD



3. Подготовьте стадию сборки (build) в .gitlab-ci.yml:

stages:
  - build

Build:
  stage: build
  script:
  - export KUBECONFIG=$(mktemp -d)/kubeconfig
  - echo $BASE64_KUBECONFIG | base64 -d -w0 > $KUBECONFIG
  - type multiwerf && source $(multiwerf use 1.1 ea --as-file)
  - type werf && source $(werf ci-env gitlab --as-file)
  - werf build-and-publish
  tags:
  - werf-demo-runner

(файл в репозитории)

В этом примере мы реализовали только стадию сборки. Для полноценного CI/CD дополнительно понадобятся стадии развертывания (deploy), очистки (cleanup) и удаления (dismiss), однако их реализацию оставим за рамками статьи. С полным примером можно ознакомиться в руководстве по использованию GitLab.

4. Убедитесь, что в вашем проекте явно не задан параметр --stages-storage и не прописана переменная окружения WERF_STAGES_STORAGE. Команда werf ci-env установит WERF_STAGES_STORAGE=CI_REGISTRY_IMAGE/stages (в нашем примере это registry.gitlab.com/distorhead/symfony-demo/stages).

5. Проверьте реестр контейнеров на соответствующей странице проекта: symfony-demo/container_registry. Здесь показаны собранные стадии проекта в хранилище стадий Docker Registry:



6. Теперь можно попробовать внести изменения в исходники своего приложений (src/Kernel.php с помощью merge_requests/2) и пересобрать его. Сборочный процесс (build job) берет существующие стадии из Docker Registry (хранилища стадий) и пересобирает только стадию gitLatestPatch:



Подобный вывод означает, что все работает правильно!

Заключение


Распределенный режим — очередная важная веха в развитии проекта werf. Он приносит масштабируемость, требуя при этом минимальных усилий от пользователя.

Распределенный режим доступен с версии werf v1.1.10. В этой статье мы описали, как его включить и как на него перевести существующий проект (уже использующий локальный режим werf). Распределенный режим рекомендуется для систем CI/CD и включается для них по умолчанию.

Попробуйте werf, если еще не сделали это! И следите за новостями: скоро мы опубликуем руководство по полной интеграции werf c GitHub Actions.

P.S.


Читайте также в нашем блоге:

Теги:werfсборкаcontinuous deliveryФлант
Хабы: Блог компании Флант Системы сборки DevOps Kubernetes
Всего голосов 29: ↑29 и ↓0 +29
Просмотры4.4K

Похожие публикации

Лучшие публикации за сутки