Ringkasan Utama
Flux 2.9, diumumkan 30 Juni 2026, membuat CLI flux bisa diperluas lewat plugin seperti Mirror dan Schema, menambah ignore rules server-side apply agar autoscaler bisa memegang replicas, mendekripsi key age post-quantum di SOPS, dan membuat Receiver webhook memverifikasi token OIDC, bukan shared secret. Cek default post-render Helm sebelum upgrade.
Pengumuman Flux 2.9 memuat belasan perubahan, dan yang jadi judul utamanya mudah disalahartikan. Sistem plugin terdengar seperti kemudahan semata. Sebenarnya ini mengubah tempat tooling GitOps tinggal: validasi, mirroring registry, dan debugging operator pindah dari script ad hoc ke binary yang sudah Anda jalankan di CI.
Saya belum menjalankan 2.9 di production, jadi ini bukan laporan lapangan. Ini pembacaan teliti atas pengumuman, RFC di balik sistem plugin, halaman referensi Kustomization dan Receiver, serta README plugin, dan setiap flag di bawah diambil dari halaman-halaman itu. Kalau sebuah sumber diam, saya bilang diam, bukan mengisinya dengan tebakan.
Sistem plugin mengikuti kontrak ala kubectl, lengkap dengan checksum
RFC-0013 memodelkan plugin flux seperti plugin kubectl: executable dengan awalan flux- menjadi subcommand, jadi flux-operator dijalankan sebagai flux operator. RFC menyebut command bawaan menang atas plugin, hanya satu direktori yang dipindai, dan tidak ada binary plugin yang dieksekusi saat discovery, sehingga plugin tidak memperlambat command flux yang tidak memakainya. Plugin di-versioning terpisah dari CLI.
Bagian install layak dibaca dengan teliti. Catalog tinggal di repository fluxcd/plugins sebagai manifest YAML dengan URL download dan checksum per platform. flux plugin install mengunduh ke file sementara, memverifikasi checksum SHA-256, mengekstrak hanya binary yang dideklarasikan, menulisnya dengan mode 0755, lalu mencatat install receipt di sebelahnya. Pengumuman menyarankan pinning dengan digest immutable untuk CI dan production, dan menyebut tiga plugin: Mirror, Schema, dan plugin Operator yang dikelola ControlPlane.
# Discover, install, inspect. Binaries land in the plugin directory the
# RFC names (~/.fluxcd/plugins, override with FLUXCD_PLUGINS) as flux-<name>.
flux plugin search
flux plugin install schema # latest: fine on a laptop
flux plugin list # versions and paths, read from install receipts
# Pin for CI and production. A version pin is the example in the announcement;
# a digest pin is immutable, so a re-tagged release cannot change what you run.
flux plugin install schema@0.5.0
flux plugin install schema@sha256:REPLACE_WITH_THE_DIGEST_FROM_THE_CATALOG
flux plugin update schema
flux plugin uninstall schemaAda satu perbedaan yang perlu diketahui: pengumuman menyebut binary mendarat di folder fluxcd/plugins di bawah home directory, sedangkan RFC menetapkan ~/.fluxcd/plugins dengan override FLUXCD_PLUGINS. Saya akan percaya RFC dan memastikannya dengan flux plugin list, yang menampilkan path tiap plugin.
Flux Schema menjadikan validasi manifest sebagai langkah CI
Plugin Schema memvalidasi YAML Kubernetes terhadap JSON Schema dan aturan CEL, dan README-nya menyatakan ia memakai semantik evaluasi API server untuk x-kubernetes-validations. Catalog default bawaannya mencakup CRD Kubernetes, OpenShift, Gateway API, Flux, Flagger, dan Flux Operator, sedangkan catalog ecosystem menambah schema proyek CNCF lain. Artinya pull request bisa gagal karena HelmRelease atau Kustomization yang salah sebelum controller pernah melihatnya.
README menyertakan workflow minimal, yang saya salin di bawah, dan image Docker yang membawa catalog default di /catalog/latest sehingga validasi juga jalan di runner air-gapped. Saya tidak mem-pin apa pun di workflow karena menyalin README; di production saya akan mengganti main dengan tag atau commit, dengan alasan yang sama seperti pengumuman mem-pin plugin lewat digest.
# .github/workflows/flux-schema.yaml (from the flux-schema README)
name: flux-schema
on:
pull_request:
branches: [main]
jobs:
validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Flux CLI with Schema Plugin
uses: fluxcd/flux2/action@main # production: pin a tag or commit
with:
plugins: |
schema
- name: Validate manifests
uses: fluxcd/flux-schema/actions/validate@main
# Air-gapped runner: the image carries the default catalog.
docker run --rm \
-v "$PWD/manifests:/manifests:ro" \
ghcr.io/fluxcd/flux-schema:latest validate /manifests \
--schema-location /catalog/latest
# Locally, including a Helm chart piped through the validator.
flux schema validate ./clusters/production -s ecosystem -v
helm template ./charts/app | flux schema validate -v --skip-missing-schemasREADME singkat soal menulis aturan CEL sendiri dan merujuk ke tempat lain, jadi saya tidak mengklaim cara menulisnya. Yang terdokumentasi adalah flux schema discover, yang mengkatalogkan resource Flux dan Kubernetes di repository GitOps, dan flux schema extract crd, yang membangun catalog dari CRD Anda sendiri.
Flux Mirror mengeluarkan registry upstream dari jalur reconcile
Mirror menyalin Helm chart, artifact OCI, dan container image dari satu registry ke registry lain berdasarkan config deklaratif. README membingkai tujuannya sebagai mengisi registry internal agar cluster tidak pernah menarik dari upstream saat reconcile, serta cara memindahkan chart dari sumber HelmRepository HTTP/S ke OCI. Run ulang hanya menyalin tag yang hilang atau berubah, dan drift dilaporkan lewat exit code tersendiri.
# flux-mirror.yaml (shape from the flux-mirror README)
apiVersion: mirror.plugin.fluxcd.io/v1beta1
kind: Config
charts:
- name: external-dns
source: https://kubernetes-sigs.github.io/external-dns/
destination: oci://ghcr.io/my-org/charts
version: "*"
limit: 3 # keep the newest three, not the whole history
artifacts:
- source: registry.k8s.io/external-dns/external-dns
destination: ghcr.io/my-org/external-dns
selector:
semver: ">=0.15.0"
limit: 3
includeReferrers: true # signatures, SBOMs and attestations travel too
# Run it on a schedule and alert on the exit code. A mirror that stopped
# syncing looks exactly like a mirror that is up to date.
# flux mirror sync flux-mirror.yamlFilter untuk artifact berupa pipeline empat langkah: regex, semver, sort, lalu top-N. Autentikasi mencakup Docker config dan credential helper, workload identity cloud untuk ECR, ACR, dan GAR, serta beberapa sumber bearer. Verifikasi signature bersifat opt-in dan memeriksa keyless signature pada artifact sumber sebelum disalin, dengan usia signature minimum yang opsional.
Trade-off-nya, mirror adalah infrastruktur yang sekarang harus Anda operasikan. Kalau job sync berhenti, registry internal diam-diam tertinggal dari upstream, dan perbaikan keamanan di upstream tidak sampai ke cluster. Jalankan flux mirror sync terjadwal dan pasang alert pada exit code-nya, jangan menganggap run pertama yang sukses sudah selesai.
SSA ignore rules membiarkan controller lain memegang sebuah field
Sebelum 2.9, autoscaler dan Flux berebut spec.replicas adalah gangguan klasik GitOps. Kustomization kini punya spec.ignore, daftar aturan dengan path JSON Pointer yang wajib dan target selector yang opsional. Halaman referensi menyebut field yang diabaikan dikeluarkan dari drift detection dan correction, dan menjelaskan dua strategi: strip, yang membuang field dari payload apply saat manager server-side apply lain memilikinya, dan adopt, yang menyalin nilai di cluster ke payload saat Flux satu-satunya apply manager.
Target menerima regular expression untuk group, version, kind, name, dan namespace, plus selector label dan annotation. Di dalam path, garis miring pada sebuah key harus ditulis ~1, yang penting untuk nama annotation. Contoh di bawah adalah buatan saya dari field yang terdokumentasi; ia mengabaikan replicas di semua Deployment dan satu annotation external-dns di satu Service.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: apps
namespace: flux-system
spec:
interval: 10m
path: ./apps/production
prune: true
sourceRef:
kind: GitRepository
name: flux-system
ignore:
# The HPA owns replicas. Scoped to Deployments only.
- paths:
- /spec/replicas
target:
kind: Deployment
# external-dns writes this annotation. "/" inside a key is escaped as "~1".
- paths:
- /metadata/annotations/external-dns.alpha.kubernetes.io~1hostname
target:
kind: Service
name: web
# Wrong: no target. This rule now matches EVERY object in the Kustomization.
# ignore:
# - paths:
# - /spec/replicasDua kalimat dari halaman referensi layak dibaca dua kali. Tanpa target, aturan berlaku untuk semua objek yang dikelola Kustomization, dan perubahan pada field yang diabaikan saja tidak memicu reconciliation. Keduanya perilaku yang benar, dan keduanya akan mengejutkan orang yang mengira edit manual dikoreksi pada interval berikutnya.
Ignore rule tanpa target berlaku untuk setiap objek di Kustomization. Kalau maksud Anda hanya Deployment, tulis kind: Deployment. Aturan yang lebih luas dari yang Anda kira menyembunyikan drift pada field yang masih ingin Anda serahkan ke Git, dan tidak ada yang akan melaporkannya.
Receiver OIDC menghapus shared secret webhook
Receiver generic selalu dilindungi token. Tipe generic-oidc yang baru memverifikasi OIDC ID token yang dikirim sebagai header Authorization Bearer, jadi CI tidak menyimpan secret. Controller mencocokkan claim iss token dengan provider yang dikonfigurasi, memeriksa signature, expiry, dan audience (default notification-controller), lalu mengevaluasi validasi CEL Anda. Semuanya harus true, kalau tidak request ditolak dan pesan yang gagal dikembalikan.
| Aspek | generic dan generic-hmac | generic-oidc |
|---|---|---|
| Yang dibawa pemanggil | Token Receiver, sebuah shared secret | OIDC ID token dari provider CI |
| Secret tersimpan di CI | Ya, tokennya | Tidak, dan spec.secretRef ditolak untuk tipe ini |
| Yang mengotorisasi request | Mengetahui token | Signature valid, expiry, audience, dan semua validasi CEL true |
| Variabel pada per-resource filter | res dan req | res, req, dan claims |
Rilis yang sama menambah per-resource filtering: tiap entri di spec.resources bisa membawa filter CEL, dan filter itu bertumpuk dengan resourceFilter tingkat atas, sehingga sebuah resource di-reconcile hanya bila keduanya true. Ada juga command flux trigger receiver yang mengirim request untuk Anda, dengan --type generic-oidc dan --oidc-provider github atau forgejo. Di GitHub, job butuh permission id-token write.
apiVersion: notification.toolkit.fluxcd.io/v1
kind: Receiver
metadata:
name: platform-deploy
namespace: flux-system
spec:
type: generic-oidc # spec.secretRef is rejected for this type
oidcProviders:
- issuerURL: https://token.actions.githubusercontent.com # must equal the token's iss claim
# audience defaults to notification-controller
validations:
# A valid signature alone authorises nothing: pin the identity.
- expression: "claims.repository_owner == 'my-org' && claims.repository == 'my-org/platform'"
message: "only my-org/platform may trigger this Receiver"
resources:
- apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
name: flux-system
# In the GitHub Actions job (needs: permissions: id-token: write)
# flux trigger receiver platform-deploy \
# --type generic-oidc --oidc-provider github \
# --url https://flux-hooks.example.com -n flux-systemHalaman referensi tegas soal risikonya: signature yang valid tidak mengotorisasi apa pun dengan sendirinya. Issuer publik seperti GitHub Actions memberi token ke workflow mana pun di platform, jadi validasi Anda harus mengunci claim identitas seperti repository_owner dan repository. Event filtering lewat spec.events tidak didukung untuk tipe ini, dan ekspresi CEL yang tidak valid membuat Receiver berstatus Stalled dengan reason InvalidCELExpression dan menolak semua request.
Key age post-quantum dan Vault tanpa secret di jalur dekripsi
Release notes mencantumkan dekripsi SOPS dengan cipher Age post-quantum untuk Kustomization, tanpa detail lain: tidak ada nomor versi SOPS atau age. Dari sisi key, README age menyebut key hybrid post-quantum dibuat dengan age-keygen -pq, sudah built-in di age v1.3.0 ke atas, identity-nya diawali AGE-SECRET-KEY-PQ-1 dan recipient-nya age1pq1, dan recipient itu panjangnya sekitar 2000 karakter.
Di sisi Flux bentuknya tidak berubah: blok decryption pada Kustomization tetap menyebut provider sops dan sebuah Secret, dan identity age ditaruh di key yang namanya berakhiran .agekey. Yang tidak bisa saya pastikan dari halaman yang saya baca adalah rilis SOPS mana yang dibutuhkan untuk enkripsi dengan recipient post-quantum, karena README SOPS tidak menyebutnya. Uji encrypt dan decrypt di repository percobaan sebelum meng-commit secret asli pertama.
# 1. A hybrid post-quantum age key (age v1.3.0 or later, per the age README).
age-keygen -pq -o key.txt
age-keygen -y key.txt > recipient.txt # age1pq1..., about 2000 characters
# 2. Give the controller the identity. The key name must end in .agekey.
kubectl -n flux-system create secret generic sops-age \
--from-file=identity.agekey=key.txt
# 3. Point the Kustomization at it.
# spec:
# decryption:
# provider: sops
# secretRef:
# name: sops-age
#
# Leave metadata, kind and apiVersion of the encrypted Secret in plain text.
# Test encrypt and decrypt in a scratch repository first: the SOPS README is
# silent on which release accepts a post-quantum recipient.Perubahan secrets kedua adalah Workload Identity untuk OpenBao dan HashiCorp Vault. Halaman referensi menjelaskan kustomize-controller menukar token ServiceAccount-nya dengan token Vault berumur pendek lewat auth method berbasis JWT, diaktifkan dengan flag --sops-vault-configmap. ConfigMap itu sekaligus allowlist alamat Vault, audience token adalah alamat Vault, dan role Vault diturunkan sebagai namespace_name dari ServiceAccount. Ia juga mensyaratkan feature gate ObjectLevelWorkloadIdentity saat memakai serviceAccountName.
sops.vault-token statis lebih diutamakan daripada Kubernetes auth, yang pada gilirannya mengalahkan environment variable VAULT_TOKEN global. Saat pindah ke Workload Identity, hapus Secret token lama, atau controller akan terus memakainya.
Apa yang rusak saat upgrade, dan apa yang tidak
Pengumuman punya daftar pendek perubahan yang bisa menggagalkan reconcile setelah upgrade. Semuanya bisa diketahui sebelumnya, makanya saya akan memasukkannya ke checklist pre-flight, bukan menemukannya dari Kustomization yang stalled.
| Perubahan di 2.9 | Dampaknya ke Anda | Cek sebelum upgrade |
|---|---|---|
| Default post-render Helm berpindah dari nohooks ke combined | Hook chart kini ikut post-rendering, yang bisa mengubah hasil render | Set nohooks secara eksplisit pada HelmRelease yang bergantung pada perilaku lama |
| API v1beta2 untuk image dan notification dihapus dari CRD | Manifest yang masih di versi itu tidak bisa lagi di-apply | Jalankan flux migrate untuk memindahkan resource ke API stabil lebih dulu |
| Receiver GCR mewajibkan email dan audience di Secret yang direferensikan | Receiver GCR kini membutuhkan field itu, jadi yang sudah ada bisa berhenti bekerja sampai Secret diperbarui (CVE-2026-40109) | Tambahkan kedua field ke Secret sebelum controller di-roll |
| Kubernetes yang didukung adalah 1.34, 1.35, dan 1.36 | Cluster lebih lama berada di luar matriks dukungan; Flux 2.6 sudah end of life | Bandingkan versi server Anda dengan minimum 1.34.1, 1.35.0, dan 1.36.0 |
Sistem plugin sendiri punya batasan yang dicantumkan RFC untuk v1beta1: checksum hanya SHA-256 dengan Cosign dan SLSA ditunda, tidak ada dependency plugin atau version range, tidak ada pengecekan update otomatis, dan tidak ada autentikasi untuk catalog privat. Nama flag juga bisa berbeda antara Flux dan plugin, contoh dari RFC sendiri adalah --context melawan --kube-context. Ini bukan alasan menghindari plugin, tapi alasan untuk mem-pin digest dan memperlakukan update plugin sebagai perubahan yang direview.
Rilis 2.8 layak masuk percakapan yang sama. Rilis itu menjadikan perilaku Helm v4, dengan server-side apply dan health check kstatus, sebagai default untuk HelmRelease baru sementara yang lama tetap memakai client-side apply sampai Anda opt in, dan feature gate UseHelm3Defaults mengembalikan default sebelumnya. Kalau Anda melompat dari sebelum 2.8, baca kedua pengumuman dan diskusi upgrade procedure yang ditautkan post 2.9.
Yang dilakukan hari Senin
Kerjakan berurutan. Langkah satu dan dua melindungi upgrade, langkah tiga tidak mengubah apa pun di cluster, dan langkah empat serta lima adalah pekerjaan adopsi yang sebenarnya.
- Cari resource pada versi API yang dihapus dan jalankan flux migrate sebelum menyentuh controller.
- Set strategi post-render Helm secara eksplisit pada HelmRelease yang chart-nya membawa hook, lalu upgrade dan bandingkan hasil render.
- Tambahkan flux schema validate ke workflow pull request dalam mode report-only, dan baca apa yang ditandainya di repository Anda selama seminggu.
- Ganti satu konflik HPA melawan Git dengan spec.ignore bertarget, dengan kind terisi, dan pastikan field itu tidak lagi dikembalikan.
- Pindahkan satu webhook dari token ke generic-oidc dengan claim repository yang dikunci, lalu hapus secret tersimpan dari CI.
# 1. Resources still on the APIs removed in 2.9.
grep -rn -E "(image|notification)\.toolkit\.fluxcd\.io/v1beta2" clusters/ apps/
flux migrate --help # then migrate to the stable APIs
# 2. Every HelmRelease, to find the ones whose charts ship hooks.
kubectl get helmreleases -A
# 3. Server version against the supported minimums: 1.34.1, 1.35.0, 1.36.0.
kubectl version
# 4. Read-only adoption: validate without blocking merges (continue-on-error
# on the workflow step) and look at what it reports.
flux plugin install schema@sha256:REPLACE_WITH_THE_DIGEST_FROM_THE_CATALOG
flux schema validate ./clusters/production -s ecosystem -vLangkah satu dan dua yang mencegah insiden. Langkah tiga sampai lima adalah peningkatan yang bisa diambil sesuai kecepatan Anda, dan masing-masing bisa dibatalkan dengan menghapus satu file atau field.
Flux 2.9 paling pas dibaca sebagai beberapa batas kecil yang digambar ulang: CLI bisa tumbuh tanpa inti ikut tumbuh, Flux bisa menyerahkan sebuah field ke pihak lain, dan webhook bisa memercayai identitas, bukan sebuah string. Pin apa yang Anda install, batasi setiap aturan dengan target atau claim, dan baca default post-render sebelum upgrade.
Sumber dan bacaan lanjutan
- Blog Flux: Announcing Flux 2.9 GA
- Flux RFC-0013: CLI plugin system
- Dokumentasi Flux: referensi Kustomization, drift ignore rules dan decryption
- Dokumentasi Flux: Receivers, generic-oidc dan per-resource filtering
- Dokumentasi Flux: flux trigger receiver
- GitHub: README fluxcd/flux-schema
- GitHub: README fluxcd/flux-mirror
- GitHub: README FiloSottile/age, key post-quantum
- Blog Flux: Announcing Flux 2.8 GA
- GitHub: release notes Flux v2.9.0

