← Blog
Kubernetes16 min czytania

Flux Operator — GitOps zarządzany na flocie klastrów

Procedura bootstrapu Fluksa działa na jednym klastrze i przestaje działać na dwudziestu. Flux Operator zamienia instalację Fluksa w jeden zasób CRD, dokłada okna wdrożeniowe z wyrażeniem cron i strefą czasową, efemeryczne środowiska z pull requestów dla dwudziestu trzech dostawców, panel webowy oraz serwer MCP z zakresami tylko do odczytu. Omawiamy też, co licencja AGPL-3.0 znaczy przy panelu udostępnianym zespołowi klienta.

GitOps na jednym klastrze jest prosty. Uruchamiasz flux bootstrap, dostajesz katalog z manifestami kontrolerów w repozytorium i od tej pory klaster sam podąża za gałęzią. Problem zaczyna się przy drugim klastrze, a robi się poważny przy dwudziestu — bo procedura bootstrapu wpisuje wersje kontrolerów do repozytorium, a każda aktualizacja Fluksa to zmiana w tych plikach, w każdym klastrze osobno.

Flux Operator (github.com/controlplaneio-fluxcd/flux-operator) odwraca ten układ: sama instalacja Fluksa staje się zasobem Kubernetesa, deklarowanym jednym obiektem CRD. Zamiast trzymać manifesty kontrolerów w repozytorium, deklarujesz, jaki Flux ma być — a operator go instaluje, konfiguruje i aktualizuje.

Do tego dokłada rzeczy, których w samym Fluksie nie ma: okna wdrożeniowe, efemeryczne środowiska z pull requestów, panel webowy i serwer MCP. Ta ostatnia część zajmuje w ostatnich wydaniach większość zmian, więc poświęcę jej osobną sekcję.

Stan projektu

Dane z API GitHuba na 2 października 2026:

  • 746 gwiazdek i 75 forków — liczby małe, ale mylące: projekt rozwijają główni opiekunowie CNCF Flux CD z zespołu ControlPlane, w tym Stefan Prodan,
  • repozytorium założone 22 maja 2024, kod w Go, licencja AGPL-3.0, strona fluxoperator.dev,
  • najnowsze wydanie v0.59.0 z 31 sierpnia 2026, a commity były w dniu pisania tego tekstu. Tempo wydań jest wysokie: od 0.56 do 0.59 w niespełna sześć tygodni,
  • 65 otwartych zgłoszeń, dystrybucja przez Helm z OCI, Terraform, OperatorHub, OLM i kubectl,
  • odznaka SLSA poziom 3 dla łańcucha dostaw i testy end-to-end na Red Hat OpenShift, Amazon EKS, Azure AKS i Google GKE.

Trzeba od razu powiedzieć, w jakim kontekście komercyjnym to powstaje, bo jest jawny i uczciwie opisany w README: operator jest kluczowym elementem oferty korporacyjnej ControlPlane i zarządza zarówno dystrybucją CNCF Flux, jak i płatną dystrybucją tej firmy. Kod operatora jest w całości otwarty — płatna jest dystrybucja kontrolerów z gwarantowanymi łatkami CVE i poprawkami awaryjnymi. Rozgraniczenie jest więc czyste: narzędzie za darmo, wsparcie i utwardzone obrazy za pieniądze.

FluxInstance: instalacja jako zasób

Zamiast procedury bootstrapu tworzysz jeden obiekt. Ten fragment jest sercem całego pomysłu:

apiVersion: fluxcd.controlplane.io/v1
kind: FluxInstance
metadata:
  name: flux
  namespace: flux-system
  annotations:
    fluxcd.controlplane.io/reconcileEvery: "1h"
    fluxcd.controlplane.io/reconcileArtifactEvery: "10m"
spec:
  distribution:
    version: "2.x"
    registry: "ghcr.io/fluxcd"
    artifact: "oci://ghcr.io/controlplaneio-fluxcd/flux-operator-manifests"
  components:
    - source-controller
    - kustomize-controller
    - helm-controller
    - notification-controller
    - image-reflector-controller
    - image-automation-controller
  cluster:
    type: kubernetes
    size: medium
    multitenant: false
    networkPolicy: true
    domain: "cluster.local"

