今天要來建立牧場的智慧導航系統!就像現代牧場需要清楚的指示牌告訴訪客「要看乳牛請往左,要看羊群請往右」,Ingress Controller 就是 Kubernetes 世界的智慧導航員。有了昨天的 MetalLB 提供外部 IP,今天要讓這些 IP 變得更聰明,能根據域名和路徑把流量精準送到對的地方!
昨天我們用 MetalLB 解決了「如何讓外部訪問到集群」的問題,但還有更精細的需求:
LoadBalancer Service 的限制:
Ingress 的價值:
重要概念區分:
nginx-ingress 的工作原理:
nginx-ingress Controller 會監聽所有 Ingress Resource,然後將它們轉換成 nginx 配置檔案。
# 這是 Ingress Resource(規則定義)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-ingress
spec:
rules:
- http:
paths:
- path: /frontend(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: frontend-service
port:
number: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
spec:
rules:
- http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 80
nginx-ingress Controller 會將上述 Ingress 轉換成類似這樣的 nginx.conf:
# nginx-ingress 自動生成的配置(簡化版)
server {
listen 80;
listen [::]:80;
# 來自 frontend-ingress 的規則
location ~ ^/frontend(/|$)(.*) {
# rewrite 規則
rewrite ^/frontend(/|$)(.*) /$2 break;
# 代理到後端 Service
proxy_pass http://default-frontend-service-80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
# 來自 api-ingress 的規則
location ~ ^/api(/|$)(.*) {
# rewrite 規則
rewrite ^/api(/|$)(.*) /$2 break;
# 代理到後端 Service
proxy_pass http://default-api-service-80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
# upstream 定義(Service 的後端 Pod)
upstream default-frontend-service-80 {
# 這些 IP 來自 Service 的 Endpoints
server 10.42.1.10:80 max_fails=1 fail_timeout=1s;
server 10.42.2.10:80 max_fails=1 fail_timeout=1s;
}
upstream default-api-service-80 {
server 10.42.1.11:80 max_fails=1 fail_timeout=1s;
server 10.42.2.11:80 max_fails=1 fail_timeout=1s;
}
整個流程:
實際驗證方式:
# RKE2 中的 nginx-ingress 是 DaemonSet,需要指定具體 Pod
kubectl get pods -n kube-system | grep ingress
# 進入任一 nginx-ingress Pod 查看實際生成的 nginx.conf
kubectl exec -it -n kube-system <ingress-nginx-controller-pod名稱> -- cat /etc/nginx/nginx.conf
# 查看 upstream 配置
kubectl exec -it -n kube-system <ingress-nginx-controller-pod名稱> -- nginx -T | grep -A 5 "upstream.*service"
常見的 Ingress Controller 選項:
RKE2 預設就包含了 nginx-ingress controller,這為我們節省了大量的選擇和配置時間:
# 檢查 RKE2 預設的 Ingress Controller
kubectl get pods -n kube-system | grep nginx
# 檢查 Ingress Class
kubectl get ingressclass
# 檢查 Ingress Controller 的 Service
kubectl get svc -n kube-system | grep nginx
RKE2 nginx-ingress 特色:
RKE2 預設使用 hostNetwork 模式部署 nginx-ingress:
# 檢查 RKE2 預設的 nginx-ingress 部署
kubectl get pods -n kube-system | grep ingress
# 檢查是否使用 hostNetwork
kubectl get pod -n kube-system <具體的pod名稱> -o yaml | grep hostNetwork
# 檢查 nginx-ingress 是否為 DaemonSet
kubectl get daemonset -n kube-system
RKE2 nginx-ingress 的特色:
RKE2 nginx-ingress 實際架構:
正確的職責說明:
根據我們在 Day 11 的設定,目前的 SSL 架構是:
現有架構優勢:
由於我們已有外部 SSL 終止,集群內的 Ingress 主要處理 HTTP 路由:
# 基本 HTTP Ingress 配置(無需 SSL)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-http-ingress
namespace: default
annotations:
# 路徑重寫規則
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: app.ranch.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
配置重點說明:
/frontend/something
重寫為 /something
SSL 憑證說明:
可以參考之前的流程(Day 7-8),為新的服務註冊新的域名並申請 SSL 憑證,但在這個範例中我們不重複執行該流程。接下來的服務將:
測試場景:使用路徑型路由測試多個應用:
/frontend
:前端應用/api
:API 服務/admin
:管理介面# 建立多個測試應用
cat > ingress-test-apps.yaml << 'EOF'
# Frontend Application
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-app
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: frontend-html
---
apiVersion: v1
kind: Service
metadata:
name: frontend-service
namespace: default
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
name: frontend-html
namespace: default
data:
index.html: |
<!DOCTYPE html>
<html>
<head><title>Frontend App</title></head>
<body>
<h1>🎯 Frontend Application</h1>
<p>This is the main frontend service</p>
<p>Served from: frontend.ranch.local</p>
</body>
</html>
---
# API Application
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-app
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: api-html
---
apiVersion: v1
kind: Service
metadata:
name: api-service
namespace: default
spec:
selector:
app: api
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
name: api-html
namespace: default
data:
index.html: |
<!DOCTYPE html>
<html>
<head><title>API Service</title></head>
<body>
<h1>🔧 API Service</h1>
<p>This is the backend API service</p>
<p>Served from: api.ranch.local</p>
</body>
</html>
---
# Admin Application
apiVersion: apps/v1
kind: Deployment
metadata:
name: admin-app
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: admin
template:
metadata:
labels:
app: admin
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: admin-html
---
apiVersion: v1
kind: Service
metadata:
name: admin-service
namespace: default
spec:
selector:
app: admin
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: ConfigMap
metadata:
name: admin-html
namespace: default
data:
index.html: |
<!DOCTYPE html>
<html>
<head><title>Admin Panel</title></head>
<body>
<h1>⚙️ Admin Panel</h1>
<p>This is the administration interface</p>
<p>Served from: admin.ranch.local</p>
</body>
</html>
EOF
# 部署測試應用
kubectl apply -f ingress-test-apps.yaml
# 確認部署狀態
kubectl get pods | grep -E "(frontend|api|admin)"
kubectl get svc
對於這個 Custom Cluster,我們使用通用的路徑型路由:
# 建立路徑型 Ingress 規則
cat > path-based-ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-based-ingress
namespace: default
annotations:
# 路徑重寫規則
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
# 不指定特定 host,接受任何 host header
- http:
paths:
# /frontend/* 路由到 frontend service
- path: /frontend(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: frontend-service
port:
number: 80
# /api/* 路由到 api service
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 80
# /admin/* 路由到 admin service
- path: /admin(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: admin-service
port:
number: 80
EOF
# 套用 Ingress 規則
kubectl apply -f path-based-ingress.yaml
# 檢查 Ingress 狀態
kubectl get ingress
kubectl describe ingress path-based-ingress
路徑重寫說明:
rewrite-target: /$2
:將 /frontend/something
重寫為 /something
path: /frontend(/|$)(.*)
:使用正規表達式捕獲路徑pathType: ImplementationSpecific
:使用 nginx 特定的正規表達式功能RKE2 的 nginx-ingress 使用 hostNetwork,直接測試節點 IP:
# 檢查 worker 節點 IP
kubectl get nodes -o wide
# 直接測試 worker 節點 IP(最簡單的方式)
curl http://192.168.0.238/frontend/
curl http://192.168.0.238/api/
curl http://192.168.0.238/admin/
# 測試時可以添加任何 Host header(因為 Ingress 不限制 host)
curl -H "Host: test.example.com" http://192.168.0.238/frontend/
curl -H "Host: anything.local" http://192.168.0.238/api/
# 檢查 nginx ingress 日誌
kubectl logs -n kube-system <具體的pod名稱> -f
為了統一 SSL 管理,在 HAProxy 中添加路由到 worker 節點:
# SSH 到 bastion 節點
ssh calvin@192.168.0.135
# 編輯 HAProxy 配置
sudo vim /etc/haproxy/haproxy.cfg
# 在 frontend https_frontend 區塊中添加:
# 檢查是否為應用路徑
acl is_app_path path_beg /frontend /api /admin
# 在路由規則中添加(注意順序,放在 rancher 規則之前):
use_backend nginx_backend if is_app_path
# 添加新的後端配置:
backend nginx_backend
balance roundrobin
option httpchk GET /healthz
http-check expect status 404 # nginx ingress 預設 404 是正常的
server worker-1 192.168.0.238:80 check
# 如果有多個 worker 節點,繼續添加
# 重新載入 HAProxy
sudo systemctl reload haproxy
# 方式1:直接訪問 worker 節點(HTTP)
curl -H "Host: ithome-rancher.duckdns.org" http://192.168.0.238/frontend/
# 方式2:透過其他域名指向(如果有設定)
curl http://custom-app.local/frontend/
# 方式3:如果想要 HTTPS,需要額外的反向代理設定
# 檢查路由是否正確工作
curl -v http://192.168.0.238/frontend/ | head -10
# 檢查 nginx ingress 日誌
kubectl logs -n kube-system <具體的pod名稱> -f
訪問方式的特點:
適用場景建議:
在多租戶環境中,管理員不可能為每個應用手動建立 Ingress 配置。建議的做法:
讓租戶自行管理:
平台管理員職責:
租戶職責:
# 檢查 Ingress Controller 狀態
kubectl get pods -n kube-system | grep ingress
# 查看即時日誌
kubectl logs -n kube-system <具體的pod名稱> -f
# 檢查 Ingress 配置是否正確載入
kubectl exec -n kube-system <具體的pod名稱> -- nginx -T | grep -A 5 -B 5 "location"
# 檢查後端健康狀況
kubectl get endpoints
# 檢查項目:
# 1. Ingress 規則是否正確建立
kubectl get ingress
kubectl describe ingress ranch-ingress
# 2. Service 和 Pod 是否正常運行
kubectl get svc
kubectl get pods
# 3. DNS 解析是否正確
nslookup frontend.ranch.local
# 或檢查 /etc/hosts
# 4. nginx 配置是否載入
kubectl logs -n kube-system <具體的pod名稱> | grep "路由相關關鍵字"
# 檢查後端服務健康狀況
kubectl get endpoints
kubectl describe endpoints frontend-service
# 檢查 Pod 是否能正常回應
kubectl port-forward svc/frontend-service 8080:80
curl http://localhost:8080
# 檢查 Service selector 是否正確
kubectl get svc frontend-service -o yaml
kubectl get pods -o wide
# 如果使用 SSL Bridging
# 1. 檢查後端服務是否支援 HTTPS
# 2. 檢查憑證是否有效
# 3. 檢查 backend-protocol 註解是否正確
# 檢查 SSL 配置
kubectl describe ingress <ingress名稱> | grep -i tls
今天我們成功建立了 Ingress Controller 流量路由系統!從基本概念到實際配置,從 Ingress Resource 轉換成 nginx.conf 的原理,到多租戶環境的自助式管理策略,我們的牧場現在有了智慧導航系統。RKE2 預設的 nginx-ingress 使用 hostNetwork 模式,讓應用能透過路徑型路由優雅地提供服務。
明天我們要開始建立牧場的觀測系統,學習 Prometheus 監控平台的部署與設定,為我們的 Kubernetes 環境建立完整的指標收集機制!
💡 牧場主小提示:Ingress Controller 就像牧場的智慧導航系統,一個入口分流到不同目的地!記住核心概念:Ingress Resource 就是 nginx.conf 的 YAML 版本,nginx-ingress Controller 會自動轉換並重載配置。有了這個理解,除錯和配置都會變得直觀許多!