Всем привет! С вами снова Виктор Ашакин, DevOps-инженер компании «Флант», и мы продолжаем готовить экосистему управления кодом и его развёртывания в Kubernetes на основе Gitea. В первой части статьи мы настроили Gitea и использовали его как Identity Provider (IdP) в Deckhouse Kubernetes Platform, а также разобрались, как настроить DKP для работы с внешним IdP и разграничить права доступа к ресурсам кластера в платформе.  

В этой части инструкции поговорим о Gitea Actions. Настроим инфраструктуру и Actions CI/CD — всё необходимое перед созданием тестового репозитория и написанием Gitea Actions-пайплайнов.

Подготовка инфраструктуры CI/CD

Перед тем как мы приступим к делу, позвольте минутку теории для начинающих. CI/CD-пайплайн (Continuous Integration & Continuous Delivery) — это сценарий, в котором описаны автоматизация процесса сборки и доставки приложения до среды его работы. В этапы пайплайна могут входить различные задания: тесты, линт, сканирования на уязвимости, очистка сборочной среды и так далее. Как его построить, я расскажу далее.  

Подготовительные работы 

Обычно задачи пайплайнов выполняются отдельной программой — агентом, раннером, билдером, в зависимости от терминологии конкретной CI/CD-системы. Раннер подключается к системе управления Git-репозиториев, получает от неё задания, выполняет их и предоставляет отчёт о выполнении.

В Gitea Actions раннер — это утилита act_runner. Она регистрируется

на сервере Gitea, а затем ожидает и выполняет задания, описанные в пайплайне Gitea Actions. К серверу Gitea может быть подключено сразу несколько раннеров. Их назначение можно гибко разделять по типам заданий, командам пользователей, отдельным проектам или просто для распараллеливания заданий. 

Мы будем собирать и упаковывать код приложения в Docker-контейнеры, а после выкатывать собранное приложение в Deckhouse Kubernetes Cluster. Для этого понадобится раннер-сервер с установленными средствами сборки Docker-контейнеров и их доставки до DKP.

Раннер должен отвечать следующим минимальным требованиям:

  • не менее 2 ядер CPU;

  • не менее 4 ГБ RAM;

  • не менее 50 ГБ дискового пространства (лучше больше, так как Docker-образы порой занимают много места);

  • сконфигурированный доступ по SSH.

Act_runner написан на Go и может работать на большинстве ОС: Linux, Windows, macOS. Так что выбор исходит из потребностей кода приложения и предпочтений инженера. Я предпочитаю Ubuntu Server 22.04. 

Для выполнения задач пайплайнов нам понадобятся Git, Docker Engine, kubectl и werf.

Установим Docker и Git:

# Подключение репозитория
apt update

apt install apt-transport-https ca-certificates curl git
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null
# Установка docker engine
apt update
apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Далее установим kubectl:

apt install apt-transport-https ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -LO https://dl.k8s.io/release/`curl -LS https://dl.k8s.io/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x ./kubectl
mv ./kubectl /usr/local/bin/kubectl
kubectl version --client

Наконец, устанавливаем werf:

sudo su - act_runner
curl -sSL https://werf.io/install.sh | bash -s -- --ci

Настройка act_runner

Создаём пользователя для act_runner и предоставляем ему права на работу с Docker:

adduser --system --shell /bin/bash  --group --disabled-password --home /home/act_runner act_runner
gpasswd -a act_runner docker

Устанавливаем act_runner:

apt install nodejs
# Указана актуальная версия act_runner на момент написания статьи
wget -O act_runner https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64
chmod +x act_runner

В официальной документации о Node.js ничего не сказано. Однако без него в Gitea Actions у меня не появлялась информация о ходе стадий пайплайна. О такой же проблеме есть сообщение на форуме Gitea

Готовим структуру каталогов:

cp act_runner /usr/local/bin/
mkdir /etc/act_runner /var/lib/act_runner
chown -R root:act_runner /etc/act_runner /var/lib/act_runner
chmod 770 /etc/act_runner /var/lib/act_runner

Создаём дефолтный конфигурационный файл:

su -c 'act_runner generate-config > /etc/act_runner/config.yaml' act_runner

В конфигурационном файле act_runner указываем путь к файлу регистрации act_runner и добавляем лейблы для нашего раннера:

vim /etc/act_runner/config.yaml
runner:
...
  file: /etc/act_runner/.runner
...
  labels:
    - "ubuntu-22.04:docker://gitea/etc/act_runner/.runner-images:ubuntu-22.04"
    - "werf"
    # - "можно указать свой тег по вкусу"