Kilka rzeczy zasługuje tu na uwagę. version: "2.x" to zakres, nie wersja — operator sam podniesie kontrolery w ramach tej gałęzi, a interwał sprawdzania artefaktu jest osobną adnotacją. components jest listą, więc klaster, który nie używa automatyzacji obrazów, po prostu nie dostaje dwóch kontrolerów. cluster.size jest gotowym profilem skalowania, a multitenant i networkPolicy włączają jednym słowem konfiguracje, które w ręcznym Fluksie są rozsypane po kilku plikach.

Dla wszystkiego, czego API nie obejmuje, jest spec.kustomize.patches — łatki Kustomize nakładane na manifesty kontrolerów. Tam trafiają selektory węzłów, tolerancje, limity zasobów i cała reszta rzeczy specyficznych dla konkretnego środowiska. To ważny szczegół projektowy: operator nie musi przewidzieć każdej potrzeby, bo daje ujście w postaci łatek.

Synchronizacja stanu klastra deklaruje się w tym samym obiekcie, w spec.sync — z repozytorium Git, z rejestru OCI albo z magazynu zgodnego z S3. Dokumentacja podkreśla przy tym własną ścieżkę migracji: operator upraszcza przejście z Gita jako mechanizmu dostarczania stanu na artefakty OCI, co w większych organizacjach jest realnym kierunkiem, bo rejestr skaluje się lepiej niż setki klonów repozytorium.

FluxReport: stan wdrożenia jako obiekt do odczytu

Drugi CRD nie przyjmuje konfiguracji — jest wyłącznie raportem. kubectl get fluxreport/flux -n flux-system -o yaml zwraca w jednym miejscu gotowość kontrolerów, szczegóły dystrybucji, statystyki reconcilerów, wersje CRD Fluksa i status synchronizacji klastra. Raport jest odświeżany w regularnych odstępach, a te same dane wychodzą jako metryki Prometheusa.

Doceniam ten wzorzec, bo rozwiązuje realny problem operacyjny. Odpowiedź na pytanie „czy GitOps na tym klastrze jest zdrowy" wymaga normalnie obejrzenia kilku wdrożeń, kilkunastu obiektów i logów. Tutaj jest to jeden obiekt, który da się odpytać skryptem, wystawić na monitoring i pokazać komuś, kto nie zna Fluksa.

ResourceSet: standard aplikacji jako szablon

To jest najbardziej rozbudowana część API i miejsce, w którym operator przestaje być instalatorem, a staje się narzędziem dla zespołu platformowego. ResourceSet pozwala zdefiniować grupę zasobów Fluksa i Kubernetesa jako jedną, sparametryzowaną jednostkę.

Najważniejsze pola specyfikacji:

  • inputs i inputsFrom — wartości podane wprost albo pobrane z osobnych dostawców wejść,
  • resources i resourcesTemplate — lista zasobów albo szablon Go generujący ją dla każdego zestawu wejść; przy obu ustawionych wyniki są łączone,
  • steps — uporządkowana lista nazwanych kroków, gdzie zasoby każdego kroku są stosowane i sprawdzane pod kątem zdrowia przed przejściem do następnego, każdy z własnym limitem czasu. To wdrożenie wieloetapowe bez pisania własnego kontrolera,
  • dependsOn — zasoby, które muszą być gotowe wcześniej,
  • serviceAccountName — konto usługi, pod które operator się podszywa przy stosowaniu zasobów. To jest mechanizm, na którym stoi bezpieczna samoobsługa: zespół produktowy dostaje ResourceSet, ale uprawnienia wynikają z konta usługi, nie z uprawnień operatora,
  • wait — czekanie na zdrowie zasobów,
  • inputStrategy z dwoma wariantami: Flatten sklejający wejścia z wielu dostawców w jedną listę i Permute generujący ich iloczyn kartezjański. Drugi wariant jest tym, czym buduje się macierze wdrożeń — na przykład trzy wersje aplikacji w czterech regionach z jednej deklaracji.

