Для улучшения работы сайта мы используем файлы cookies. Оставаясь на сайте, вы соглашаетесь с политикой обработки персональных данных.

CI/CD и GitOps-доставка приложения в Nova

Данная статья описывает демонстрационный сценарий сборки и доставки приложения в кластер Nova. Nova предоставляет платформенные контроллеры, маршрутизацию, выпуск сертификатов и встроенные механизмы GitOps-доставки на базе FluxCD.

GitLab и Harbor в этом сценарии используются как внешние вспомогательные системы: GitLab хранит исходный код и запускает CI-пайплайн, а Harbor выступает контейнерным registry для собранных образов. Они не являются частью платформы Nova, но нужны для демонстрации полного цикла CI/CD.

Схема CI/CD и GitOps-доставки приложения в Nova

1. Ожидаемый результат

В результате настройки будет реализован следующий процесс:

  1. Разработчик публикует исходный код приложения в GitLab.

  2. GitLab Runner, запущенный в кластере Nova, получает задание из GitLab CI.

  3. CI-пайплайн собирает контейнерные образы приложения и публикует их в Harbor.

  4. FluxCD, встроенный в Nova, отслеживает GitOps-репозиторий и применяет Kubernetes-манифесты в кластер.

  5. FluxCD Image Automation отслеживает новые теги образов в Harbor, обновляет GitOps-репозиторий и инициирует обновление приложения в кластере.

2. Архитектура решения

В демонстрации участвуют четыре логических компонента:

Компонент Роль в сценарии Статус относительно Nova

GitLab

Хранение исходного кода, GitOps-репозитория и запуск CI/CD-пайплайнов

Внешний сервис

GitLab Runner

Исполнитель CI-задач, запускающий job-Pod в Nova

Внешний CI-агент, установленный в кластер

Harbor

Контейнерный registry для хранения собранных образов

Внешний сервис

FluxCD

GitOps-доставка, синхронизация манифестов и автоматизация обновления образов

Встроенный компонент платформы Nova

Nova не заменяет GitLab или Harbor, но предоставляет среду выполнения и встроенный GitOps-механизм, через который приложение доставляется в кластер.

3. Теория

3.1. Continuous Integration

Continuous Integration (CI) — практика автоматической проверки и сборки кода после его изменения. В этом сценарии CI отвечает за сборку контейнерных образов приложения. GitLab запускает CI-пайплайн, а GitLab Runner выполняет задания внутри кластера Nova.

Для сборки образов используется подход, при котором каждая job запускается в отдельном Pod. Runner не требует отдельного выделенного сервера и использует ресурсы кластера Nova.

3.2. Continuous Delivery и GitOps

Continuous Delivery (CD) отвечает за доставку приложения в целевую среду. В платформенных Kubernetes-сценариях часто используется GitOps-подход: желаемое состояние приложения хранится в Git-репозитории, а контроллер в кластере регулярно сверяет фактическое состояние с описанным в Git.

В Nova роль GitOps-контроллера выполняет FluxCD. Он читает ресурс GitRepository, применяет ресурс Kustomization и поддерживает состояние приложения в соответствии с манифестами.

3.3. Почему GitOps удобен для платформы

GitOps делает доставку приложения управляемой и воспроизводимой:

  • история изменений хранится в Git;

  • любое изменение можно проверить через commit diff;

  • откат выполняется возвратом к предыдущей версии манифестов;

  • кластер сам подтягивает изменения из репозитория;

  • доступ к кластеру можно ограничить, оставив разработчикам работу через Git.

3.4. Роль Harbor в демонстрации

Harbor используется как registry для хранения контейнерных образов. CI-пайплайн публикует туда новые версии сервисов, а FluxCD Image Reflector сканирует Harbor и определяет, какие теги доступны для обновления.

В продуктивной среде вместо Harbor может использоваться другой совместимый registry. Для Nova важно, чтобы узлы кластера могли доверять TLS-сертификату registry и скачивать из него образы.

4. Демонстрационное приложение ORIONSHOP

В сценарии используется демонстрационное приложение ORIONSHOP. Приложение состоит из 12 микросервисов, написанных на разных языках программирования, и подходит для демонстрации платформенной доставки микросервисного приложения в Nova.

Исходный код демонстрационного приложения можно скачать или клонировать из публичного репозитория:

git clone https://gitlab-nova.orionsoft.ru/nova-public/nova-demo-app.git

Этот репозиторий можно использовать двумя способами:

  • как исходный код приложения для CI-сценария: код копируется в пользовательский проект GitLab, после чего CI-пайплайн собирает образы и публикует их в Harbor;

  • как GitOps-репозиторий для быстрой демонстрации CD: встроенный в Nova FluxCD читает манифесты напрямую из репозитория и применяет их в кластер.

Для полного сценария CI/CD с ImageUpdateAutomation рекомендуется использовать собственную копию репозитория в GitLab, потому что FluxCD должен иметь право записывать изменения тегов обратно в GitOps-репозиторий.

