Phần 4 — Networking: Service, Ingress, DNS, NetworkPolicy
Ý kiến
0
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Chưa có ý kiến nào. Hãy là người đầu tiên chia sẻ!
Phần cuối series K8s: Cluster API (CAPI) quản lý cluster bằng K8s API, FinOps với OpenCost + Karpenter + right-sizing, AI/ML workloads (GPU, Kubeflow, KServe, vLLM, Argo Workflows, gang scheduling), Edge K8s và WebAssembly.
Multi-tenancy K8s (soft: namespace+RBAC+Quota, HNC, Capsule, vCluster; hard: cluster riêng) và multi-cluster: management cluster, GitOps ApplicationSet, Cluster API, Karmada, Crossplane, observability liên cluster.
Service Mesh chuyên sâu cho K8s: Istio (sidecar + ambient), Linkerd (proxy Rust nhẹ), Cilium Service Mesh (eBPF sidecarless) — mTLS, VirtualService, AuthorizationPolicy, canary, mirror, multi-cluster, đo overhead, khi không nên dùng mesh.
Series Kubernetes Toàn Tập — 13 phần:
Phần 4 — Networking ← bạn đang đọc
Networking là phần dễ fail nhất khi học K8s vì nó khác hẳn cách bạn quen với VM hay Docker. Phần này đi từ model cơ bản đến Service, DNS, Ingress, Gateway API và NetworkPolicy.
Theo tài liệu chính thức, mọi K8s cluster phải tuân thủ 4 quy tắc:
Mỗi pod có IP riêng (cluster-wide), không cần NAT.
Pod giao tiếp với mọi pod khác qua IP, không qua NAT.
Node giao tiếp với pod (và ngược lại) không qua NAT.
IP pod thấy mình chính là IP pod khác thấy.
Container trong cùng pod chia chung network namespace — nói chuyện qua localhost. Cross-pod, cross-node thì CNI lo phần encapsulation/routing.
CNI (Container Network Interface) là spec cho phép plugin tạo network namespace và gán IP cho container. Một số CNI phổ biến:
| CNI | Mô hình | Mạnh |
|---|---|---|
| Cilium | eBPF, có/không tunnel | Performance, NetworkPolicy nâng cao, replace kube-proxy |
| Calico | BGP/VXLAN, iptables/eBPF | Production tested, NetworkPolicy chuẩn |
| Flannel | VXLAN/host-gw | Đơn giản, ít feature |
| Weave | Mesh overlay | Encryption sẵn, đang ít update |
| AWS VPC CNI / Azure / GKE | Native cloud | Mỗi pod 1 ENI/IP — performance tốt, tốn IP |
Pod là ephemeral — bị xoá, IP mới sinh ra mỗi lần. Bạn không thể bảo client cứ gọi 10.244.1.23, vì lần sau IP đã khác. Service giải quyết điều này: nó là endpoint stable đứng trước một group pod.
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- name: http
port: 80 # port của Service
targetPort: 8080 # port của container trong Pod
K8s gán cho Service một ClusterIP ổn định (vd. 10.96.10.5). Mọi pod gọi http://api hay http://10.96.10.5 đều được route đến pod backend qua kube-proxy.
Không expose ra ngoài cluster. Đây là loại bạn dùng nhiều nhất.
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # range 30000-32767
K8s mở port 30080 trên mọi node. Client gọi http://<NODE_IP>:30080 sẽ vào Service. Dùng cho dev/test, không phù hợp production (cần biết IP node, không có TLS termination).
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
Yêu cầu cloud controller (AWS/GCP/Azure) hoặc MetalLB/kube-vip cho on-prem. K8s sẽ tạo LB ngoài (ELB/ALB/NLB…) trỏ về NodePort. Đây là cách tự nhiên để expose service ra Internet.
Mỗi Service LoadBalancer = 1 LB ngoài. Khi có 50 service public, bạn tốn 50 LB → tốn tiền → đó là lý do sinh ra Ingress.
spec:
type: ExternalName
externalName: db.staging.internal.example.com
Service không có pod backend, K8s chỉ tạo bản ghi DNS CNAME. Dùng để alias dịch vụ ngoài cluster cho code trong cluster.
spec:
clusterIP: None
selector:
app: postgres
Không có ClusterIP. DNS trả về tất cả pod IP match selector (A record nhiều giá trị). Bắt buộc cho StatefulSet để có DNS theo pod (vd. postgres-0.postgres.default.svc.cluster.local).
Đằng sau mỗi Service là object EndpointSlice liệt kê pod IP + port hiện tại match selector. Controller cập nhật khi pod ready/unready. EndpointSlice thay thế Endpoints cũ cho cluster lớn (chia nhỏ thành nhiều slice 100 endpoint).
spec:
sessionAffinity: ClientIP # sticky session theo IP
externalTrafficPolicy: Local # giữ source IP, không SNAT
externalTrafficPolicy: Local giữ được source IP nhưng yêu cầu pod backend phải có mặt trên cùng node nhận traffic.
CoreDNS (mặc định từ K8s 1.13) chạy trong namespace kube-system. Mỗi pod được kubelet inject /etc/resolv.conf trỏ đến CoreDNS Service IP.
Quy tắc DNS:
| Object | FQDN |
|---|---|
Service api ở namespace prod | api.prod.svc.cluster.local |
| Pod thường (theo IP) | 10-244-1-23.prod.pod.cluster.local |
| StatefulSet pod (qua headless) | postgres-0.postgres.prod.svc.cluster.local |
Bên trong cùng namespace, bạn chỉ cần api. Khác namespace, dùng api.prod hoặc FQDN đầy đủ.
Debug DNS:
kubectl run -it --rm dns-debug --image=busybox:1.36 --restart=Never -- \
nslookup api.prod
# Hoặc dùng image netshoot có dig, tcpdump, ss
kubectl run -it --rm net --image=nicolaka/netshoot --restart=Never
Ingress là object K8s mô tả HTTP routing rules (host/path → Service). Bản thân Ingress object không làm gì — phải có Ingress Controller đọc rules và thực thi.
Phổ biến:
ingress-nginx (Kubernetes SIG) — phổ biến nhất, ổn định.
Traefik — config động, dashboard.
HAProxy Ingress — perf cao.
AWS ALB Controller, GKE Ingress — quản lý LB cloud.
Contour, Emissary (Envoy-based).
Cilium — Ingress qua Envoy, cùng CNI.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml
# Hoặc Helm
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
ingressClassName: nginx
tls:
- hosts: ["app.example.com"]
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
pathType:
Exact — match chính xác.
Prefix — match theo path element. /api match /api, /api/v1, không match /apifoo.
ImplementationSpecific — tuỳ controller.
Bạn không nên tự gia hạn cert thủ công. cert-manager là chuẩn de facto: tạo Let's Encrypt cert tự động, đổi tự động trước hạn.
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.0/cert-manager.yaml
Tạo ClusterIssuer:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
ingressClassName: nginx
Thêm annotation cho Ingress:
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
cert-manager tự tạo Secret TLS, Ingress tự dùng.
Ingress đã đủ tuổi (2015) và bộc lộ giới hạn: annotation-driven, không chuẩn hoá feature giữa controller. Gateway API là API thế hệ mới (graduated v1 năm 2023), tách thành 3 role:
| Resource | Owner | Vai trò |
|---|---|---|
GatewayClass | Infra provider | Implementation (vd. cilium, envoy) |
Gateway | Cluster operator | Listener: host, port, TLS |
HTTPRoute / GRPCRoute / TCPRoute | App developer | Routing rules |
Ví dụ HTTPRoute:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api
spec:
parentRefs:
- name: my-gateway
hostnames: ["app.example.com"]
rules:
- matches:
- path: { type: PathPrefix, value: /api }
backendRefs:
- name: api
port: 80
weight: 90
- name: api-canary
port: 80
weight: 10
Gateway API hỗ trợ traffic splitting (canary), header rewriting, retry, timeout… mà Ingress phải dùng annotation. Khi chọn ingress controller hôm nay, ưu tiên cái support Gateway API: Cilium, Envoy Gateway, Istio, Contour.
Mặc định mọi pod có thể nói chuyện với mọi pod — không tốt cho production. NetworkPolicy là object K8s mô tả luật cho phép, được CNI thực thi (nếu CNI hỗ trợ).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: prod
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
podSelector: {} = mọi pod trong namespace. Vì không có rule cho phép nào, mọi traffic in/out đều bị block. Tiếp theo bạn allow rõ ràng các đường cần thiết.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-frontend
namespace: prod
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: prod
spec:
podSelector: {}
policyTypes: [Egress]
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Đây là rule quên đầu tiên: bạn enable default-deny rồi app không gọi được gì vì DNS resolve fail.
CNI không support (Flannel mặc định). Đổi CNI hoặc cài Calico-as-policy.
Pod chạy với hostNetwork: true — NetworkPolicy không áp dụng vì pod dùng network của node.
Cilium có CRD CiliumNetworkPolicy mạnh hơn (FQDN, L7) — chỉ áp dụng nếu dùng Cilium.
NetworkPolicy chỉ làm L3/L4. Khi cần mTLS giữa service, retry, circuit breaker, observability per-request, traffic split fine-grained… bạn cần Service Mesh.
| Mesh | Mô hình |
|---|---|
| Istio | Envoy sidecar / ambient mode mới |
| Linkerd | Sidecar Rust proxy, đơn giản |
| Cilium Service Mesh | eBPF, không sidecar |
| Consul | HashiCorp, multi-runtime |
Mesh phức tạp và tốn tài nguyên — chỉ thêm khi bạn thật sự cần feature mà mesh cung cấp.
Trên cloud, Service LoadBalancer tự sinh LB. On-prem thì không. Hai cách:
MetalLB — pool IP, ARP/L2 hoặc BGP. Đơn giản, phổ biến.
kube-vip — VIP qua VRRP/BGP, có thể dùng cho cả control plane HA.
Ví dụ MetalLB L2:
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: pool
namespace: metallb-system
spec:
addresses:
- 192.168.1.240-192.168.1.250
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: l2-adv
namespace: metallb-system
Sau đó bất kỳ Service LoadBalancer sẽ nhận IP từ pool.
# Pod IP, node
kubectl get pods -o wide
# Endpoints của Service
kubectl get endpointslices -l kubernetes.io/service-name=api
kubectl describe svc api
# Pod debug full toolkit
kubectl run -it --rm net --image=nicolaka/netshoot --restart=Never
# trong shell: dig api.prod.svc, curl -v api, ss -tnlp, tcpdump, mtr
# Verify NetworkPolicy đang chặn cái gì
kubectl get networkpolicy -A
# Cilium: cilium connectivity test
# Ingress controller log
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller -f
Một Service ClusterIP cho mỗi Deployment luôn luôn, dù chỉ có 1 replica.
Đặt port có name (name: http). Pod port cũng nên có name → Service có thể tham chiếu theo name, đổi port không phá Service.
Ingress: một controller mỗi cluster cho HTTP, route bằng host, dùng cert-manager cho TLS.
Bật NetworkPolicy default-deny mỗi namespace production, allow rõ ràng từng đường.
Theo dõi Gateway API — chuẩn tương lai, Ingress sẽ chỉ còn maintain mode.
Dùng externalTrafficPolicy: Local nếu cần source IP — kèm topology hint hoặc daemonset workload để pod ở cùng node nhận traffic.
Không spam Service LoadBalancer cho mỗi app — gộp qua Ingress hoặc Gateway.
Pod IP ephemeral → Service là endpoint ổn định.
4 type Service: ClusterIP, NodePort, LoadBalancer, ExternalName + Headless biến thể.
DNS theo template service.namespace.svc.cluster.local.
Ingress / Gateway API cho HTTP routing layer 7 + TLS.
NetworkPolicy là firewall L3/L4 trong cluster — default deny rồi allow rõ ràng.
Cần feature nâng cao (mTLS, retry, traffic split) → Service Mesh.
Trong Phần 5, ta xem cách K8s xử lý storage: volume ephemeral, PersistentVolume, PVC, StorageClass và CSI.