В этой статье рассмотрен новый экспериментальный режим работы werf — Open Source-утилиты для сборки приложений и их деплоя в Kubernetes, — в котором не требуется наличие Docker-сервера. Мы покажем, как убедиться, что этот режим будет работать на вашей машине, соберем первый образ и научимся использовать Kubernetes executor для автоматизации сборки в GitLab CI/CD.

Введение

Для того, чтобы среда сборки была предсказуемой и воспроизводимой, сборку нужно запускать в контейнерах или виртуальных машинах, которые пересоздаются после каждой сборки. В GitLab CI это может достигаться за счет использования для сборок Docker executor'а. Docker executor запускает Docker-контейнер на GitLab Runner'е, внутри которого уже и исполняются shell-инструкции, описанные в GitLab CI. Этими инструкциями в случае использования werf могут быть werf build, werf converge или werf cleanup.

Альтернативой Docker executor'у является Kubernetes executor: он, как и Docker executor, делает среду для запуска werf предсказуемой и воспроизводимой, но при этом запускается в виде Pod'а в Kubernetes. Это может упростить масштабирование, перенеся нагрузку с GitLab Runner'ов в Kubernetes, а также позволит легко использовать другие ресурсы K8s-кластера.

werf предоставляет готовые образы для запуска werf в Docker/Kubernetes, которые мы можем использовать для выполнения инструкций GitLab CI в Docker или Kubernetes executor'е. Как ими воспользоваться — расскажем ниже.

Как это реализовано в werf

На данный момент werf поддерживает два режима работы: с использованием Docker-сервера и без него. Последний режим — экспериментальный.

При работе в последнем режиме werf вместо Docker-сервера и Docker-клиента использует встроенный Buildah в rootless-режиме.

Сейчас в этом режиме поддерживается только сборка с использованием Dockerfile’ов. Сборщик Stapel еще находится на стадии доработки, но в скором времени также будет доступен.

Давайте посмотрим, как воспользоваться новым режимом для запуска сборки внутри Kubernetes без Docker-сервера. Для этого сначала подготовим окружение, убедившись, что система поддерживает использование Buildah, затем попробуем собрать образ новым способом, а следом настроим автоматизацию сборки с помощью Kubernetes executor в GitLab CI/CD.

1. Подготовка окружения

Использование werf без Docker-сервера нативно поддерживается в операционных системах Linux, где работает сразу через Buildah. В Windows и macOS Docker-сервер все же потребуется, т.к. в силу особенностей этих ОС запустить работу с Buildah можно только с помощью слоя совместимости в виде Docker-образа, содержащего Buildah.

Далее будет рассмотрен пример включения и функционирования в этом режиме для Linux.

Перед включением экспериментального режима необходимо убедиться, что ваша система поддерживает rootless-файловую систему OverlayFS. Она доступна по умолчанию в ядре Linux, начиная с версии 5.11 (строго говоря, с версии 5.13, содержащей багфикс для включения rootless OverlayFS в SELinux, но во многих основных дистрибутивах Linux этот багфикс бэкпортировали в ядро 5.11).  Если ядро вашей системы не поддерживает OverlayFS в режиме rootless, будет использоваться fuse-overlayfs.

Далее можно воспользоваться уже подготовленными образами с werf. Образы поддерживаются в актуальном состоянии и обновляются вместе с релизами утилиты: для доставки самой werf используется менеджер trdl в рамках релизного процесса с разными каналами обновлений (от alpha до stable).

Всего доступно несколько образов. Основным можно считать ghcr.io/werf/werf:latest, который представляет собой образ ghcr.io/werf/werf:1.2-stable, собранный на базе Alpine Linux (его полное название — ghcr.io/werf/werf:1.2-stable-alpine). Именно его мы и будем использовать.

Ознакомиться со всеми доступными образами можно здесь.

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

2. Выбор режима работы внутри контейнера

В зависимости от требований пользователя, а также от соответствия системы минимальным требованиям существует три способа использовать werf с Buildah внутри контейнеров:

  1. Использование ядра Linux с поддержкой OverlayFS в режиме rootless. При работе в этом режиме необходимо только отключить профили seccomp и AppArmor в контейнере с werf. 

  2. Использование ядра Linux без поддержки OverlayFS в режиме rootless и привилегированного контейнера. В случае, если ядро системы не поддерживает OverlayFS в rootless-режиме, werf и Buildah будут использовать fuse-overlayfs. Чтобы это стало возможным, необходимо воспользоваться привилегированным контейнером для запуска werf.

  3. Использование ядра Linux без поддержки OverlayFS в режиме rootless и непривилегированного контейнера с дополнительными настройками. По аналогии с предыдущим режимом в качестве файловой системы будет использоваться fuse-overlayfs. Для работы без привилегированного контейнера нужно запустить werf в контейнере со следующими параметрами:

    1. Отключены профили seccomp и AppArmor;

    2. Включено устройство /dev/fuse.

Мы будем использовать наиболее распространенный и желательный режим работы — №1 (непривилегированный контейнер с поддержкой OverlayFS в режиме rootless).

3. Собираем образ без Docker-сервера

Для запуска сборки необходимо выполнить следующую команду:

docker run \
    --security-opt seccomp=unconfined --security-opt apparmor=unconfined \
    ghcr.io/werf/werf:latest werf_command

В результате будет запущен выбранный контейнер ghcr.io/werf/werf:latest, внутри которого будет выполнена указанная команда werf.

4. Настройка сборки в GitLab CI/CD с использованием Kubernetes executor

Настраиваем GitLab runner в Kubernetes

Отредактируем файл /etc/gitlab-runner/config.toml:

[[runners]]
  name = "kubernetes-runner-for-werf"
  executor = "kubernetes"
  ...
  [runners.kubernetes]
    ...
    pod_annotations = ["container.apparmor.security.beta.kubernetes.io/werf-converge=unconfined"]

NB. О дополнительных параметрах Kubernetes executor можно почитать в официальной документации.

Настраиваем доступ к кластеру Kubernetes

Есть два способа настройки доступа к кластеру, в который разворачивается приложение:

  • использование Service Account для Kubenetes executor;

  • использование kubeconfig с соответствующим настройками.

Рассмотрим каждый вариант подробнее.

Service Account

Обратите внимание, что этот метод подходит только в том случае, если Kubernetes executor работает в целевом кластере Kubernetes.

Ниже приведен пример Service Account с именем gitlab-kubernetes-runner-deploy:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab-kubernetes-runner-deploy
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: gitlab-kubernetes-runner-deploy
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: gitlab-kubernetes-runner-deploy
    namespace: default

Скорректируем конфигурацию GitLab-раннера в файле /etc/gitlab-runner/config.toml, чтобы использовать этот Service Account:

[[runners]]
  name = "kubernetes-runner-for-werf"
  ...
  [runners.kubernetes]
    service_account = "gitlab-kubernetes-runner-deploy"
    ...

Kubeconfig

Для использования этого метода необходимо присвоить переменной окружения WERF_KUBECONFIG_BASE64 в GitLab-проекте содержимое файла ~/.kube/config, закодированное в base64. werf автоматически использует эту конфигурацию для подключения в целевому кластеру Kubernetes.

Такой метод хорошо подходит в том случае, если Kubernetes executor и целевой кластер Kubernetes — два разных кластера.

Настраиваем файл gitlab-ci.yml проекта

Остался последний шаг. Ниже приведен пример базового задания по сборке и развертыванию проекта в GitLab CI/CD:

stages:
  - build-and-deploy

Build and deploy application:
  stage: build-and-deploy
  image: ghcr.io/werf/werf
  script:
    - source $(werf ci-env gitlab --as-file)
    - werf converge
  tags: ["kubernetes-runner-for-werf"]

Теперь у нас все готово для запуска сборки внутри контейнера с werf, работающего в кластере Kubernetes.

Выводы

В этой статье мы рассмотрели пример использования werf в GitLab CI/CD без использования Docker-сервера, что позволяет сделать среду сборки предсказуемой и воспроизводимой. Для работы был использован Kubernetes executor, с помощью которого сборка будет запускаться в отдельном Pod’е внутри кластера с помощью уже подготовленного контейнера с werf. 

Также рекомендуем ознакомиться с документацией werf, где есть более подробное описание всех доступных режимов работы werf без Docker-сервера, а также возможные проблемы и способы их решения.

P.S.

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

Комментарии (5)


  1. tchu
    07.02.2022 13:19
    +2

    Не страшно давать контейнеру с Werf права cluster-admin на весь кластер?

    Может хотя бы ограничить каким-либо namespace?


    1. ilya-lesikov
      07.02.2022 18:53
      +3

      Вы правы, лучше будет создать на каждый namespace свой GitLab Kubernetes executor. У каждого executor'а свой service account, который имеет доступ только к одному namespace. Соответственно в CI каждого репозитория для запуска джоб использовать соответствующий namespace'у Kubernetes executor. Executor/runner нужно крепить к конкретным репозиториям, не использовать shared runners.

      Если в один неймспейс деплоите из нескольких репозиториев и хочется дополнительно обезопаситься, можно создавать по несколько SA на неймспейс и тонко нарезать права каждому SA.

      Альтернативно, можно обойтись без создания нескольких Kubernetes executor'ов, пробрасывая $WERF_KUBECONFIG_BASE64 (свой для каждого репозитория, через секретные переменные GitLab) напрямую в werf.

      Текущий вариант проще всего, потому и продемонстрирован, но упомянуть о нормальном разграничении прав стоило. Спасибо, документацию тоже доработаем и обновим.

      Только с динамическими окружениями (напр. ревью-окружения), которые надо создавать и удалять на лету, вероятно таки потребуется ограниченный кластерный доступ.


      1. tchu
        07.02.2022 20:30
        +3

        Запускать несколько Executor'ов (Runner'ов) тоже не обязвтельно.

        При исполнении задач CI есть возможность указать namespace/sa-bearer-token для запуска пода с задачей. И вот к этому sa-bearer-token можно привязать необходимыя для werf права.

        См. подробнее


        1. ilya-lesikov
          08.02.2022 19:28

          Спасибо, хороший способ, его и упомянем в доках.


  1. 1A1A1
    07.02.2022 14:58
    +4

    Что за верфь без докеров? :)