Если вы используете собственный Git-репозиторий и теги для полного сценария, в файле kustomize/kustomization.yaml раскомментируйте строки:

- components/container-images-tag
- components/container-images-registry

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

- components/image-pull-secret

5. Предварительные требования

Перед началом настройки должны быть доступны:

  • кластер Nova

  • доступ к kubectl и helm

  • внешний GitLab

  • внешний Harbor

  • исходный код демонстрационного приложения

  • сетевой доступ к сервисам, которые используются в сценарии: GitLab, Harbor, Nova, registry с базовыми образами и репозиториям с Helm-чартами, если они не перенесены во внутренний контур

В примерах ниже используются плейсхолдеры:

Группа Плейсхолдер Что заменить

Общие

<user_login>

логин пользователя или namespace в GitLab, например admin

Общие

<workdir>

путь до рабочей директории

GitLab

<gitlab_url>

URL GitLab со схемой, например https://gitlab.mycompany.local

GitLab

<gitlab_host>

hostname GitLab без схемы, например gitlab.mycompany.local

GitLab

<gitlab_project_name>

имя проекта приложения в GitLab, например nova-demo-app

GitLab

<runner_registration_token>

registration token GitLab Runner

GitLab

<gitlab_token_name>

имя GitLab access token

GitLab

<gitlab_access_token>

GitLab access token для FluxCD automation

Harbor

<harbor_host>

hostname Harbor без схемы, например harbor.apps.nova.mycompany.local

Harbor

<harbor_project>

проект Harbor для образов приложения, например nova-demo-app

Harbor

<harbor_robot_user>

имя robot account в Harbor для публикации образов

Harbor

<harbor_robot_secret>

secret robot account в Harbor для публикации образов

Harbor

<harbor_read_user>

учётная запись Harbor для скачивания образов

Harbor

<harbor_read_password>

пароль или token учётной записи Harbor для скачивания образов

Если для демонстрации используется одинаковое имя проекта в GitLab и проекта в Harbor, для <gitlab_project_name> и <harbor_project> можно указать одно значение, например nova-demo-app.

6. Принцип выдачи прав в демонстрации

В примерах ниже для упрощения демонстрации могут использоваться расширенные права, роль Owner, полный доступ robot account, бессрочные токены или учётная запись администратора. Это сделано только для демонстрационного стенда, чтобы показать полный CI/CD-цикл без дополнительных ограничений.

В реальной эксплуатации необходимо выдавать только минимально необходимые права для конкретной задачи: отдельно на чтение и запись в Git-репозиторий, публикацию или скачивание образов из registry, создание объектов в namespace и выполнение GitOps-операций. Выбор конкретных ролей, сроков действия токенов, границ доступа и соответствие требованиям безопасности остаются ответственностью администратора кластера.

7. Настройка Continuous Integration

7.1. Создание репозитория приложения в GitLab

Создайте новый проект в GitLab. В примерах ниже его имя обозначено как <gitlab_project_name>, например nova-demo-app.

Создание проекта в GitLab

После создания проекта инициализируйте локальный репозиторий и отправьте исходный код в GitLab:

cd <gitlab_project_name>
git init --initial-branch=main
git remote add origin <gitlab_url>/<user_login>/<gitlab_project_name>.git
git add .
git commit -m "Initial commit"
git push --set-upstream origin main

После отправки кода проект должен появиться в GitLab.

Проект с исходным кодом в GitLab

7.2. Создание GitLab Runner для проекта

GitLab Runner — компонент, который забирает задания из GitLab CI и выполняет их. В этом сценарии runner будет установлен в Nova и будет запускать CI job в виде Pod.

В GitLab откройте проект и перейдите в раздел Settings → CI/CD → Runners → New project runner.

В поле Tag укажите тег runner, например:

nova-demo-app-runner
Создание project runner в GitLab

В качестве платформы выберите Linux. GitLab покажет registration token. Скопируйте его сразу: обычно он отображается только один раз.

Registration token GitLab Runner

В дальнейшем этот токен будет использован в gitlab-runner-values.yaml:

runnerRegistrationToken: <runner_registration_token>

7.3. Создание namespace для GitLab Runner

Создайте namespace gitlab-runner и добавьте метку, чтобы Nova могла передать в него доверенный CA bundle:

kubectl create namespace gitlab-runner
kubectl label namespace gitlab-runner nova-platform.io/trusted-ca-bundle=enabled
Namespace gitlab-runner с меткой trusted-ca-bundle

Если GitLab использует внутренний или самоподписанный сертификат, добавьте сертификат GitLab в namespace runner как ConfigMap:

kubectl -n gitlab-runner create configmap gitlab-ca \
  --from-file=<workdir>/<gitlab_host>.crt

kubectl -n gitlab-runner get configmap gitlab-ca -o yaml

