Dự án ingress-nginx đã được Kubernetes cho nghỉ hưu và không còn ra bản vá bảo mật. Controller vẫn chạy, nhưng mọi lỗ hổng phát hiện sau đó sẽ không được sửa, nên cluster nào đang dùng nó đều phải tính đường chuyển. Bài này giải thích Gateway API khác Ingress ở đâu, cách chạy pilot Traefik song song trên production mà không đổi DNS, cách giữ nguyên IP load balancer khi đổi controller, những lỗi hay gặp trong pilot và vì sao một pilot có thể kết thúc bằng rollback mà vẫn là thành công.
1. Các hướng thay ingress-nginx
Các bài Ingress và NodeBalancer: đưa traffic vào cluster Kubernetes và ingress-nginx và cert-manager: HTTPS tự động trên Kubernetes đã mô tả cách traffic vào cluster. Khi kubernetes/ingress-nginx ngừng phát triển, có vài hướng:
| Hướng | Đánh giá |
|---|---|
| Bản fork tiếp tục vá ingress-nginx | Mua thêm thời gian, không phải đích đến |
| Traefik | Một release hỗ trợ cả Ingress lẫn Gateway API, cộng đồng lớn |
| NGINX Gateway Fabric | Cùng lõi nginx, chỉ làm Gateway API |
| HAProxy, Envoy Gateway, Istio, Cilium | Làm được, nhưng nặng hơn nhu cầu của cluster web thông thường |
Traefik thường được thử trước vì nó cho phép chuyển tiếp mềm: site cũ vẫn dùng Ingress, site mới dùng HTTPRoute của Gateway API, chung một controller.
2. Gateway API khác Ingress ở đâu
Gateway API là bộ API kế nhiệm Ingress, đã ở trạng thái GA (v1). Thay vì một object Ingress gánh mọi thứ và đẩy phần còn lại vào annotation, Gateway API tách theo vai trò:
- GatewayClass: controller nào xử lý (Traefik, NGINX Gateway Fabric...). Tương đương
IngressClass. - Gateway: điểm vào, gồm các listener (cổng, giao thức, hostname, chứng chỉ TLS). Đội hạ tầng giữ object này.
- HTTPRoute: quy tắc định tuyến của từng ứng dụng, gắn vào Gateway qua
parentRefs. Mỗi site tự giữ route của mình. - ReferenceGrant: cho phép tham chiếu chéo namespace, ví dụ Gateway đọc Secret TLS nằm trong namespace của site.
Chuyển chart web sang Gateway API nghĩa là đổi từng annotation của ingress-nginx thành cơ chế tương ứng:
| Chart dùng Ingress | Chart dùng Gateway API + Traefik |
|---|---|
ingressClassName: nginx | parentRefs trỏ tới Gateway dùng chung |
rules[].host | hostnames của HTTPRoute |
from-to-www-redirect | HTTPRoute riêng cho www với filter chuẩn RequestRedirect |
ssl-redirect | HTTPRoute https-redirect, chỉ tạo khi site bật TLS |
proxy-body-size | Middleware buffering của Traefik, gắn qua filter ExtensionRef |
| Các timeout proxy | Cấu hình ở entrypoint của Traefik nếu mọi site dùng cùng giá trị |
Hai dòng cuối cho thấy giới hạn của Gateway API chuẩn: body size hay timeout chưa có trong phần chuẩn, mỗi controller làm một kiểu. Phần đó vẫn gắn chặt với controller được chọn.
3. Giữ nguyên IP load balancer khi đổi controller
Trên LKE, NodeBalancer gắn với Service kiểu LoadBalancer, không gắn với phần mềm phía sau. Gỡ Helm release ingress-nginx là xoá Service, Cloud Controller Manager xoá theo NodeBalancer, IP mất. Cài controller mới theo mặc định thì nhận IP mới. Cả hai đường đều dẫn tới đổi DNS hàng loạt.
Cách giữ IP: không xoá Service cũ, cài controller mới với Service ClusterIP, rồi đổi selector của Service cũ sang pod của controller mới. Ba lỗi hay gặp:
- Sai tên field trong chart. Một số bản chart Traefik đặt kiểu Service ở
service.spec.type, không phảiservice.type. Đặt sai, Helm không báo gì, Service vẫn làLoadBalancervà một load balancer mới được tạo, tốn tiền. Luôn chạyhelm show valuestrước khi--set. - Khác namespace. Service chỉ chọn được pod cùng namespace. Cài controller mới ở namespace khác thì patch xong Service có 0 endpoint, domain mất traffic ngay.
- Merge patch không xoá key cũ.
kubectl patch --type mergegộp từng key trongselector. Key cũ còn sót, Service đòi khớp cả label cũ lẫn mới và lại có 0 endpoint. Phải đặt key cũ bằngnulltrong cùng lệnh.
kubectl patch svc ingress-nginx-controller -n <ns> --type merge -p '{
"spec": {
"selector": {
"app.kubernetes.io/component": null,
"app.kubernetes.io/name": "traefik",
"app.kubernetes.io/instance": "traefik"
},
"ports": [
{"name": "http", "port": 80, "targetPort": 8000, "protocol": "TCP"},
{"name": "https", "port": 443, "targetPort": 8443, "protocol": "TCP"}
]
}
}'
kubectl describe svc ingress-nginx-controller -n <ns> | grep -E "Selector|Endpoints"Rollback là patch ngược selector về pod ingress-nginx, có hiệu lực trong vài giây nếu pod cũ vẫn chạy song song. Sau mỗi lần patch, kiểm tra Endpoints không rỗng rồi mới coi là xong. "patched" không có nghĩa là traffic đã tới đúng chỗ. Nên thử toàn bộ quy trình này trên cluster test trước.
4. Chạy pilot Gateway API song song trên production
Cách thử rẻ và an toàn nhất:
- Cài CRD Gateway API (kênh standard) và CRD của controller. CRD là API group mới, không ảnh hưởng
Ingressđang chạy. - Cài controller pilot thành release riêng, namespace riêng, Service
LoadBalancerriêng với IP mới. - Không đổi DNS. Khách vẫn đi qua ingress-nginx. Pilot được test bằng cách gọi thẳng IP mới với header
Host:curl -H "Host: site-a.example.com" http://<ip-pilot>/. - Chọn vài site đại diện: ít nhất một site có TLS và một site không, mỗi site pilot chạy trong namespace riêng.
- Lưu lại toàn bộ values, manifest, lịch sử Helm của từng site trước khi đụng vào.
Lỗi đầu tiên hay gặp khi tạo Gateway: listener HTTPS có certificateRefs rỗng bị từ chối (certificateRefs or options must be specified when mode is Terminate). Cách xử lý là gắn một chứng chỉ tự ký làm placeholder.
Lỗi lưu trữ khi chạy song song
- PersistentVolume trùng tên. PV là tài nguyên cấp cluster. Nếu tên lấy từ
fullnameOverride, dùng nguyên values của site thật thì PV pilot trùng PV production, Helm từ chối. Thiết kế PV tĩnh này được trình bày ở bài Helm chart với NFS PersistentVolume tĩnh, claimRef và ba loại probe. - Đổi tên thì đường dẫn NFS đổi theo, thư mục mới chưa tồn tại, mount thất bại.
- Lệnh
helm installquá thời gian chờ vẫn chạy nền và có thể ghi đè patch bạn vừa làm. Chờ release về trạng thái cuối trước khi sửa tay. - Xoá PVC bị treo vì pod kẹt init vẫn giữ nó (finalizer
kubernetes.io/pvc-protection). Scale Deployment về 0 trước rồi mới xoá. - Init container chạy trên dữ liệu thật. Nếu pilot đọc chung database production,
php artisan migrate --forcetrong init container sẽ đổi schema của site thật khi image pilot mới hơn. Pilot phải dùng đúng image đang chạy, hoặc tắt migrate.
Redirect toàn cục làm site không TLS trả 404
Đặt redirect HTTP sang HTTPS một lần ở entrypoint là dựa trên giả định mọi site giống nhau. Giả định đó đúng với timeout, nhưng sai với TLS: site không bật TLS bị chuyển sang https://, nơi không có route nào, và trả 404. Redirect nên nằm ở từng site: chart chỉ tạo HTTPRoute https-redirect khi tls.enabled là true.
Hai lỗi nhỏ
- Helm render số lớn như
134217728(128 MiB) thành1.34217728e+08, API từ chối vì trường cần số nguyên. Sửa bằng{{ .Values.maxBodySizeBytes | int64 }}. - cert-manager có thể cấp chứng chỉ mới cho domain của site pilot vì challenge HTTP-01 vẫn đi qua controller production. Không hại gì, nhưng tốn lượt trong giới hạn cấp trùng chứng chỉ hằng tuần của Let's Encrypt.
5. certificateRefs của Gateway API là trường atomic
Đây thường là vấn đề quyết định số phận của pilot. Trong Gateway API, danh sách certificateRefs của một listener là trường atomic: ai ghi sau sẽ thay toàn bộ danh sách, không gộp với người khác. Hai Helm release độc lập cùng ghi vào Gateway dùng chung sẽ làm mất chứng chỉ của nhau, site bị phục vụ bằng chứng chỉ mặc định của controller.
Giải pháp tạm là bỏ quyền ghi Gateway khỏi chart site: mỗi site chỉ gắn label vào Secret TLS của mình, một script duy nhất gom mọi Secret có label đó rồi ghi lại certificateRefs, dựa vào resourceVersion để thử lại khi xung đột, kèm CronJob làm lưới an toàn. Cách này chạy được nhưng thêm một điểm lỗi chung cho mọi hostname. Cách đúng là ListenerSet, cho phép mỗi site tự khai listener và chứng chỉ mà không sửa Gateway. Nếu controller bạn chọn chưa hỗ trợ ListenerSet, đó là lý do chính đáng để dừng pilot.
6. Rollback và danh sách dọn dẹp
Vì production chưa từng cutover, rollback chỉ là gỡ namespace pilot và controller pilot, đưa values về chỗ cũ, không gián đoạn nào. Nhưng rollback cần một danh sách dọn dẹp:
- CRD. Helm không xoá CRD khi gỡ release. CRD Gateway API dùng lại được cho bước tiếp theo, CRD riêng của controller pilot cần gỡ có chủ đích.
- PV, PVC và thư mục NFS của site pilot.
- Tác dụng phụ lên pipeline. Nếu bạn cô lập site pilot bằng cách dời file values sang thư mục khác để pipeline deploy không ghi đè, pipeline cũng sẽ bỏ qua chính các site production đó. Sau rollback, kiểm tra các site này đã nhận lại bản build mới nhất.
- Load balancer và IP của controller pilot.
7. Checklist chuyển từ ingress-nginx sang Gateway API
- Liệt kê mọi annotation ingress-nginx đang dùng và cơ chế thay thế tương ứng.
- Kiểm chứng giả định "mọi site giống nhau" bằng values thật, từng key một.
- Đọc ngữ nghĩa trường của Gateway API (atomic hay merge) trước khi thiết kế chart.
- Pilot song song: IP riêng, namespace riêng, DNS không đổi, test bằng header
Host. - Pilot dùng đúng image đang chạy, không để migrate đổi schema production.
- Có quy trình giữ IP load balancer đã thử trên cluster test.
- Có danh sách dọn dẹp cho rollback.
Tài liệu khái niệm Gateway API trên kubernetes.io là điểm bắt đầu tốt. Các bài khác của series nằm trong chuyên mục blog kỹ thuật của Sudo.
Bài tiếp theo
NGINX Gateway Fabric: ListenerSet và cert-manager 1.21 trên LKE. Một Gateway, một load balancer, mỗi site tự khai chứng chỉ bằng ListenerSet. Đây cũng là bài kết series.