С помощью лейблов в CI/CD-пайплайне можно отправлять задачи на разные раннеры. Например, можно разделить раннеры для сборки и доставки или на одном раннере собирать бэкенд, а на другом — фронтенд. В моём случае указан универсальный лейбл werf.

На этом этапе нужно зарегистрировать act_runner на сервере Gitea. В Gitea раннер может быть зарегистрирован на разном уровне использования: глобальный уровень,

уровень организации или уровень отдельного репозитория. Выбираем глобальный уровень.

На сервере Gitea через веб-интерфейс получаем токен для регистрации раннера: Site Administration → Actions → Runners → Create new Runner.

На сервере с act_runner выполняем регистрацию, где указываем домен Gitea и подставляем полученный выше токен:

act_runner register --no-interactive \
  --instance https://<your_gitea_server_domain> \
  --token JgZESdw13qUl0gk0hkL0YfkjnQ9qA0rH2OBnBcUFeoupR \
  --name sandbox-gitea-runner

Переносим регистрационный файл .runner в каталог с настройками:

chown act_runner:act_runner .runner
mv .runner /etc/act_runner/

Создаём юнит systemd:

vim /etc/systemd/system/act_runner.service
[Unit]
Description=Gitea Actions runner
Documentation=https://gitea.com/gitea/act_runner
After=docker.service

[Service]
ExecStart=/usr/local/bin/act_runner daemon --config /etc/act_runner/config.yaml
ExecReload=/bin/kill -s HUP $MAINPID
WorkingDirectory=/var/lib/act_runner
TimeoutSec=0
RestartSec=10
Restart=always
User=act_runner

[Install]
WantedBy=multi-user.target

Инициализируем юнит и проверяем его работу:

systemctl daemon-reload
systemctl enable act_runner --now
systemctl status act_runner.service

Готово, теперь можно посмотреть зарегистрированный раннер в Gitea. Для этого в веб-интерфейсе заходим по пути: Site Administration → Actions → Runners. В графе Status видно, что раннер в текущее время подключён и ожидает заданий.

Подготовка раннера для работы с Kubernetes

Теперь нам нужно настроить доступ к Kubernetes API. Он понадобится для выполнения стадий пайплайнов, в которых предусматривается работа с K8s: например, деплой приложения, создание/удаление ревью окружений, задания по расписанию и тому подобное.

Со стороны кластера мы создаём системного пользователя и предоставляем ему права. Затем генерируем конфигурационный файл kubeconfig, который позволит раннеру управлять кластером через утилиту kubectl.

Настраиваем права доступа. В кластере DKP создаём сервисного пользователя с помощью ресурса ServiceAccount:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: act-runner
  namespace: d8-service-accounts

Генерируем секретный токен:

---
apiVersion: v1
kind: Secret
metadata:
  name: act-runner-token
  namespace: d8-service-accounts
  annotations:
    kubernetes.io/service-account.name: act-runner
type: kubernetes.io/service-account-token

Назначаем права доступа для нашего сервисного пользователя. Для примера создаём пользователя так, как указано ниже, но в целом организация доступа в кластер для раннера — это тема для отдельной статьи.

---
apiVersion: deckhouse.io/v1
kind: ClusterAuthorizationRule
metadata:
  name: act-runner
spec:
  subjects:
  - kind: ServiceAccount
    name: act-runner
    namespace: d8-service-accounts
  accessLevel: SuperAdmin

Создаём файл kubeconfig. На основе созданного сервисного пользователя act-runner сформируем конфигурационный файл для kubectl.

Моя конфигурация стенда предполагает, что act_runner и Deckhouse Kubernetes Cluster находятся в одной локальной сети. Если у вас другая архитектура, процесс формирования конфигурационного файла будет иным, подробности ищите в документации.

1. Заполняем переменные окружения:

export SERVISE_ACCOUNT_TOKEN_NAME=act-runner-token
# Переменные CLUSTER_NAME и USER_NAME могут быть произвольными
export CLUSTER_NAME=charlie-sandbox
export USER_NAME=act-runner.${CLUSTER_NAME}
export CONTEXT_NAME=${CLUSTER_NAME}-${USER_NAME}
export FILE_NAME=kube.config

2. Получаем сертификат CA-кластера Kubernetes:

kubectl get cm kube-root-ca.crt -o jsonpath='{ .data.ca\.crt }' > /tmp/ca.crt

3. Формируем секцию cluster. Для доступа используется IP-адрес API-сервера:

kubectl config set-cluster $CLUSTER_NAME --embed-certs=true \
  --server=https://$(kubectl get ep kubernetes -o json | jq -rc '.subsets[0] | "\(.addresses[0].ip):\(.ports[0].port)"') \
  --certificate-authority=/tmp/ca.crt \
  --kubeconfig=$FILE_NAME