Это нужно, чтобы job-Pod runner доверяли TLS-сертификату GitLab. Иначе подключение к GitLab может завершаться ошибками проверки сертификата.

7.4. Подготовка values-файла для GitLab Runner

Создайте файл gitlab-runner-values.yaml:

touch <workdir>/gitlab-runner-values.yaml

Добавьте в него настройки runner:

runnerRegistrationToken: <runner_registration_token>
gitlabUrl: "<gitlab_url>"
concurrent: 3

image:
  registry: hub-nova.orionsoft.ru
  image: library/gitlab-runner
  tag: alpine-v18.2.1

runners:
  config: |
    [[runners]]
      [runners.kubernetes]
        helper_image = "hub-nova.orionsoft.ru/library/gitlab-runner-helper:x86_64-v18.2.1"
        [[runners.kubernetes.volumes.config_map]]
          name = "trusted-ca-bundle"
          mount_path = "/certs"
          [runners.kubernetes.volumes.config_map.items]
            "ca-certificates.pem" = "./ca.crt"

volumes:
  - name: gitlab-ca
    configMap:
      name: gitlab-ca
  - name: trusted-ca
    configMap:
      name: trusted-ca-bundle

volumeMounts:
  - name: gitlab-ca
    mountPath: "/etc/ssl/certs/<gitlab_host>.crt"
    subPath: "<gitlab_host>.crt"
  - name: trusted-ca
    mountPath: "/certs/ca.crt"
    subPath: "ca-certificates.pem"

Параметр concurrent ограничивает количество одновременно выполняемых job.

7.5. Установка GitLab Runner в Nova

Если стенд имеет доступ к публичному Helm-репозиторию, добавьте Helm-репозиторий GitLab и установите runner:

helm repo add gitlab https://charts.gitlab.io
helm upgrade --install \
  --namespace gitlab-runner \
  gitlab-runner \
  -f gitlab-runner-values.yaml \
  gitlab/gitlab-runner

В закрытом контуре используйте внутренний Helm-репозиторий или локальный архив чарта, как описано в разделе Доступ в интернет и работа в закрытом контуре.

Проверьте, что Pod runner запущен:

kubectl -n gitlab-runner get pods
Пример вывода:
NAME                             READY   STATUS    RESTARTS   AGE
gitlab-runner-77dc5bd648-m9djl   1/1     Running   0          111s
Pod GitLab Runner в namespace gitlab-runner

7.6. Предоставление прав GitLab Runner

Чтобы runner мог создавать job-Pod и работать с объектами в namespace gitlab-runner, настройте RBAC:

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: gitlab-runner-role
  namespace: gitlab-runner
rules:
  - apiGroups:
      - ''
    resources:
      - '*'
    verbs:
      - '*'
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: gitlab-runner-rb
  namespace: gitlab-runner
subjects:
  - kind: ServiceAccount
    name: default
    namespace: gitlab-runner
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: gitlab-runner-role

Примените манифест:

kubectl apply -f gitlab-runner-rbac.yaml

В демонстрации runner получает расширенные права внутри namespace gitlab-runner, чтобы выполнять все job из сценария. В реальной эксплуатации RBAC для runner нужно ограничить минимально необходимыми действиями и ресурсами. Ответственность за модель доступа runner остаётся на администраторе кластера.

После настройки runner должен отображаться в GitLab как Online.

Runner в статусе Online в GitLab

8. Настройка Harbor

8.1. Создание проекта в Harbor

Harbor будет использоваться как registry, куда CI-пайплайн отправит собранные контейнерные образы.

Откройте Harbor:

https://<harbor_host>/

Авторизуйтесь и создайте новый проект. В качестве имени проекта используйте значение <harbor_project> (в примерах — nova-demo-app) или измените логику .gitlab-ci.yml под свой стенд.

Для демонстрационного стенда можно указать Project quota limits = -1.

Создание проекта в Harbor

8.2. Создание robot account

В демонстрации для публикации образов из CI создаётся robot account в проекте Harbor:

Project -> Robot Accounts -> New Robot Account

Параметры, используемые в демонстрации:

  • имя: <harbor_robot_user>;

  • expiration time: never;

  • права: полный доступ к проекту.

Для реальной эксплуатации нужно выдать этой учётной записи только минимально необходимые права, например только публикацию образов в конкретный проект Harbor. Срок действия robot account и набор разрешений администратор определяет самостоятельно.

Robot account в Harbor

Сохраните Name и Secret. Они понадобятся для переменных GitLab CI.

9. Настройка GitLab CI для публикации образов

9.1. Добавление переменных CI/CD

В проекте GitLab откройте Settings → CI/CD → Variables → Add variable.

Добавьте переменные:

Key Value

CI_REGISTRY

<harbor_host>

REGISTRY_RW_USER

<harbor_robot_user>

REGISTRY_RW_PASSWORD

<harbor_robot_secret>