Dwadzieścia trzy dostawcy wejść i środowiska z pull requestów

ResourceSetInputProvider jest osobnym CRD, którego jedynym zadaniem jest dostarczyć listę wejść — a lista obsługiwanych typów jest zaskakująco długa. W kodzie jest ich dwadzieścia trzy:

  • gałęzie, tagi i pull requesty dla GitHuba, GitLaba (tu dodatkowo środowiska GitLaba), Azure DevOps, AWS CodeCommit i Gitei,
  • tagi artefaktów OCI — ogólne oraz osobno dla Azure Container Registry, Amazon ECR i Google Artifact Registry,
  • ExternalService — dowolne API HTTP zwracające wejścia, oraz ExternalArtifact i Static.

Uwierzytelnianie do chmur idzie przez federację tożsamości obciążeń z konta usługi wskazanego w serviceAccountName — czyli bez trzymania kluczy dostawcy w sekretach. Warto docenić też, jak API pilnuje spójności: reguły walidacji CEL wymuszają na przykład, że adres dostawcy OCI musi zaczynać się od oci:// i zawierać ścieżkę repozytorium po nazwie hosta, a połączenie nieszyfrowane wolno włączyć wyłącznie dla dwóch typów dostawców. Błąd konfiguracji jest odrzucany przy zapisie z czytelnym komunikatem, nie odkrywany godzinę później w logach kontrolera.

Praktyczne zastosowanie numer jeden to efemeryczne środowiska: dostawca wejść wystawia listę otwartych pull requestów, ResourceSet generuje dla każdego pełne środowisko z szablonu, a zamknięcie pull requesta usuwa je wraz z całą zawartością. Dla agencji pracującej na kilku równoległych gałęziach funkcjonalnych to jest ta funkcja, która zmienia sposób pracy z klientem — recenzja odbywa się na działającym środowisku, nie na zrzutach ekranu.

Okna wdrożeniowe

Prosta rzecz, której brak boli w każdym poważnym wdrożeniu. Typ Schedule ma trzy pola i to wszystko:

schedule:
  - cron: "0 9 * * MON-FRI"
    timeZone: "Europe/Warsaw"
    window: "8h"

Wyrażenie cron wyznacza początek, strefa czasowa domyślnie jest ustawiona na UTC, a okno określa czas, w którym wykonanie jest dozwolone (domyślnie zero, czyli bez okna). Zasób pominięty ze względu na harmonogram dostaje status z powodem SkippedDueToSchedule, więc widać, dlaczego nic się nie stało.

Jawna strefa czasowa jest tu drobiazgiem, który świadczy o zrozumieniu problemu: „wdrażamy tylko w godzinach pracy" znaczy w Warszawie coś innego niż w UTC, a różnica dwóch godzin przy zmianie czasu zamienia to ustawienie w pułapkę.

Panel webowy

Operator ma wbudowany interfejs, dostępny natychmiast przez przekierowanie portu:

kubectl -n flux-system port-forward svc/flux-operator 9080:9080

Pokazuje potoki GitOps w czasie rzeczywistym — stan wdrożeń, postęp uzgadniania, a od wydania 0.58 również zużycie procesora i pamięci na pulpicie obciążeń. Wydanie 0.59 dołożyło liczenie statusu inwentarza na podstawie healthCheckExprs, czyli własnych wyrażeń oceny zdrowia. Do zewnętrznego dostępu jest konfiguracja Ingressu i logowanie jednokrotne z zarządzaniem użytkownikami.

To jest funkcja, którą w praktyce docenia się nie samemu, a przy kliencie: pokazanie stanu wdrożeń w panelu zamiast tłumaczenia wyjścia kubectl skraca rozmowę o dwadzieścia minut.

Serwer MCP: dwadzieścia narzędzi i zakresy uprawnień