4. Генерируем секцию user с токеном из Secret’а ServiceAccount в файле конфигурации kubectl:

kubectl config set-credentials $USER_NAME \
  --token=$(kubectl -n d8-service-accounts get secret $SERVISE_ACCOUNT_TOKEN_NAME -o json |jq -r '.data["token"]' | base64 -d) \
  --kubeconfig=$FILE_NAME

5. Формируем контекст в файле конфигурации kubectl:

kubectl config set-context $CONTEXT_NAME \
  --cluster=$CLUSTER_NAME --user=$USER_NAME \
  --kubeconfig=$FILE_NAME

6. Устанавливаем сгенерированный контекст как используемый по умолчанию в файле конфигурации kubectl:

kubectl config use-context $CONTEXT_NAME --kubeconfig=$FILE_NAME

Копируем полученный конфигурационный файл на сервер с act_runner:

scp kube.config <your_gitea_runner_server>:~/kube.config

Переходим на раннер-сервер и переносим конфигурационный файл kubectl в каталог пользователя act_runner:

mkdir /home/act_runner/.kube/
mv kube.config /home/act_runner/.kube/config
chown -R act_runner:act_runner /home/act_runner/.kube

Проверяем, работает ли kubectl со сформированным конфигом:

su - act_runner
kubectl get ns

Будет выведен список пространств имён кластера, создадим тестовое пространство имён и под:

kubectl create ns runner-test-ns
kubectl -n runner-test-ns run test-pod
kubectl delete ns runner-test-ns 
namespace "runner-test-ns" deleted--image=nginx
pod/test-pod created

Видим, что тестовый под с nginx работает, удаляем пространство имён:

kubectl delete ns runner-test-ns 
namespace "runner-test-ns" deleted

Ура, с настройками раннера можно закончить! Перейдём к настройке Gitea Actions CI/CD.

Настройка Gitea Actions CI/CD

Перед написанием Gitea Actions-пайплайнов нам нужно создать специального пользователя и сформировать для него токен для работы с встроенным в Gitea container registry. Токен данного пользователя будем использовать в пайплайнах и при скачивании образов контейнеров в кластере Kubernetes.

Создаём пользователя docker_puller: Site administration → Identity & Access → Organization → User Accounts → Create User Account.

Чтобы у созданного пользователя docker_puller был доступ ко всем репозиториям организации, добавляем его в группу Owners своей организации (у меня это team-romeo): Site administration → Identity & Access → Organization → org_name → Teams → Owners → Add Team Member.

Логинимся в Gitea под пользователем docker_puller и выписываем токен для работы с container registry: User → Settings → Applications → Generate New Token.

Укажем права, которые будут доступны пользователю по данному токену:

token Name: registry_api
repository: Read
package: Read and Write

Обратите внимание: новый токен нужно сохранить отдельно, так как он будет недоступен для просмотра после перехода со страницы.

Для возможности использования токена во всех пайплайнах добавляем его в секреты на уровне организации: Organization → Settings → Actions → Secrets WERF_IMAGES_REPO_TOKEN=<your_token_value>

Аналогичным образом в переменные добавляем логин специального пользователя и доменное имя сервера Gitea. Но уже не в секреты, а в обычные переменные: Organization → Settings → Actions → Variables
WERF_IMAGES_REPO_USER=docker_puller
WERF_REPO=your_gitea_domain_name.com

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

Чтобы кластер Kubernetes мог при деплое приложения пулить образы из репозитория, ему нужны логин и пароль от container registry. Для этого создаём в кластере Deckhouse YAML-манифест Secret, в котором указываем адрес нашего container registry, пользователя docker_puller и его токен:

# Создаём в дефолтном пространстве имён Secret с именем gitea-regsecret
kubectl -n default create secret docker-registry gitea-regsecret \
 --docker-server=$WERF_REPO \
 --docker-username=$WERF_IMAGES_REPO_USER \
 --docker-password=$WERF_IMAGES_REPO_TOKEN

В Deckhouse Kubernetes Platform есть незаменимый модуль secret-copier, который автоматически копирует секреты по всем пространствам имён. Для копирования секретов достаточно добавить специальный лейбл на нужный секрет.

Проставим лейблы на наш секрет с доступом к registry:

# Добавляем лейбл для secret-copier
kubectl -n default label secrets gitea-regsecret secret-copier.deckhouse.io/enabled=""

Наш секрет gitea-regsecret будет автоматически скопирован во все пространства имён, подобным образом можно раскидывать секреты SSL-сертификатов.

Промежуточный итог

Теперь всё готово к созданию тестового репозитория и написанию Gitea Actions-пайплайнов! Этим мы займёмся в заключительной части статьи. 

P. S. 

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

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