Ingress trong Kubernetes: đưa ứng dụng ra ngoài thế giới
1. Tổng quan
Khi bạn đã có Service chạy trong cluster nhưng muốn người dùng bên ngoài truy cập qua domain đẹp như api.nonladev.com thay vì IP:port lộn xộn, đó là lúc cần Ingress. Bài này giải thích Ingress là gì, khác gì với Service type LoadBalancer, cách cài Ingress Controller và viết Ingress resource cho các tình huống thường gặp: path-based routing, host-based routing, TLS. Đọc xong, bạn tự expose được một ứng dụng Spring Boot ra ngoài với domain và HTTPS.
2. Vì sao cần Ingress
2.1. Vấn đề với Service
Service type NodePort hoặc LoadBalancer hoạt động tốt, nhưng có hạn chế:
LoadBalancer: mỗi Service cần một external IP riêng → tốn tiền nếu có nhiều service (mỗi cloud LB tính phí riêng).NodePort: expose port lạ (30000-32767), không có TLS termination, không routing theo domain/path.
Giả sử bạn có 5 microservice cần expose ra ngoài, dùng LoadBalancer nghĩa là 5 cloud load balancer, 5 external IP. Ingress giải quyết bằng cách dùng một entry point để route traffic tới nhiều Service bên trong, dựa trên hostname hoặc path.
2.2. Ingress là gì
Ingress là một API object định nghĩa rule routing HTTP/HTTPS từ ngoài vào Service trong cluster. Ingress tự nó không làm gì cả — nó cần Ingress Controller (một Pod chạy reverse proxy như NGINX, Traefik, HAProxy) để đọc rule và thực thi.
Client → Ingress Controller (LoadBalancer/NodePort) → Ingress rules → Service → Pod
Lưu ý: Nếu cluster chưa cài Ingress Controller, tạo Ingress resource cũng không có tác dụng gì — nó chỉ là config “nằm chờ” controller đọc.
3. Cài Ingress Controller
Phổ biến nhất là ingress-nginx. Cài bằng Helm:
<!-- không áp dụng — dùng Helm/kubectl, không phải Maven dependency -->
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx --create-namespace
Lệnh này tạo namespace ingress-nginx, deploy Ingress Controller cùng một Service type LoadBalancer để nhận traffic từ ngoài. Kiểm tra:
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
NAME TYPE EXTERNAL-IP PORT(S)
ingress-nginx-controller LoadBalancer 34.123.45.67 80:31234/TCP,443:31235/TCP
External IP này chính là điểm vào duy nhất cho mọi Ingress trong cluster.
4. Deployment và Service mẫu
Trước khi viết Ingress, cần có Service để route tới. Giả sử ứng dụng order-service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 80
targetPort: 8080
Service order-service expose port 80, forward vào container port 8080. Lưu ý Service này không cần type LoadBalancer, dùng ClusterIP (mặc định) là đủ, vì Ingress Controller sẽ gọi vào Service qua internal network.
5. Ingress resource cơ bản — host-based routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: api.nonladev.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
Giải thích từng phần:
ingressClassName: nginx— chỉ định controller nào xử lý rule này (quan trọng khi cluster có nhiều controller).rules.host— chỉ traffic có headerHost: api.nonladev.commới match rule này.path+pathType: Prefix— mọi request bắt đầu bằng/(tức toàn bộ) sẽ forward tớiorder-service:80.- annotation
rewrite-target— riêng của ingress-nginx, dùng để rewrite URL trước khi forward (tùy controller mà annotation khác nhau).
Áp dụng và test:
kubectl apply -f order-ingress.yaml
curl -H "Host: api.nonladev.com" http://34.123.45.67/
{"service":"order-service","status":"UP"}
Trong môi trường thật, bạn trỏ DNS record api.nonladev.com về external IP của Ingress Controller, không cần header Host giả như trên.
6. Path-based routing — nhiều service dùng chung domain
Tình huống thường gặp: một domain, nhiều service theo path khác nhau.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-gateway-ingress
spec:
ingressClassName: nginx
rules:
- host: api.nonladev.com
http:
paths:
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
- path: /customers
pathType: Prefix
backend:
service:
name: customer-service
port:
number: 80
Request tới api.nonladev.com/orders/123 route vào order-service, còn /customers/55 route vào customer-service. pathType: Prefix nghĩa là match theo tiền tố — khác với Exact (phải khớp y hệt) và ImplementationSpecific (tùy controller tự diễn giải, thường dùng cho regex).
Lưu ý: Thứ tự path không ảnh hưởng matching trong Ingress v1 — controller tự chọn path dài nhất khớp trước. Nhưng viết path rõ ràng, không chồng lấn, để tránh bug khó debug.
7. TLS — HTTPS cho Ingress
Để có HTTPS, cần Secret chứa cert và key, rồi tham chiếu trong Ingress.
kubectl create secret tls nonladev-tls \
--cert=tls.crt --key=tls.key
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-ingress-tls
spec:
ingressClassName: nginx
tls:
- hosts:
- api.nonladev.com
secretName: nonladev-tls
rules:
- host: api.nonladev.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
spec.tls khai báo host nào dùng cert nào. Controller tự động làm TLS termination — tức giải mã HTTPS tại Ingress, rồi forward HTTP thuần vào Service bên trong. Pod của bạn không cần biết gì về TLS.
Trong thực tế, bạn thường dùng cert-manager để tự động xin cert Let’s Encrypt thay vì tạo Secret tay:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-ingress-tls
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- api.nonladev.com
secretName: nonladev-tls
rules:
- host: api.nonladev.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
Annotation cluster-issuer yêu cầu cert-manager tự tạo và renew Secret nonladev-tls — không cần tay động gì thêm.
8. Viết test kiểm tra endpoint sau khi expose
Sau khi Ingress hoạt động, bạn có thể viết integration test đơn giản kiểm tra controller class xử lý path đúng (test ở tầng ứng dụng Spring Boot, không test trực tiếp YAML):
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class OrderControllerIT {
@Autowired
private TestRestTemplate restTemplate;
@Test
void shouldReturnOrderWhenCalledWithOrdersPrefix() {
ResponseEntity<String> response =
restTemplate.getForEntity("/orders/123", String.class);
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).contains("orderId");
}
}
Test này đảm bảo ứng dụng Spring Boot đáp ứng đúng path /orders/123 mà Ingress rewrite tới — tách biệt lỗi do code backend với lỗi do cấu hình Ingress.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
9. Lỗi thường gặp
- Quên
ingressClassName: nếu cluster có nhiều Ingress Controller (ví dụ cả nginx và traefik), Ingress không rõ controller nào xử lý → không hoạt động hoặc bị cả hai xử lý nhầm. - Service type sai: Service backend để
LoadBalancerkhông cần thiết, gây lãng phí — chỉ cầnClusterIP. pathTypekhông khớp mong đợi: dùngExactkhi cầnPrefixkhiến request con (/orders/123) bị 404 vì chỉ/orderskhớp chính xác.- DNS chưa trỏ: test bằng
curl -H "Host: ..."trước khi cấu hình DNS thật, tránh mất thời gian chờ DNS propagate. - Rewrite annotation sai controller: annotation của ingress-nginx không áp dụng được cho Traefik hay AWS ALB Ingress Controller — mỗi controller có bộ annotation riêng, đọc kỹ doc tương ứng.
Khi nào KHÔNG nên dùng Ingress: nếu ứng dụng dùng protocol không phải HTTP/HTTPS (ví dụ raw TCP, gRPC streaming phức tạp, hay UDP), Ingress chuẩn không hỗ trợ tốt — cần xem
Gateway APIhoặc Service typeLoadBalancertrực tiếp cho TCP/UDP.
10. Kết luận
Ingress là lớp routing HTTP/HTTPS tập trung, giúp bạn expose nhiều Service ra ngoài qua một entry point duy nhất, tiết kiệm chi phí LoadBalancer và dễ quản lý domain/path/TLS. Điểm mấu chốt cần nhớ: Ingress resource chỉ là rule, cần Ingress Controller để thực thi; chọn đúng ingressClassName và pathType; dùng cert-manager để tự động hóa TLS. Với ứng dụng không phải HTTP thuần, cân nhắc Gateway API thay vì ép dùng Ingress.