В этой статье рассмотрен новый экспериментальный режим работы 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 внутри контейнеров:
Использование ядра Linux с поддержкой OverlayFS в режиме rootless. При работе в этом режиме необходимо только отключить профили
seccomp
иAppArmor
в контейнере с werf.Использование ядра Linux без поддержки OverlayFS в режиме rootless и привилегированного контейнера. В случае, если ядро системы не поддерживает OverlayFS в rootless-режиме, werf и Buildah будут использовать fuse-overlayfs. Чтобы это стало возможным, необходимо воспользоваться привилегированным контейнером для запуска werf.
-
Использование ядра Linux без поддержки OverlayFS в режиме rootless и непривилегированного контейнера с дополнительными настройками. По аналогии с предыдущим режимом в качестве файловой системы будет использоваться fuse-overlayfs. Для работы без привилегированного контейнера нужно запустить werf в контейнере со следующими параметрами:
Отключены профили
seccomp
иAppArmor
;Включено устройство
/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.
Читайте также в нашем блоге:
tchu
Не страшно давать контейнеру с Werf права cluster-admin на весь кластер?
Может хотя бы ограничить каким-либо namespace?
ilya-lesikov
Вы правы, лучше будет создать на каждый 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.
Текущий вариант проще всего, потому и продемонстрирован, но упомянуть о нормальном разграничении прав стоило. Спасибо, документацию тоже доработаем и обновим.
Только с динамическими окружениями (напр. ревью-окружения), которые надо создавать и удалять на лету, вероятно таки потребуется ограниченный кластерный доступ.
tchu
Запускать несколько Executor'ов (Runner'ов) тоже не обязвтельно.
При исполнении задач CI есть возможность указать namespace/sa-bearer-token для запуска пода с задачей. И вот к этому sa-bearer-token можно привязать необходимыя для werf права.
См. подробнее
ilya-lesikov
Спасибо, хороший способ, его и упомянем в доках.