Ostatnie wydania są w większości o tym. Serwer MCP łączy asystenta AI z klastrem zarządzanym przez operatora, a zestaw narzędzi jest już poważny — w kodzie jest ich dwadzieścia:

  • odczyt: get_kubernetes_resources (od 0.59 z wyborem pól), get_kubernetes_logs (rozszerzone na obciążenia), get_kubernetes_events, get_kubernetes_metrics, get_flux_instance, get_kubernetes_api_versions,
  • diagnostyka: trace_kubernetes_resource (dodane pod koniec sierpnia) — prześledzenie, który zasób Fluksa zarządza danym obiektem, oraz diff_kubernetes_manifest pokazujący różnicę między manifestem a stanem klastra,
  • operacje: reconcile_flux_resource (skonsolidowane z kilku wcześniejszych), suspend_reconciliation, resume_reconciliation, apply_kubernetes_manifest, patch_kubernetes_resource, delete_kubernetes_resource,
  • dokumentacja: search_flux_docs i read_flux_doc — z wyszukiwaniem rozmytym po pociętym na fragmenty indeksie, w kodzie zaimplementowanym algorytmem BM25. Asystent nie zgaduje składni API Fluksa, tylko szuka jej w wersjonowanej dokumentacji,
  • kontekst: get_kubernetes_contexts i set_kubernetes_context — czyli praca na wielu klastrach w jednej rozmowie.

Najważniejszy element tej konstrukcji nie jest jednak narzędziem, a mechanizmem uprawnień. Serwer definiuje zakresy: toolbox:read_only i toolbox:read_write, przy czym każde narzędzie ma dodatkowo własny zakres z opisem przeznaczonym wprost na ekran zgody — komentarz w kodzie precyzuje, że opis ma mówić, co zakres pozwala zrobić, a nie odwoływać się do własnej nazwy. To dojrzałe podejście: różnica między asystentem, który może obejrzeć klaster, a takim, który może w nim usunąć zasób, jest jedną flagą konfiguracji, nie kwestią zaufania.

Wydanie 0.59 przeniosło serwer na bezstanowy model MCP zgodny ze specyfikacją z 28 lipca 2026 i dodało rozgłaszanie instrukcji serwera klientom MCP — czyli serwer sam mówi asystentowi, jak z niego korzystać.

Świeżo w gałęzi głównej: katalog umiejętności agentów

Rzecz, której nie ma jeszcze w notatkach wydania 0.59, a która w kodzie jest i warto o niej wiedzieć. Nowe API Catalog zarządza katalogiem umiejętności dla agentów AI, dystrybuowanych jako artefakty OCI. Źródło to repozytorium OCI z tagiem, a do tego dochodzi weryfikacja podpisu przez cosign z dopasowaniem tożsamości OIDC, wraz z listami targetAgents i targetSkills określającymi, które umiejętności i dla których agentów zainstalować.

Kierunek jest tu wart odnotowania niezależnie od tego narzędzia: instrukcje dla agentów traktowane jako artefakt dostarczany łańcuchem GitOps, z podpisem kryptograficznym i weryfikacją tożsamości wydawcy. W tej serii widzieliśmy już kilka projektów wersjonujących prompty razem z kodem; to jest pierwszy, który dokłada do tego podpisywanie i dystrybucję przez rejestr.

Narzędzie wiersza polecenia, którego się nie spodziewałem

Operator ma własne CLI i jest ono zauważalnie obszerniejsze, niż sugeruje README. Kilka grup poleceń zasługuje na uwagę, bo odpowiadają na realne problemy pracy z tym API.

build instance i build resourceset renderują manifesty lokalnie, bez dotykania klastra. To jest bezpośrednia odpowiedź na najgroźniejszą pułapkę szablonów Go: zamiast zapisywać ResourceSet do klastra i sprawdzać w logach, czy szablon wygenerował sensowny YAML, renderujesz go u siebie i czytasz wynik. Do tego jest diff yaml pokazujący różnicę i tree z wariantami dla Kustomization, HelmRelease i ResourceSet, wypisujące drzewo zarządzanych obiektów.

migrate owner i migrate resources obsługują przejście z klastra postawionego procedurą bootstrapu na zarządzanie operatorem. Migracja nie polega więc na skasowaniu Fluksa i postawieniu go od nowa — istniejące zasoby zmieniają właściciela.

