Ringkasan Utama
Ingress2Gateway 1.0, asisten migrasi resmi dari SIG Network, mengonversi lebih dari 30 annotation ingress-nginx ke Gateway API dan menguji tiap annotation pada controller sungguhan. Tool ini tetap tidak bisa menerjemahkan configuration snippet, batas body size atau URL normalization, jadi setiap hasil perlu direview, diuji di dev cluster, dan trafik dialihkan bertahap sebelum Ingress dihapus.
Migrasi yang paling ditakuti bukan HTTPRoute pertama. Yang menakutkan adalah annotation yang tiga tahun lalu ditambahkan entah oleh siapa dan diam-diam mengubah perilaku satu path. ingress-nginx mengumpulkan hal seperti itu lewat annotation, ConfigMap dan snippet, sementara Gateway API tidak punya tempat untuk menyalinnya. Ingress2Gateway 1.0 adalah tool dari proyek Kubernetes untuk menemukannya.
Tulisan ini adalah pembacaan teliti atas pengumuman 1.0, README repository, dokumentasi provider ingress-nginx, dan artikel Kubernetes tentang perilaku ingress-nginx yang merusak migrasi. Saya tidak menjalankannya pada cluster sungguhan untuk artikel ini, jadi semua input, output dan warning di bawah dikutip dari sumber tersebut, dan setiap angka bisa ditelusuri ke sana. Urutannya mengikuti cara kerja nyata: install, jalankan, baca warning, perbaiki manual, lalu cutover.
Yang berubah di 1.0, dan yang masih bukan
Perubahan utamanya adalah cakupan. Sebelum 1.0, Ingress2Gateway hanya mendukung tiga annotation ingress-nginx. Rilis 1.0 mendukung lebih dari 30 annotation umum, termasuk CORS, backend TLS, regex matching dan path rewrite. Perubahan kedua lebih penting untuk kepercayaan: setiap annotation yang didukung, beserta kombinasi representatif annotation umum, diuji dengan integration test yang menjalankan controller ingress-nginx sungguhan dan beberapa controller Gateway API, menerapkan Ingress, menerjemahkannya dengan tool ini, menerapkan hasilnya, lalu membandingkan perilaku runtime seperti routing, redirect dan rewrite. Artikel rilisnya menegaskan bahwa yang dibandingkan adalah perilaku, bukan bentuk YAML.
Arsitekturnya punya dua sisi. Provider membaca resource Ingress dan CRD provider menjadi intermediate representation. Emitter mengubahnya menjadi output: emitter standard bawaan menulis resource Gateway dan HTTPRoute inti, sedangkan emitter lain menambah resource untuk satu implementasi. Artikel rilis menyebut agentgateway, envoy-gateway dan kgateway. README di branch main, yang saya baca pada 2026-10-17 dan bisa lebih baru dari tag 1.0, juga mencantumkan airlock-microgateway dan gce, serta sembilan provider seperti istio, kong, traefik dan nginx. v1.0 menargetkan Gateway API v1.5.0, dan README menyebut outputnya umumnya forward-compatible dengan v1.4.
Yang bukan dilakukannya: konversi sekali jalan. Artikel rilis mendefinisikan tiga tugas, yaitu memigrasi konfigurasi dan perilaku yang didukung, mengidentifikasi konfigurasi yang tidak didukung beserta alternatifnya, dan memberi kesempatan menilai ulang serta membuang konfigurasi yang tidak lagi diinginkan. README menambahkan bahwa tool ini tidak dimaksudkan untuk menyalin annotation dari Ingress ke Gateway API. Anggap outputnya sebagai draf pertama yang disertai daftar tugas. Panduan migrasi dari proyek Gateway API sendiri sejalan soal cakupan: menjelaskan konsep, menunjukkan konversi manual dan menunjuk tool ini untuk konversi otomatis, tetapi menyatakan tidak akan menyiapkan Anda untuk live migration.
Install dan jalankan dengan tiga cara
Install dengan go install pada tag 1.0.0 atau dengan Homebrew; README juga menunjuk halaman releases berisi binary. Perintah print menerima input dengan tiga cara: file manifest, satu namespace yang dibaca langsung dari cluster, atau semua namespace. Flag yang wajib adalah --providers. Semua di bawah ini diambil dari artikel rilis dan tabel flag di README.
# Install: pin the tag, or use Homebrew
go install github.com/kubernetes-sigs/ingress2gateway@v1.0.0
brew install ingress2gateway
# 1. From manifest files (comma-separated)
ingress2gateway print --input-file my-manifest.yaml,my-other-manifest.yaml \
--providers=ingress-nginx > gwapi.yaml
# 2. One namespace, read live from your current kubeconfig context
ingress2gateway print --namespace my-api --providers=ingress-nginx > gwapi.yaml
# 3. The whole cluster
ingress2gateway print --providers=ingress-nginx --all-namespaces > gwapi.yaml
# Optional: implementation-specific extras (agentgateway | envoy-gateway | kgateway)
ingress2gateway print --providers=ingress-nginx -A --emitter envoy-gateway > gwapi.yaml
# If your controller uses a different IngressClass, the default (nginx) selects nothing
ingress2gateway print --providers=ingress-nginx \
--ingress-nginx-ingress-class=ingress-nginx -A > gwapi.yamlAda empat flag yang perlu diketahui sebelum run pertama. --output menerima yaml, json atau kyaml. --ingress-nginx-ingress-class bawaannya nginx, jadi cluster yang memakai nama class lain tidak memilih apa pun sampai flag ini diatur. --allow-experimental-gw-api menyertakan field Gateway API experimental seperti URLRewrite di output. Urutannya deterministik: Ingress tertua berdasarkan creation timestamp menang saat konflik, seri diputuskan oleh namespace dan nama, dan rule konflik yang datang belakangan dilaporkan sebagai error, bukan digabung.
Contoh konversi: input, output dan warning
Artikel rilis memproses satu Ingress dengan tool ini. Ingress itu membawa enam annotation: proxy-body-size 1G, use-regex, proxy-send-timeout dan proxy-read-timeout bernilai 1, enable-cors, serta configuration-snippet yang mengatur header Request-Id. Path-nya regex, /users/(\d+), dengan path type ImplementationSpecific, dan TLS dikonfigurasi.
# Input: the Ingress used in the Kubernetes release post
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
namespace: my-ns
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "1G"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-send-timeout: "1"
nginx.ingress.kubernetes.io/proxy-read-timeout: "1"
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/configuration-snippet: |
more_set_headers "Request-Id: $req_id";
spec:
ingressClassName: nginx
rules:
- host: my-host.example.com
http:
paths:
- path: /users/(\d+)
pathType: ImplementationSpecific
backend:
service:
name: website-service
port:
number: 80
tls:
- hosts: [my-host.example.com]
secretName: my-secretManifest hasilnya berisi Gateway dengan listener HTTP dan HTTPS, sebuah HTTPRoute dengan CORS filter, path match RegularExpression dan request timeout, ditambah HTTPRoute kedua yang mengalihkan port 80 ke HTTPS dengan status 308. Route kedua itu meniru redirect bawaan ingress-nginx untuk host ber-TLS. Baca baris timeout dua kali: kedua annotation sumber bernilai 1, tetapi tool menghasilkan request timeout 10 detik, yang disebut artikel sebagai terjemahan best-effort dari timeout level TCP. Informasi sebenarnya ada di warning.
# Output (excerpt): the HTTPRoute as first generated
rules:
- backendRefs:
- name: website-service
port: 80
filters:
- type: CORS
cors:
allowOrigins: ['*']
maxAge: 1728000
matches:
- path:
type: RegularExpression
value: (?i)/users/(\d+).* # prefix + case-insensitive, like ingress-nginx
timeouts:
request: 10s # input said 1 and 1: this is the tool's best effort
# plus a second HTTPRoute on port 80: RequestRedirect, scheme https, statusCode 308| Pesan dari tool | Artinya | Keputusan yang dipaksakan |
|---|---|---|
| WARN: annotation configuration-snippet tidak didukung | Directive NGINX mentah tidak punya field di Gateway API. | Cari padanan spesifik implementasi, atau buang perilakunya. |
| INFO: regex path match case-insensitive | Pola output memakai (?i) di depan dan .* di belakang. | Pertahankan jika klien memakai huruf campuran; jika tidak, hapus keduanya untuk exact match. |
| WARN: timeouts.request best-effort | Timeout ingress-nginx berlevel TCP; tool memperkirakannya menjadi 10s. | Pilih request timeout yang benar-benar diinginkan, misalnya 3s. |
| WARN: gagal menerapkan proxy-body-size | Tidak ada field standar Gateway API; kebanyakan implementasi punya default. | Periksa batas di implementasi Anda, atau coba ekstensi emitter. |
| WARN: URL normalization tidak bisa dikonfigurasi | Gateway API tidak bisa mengatur normalisasi RFC 3986; tiap implementasi berbeda. | Uji dot segment dan double slash pada controller tujuan. |
Rekomendasi tool untuk listener HTTP juga merupakan keputusan: port 80 dan redirect ditambahkan hanya untuk menyamai ingress-nginx, dan artikel menyarankan Anda boleh menghapus keduanya jika tidak ingin melayani HTTP sama sekali.
Yang tidak bisa diterjemahkan dan yang hanya setengah
README provider ingress-nginx memisahkan annotation menjadi yang dikonversi dan yang dikenali tetapi tidak dikonversi. Kelompok kedua hanya memberi warning tanpa output, itu mode gagal yang aman: Anda tahu saat review. Tabel di bawah dirangkum dari README tersebut sebagaimana dibaca pada 2026-10-17; isinya keluarga annotation yang menentukan apakah cluster nyata bisa bermigrasi mulus.
| Keluarga annotation | Dikonversi menjadi | Catatan di README |
|---|---|---|
| canary, canary-weight, canary-by-header | Weight backend dan HTTPHeaderMatch | canary-by-header-pattern dan canary-by-cookie dikenali tetapi tidak dikonversi |
| rewrite-target, app-root | URLRewrite dengan ReplaceFullPath; redirect 302 untuk app-root | Referensi capture group seperti $1 tidak didukung dan ditandai |
| permanent-redirect, temporal-redirect, ssl-redirect | Filter HTTPRequestRedirect dengan status code yang bisa dipilih | proxy-redirect-from dan proxy-redirect-to dikenali tetapi tidak dikonversi |
| upstream-vhost, connection-proxy-header, x-forwarded-prefix | Filter HTTPRequestHeaderModifier | custom-headers dikenali tetapi tidak dikonversi |
| proxy-connect, send dan read timeout | Konfigurasi timeout Gateway API, nilai dalam detik | Artikel rilis menyebutnya best-effort dan menunjukkan input 1s menghasilkan request timeout 10s |
| backend-protocol, proxy-ssl-* | GRPCRoute untuk GRPC dan GRPCS; BackendTLSPolicy untuk HTTPS dan GRPCS | proxy-ssl-verify-depth dan proxy-ssl-protocols tidak dikonversi; FCGI memberi warning |
| whitelist-source-range, denylist-source-range | Tercantum sebagai kontrol CIDR yang didukung | README tidak menyebut objek Gateway API yang membawanya; baca outputnya |
| ssl-passthrough | TLSRoute plus listener Passthrough di port 443 | HTTPRoute untuk Ingress itu digantikan, bukan ditambah |
Dua hal yang tidak diterjemahkan perlu disebut. configuration-snippet, annotation yang dalam pengumuman retirement Kubernetes disebut sebagai celah keamanan serius, memang tidak punya terjemahan. Lalu proxy-body-size dikonversi oleh README provider menjadi Kubernetes quantity, tetapi run di artikel rilis sendiri melaporkan bahwa emitter standard tidak bisa menerapkannya karena Gateway API tidak punya field standar untuk itu. Keduanya bersumber; hanya beda lapisan, dan aturan praktisnya: percayai warning di output Anda sendiri di atas tabel mana pun.
# Which ingress-nginx annotations does this cluster actually use, most common first?
# Compare the result with the provider README before you trust a clean run.
kubectl get ingress -A -o json \
| jq -r '.items[].metadata.annotations // {} | keys[]' \
| grep '^nginx.ingress.kubernetes.io/' \
| sort | uniq -c | sort -rnBuat inventaris sebelum menjalankan. Hitung annotation ingress-nginx mana yang benar-benar dipakai cluster Anda, lalu bandingkan dengan README provider. Annotation yang muncul di banyak Ingress dan tidak didukung adalah biaya migrasi yang sesungguhnya.
Kegagalan yang harus diantisipasi: terjemahan benar yang menghasilkan 404
Artikel Kubernetes tentang perilaku mengejutkan ingress-nginx menyatakan pola risikonya dalam satu kalimat: terjemahan yang tampak benar tetap bisa menyebabkan outage jika mengabaikan keanehan ingress-nginx. Ada lima. Path regex bersifat prefix match dan case-insensitive. use-regex berlaku untuk setiap path pada host itu di semua Ingress. rewrite-target diam-diam berarti regex. Request tanpa trailing slash dialihkan dengan 301 ke path ber-slash. Dan URL dinormalisasi sebelum dicocokkan, misalnya menggabungkan double slash dan dot segment.
Contoh paling tajam adalah soal regex. Pola yang dimaksudkan mencocokkan tiga huruf kapital ternyata mencocokkan /uuid di ingress-nginx, karena aturan prefix dan case. Implementasi berbasis Envoy seperti Istio, Envoy Gateway dan Kgateway melakukan full match yang case-sensitive, sehingga route yang tampak setia itu mengembalikan 404 untuk trafik yang tadinya berjalan. Perbaikan di artikel adalah pola prefix yang case-insensitive, atau flag (?i). Logika yang sama menjebak rewrite-target: ada typo di path yang tidak disadari karena matching-nya longgar, lalu beralih ke Exact mengubah setiap request yang dimaafkan menjadi 404.
# Wrong: a "faithful" copy of use-regex with path /[A-Z]{3}.
# ingress-nginx matched /uuid (prefix + case-insensitive).
# A full, case-sensitive matcher returns 404 for it.
- matches:
- path:
type: RegularExpression
value: "/[A-Z]{3}"
# Right: preserve the old semantics explicitly...
- matches:
- path:
type: RegularExpression
value: "/[a-zA-Z]{3}.*"
# ...or use the (?i) flag, which Istio, Envoy Gateway and Kgateway support
- matches:
- path:
type: RegularExpression
value: "(?i)/[a-z]{3}.*"Jangan merapikan output regex dari tool sebelum tahu apa yang dikirim klien. Menghapus (?i) dan .* di belakang hanya benar jika tidak ada klien yang memanggil path dengan huruf berbeda atau dengan suffix. 404 di gateway bersifat senyap, karena tidak ada yang gagal selama migrasi sampai trafik tiba.
Kasus trailing slash adalah bayangan cerminnya. ingress-nginx mengalihkan untuk Anda; implementasi Gateway API yang conformant tidak mengatur redirect secara diam-diam, jadi apa pun yang bergantung pada 301 itu butuh filter request redirect eksplisit. Uji kelimanya dengan perintah curl yang sama ke kedua data plane sebelum mempercayai hasilnya.
Edit manual output menjadi sesuatu yang bisa Anda pertanggungjawabkan
Proses edit di artikel rilis adalah template yang bagus. Edit itu menghapus (?i) di depan dan .* di belakang untuk mendapat pola exact yang case-sensitive, menurunkan request timeout dari 10s ke 3s karena service seharusnya menjawab cepat, membuang listener port 80 beserta route redirect-nya karena HTTP tidak diinginkan, dan menyerahkan body size ke default implementasi setelah dicek. Tiap edit menjawab satu warning, kebiasaan yang layak ditiru. Jika sebuah route tidak boleh punya timeout sama sekali, panduan timeout Gateway API menyebut agar request diatur ke 0s; HTTPRouteRequestTimeout adalah fitur extended support, jadi pastikan implementasi Anda mendukungnya.
# The release post's hand-edited result (excerpt)
spec:
gatewayClassName: nginx
listeners: # port 80 listener removed: no HTTP wanted
- hostname: my-host.example.com
name: my-host-example-com-https
port: 443
protocol: HTTPS
tls:
certificateRefs:
- group: ""
kind: Secret
name: my-secret
---
# HTTPRoute rule, after editing
matches:
- path:
type: RegularExpression
value: /users/(\d+) # (?i) and trailing .* removed on purpose
timeouts:
request: 3s # was 10s as generated
# The ssl-redirect HTTPRoute (port 80, 308) was deleted with the listener.Lalu verifikasi sebelum trafik apa pun dipindah. Dry-run sisi server menangkap error skema, status route memberi tahu apakah controller menerimanya, dan curl dengan header Host menguji aturan path tanpa menyentuh DNS. Periksa minimal bahwa default body size cocok dan timeout tiga detik cukup, dua pengecekan yang sama dengan yang disebut artikel rilis.
# Schema and admission errors, without creating anything
kubectl apply --dry-run=server -f gwapi.yaml
# Apply to the DEV cluster, then read the status the controller wrote back
kubectl apply -f gwapi.yaml
kubectl get gateway,httproute -n my-ns
kubectl describe httproute my-ingress-my-host-example-com -n my-ns
# Exercise the rules without touching DNS: pin the host to the new gateway address
curl -isS --resolve my-host.example.com:443:<gateway-ip> https://my-host.example.com/users/42Cutover tanpa big bang
Saran cutover di artikel rilis singkat dan layak diikuti apa adanya. Uji di development cluster, deploy konfigurasi Gateway API berdampingan dengan Ingress yang ada, alihkan trafik bertahap dengan weighted DNS, cloud load balancer atau fitur traffic splitting platform Anda, dan hanya setelah semuanya pindah, hapus resource Ingress dan uninstall Ingress controller. Alasannya adalah kemampuan pulih: kesalahan konfigurasi yang lolos dari tes hanya memakan perubahan weight, bukan insiden.
#!/usr/bin/env bash
# My scaffolding, not part of ingress2gateway: same paths against both data planes,
# print any status code that differs. Fill in both addresses and your real paths.
HOST=my-host.example.com
OLD=<ingress-nginx-ip>
NEW=<gateway-ip>
for path in /users/42 /USERS/42 /users/42/orders /users/42/ //users/42; do
a=$(curl -sk -o /dev/null -w '%{http_code}' --resolve "$HOST:443:$OLD" "https://$HOST$path")
b=$(curl -sk -o /dev/null -w '%{http_code}' --resolve "$HOST:443:$NEW" "https://$HOST$path")
[ "$a" = "$b" ] || echo "DIFF $path ingress-nginx=$a gateway=$b"
doneTrade-off-nya adalah biaya dan waktu. Menjalankan dua data plane paralel berarti dua set load balancer dan dua tempat bug bisa bersembunyi, dan retirement ingress-nginx, yang dalam pengumuman dijadwalkan Maret 2026 tanpa rilis maupun perbaikan keamanan sesudahnya, adalah alasan Anda tidak bisa paralel selamanya. Deployment yang ada tetap berjalan menurut pengumuman, jadi tekanannya ada pada postur keamanan, bukan outage seketika.
Yang dikerjakan hari Senin
Mulai dengan pass read-only. Tidak ada langkah di bawah yang mengubah cluster sampai langkah kelima.
- Hitung annotation ingress-nginx yang dipakai cluster Anda dan bandingkan dengan README provider.
- Jalankan ingress2gateway print untuk semua namespace, tulis manifest dan warning ke file terpisah.
- Ubah setiap WARN menjadi tiket dengan pemilik dan keputusan: terjemahkan, ganti atau buang.
- Daftar klien yang mengirim path huruf campuran atau ber-suffix dan yang bergantung pada redirect trailing slash.
- Terapkan ke development cluster, bandingkan status code dengan jalur lama, lalu alihkan trafik bertahap.
# Steps 1 and 2, read-only. The manifest goes to stdout and the warnings to the
# terminal in the release post's examples, so split them to keep both.
ingress2gateway print --providers=ingress-nginx --all-namespaces \
> gwapi.yaml 2> warnings.log
grep -c 'WARN' warnings.log
grep -B1 -A3 'Unsupported annotation' warnings.logLoop perbandingan di blok terakhir adalah scaffolding buatan saya, bukan bagian dari tool: ia mengambil path yang sama dari kedua controller dan mencetak status code yang berbeda. Isi alamat dan path dari daftar langkah empat Anda.
Ingress2Gateway 1.0 layak dipakai karena jujur soal batasnya: ia menerjemahkan apa yang bisa dibuktikan, memperingatkan sisanya, dan proyeknya menguji terjemahan terhadap controller sungguhan. Aturan yang saya bawa dari sini: perlakukan setiap warning sebagai keputusan, bukan noise, dan anggap 404 dari route yang tampak setia sebagai kegagalan yang paling mungkin. Migrasi dengan kedua plane berjalan dan hapus Ingress paling akhir.
Sumber
- Blog Kubernetes: Announcing Ingress2Gateway 1.0
- Blog Kubernetes: Lima perilaku mengejutkan ingress-nginx sebelum migrasi
- GitHub: README kubernetes-sigs/ingress2gateway
- GitHub: annotation yang didukung provider ingress-nginx
- Blog Kubernetes: Retirement Ingress NGINX, yang perlu Anda ketahui
- Dokumentasi Gateway API: HTTP timeouts
- Dokumentasi Gateway API: migrasi dari Ingress

