Ingress và Ingress controller
Mục tiêu bài học
Sau bài này, bạn phải làm được những việc sau, và làm được trong khoảng 8 đến 10 phút nếu gặp trong đề thi:
- Viết Ingress rule route theo host và theo path tới hai Service khác nhau.
- Kiểm tra Ingress đã được controller nhận, và test đúng bằng
curlvới headerHost. - Phân biệt và xử lý hai lỗi test hay gặp nhất: 404 (không match rule) và 502/503 (backend có vấn đề).
Ôn nhanh 30 giây
Bài trước bạn đã chốt: Service có ClusterIP và DNS name dạng <svc>.<ns>.svc.cluster.local, và nếu Service không có endpoint thì kết nối sẽ fail. Ingress đứng trên Service: nó không thay thế Service, nó chỉ quyết định request HTTP nào được đẩy vào Service nào.
[!NOTE] Ingress là object khai báo rule ở layer 7 (HTTP/HTTPS). Bản thân object Ingress không làm gì cả. Phải có Ingress controller (thường là
ingress-nginx) đọc object đó rồi cấu hình reverse proxy. Không có controller thìkubectl get ingressvẫn ra resource, nhưng không có traffic nào được route.
Luồng traffic đầy đủ trông như sau:
flowchart LR
Client["👤<br/>Client gửi Host header"] --> IC["🚪<br/>Ingress controller<br/>NodePort 30080"]
IC --> Rule["📜<br/>Match rule<br/>host + path"]
Rule --> Svc["🔗<br/>Service ClusterIP"]
Svc --> EP["📦<br/>Pod endpoints"]
Rule -.->|không khớp rule| E404["❌ 404 Not Found"]
Svc -.->|không có endpoint| E503["⚠️ 502 / 503"]
Ví dụ có hướng dẫn
Bối cảnh: namespace shop có hai app, web (nginx, port 80) và api (http-echo, port 5678). Yêu cầu: shop.example.com/ vào web, shop.example.com/api vào api.
Bước 1: Kiểm tra cluster đã có Ingress controller chưa. Đây là bước nhiều người bỏ qua rồi ngồi sửa YAML 15 phút.
kubectl get ingressclass
kubectl get pods -A | grep -i ingress
Có nginx trong danh sách ingressclass là dùng được. Không có thì phải cài controller trước, nhưng trong đề CKA controller gần như luôn được cài sẵn.
Bước 2: Tạo workload và Service.
kubectl create ns shop
kubectl -n shop create deploy web --image=nginx:1.25 --port=80
kubectl -n shop expose deploy web --port=80 --name=web
kubectl -n shop create deploy api --image=hashicorp/http-echo:1.0
kubectl -n shop expose deploy api --port=80 --target-port=5678 --name=api
Chú ý: --port=80 là port của Service, --target-port=5678 là port container đang listen. Nhầm chỗ này chính là nguồn gốc lỗi 502/503 ở phần sau.
Bước 3: Sinh YAML bằng kubectl create ingress thay vì gõ tay. Cú pháp --rule="host/path=service:port", path kết thúc bằng * thì thành pathType: Prefix.
kubectl -n shop create ingress shop-ing \
--class=nginx \
--rule="shop.example.com/*=web:80" \
--rule="shop.example.com/api*=api:80" \
--dry-run=client -o yaml > shop-ing.yaml
Bước 4: Đọc lại và chỉnh YAML (thêm TLS, đổi port number sang port name, sửa path nếu cần):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-ing
namespace: shop
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 80
kubectl apply -f shop-ing.yaml
kubectl -n shop get ingress
kubectl -n shop describe ingress shop-ing
Trong describe, phần Rules: phải hiện đúng Host, Path và Backends. Đó là bằng chứng rule đã vào đúng chỗ.
Bước 5: Test bằng curl với header Host.
kubectl -n ingress-nginx get svc ingress-nginx-controller
# ví dụ trả về NodePort 30080
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: shop.example.com" http://10.0.0.10:30080/
curl -s -H "Host: shop.example.com" http://10.0.0.10:30080/api
Không tiện dùng NodePort thì dùng port-forward, miễn giữ nguyên header Host:
kubectl -n ingress-nginx port-forward svc/ingress-nginx-controller 8080:80
curl -s -H "Host: shop.example.com" http://localhost:8080/api
Bước 6: Xử lý lỗi khi test.
- Ra 404: controller nhận request nhưng không rule nào khớp. Kiểm tra lại
Hostbạn gửi có đúng bằnghosttrong YAML, và path có khớppathTypekhông. - Ra 503: rule đã khớp, nhưng Service backend không có endpoint. Chạy
kubectl -n shop get endpoints web api. Nếu ENDPOINTS là<none>thì saitargetPort, hoặc pod chưa Ready.
[!TIP] Trong đề thi, thứ tự path trong YAML không quan trọng. Nginx Ingress luôn chọn prefix dài nhất trước, nên
/apithắng/mà bạn không cần sắp xếp gì.
Thực hành có hướng dẫn
Phần này là phần chính. Bạn hãy tự làm trước, chỉ mở phần giải sau khi đã thử ít nhất một lần.
Bài 1: Hai host, một cluster (4 phút)
Tạo Ingress tên multi-host trong namespace shop với:
web.example.comroute mọi path vào Servicewebport 80.api.example.comroute mọi path vào Serviceapiport 80.
kubectl -n shop create ingress multi-host \
--class=nginx \
--rule="web.example.com/*=web:80" \
--rule="api.example.com/*=api:80" \
--dry-run=client -o yaml | kubectl apply -f -
Xem cách kiểm tra kết quả
kubectl -n shop describe ingress multi-host
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: web.example.com" http://<NODE_IP>:30080/
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: api.example.com" http://<NODE_IP>:30080/
Cả hai phải trả 200. Nếu một cái trả 404, gần như chắc chắn bạn gõ sai host trong lệnh curl hoặc trong --rule.
Bài 2: Sửa lỗi 404 do pathType quá chặt (3 phút)
Apply manifest dưới đây, sau đó test curl -H "Host: shop.example.com" .../api/v1/users và giải thích tại sao ra 404.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: exact-demo
namespace: shop
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Exact
backend:
service:
name: api
port:
number: 80
Xem lời giải
pathType: Exact chỉ khớp chính xác chuỗi /api, không khớp /api/v1/users. Sửa nhanh nhất:
kubectl -n shop patch ingress exact-demo --type=json \
-p '[{"op":"replace","path":"/spec/rules/0/http/paths/0/pathType","value":"Prefix"}]'
Test lại, phải ra 200. Đây là bẫy số một của dạng bài Ingress: đề cho path kiểu /api rồi bảo "mọi request dưới /api", bạn phải dùng Prefix.
Bài 3: Sửa lỗi 502 do sai targetPort (3 phút)
kubectl -n shop expose deploy api --port=80 --target-port=8080 --name=api-broken
Tạo Ingress trỏ vào api-broken rồi test. Bạn sẽ thấy 502 hoặc 503. Chẩn đoán theo đúng thứ tự sau:
kubectl -n shop get endpoints api-broken
Xem lời giải
ENDPOINTS sẽ là <none> vì targetPort: 8080 không khớp port container đang listen (5678). Không có endpoint thì controller không có gì để proxy tới.
kubectl -n shop patch svc api-broken -p '{"spec":{"ports":[{"port":80,"targetPort":5678}]}}'
kubectl -n shop get endpoints api-broken
Đợi vài giây, endpoint xuất hiện, test lại ra 200.
Bài học: 404 là lỗi ở tầng rule, 502/503 là lỗi ở tầng backend. Nhìn mã lỗi là biết phải đi kiểm tra phía nào.
Bài 4: Đề mini bấm giờ 6 phút
Trong namespace
shop, tạo Ingress têntimeboxrouteshop.example.com/api(và mọi path con của nó) tới Serviceapiport 80, đồng thời route tất cả host khác vàoweb. Kiểm tra bằngcurlvà chứng minh cả hai rule đều hoạt động.
Xem gợi ý và lời giải
Gợi ý: rule không có host sẽ đóng vai trò catch-all.
kubectl -n shop create ingress timebox --class=nginx \
--rule="shop.example.com/api*=api:80" \
--rule="*=web:80" \
--dry-run=client -o yaml | kubectl apply -f -
Kiểm tra:
kubectl -n shop describe ingress timebox
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: shop.example.com" http://<NODE_IP>:30080/api
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: batky.example.com" http://<NODE_IP>:30080/
Lỗi thường gặp và troubleshoot
| Triệu chứng | Nguyên nhân hay gặp | Lệnh kiểm tra |
|---|---|---|
| 404 từ controller | Host gửi lên không khớp host trong rule, hoặc pathType: Exact quá chặt, hoặc thiếu ingressClassName |
kubectl -n shop describe ingress <name> |
| 404 dù YAML đúng | Rule nằm ở namespace khác với Service, hoặc controller không phải class bạn khai | kubectl get ingressclass, kubectl -n shop get ingress |
| 503 | Service backend không có endpoint (sai targetPort, pod chưa Ready, sai tên Service) |
kubectl -n shop get endpoints <svc> |
| 502 | Controller nối được tới IP nhưng backend đóng kết nối, sai targetPort, hoặc app không nói HTTP |
kubectl -n shop exec deploy/web -- curl -s <cluster-ip>:80 |
| Connection refused khi curl | Sai NodePort, controller Service không tồn tại, hoặc firewall node | kubectl -n ingress-nginx get svc ingress-nginx-controller |
Ingress có mà describe không có Rules |
Rule sai cấu trúc, backend thiếu port |
kubectl -n shop get ingress -o yaml |
Thêm một mẹo chẩn đoán: log của controller thường ghi rõ request nào bị 404.
kubectl -n ingress-nginx logs deploy/ingress-nginx-controller --tail=50
==Không có Ingress controller thì Ingress chỉ là file YAML vô hồn.== Luôn kiểm tra controller và ingressclass trước khi sửa manifest.
Góc chuyên gia
- Trong đề CKA, controller thường đã cài sẵn. Việc của bạn là viết rule đúng, không phải cài lại controller. Đừng mất thời gian debug controller.
- Dùng
kubectl create ingress ... --dry-run=client -o yamlđể có skeleton, sau đóedithoặcapply. Gõ tay toàn bộ manifest Ingress rất dễ sai indent ở tầngpaths. - Nhớ quy tắc dấu
*: có*thìPrefix(và dấu*bị cắt), không có*thìExact. ==Đây là bẫy số một của dạng bài này.== - Nếu đề yêu cầu backend dùng port name thay vì số, sửa thành
port: { name: http }và đảm bảo Service cóname: httptrongspec.ports. - Đặt namespace mặc định cho context để khỏi gõ
-nmỗi lệnh:kubectl config set-context --current --namespace=shop. - Test nhanh bằng một dòng, đỡ đọc output dài:
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: ..." http://... - Chỉ thêm annotation
nginx.ingress.kubernetes.io/rewrite-targetkhi đề nói rõ yêu cầu rewrite. Thêm khi không được yêu cầu là tự tạo bug. - Quy tắc thời gian: task Ingress đơn giản nên xong trong 6 đến 8 phút. Quá 8 phút thì ghi chú lại, chuyển task khác, quay lại sau. Một task 4% không đáng để mất 15 phút.
Tóm tắt và bài tiếp theo
Chốt lại 4 dòng, đúng kiểu mình hay ghi ra giấy nháp:
- Ingress = rule L7 theo host + path, cần Ingress controller mới có tác dụng.
apiVersion: networking.k8s.io/v1, chọn controller bằngingressClassName, backend trỏ tới Service cùng namespace.pathTypechỉ có 3 giá trị:Prefix,Exact,ImplementationSpecific. Mặc định khi tạo bằngkubectllàExactnếu path không có*.- 404 = rule không khớp. 502/503 = backend không có endpoint hoặc sai port.
Điền thử trước khi xem đáp án:
1. Ingress object chỉ có hiệu lực khi cluster đang chạy một {{c1::Ingress controller}}.
2. `kubectl create ingress` sinh ra `pathType` {{c2::Prefix}} khi path kết thúc bằng `*`, và {{c3::Exact}} khi path không có `*`.
3. Mã 404 từ Ingress nghĩa là request không khớp {{c4::host hoặc path trong rules}}.
4. Mã 502 hoặc 503 nghĩa là rule đã khớp nhưng {{c5::Service backend không có endpoint hoặc sai targetPort}}.
5. Lệnh kiểm tra nhanh backend có endpoint hay không là `kubectl get {{c6::endpoints}} <service>`.
Ôn lại bằng bộ flashcard dưới đây, mỗi ngày một lượt là đủ nhớ:
[
{ "front": "Ingress hoạt động ở layer nào và cho giao thức nào?", "back": "Layer 7, chỉ HTTP/HTTPS (khác Service là layer 4)." },
{ "front": "Điều kiện bắt buộc để một Ingress object có tác dụng?", "back": "Cluster phải có Ingress controller đang chạy và nhận đúng ingressClassName." },
{ "front": "apiVersion hiện tại của Ingress?", "back": "networking.k8s.io/v1 (từ Kubernetes 1.19)." },
{ "front": "Field nào chọn Ingress controller cho một Ingress?", "back": "spec.ingressClassName." },
{ "front": "Ba giá trị hợp lệ của pathType?", "back": "Prefix, Exact, ImplementationSpecific." },
{ "front": "`--rule=\"foo.com/path*=svc:80\"` tạo ra pathType gì?", "back": "Prefix, với path là /path (dấu * bị cắt)." },
{ "front": "`--rule=\"foo.com/path=svc:80\"` (không có dấu *) tạo ra pathType gì?", "back": "Exact, chỉ khớp đúng chuỗi /path." },
{ "front": "Backend của Ingress trỏ tới đối tượng nào?", "back": "Một Service cùng namespace, chỉ định port bằng number hoặc name." },
{ "front": "Ingress trả 404 thường do nguyên nhân gì?", "back": "Host hoặc path không khớp rule, pathType quá chặt, hoặc thiếu ingressClassName." },
{ "front": "Ingress trả 503 thường do nguyên nhân gì?", "back": "Service backend không có endpoint (sai targetPort, pod chưa Ready, sai tên Service)." },
{ "front": "Lệnh kiểm tra Service có endpoint hay không?", "back": "kubectl get endpoints <svc>, hoặc kubectl get endpointslices." },
{ "front": "Test Ingress bằng curl như thế nào?", "back": "Gửi header Host đúng bằng host trong rule tới địa chỉ controller: curl -H \"Host: shop.example.com\" http://<node-ip>:<nodeport>/" },
{ "front": "spec.tls của Ingress gồm những field nào?", "back": "Danh sách hosts và secretName trỏ tới Secret type kubernetes.io/tls." },
{ "front": "Khi nhiều path cùng khớp, Ingress controller chọn path nào?", "back": "Path có prefix dài nhất (longest match), không phụ thuộc thứ tự trong YAML." }
]
Bài tiếp theo: NetworkPolicy và CNI. Bạn đã biết traffic từ ngoài vào pod đi qua những đâu. Bài sau chúng ta sẽ chặn traffic đó ở tầng mạng: viết NetworkPolicy theo label, hiểu vì sao mặc định mọi pod nói chuyện được với nhau, và vì sao policy đúng mà vẫn "không ăn" khi CNI không hỗ trợ.