ADG Оставить заявку
Блог DevOps 3 мин чтения

GitOps без GitHub: Flux v2 OCI + Harbor на отечественной инфраструктуре

Flux v2.0 поддерживает хранение чартов и манифестов в OCI-реестрах. Настраиваем GitOps через Harbor без зависимости от зарубежных сервисов.

Контекст момента

Flux v2.0 выпустил стабильную поддержку OCI-артефактов - Helm-чарты и Kustomize-манифесты можно хранить в любом OCI-совместимом реестре без зависимости от GitHub

Flux v2.0 стал стабильным несколько недель назад, и в числе новинок - нормальная поддержка OCI-артефактов. Helm-чарты и Kustomize-манифесты теперь можно хранить прямо в OCI-реестре: push как образ, pull через OCIRepository в Flux, никакого GitRepository-источника из GitHub не нужно. Для нас это важно - большинство клиентов с требованиями по КИИ не могут использовать GitHub или публичные Helm-репозитории. CI/CD должен работать в периметре.

Проблема, которую решает OCI-источник

Стандартный Flux-паттерн - GitRepository или HelmRepository. GitRepository - это git-репозиторий, обычно на GitHub или GitLab. HelmRepository - index.yaml с чартами, обычно на artifacthub.io или где-то снаружи. Оба варианта в закрытом контуре требуют зеркалирования или альтернативы.

Мы уже разбирали GitFlic как замену GitLab - с git-источником всё решаемо. Но с Helm-чартами история сложнее: поддерживать свой HelmRepository с index.yaml - это отдельная инфраструктура, которая постоянно требует внимания. OCI-подход убирает эту прослойку: реестр образов уже есть, чарт кладётся туда же как артефакт, Flux тянет его напрямую.

Что за стек и почему Harbor

На нескольких клиентах мы ведём K8s-кластеры на РЕД ОС в рамках managed-сопровождения. Harbor уже стоял как реестр образов - он входит в реестр отечественного ПО через несколько дистрибутивов, у него есть российская коммерческая поддержка от нескольких вендоров, и он умеет OCI из коробки начиная с версии 2.0. Логично было не добавлять новый компонент, а использовать то, что уже работает.

Flux мы используем с версии 0.x, на GA v2.0 переходили постепенно в течение октября-ноября.

Как это устроено

Схема выглядит так: CI-пайплайн собирает чарт и публикует его в Harbor как OCI-артефакт. Flux в кластере следит за OCIRepository-ресурсом и при появлении нового тега применяет изменения. GitOps-цикл работает, но git хранит только исходники и конфигурацию Flux - не сами чарты.

Публикация чарта из пайплайна:

helm package ./chart --version "${CI_COMMIT_TAG}"
helm push chart-name-${CI_COMMIT_TAG}.tgz oci://harbor.internal/charts

Ресурс в кластере:

apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: OCIRepository
metadata:
  name: app-chart
  namespace: flux-system
spec:
  interval: 5m
  url: oci://harbor.internal/charts/chart-name
  ref:
    semver: ">=1.0.0"
  secretRef:
    name: harbor-credentials

Поверх него - HelmRelease, который ссылается на этот OCIRepository как источник. Flux опрашивает реестр по интервалу, видит новый тег, подходящий под semver-фильтр, и запускает reconciliation.

Что пришлось разобрать

Аутентификация. Harbor с TLS и внутренним CA - стандартная ситуация. Flux при работе с OCI использует те же механизмы, что и при pull образов: imagePullSecret формата kubernetes.io/dockerconfigjson. Но для OCIRepository секрет указывается в spec.secretRef, а не через serviceaccount - это несколько сбивает с толку, если привык к стандартным image pull secrets. Пришлось покопаться в документации.

Верификация подписи. Flux v2 умеет проверять подпись OCI-артефактов через Cosign. У клиентов нет PKI-инфраструктуры, которая покрывала бы подпись артефактов, поэтому эту функцию пропустили - не блокер для текущих задач.

Semver-фильтр и теги. Flux умеет подписываться на диапазон версий: >=1.2.0 <2.0.0, ~1.3, конкретный тег или latest. Мы используем semver-диапазон для staging (берёт любую новую patch-версию) и точный тег для production (обновляем вручную через коммит в flux-конфиг). Это стандартный GitOps-паттерн, но с OCI он работает приятнее: источник истины - тег в реестре, а не отдельный values.yaml с версией чарта.

Совместимость flux push. Flux CLI умеет публиковать не только чарты, но и Kustomize-директории как OCI-артефакт через flux push artifact. Для инфраструктурных компонентов, которые мы раньше держали как GitRepository + Kustomize, это даёт тот же результат - без зависимости от внешнего git.

Что получилось

CI/CD-цикл теперь замкнут внутри периметра: GitFlic как git-хостинг, GitFlic Runner (или Woodpecker, у одного клиента) как CI, Harbor как реестр и образов, и OCI-чартов, Flux как оператор GitOps. Ни одного обращения наружу в runtime.

Это не полная автономия - upstream-зависимости (образы базовых систем, Helm-чарты сторонних компонентов) по-прежнему надо зеркалировать отдельно. Но оркестрационный слой теперь не требует внешних сервисов.

Мы только начали катить этот подход на клиентские кластеры - первые две среды работают несколько недель. Пока трений меньше, чем ожидали: операторы быстро разобрались, что чарт публикуется как образ, и перестали удивляться oci://-адресам. Посмотрим, как поведёт себя при более частых деплоях и при обновлении самого Flux - v2.0 только вышел, и граблей впереди наверняка хватит.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.