HARBOR_REPO

<harbor_project>

В демонстрации эти переменные используются без флага Protected, если теги и ветки в демопроекте не защищены. Также отключается Expand variable reference, если значение содержит спецсимволы.

В реальной эксплуатации параметры Protected, Masked, доступность переменных для веток и тегов, а также права учётной записи registry должны быть настроены по принципу минимально необходимых прав. Ответственность за выбор этих ограничений остаётся на администраторе.

Переменные CI/CD в GitLab

9.2. Запуск сборки по тегу

Создайте Git-тег, например v1.0.0:

Code -> Tags -> New tag

Если .gitlab-ci.yml публикует Docker-тег без префикса v, в Harbor появится тег 1.0.0. В примерах FluxCD ниже используется формат тегов без префикса v.

Создание Git-тега в GitLab

После создания тега GitLab запустит CI-пайплайн.

9.3. Проверка pipeline

Откройте раздел Build → Pipelines.

Список pipeline в GitLab

Если runner настроен корректно, pipeline перейдёт в состояние Running.

Pipeline в состоянии Running

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

Лог выполнения CI job

Параллельность выполнения job зависит от параметра concurrent в values-файле runner. Например, при concurrent: 3 одновременно могут выполняться три задания.

9.4. Проверка job-Pod в Nova

Во время выполнения pipeline в namespace gitlab-runner появятся job-Pod runner:

kubectl get pods -n gitlab-runner | egrep runner
Пример вывода:
runner-t3lfvbsx-project-7-concurrent-0-24k5uhic   0/2   Init:0/1   0   7m24s
runner-t3lfvbsx-project-7-concurrent-0-o72ypxme   2/2   Running    0   61s
runner-t3lfvbsx-project-7-concurrent-1-hwdu4vly   0/2   Init:0/1   0   6m54s
runner-t3lfvbsx-project-7-concurrent-1-yknaqnwp   2/2   Running    0   60s

9.5. Проверка образов в Harbor

После успешного завершения pipeline образы должны появиться в Harbor.

Образы приложения в Harbor

На этом этапе CI-часть завершена: исходный код был собран в контейнерные образы, а образы опубликованы во внешний registry.

10. Настройка Continuous Delivery через FluxCD

10.1. Общая логика CD

В Nova FluxCD уже входит в платформу. Поэтому в этом разделе не устанавливается FluxCD как отдельный продукт, а создаются ресурсы, которыми встроенный FluxCD будет управлять:

  • GitRepository — источник манифестов в Git;

  • Kustomization — описание того, что и как применять в кластер;

  • ImageRepository — источник тегов образов в registry;

  • ImagePolicy — правило выбора подходящего тега;

  • ImageUpdateAutomation — автоматическое изменение тегов в GitOps-репозитории.

10.2. Подготовка Kustomize-компонентов

В GitLab-проекте обновите файлы:

kustomize/components/container-images-registry/kustomization.yaml
kustomize/components/container-images-tag/kustomization.yaml

Замените значения MY_CONTAINER_REGISTRY и CONTAINER_IMAGES_TAG на адрес Harbor и тег образа, который был собран в pipeline.

Настройка container-images-registry
Настройка container-images-tag

10.3. Создание namespace приложения

Создайте namespace для приложения:

kubectl create namespace orion-shop

10.4. RBAC для kustomize-controller

FluxCD применяет манифесты в namespace приложения от имени service account. Создайте service account и выдайте ему права на управление нужными объектами:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kustomize-controller-sa
  namespace: orion-shop
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: kustomize-controller-role
  namespace: orion-shop