create secret ma dziesięć wariantów: basicauth, ssh, tls, registry, proxy, sops, githubapp (uwierzytelnianie aplikacją GitHuba zamiast tokenem osobistym) oraz trzy dotyczące panelu webowego, w tym webauth i web-config. Sekrety w Kubernetesie tworzy się co prawda ręcznie w pięć minut, ale te polecenia pilnują struktury, której kontrolery oczekują.

Grupa distro jest maszynerią dystrybucji korporacyjnej i mówi wiele o tym, jak ta oferta jest zbudowana: mirror (najobszerniejsze polecenie w całym CLI) kopiuje dystrybucję do własnego rejestru, co jest wymogiem przy klastrach odciętych od internetu; do tego sign i verify dla artefaktów oraz manifestów, encrypt i decrypt manifestów, generowanie kluczy szyfrujących i podpisujących, a także podpisywanie, weryfikowanie i unieważnianie kluczy licencyjnych. Ta ostatnia trójka to część płatna — ale sam fakt, że mechanizm licencjonowania jest w otwartym kodzie i da się go przeczytać, jest uczciwszy niż zamknięty moduł sprawdzający licencję.

Grupa skills — install, list, publish, uninstall, update — to strona klienta katalogu umiejętności agentów opisanego wyżej. Jest tam więc również polecenie publikowania własnych umiejętności jako artefaktu OCI, nie tylko instalowania cudzych.

Reszta to spodziewany zestaw operacyjny: get, reconcile, suspend, resume, wait, delete i patch dla każdego z typów zasobów, export report wyciągające raport, stats ze statystykami oraz trace odnajdujące, który zasób Fluksa zarządza wskazanym obiektem.

Pułapki

  • Licencja AGPL-3.0, nie Apache jak w samym Fluksie. Szczegóły niżej, ale to pierwsza rzecz do przemyślenia przed wdrożeniem u klienta,
  • Wersje 0.x przy bardzo szybkim tempie wydań — od 0.56 do 0.59 w sześć tygodni. API jest stabilne w wersji v1, ale wersję operatora warto przypinać i czytać notatki wydań,
  • Trzeba najpierw znać Fluksa. Operator upraszcza jego instalację i konfigurację, ale nie zastępuje zrozumienia, czym jest Kustomization, HelmRelease i jak działa uzgadnianie,
  • Płatna jest dystrybucja, nie operator. Utwardzone obrazy, łatki CVE i poprawki awaryjne to oferta korporacyjna ControlPlane — jeśli klient oczekuje umowy wsparcia, to jest właśnie ten koszt,
  • Narzędzia zapisujące w serwerze MCP mogą zmienić klaster. Uruchamiając go w środowisku produkcyjnym, zaczynaj od toolbox:read_only i nadawaj uprawnienia zapisu świadomie,
  • Panel webowy wystawiony na zewnątrz wymaga Ingressu i logowania jednokrotnego — przekierowanie portu jest do pracy własnej, nie do udostępniania zespołowi,
  • Projekt jest mały kadrowo. Zdecydowaną większość kodu piszą dwie osoby, a prawa autorskie w plikach są podpisane nazwiskiem głównego autora,
  • ResourceSet z szablonami Go to realna moc i realne ryzyko. Walidacja przy zapisie łapie strukturę CRD, nie logikę szablonu — dlatego każdy nowy ResourceSet warto najpierw wyrenderować lokalnie poleceniem build resourceset,
  • Efemeryczne środowiska kosztują. Każdy otwarty pull request to działające środowisko; przy dwudziestu otwartych gałęziach rachunek za klaster wygląda inaczej, niż przy wdrożeniu ręcznym.

