為什麼要把它們串起來?
前 20 篇我們把 K8s 的零件一個一個拆開講:
- Pod:最小執行單位
- Deployment:管 N 個 Pod
- Service:固定的入口
- Ingress:對外的網域路由
完成後從瀏覽器打 http://demo.local/,流量會一路穿過:
你的瀏覽器
↓
Ingress(對外網域、路徑路由)
↓
Service(叢集內 DNS、固定 IP)
↓
Pod(你的容器,可能 3 個副本)
完整流程:6 個檔案
我們要部署一個 nginx 服務,從零到 Ingress:
Step 1:建 Namespace → 隔離環境
Step 2:寫 Deployment → 定義「3 個 nginx 副本」
Step 3:寫 ClusterIP Service → 叢集內固定入口
Step 4:寫 Ingress → 對外路由 demo.local
Step 5:本機 hosts 設定 → 把 demo.local 指到 Node IP
Step 6:瀏覽器驗證 + 排錯
Step 1:建 Namespace
$ kubectl create namespace demo
為什麼第一步是這個? 真實工作上不會把資源直接丟到 default。一個專案 = 一個 namespace,避免互相干擾。
Step 2:Deployment
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy
namespace: demo # ← 注意每個資源都要寫
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
$ kubectl apply -f deployment.yaml
$ kubectl get pods -n demo
NAME READY STATUS
nginx-deploy-7d8c9f-abc12 1/1 Running
nginx-deploy-7d8c9f-def34 1/1 Running
nginx-deploy-7d8c9f-ghi56 1/1 Running
3 個 Pod 跑起來。但現在還連不到 — Pod IP 隨時會變,沒有穩定入口。
Step 3:ClusterIP Service
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: demo
spec:
type: ClusterIP # 預設值,可省略
selector:
app: nginx # ← 對應 Pod labels
ports:
- port: 80
targetPort: 80
$ kubectl apply -f service.yaml
$ kubectl get svc -n demo
NAME TYPE CLUSTER-IP PORT(S)
nginx-svc ClusterIP 10.43.0.150 80/TCP
# 從叢集內驗證
$ kubectl run test --image=busybox:1.36 --rm -it --restart=Never -n demo \
-- wget -qO- http://nginx-svc
<h1>Welcome to nginx!</h1>
叢集內的 Pod 已經能用 nginx-svc:80 連到。但外面的瀏覽器還是連不上。
Step 4:Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
namespace: demo
spec:
ingressClassName: traefik # k3d 預設用 traefik
rules:
- host: demo.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc # ← 對應 Service 名稱
port:
number: 80
$ kubectl apply -f ingress.yaml
$ kubectl get ingress -n demo
NAME CLASS HOSTS ADDRESS PORTS
nginx-ingress traefik demo.local 192.168.97.2 80
ADDRESS 顯示 Ingress Controller 的對外 IP。如果是 ,表示 Ingress Controller 還沒裝(k3d / minikube 預設都有)。
Step 5:本機 hosts 設定
Ingress 用 demo.local 路由,但這個網域 DNS 不認識。最簡單做法:把它寫進 /etc/hosts。
# macOS / Linux
$ sudo vim /etc/hosts
# 加一行
192.168.97.2 demo.local
192.168.97.2 就是上一步 ADDRESS 顯示的 IP。
Step 6:瀏覽器驗證
$ curl http://demo.local/
<h1>Welcome to nginx!</h1>
或直接打開瀏覽器輸入 http://demo.local/。
完整鏈路通了:
瀏覽器 demo.local
↓ DNS(hosts)→ 192.168.97.2:80
Ingress Controller (Traefik)
↓ host=demo.local, path=/
nginx-svc:80 (ClusterIP)
↓ selector app=nginx
nginx-deploy 的 3 個 Pod 之一
排錯:每一層都能單獨驗證
這條鏈路最棒的是每一段都能拆開測。任何一段斷了,從下往上一層一層看:
| 症狀 | 檢查 |
|---|---|
| 瀏覽器轉圈圈 | curl http://demo.local/ 看 status code |
curl: Could not resolve host | hosts 沒設好,ping demo.local 看 IP |
curl 通但 404 | Ingress host 寫錯,kubectl describe ingress |
| Ingress 通但 503 | Service 沒 endpoint,kubectl get endpoints -n demo |
| Service 沒 endpoint | Pod labels 跟 selector 對不上,kubectl get pods --show-labels |
| Pod 不是 Running | kubectl describe pod + kubectl logs |
kubectl get / describe 看一眼。
真實工作的差別
正式環境跟這個 demo 主要差三件事:
/api → backend-svc、/admin → admin-svc(一個 Ingress 管多個 Service)api.company.com 指到 LoadBalancer 的公網 IP但底層原理完全一樣。學會這條鏈路,後面只是疊功能上去。
對照 Docker Compose
| Docker Compose | Kubernetes |
|---|---|
docker-compose.yml 一份 | 4 份 YAML(Namespace + Deploy + Svc + Ingress) |
ports: 8080:80 | NodePort 或 Ingress |
networks | Namespace + DNS |
docker-compose up | kubectl apply -f . |
重點整理
- 完整鏈路:Namespace → Deployment → Service → Ingress
- ClusterIP 對內、Ingress 對外,Service 和 Ingress 是兩件事
- 排錯順序從外往內,每層拆開驗證
/etc/hosts是本機開發的方便做法,prod 走真 DNS- 3 + 1 個 YAML 檔,apply 完就有完整服務
下一步
你已經把整條 K8s 部署鏈路串起來了。但 Ingress 篇只講了 path-based 路由 — 真實工作會用多個 host(api.example.com / admin.example.com)並走 HTTPS。