rules:
  - apiGroups: ["apps", ""]
    resources: ["deployments", "services", "configmaps", "secrets", "serviceaccounts", "replicasets", "pods", "namespaces"]
    verbs: ["get", "list", "create", "update", "delete", "patch"]
  - apiGroups: ["rbac.authorization.k8s.io"]
    resources: ["roles", "rolebindings"]
    verbs: ["get", "list", "create", "update", "delete", "patch"]
  - apiGroups: ["networking.k8s.io"]
    resources: ["ingresses"]
    verbs: ["get", "list", "create", "update", "delete", "patch"]
  - apiGroups: ["kustomize.toolkit.fluxcd.io"]
    resources: ["kustomizations"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["source.toolkit.fluxcd.io"]
    resources: ["gitrepositories"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["autoscaling"]
    resources: ["horizontalpodautoscalers"]
    verbs: ["create", "get", "list", "watch", "update", "patch", "delete"]
  - apiGroups: ["metrics.k8s.io"]
    resources: ["pods"]
    verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: kustomize-controller-binding
  namespace: orion-shop
subjects:
  - kind: ServiceAccount
    name: kustomize-controller-sa
    namespace: orion-shop
roleRef:
  kind: Role
  name: kustomize-controller-role
  apiGroup: rbac.authorization.k8s.io

Примените манифест:

kubectl apply -f rbac.yaml

В демонстрации сервисной учётной записи выдаётся набор прав, достаточный для применения всех объектов приложения. В реальной эксплуатации права service account должны быть ограничены минимально необходимыми ресурсами и действиями.

10.5. Секрет для доступа FluxCD к GitLab

FluxCD должен иметь возможность читать GitOps-репозиторий. Если используется ImageUpdateAutomation, ему также понадобится право записывать изменения обратно в репозиторий.

В GitLab создайте access token:

Settings -> Access tokens -> Add new token

Параметры, используемые в демонстрации:

  • role: Owner;

  • scopes: read_repository, write_repository.

В реальной эксплуатации токену нужно выдавать только минимально необходимые права: чтение репозитория для GitRepository и запись в репозиторий только при использовании ImageUpdateAutomation. Роль, scopes, срок действия токена и границы доступа администратор определяет самостоятельно.

Создание access token в GitLab
Параметры access token в GitLab

Получите CA-сертификат GitLab, если он нужен для проверки TLS:

openssl s_client -showcerts \
  -connect <gitlab_host>:443 \
  -servername <gitlab_host> < /dev/null 2>/dev/null \
  | openssl x509 -outform PEM \
  > <workdir>/<gitlab_host>.crt

Создайте Kubernetes Secret в namespace orion-shop. В примере используется stringData: значения указываются в открытом виде в манифесте, а Kubernetes сохранит их в Secret в Base64.

apiVersion: v1
kind: Secret
metadata:
  name: gitrepo-secret
  namespace: orion-shop
type: Opaque
stringData:
  username: <gitlab_token_name>
  password: <gitlab_access_token>
  ca.crt: |
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----

В поле ca.crt вставьте содержимое файла <workdir>/<gitlab_host>.crt. Если GitLab использует сертификат, которому FluxCD уже доверяет, поле ca.crt можно не добавлять.

Примените secret:

kubectl apply -f flux-gitops-repo-secret.yaml

10.6. Создание GitRepository

Создайте ресурс GitRepository, чтобы FluxCD мог получать манифесты из GitLab:

---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
  name: gitops-repo
  namespace: orion-shop
spec:
  interval: 5m0s
  url: <gitlab_url>/<user_login>/<gitlab_project_name>.git
  ref:
    branch: main
  secretRef:
    name: gitrepo-secret

Примените манифест:

kubectl apply -f gitops-repo.yaml

Проверьте состояние репозитория:

kubectl -n orion-shop get gitrepo
Пример вывода:
NAME          URL                                                            AGE   READY   STATUS
gitops-repo   https://gitlab.mycompany.local/<user_login>/<gitlab_project_name>.git   8s    True    stored artifact for revision 'main@sha1:...'

Для диагностики можно использовать:

kubectl -n orion-shop describe gitrepo gitops-repo

11. Подключение приложения к Harbor

11.1. Создание imagePullSecret

Чтобы Pod приложения могли скачивать приватные образы из Harbor, создайте docker-registry secret:

kubectl create secret docker-registry regcred -n orion-shop \
  --docker-server=<harbor_host> \
  --docker-username=<harbor_read_user> \
  --docker-password=<harbor_read_password> \
  --docker-email=<user_login>@mycompany.local

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

Для скачивания образов из Harbor worker-узлы должны доверять TLS-сертификату registry. Даже если сертификат Harbor доверен внутри Kubernetes-кластера, containerd выполняет pull образов на уровне ОС worker-узла и использует системное хранилище доверенных CA. Поэтому сертификат необходимо добавить в доверенные сертификаты каждого worker-узла и обновить системное хранилище сертификатов.

Пример:

cp harbor-ca.pem /etc/pki/ca-trust/source/anchors/
update-ca-trust enable
update-ca-trust extract
systemctl restart containerd-nova.service

12. Деплой приложения через FluxCD

12.1. Создание Kustomization

Kustomization указывает FluxCD, какие манифесты применять и как подставлять переменные окружения для конкретного стенда. Убедитесь, что spec.sourceRef.name совпадает с именем ресурса GitRepository. Укажите imagepullsecretname, если используется приватный registry.

---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: orion-shop
  namespace: orion-shop
spec:
  serviceAccountName: kustomize-controller-sa
  commonMetadata:
    labels:
      app.kubernetes.io/managed-by: Nova
  interval: 10m
  retryInterval: 30s
  timeout: 5m
  prune: true
  sourceRef:
    kind: GitRepository
    name: gitops-repo
  path: ./kustomize
  postBuild:
    substitute:
      frontendURL: "frontend.apps.<user_login>.mycompany.local"
      loadgeneratorURL: "loadgenerator.apps.<user_login>.mycompany.local"
      certManagerClusterIssuer: "nova-dynamic-internal-cluster-issuer"
      imagepullsecretname: regcred
  healthChecks:
    - apiVersion: apps/v1
      kind: Deployment
      name: loadgenerator
      namespace: orion-shop

Примените манифест:

kubectl apply -f flux-deploy.yaml

Параметр prune: true означает, что FluxCD будет удалять из кластера объекты, которые больше не описаны в GitOps-репозитории. Это помогает поддерживать соответствие фактического состояния кластера желаемому состоянию в Git.

12.2. Проверка запуска приложения

Дождитесь запуска Pod в namespace orion-shop:

kubectl get pods -n orion-shop
Пример вывода:
NAME                                     READY   STATUS    RESTARTS   AGE
adservice-76bdd69666-ckc5j               1/1     Running   0          2m58s
cartservice-66d497c6b7-dp5jr             1/1     Running   0          2m59s
checkoutservice-666c784bd6-4jd22         1/1     Running   0          3m1s
currencyservice-5d5d496984-4jmd7         1/1     Running   0          2m59s
emailservice-667457d9d6-75jcq            1/1     Running   0          3m2s
frontend-6b8d69b9fb-wjqdg                1/1     Running   0          3m1s
loadgenerator-665b5cd444-gwqdq           1/1     Running   0          3m
paymentservice-68596d6dd6-bf6bv          1/1     Running   0          3m
productcatalogservice-557d474574-888kr   1/1     Running   0          3m
recommendationservice-69c56b74d4-7z8r5   1/1     Running   0          3m1s
redis-cart-5f59546cdd-5jnqf              1/1     Running   0          2m58s
shippingservice-6ccc89f8fd-v686r         1/1     Running   0          2m58s

Проверьте объект Kustomization:

kubectl get ks orion-shop -n orion-shop

Получите адрес ingress:

kubectl get ing frontend-ingress -n orion-shop | awk '{print $3}'

Откройте приложение в браузере, например:

https://frontend.apps.<user_login>.mycompany.local/

13. Автоматическое обновление образов через FluxCD

13.1. Назначение ImageRepository и ImagePolicy

FluxCD может не только применять манифесты из Git, но и отслеживать новые версии контейнерных образов.

Для этого используются два ресурса:

  • ImageRepository — сканирует registry и получает список доступных тегов;

  • ImagePolicy — выбирает подходящий тег по заданному правилу, например по SemVer-диапазону.

13.2. Создание ImageRepository и ImagePolicy

Пример для сервиса adservice:

---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
  name: adservice
  namespace: orion-shop
spec:
  image: <harbor_host>/<harbor_project>/adservice
  interval: 5m
  secretRef:
    name: regcred
  certSecretRef:
    name: default-ingress-certificate
---
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
  name: adservice
  namespace: orion-shop
spec:
  imageRepositoryRef:
    name: adservice
  policy:
    semver:
      range: 1.x.x

Примените манифест:

kubectl apply -f flux-image-policy.yaml

Проверьте, что FluxCD нашёл теги в Harbor:

kubectl -n orion-shop describe imagerepository adservice
Пример успешного состояния:
Status:
  Canonical Image Name:  <harbor_host>/<harbor_project>/adservice
  Conditions:
    Message:  successful scan: found 1 tags
    Reason:   Succeeded
    Status:   True
    Type:     Ready
  Last Scan Result:
    Latest Tags:
      1.0.0
    Tag Count:  1

13.3. Создание ImageUpdateAutomation

ImageUpdateAutomation изменяет GitOps-репозиторий: FluxCD находит новый тег, обновляет файл с манифестами и делает commit обратно в Git.

---
apiVersion: image.toolkit.fluxcd.io/v1beta1
kind: ImageUpdateAutomation
metadata:
  name: image-update-app
  namespace: orion-shop
spec:
  interval: 2m
  sourceRef:
    kind: GitRepository
    name: gitops-repo
  git:
    checkout:
      ref:
        branch: main
    commit:
      author:
        email: fluxcdbot@users.noreply.github.com
        name: fluxcdbot
      messageTemplate: '{{range .Updated.Images}}{{println .}}{{end}}'
    push:
      branch: main
  update:
    path: ./
    strategy: Setters

Примените манифест:

kubectl apply -f flux-image-update-automation.yaml

14. Подготовка GitOps-репозитория к автоматическому обновлению

Чтобы FluxCD знал, какой тег можно обновлять, добавьте setter-комментарий рядом с тегом образа в файле kustomize/components/container-images-tag/kustomization.yaml.

Пример:

apiVersion: kustomize.config.k8s.io/v1alpha1
kind: Component
images:
  - name: <harbor_host>/<harbor_project>/adservice
    newTag: 1.0.0 # {"$imagepolicy": "orion-shop:adservice:tag"}
  - name: hub-nova.orionsoft.ru/orion-shop/cartservice

Этот комментарий связывает поле newTag с ImagePolicy orion-shop:adservice. Когда FluxCD найдёт новый подходящий тег, он изменит это поле и отправит commit в GitLab.

15. Проверка автоматического обновления

15.1. Сборка новой версии образа

Внесите изменение в код сервиса adservice и создайте новый Git-тег, например v1.0.1.

Создание Git-тега для новой версии adservice

После успешного завершения CI-пайплайна в Harbor должны появиться новые образы. Если .gitlab-ci.yml удаляет префикс v, новый Docker-тег будет выглядеть как 1.0.1.

15.2. Проверка обнаружения нового тега

Проверьте ImageRepository:

kubectl -n orion-shop describe imagerepository adservice
Пример событий:
Events:
  Type    Reason     Age                  From                        Message
  ----    ------     ----                 ----                        -------
  Normal  Succeeded  21m                  image-reflector-controller  successful scan: found 1 tags
  Normal  Succeeded  6m27s (x3 over 16m)  image-reflector-controller  no new tags found, next scan in 5m0s
  Normal  Succeeded  86s                  image-reflector-controller  successful scan: found 2 tags

15.3. Проверка выбранного тега

Проверьте ImagePolicy:

kubectl describe imagepolicy adservice -n orion-shop
Пример событий:
Events:
  Type     Reason              Age                    From                        Message
  ----     ------              ----                   ----                        -------
  Warning  DependencyNotReady  23m (x2 over 23m)      image-reflector-controller  referenced ImageRepository has not been scanned yet
  Normal   Succeeded           3m15s (x12 over 23m)   image-reflector-controller  Latest image tag for '<harbor_host>/<harbor_project>/adservice' resolved to 1.0.0
  Normal   Succeeded           3m14s (x2 over 3m14s)  image-reflector-controller  Latest image tag for '<harbor_host>/<harbor_project>/adservice' updated from 1.0.0 to 1.0.1

15.4. Проверка обновления Pod

Проверьте, что приложение запускается из нового образа:

kubectl -n orion-shop describe pod <adservice_pod_name>
Пример события:
Normal  Pulling  4m46s  kubelet  Pulling image "<harbor_host>/<harbor_project>/adservice:1.0.1"

15.5. Проверка commit в GitLab

В GitLab откройте GitOps-репозиторий и проверьте, что FluxCD изменил тег образа в файле Kustomize.

Commit FluxCD в GitOps-репозитории

Это подтверждает полный GitOps-цикл:

  1. CI собрал новый образ.

  2. Образ был опубликован в Harbor.

  3. FluxCD обнаружил новый тег.

  4. FluxCD обновил GitOps-репозиторий.

  5. Nova применила обновлённое состояние приложения.

16. Диагностика типовых проблем

16.1. Runner не появляется в GitLab как Online

Проверьте:

kubectl -n gitlab-runner get pods
kubectl -n gitlab-runner logs deploy/gitlab-runner

Частые причины:

  • неверный runnerRegistrationToken;

  • runner не доверяет сертификату GitLab;

  • GitLab недоступен из кластера;

  • неверно указан gitlabUrl.

16.2. Pipeline зависает в Pending

Проверьте, что runner имеет нужный tag и что .gitlab-ci.yml использует этот tag. Также убедитесь, что runner не отключён в настройках проекта.

16.3. Образы не публикуются в Harbor

Проверьте переменные GitLab CI:

  • CI_REGISTRY;

  • REGISTRY_RW_USER;

  • REGISTRY_RW_PASSWORD;

  • HARBOR_REPO.

Также проверьте права robot account в Harbor.

16.4. Pod не может скачать образ из Harbor

Проверьте:

kubectl -n orion-shop describe pod <pod_name>

Типовые причины:

  • отсутствует или неверно указан imagePullSecret;

  • worker-узел не доверяет сертификату Harbor;

  • образ или тег отсутствует в Harbor;

  • у robot account нет прав на чтение проекта Harbor.

16.5. GitRepository не переходит в Ready

Проверьте:

kubectl -n orion-shop describe gitrepo gitops-repo

Типовые причины:

  • неверный URL репозитория;

  • неверный secret с access token;

  • FluxCD не доверяет сертификату GitLab;

  • токен не имеет прав на чтение репозитория.

16.6. ImageUpdateAutomation не делает commit

Проверьте:

kubectl -n orion-shop describe imageupdateautomation image-update-app
kubectl -n orion-shop describe imagepolicy adservice
kubectl -n orion-shop describe imagerepository adservice

Частые причины:

  • в GitOps-файле не добавлен setter-комментарий {"$imagepolicy": …​};

  • access token не имеет права write_repository;

  • новая версия образа не подходит под SemVer policy;

  • ImageRepository не может прочитать теги из Harbor.

17. Результат

После выполнения настройки получается демонстрационный CI/CD-контур для Nova:

  • GitLab хранит исходный код и запускает CI pipeline;

  • GitLab Runner выполняет сборку внутри Nova;

  • Harbor хранит контейнерные образы;

  • FluxCD, встроенный в Nova, доставляет приложение в кластер по GitOps-модели;

  • Image Automation обновляет версии образов через commit в GitOps-репозиторий.

Такой сценарий демонстрирует GitOps: разработчик работает с Git и pipeline, а Nova обеспечивает безопасное выполнение рабочей нагрузки, применение манифестов, маршрутизацию, сертификаты и поддержание желаемого состояния приложения.

18. Доступ в интернет и работа в закрытом контуре

Демонстрационный сценарий может выполняться как в среде с доступом к внешним ресурсам, так и в закрытом контуре. Под закрытым контуром понимается среда, где кластер Nova, GitLab, Harbor, рабочие станции администраторов и CI/CD-процессы не имеют прямого выхода в интернет, а все необходимые зависимости заранее размещаются во внутренних репозиториях и registry.

GitLab и Harbor в этой статье называются внешними сервисами относительно платформы Nova, но это не означает, что они обязательно должны находиться в интернете. В закрытом контуре они обычно размещаются внутри периметра организации и доступны только по внутренним DNS-именам и маршрутам.

18.1. Что подготовить для закрытого контура

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

В этом разделе дополнительно используются плейсхолдеры:

Плейсхолдер Что заменить

<internal_helm_repo>

URL внутреннего Helm-репозитория

<chart_version>

версия Helm-чарта GitLab Runner

Минимально нужно подготовить:

Что подготовить Где должно быть размещено Зачем нужно

GitLab

внутри закрытого контура

хранение исходного кода, GitOps-репозитория и запуск CI pipeline

Harbor или другой OCI-compatible registry

внутри закрытого контура

хранение базовых образов, CI-образов и образов приложения

Helm-чарт GitLab Runner

внутренний Helm-репозиторий или локальный .tgz-архив

установка runner без обращения к charts.gitlab.io

образы GitLab Runner и GitLab Runner Helper

внутренний registry

запуск runner и служебных Pod в Kubernetes executor

образы Kaniko/build executor из .gitlab-ci.yml

внутренний registry

сборка контейнерных образов без Docker daemon

базовые образы приложения

внутренний registry

сборка сервисов приложения без скачивания из публичных registry

зависимости языков и пакетных менеджеров

внутренние зеркала Maven, npm, PyPI, Go proxy, apt/yum/apk и т. п.

установка библиотек во время CI-сборки

исходный код демонстрационного приложения

внутренний GitLab

воспроизводимость сценария внутри периметра

TLS-сертификаты и CA bundle

доверенные хранилища Nova, runner, FluxCD и worker-узлов

корректная проверка сертификатов GitLab и Harbor

DNS-записи

внутренний DNS

разрешение имён GitLab, Harbor, ingress-адресов и registry

18.2. Перенос образов во внутренний registry

Все образы, которые указаны в Helm values, .gitlab-ci.yml, Dockerfile и Kubernetes-манифестах, должны ссылаться на внутренний registry. Например, вместо публичного или внешнего адреса используется адрес Harbor внутри закрытого контура:

image:
  registry: <harbor_host>
  image: library/gitlab-runner
  tag: alpine-v18.2.1

То же правило применяется к helper image:

helper_image = "<harbor_host>/library/gitlab-runner-helper:x86_64-v18.2.1"

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

FROM <harbor_host>/library/alpine:3.20

Перед запуском практики проверьте, что GitLab Runner, job-Pod, FluxCD и worker-узлы Nova могут получить доступ к этим образам.

18.3. Перенос Helm chart в закрытый контур

Если нет доступа к charts.gitlab.io, чарт GitLab Runner необходимо скачать заранее и перенести во внутренний контур. После этого возможны два варианта установки.

Первый вариант — установить чарт из локального архива:

helm upgrade --install \
  --namespace gitlab-runner \
  gitlab-runner \
  -f gitlab-runner-values.yaml \
  ./gitlab-runner-<chart_version>.tgz

Второй вариант — разместить чарт во внутреннем Helm-репозитории:

helm repo add internal-helm <internal_helm_repo>
helm upgrade --install \
  --namespace gitlab-runner \
  gitlab-runner \
  -f gitlab-runner-values.yaml \
  internal-helm/gitlab-runner

18.4. Зависимости сборки приложения

Даже если все контейнерные образы перенесены во внутренний Harbor, pipeline может обращаться к внешним источникам во время сборки. Это зависит от стека приложения и содержимого Dockerfile или CI job.

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

  • apt, yum, dnf, apk или другие системные пакетные менеджеры;

  • npm, yarn, pnpm;

  • pip, poetry;

  • maven, gradle;

  • go mod download или внешний Go proxy;

  • скачивание бинарных файлов через curl или wget;

  • обращения к публичным container registry.

Для каждого такого источника настройте внутреннее зеркало или заранее подготовленный артефакт. Иначе pipeline может успешно стартовать, но завершиться ошибкой на этапе скачивания зависимостей.