Gdzie to ma sens w naszej pracy

  • Więcej niż jeden klaster. To jest główny powód istnienia tego narzędzia — aktualizacja Fluksa na flocie przestaje być pracą w repozytoriach, a staje się zmianą zakresu wersji w jednym obiekcie,
  • Środowiska recenzenckie z pull requestów. Klient klika w link z komentarza i widzi funkcję na działającym środowisku. Zamknięcie pull requesta sprząta po sobie,
  • Okna wdrożeniowe dla klientów z ruchem sezonowym. Sklep, w którym nie wdraża się w piątek po południu ani w Czarny Piątek, dostaje to jako konfigurację, nie jako ustalenie na Slacku,
  • Standard aplikacji dla zespołu produktowego. ResourceSet z kontem usługi do podszywania się pozwala dać zespołowi samoobsługę bez dawania mu uprawnień administratora klastra,
  • Diagnostyka z asystentem. Serwer MCP w trybie tylko do odczytu, z narzędziami śledzenia zasobu i różnicy manifestu, skraca odpowiedź na pytanie „dlaczego to wdrożenie nie doszło" z kwadransa do jednego zapytania,
  • Rozmowa z klientem o stanie wdrożeń. Panel webowy i FluxReport dają obraz, który da się pokazać osobie nietechnicznej.

Podsumowanie

  • FluxInstance zamienia instalację Fluksa w jeden zasób — z zakresem wersji, listą kontrolerów, profilem rozmiaru i ujściem w postaci łatek Kustomize,
  • FluxReport to stan GitOps jako obiekt do odczytu, z metrykami Prometheusa,
  • ResourceSet daje szablony Go, kroki z kontrolą zdrowia i podszywanie się pod konto usługi — samoobsługa bez uprawnień administratora,
  • Dwadzieścia trzy typy dostawców wejść, w tym pull requesty pięciu platform i tagi artefaktów czterech rejestrów, z federacją tożsamości obciążeń zamiast sekretów,
  • Okna wdrożeniowe mają jawną strefę czasową i czytelny powód pominięcia,
  • Panel webowy działa od razu, a do dostępu zewnętrznego ma Ingress i logowanie jednokrotne,
  • Serwer MCP ma dwadzieścia narzędzi i zakresy uprawnień — zaczynaj od trybu tylko do odczytu,
  • Wyszukiwanie w dokumentacji Fluksa jest narzędziem MCP, więc asystent sprawdza składnię, zamiast ją wymyślać,
  • W gałęzi głównej powstaje katalog umiejętności agentów dystrybuowanych jako podpisane artefakty OCI,
  • Operator jest darmowy, dystrybucja korporacyjna płatna — granica jest jasna i opisana.

Licencja: Flux Operator jest na AGPL-3.0 i to nie jest szczegół, który można pominąć — tym bardziej że sam Flux CD jest na Apache-2.0, więc łatwo przenieść założenia z jednego projektu na drugi. Zwykłe używanie nie rodzi żadnych zobowiązań: uruchomienie operatora we własnym klastrze albo w klastrze klienta jest korzystaniem z programu, nie jego rozpowszechnianiem, i AGPL nie stawia temu warunków. Obowiązki zaczynają się w dwóch miejscach. Pierwsze to modyfikacja: zmieniony operator albo panel, jeśli je rozpowszechniamy albo udostępniamy przez sieć, muszą być opublikowane na AGPL-3.0 wraz z naszymi zmianami. Drugie jest specyfiką tej właśnie licencji i dotyczy panelu: paragraf trzynasty obejmuje interakcję użytkowników z programem przez sieć, więc udostępnienie panelu webowego zespołowi klienta oznacza obowiązek zaoferowania tym użytkownikom dostępu do kodu odpowiadającego. Przy wersji niezmodyfikowanej spełnienie tego jest trywialne — wystarczy wskazać publiczne repozytorium projektu — ale przy własnych zmianach w panelu, choćby kosmetycznych, obowiązek dotyczy już naszej wersji. Praktyczny wniosek dla agencji: wdrażanie operatora bez modyfikacji jest w porządku i nie wymaga niczego poza wskazaniem źródła, natomiast „dołożymy klientowi własną zakładkę w panelu" to decyzja licencyjna, nie zadanie frontendowe. Osobno warto pamiętać, że licencja operatora nie mówi nic o dystrybucji kontrolerów Fluksa od ControlPlane — utwardzone obrazy i wsparcie to odrębna oferta handlowa z własnymi warunkami.