K8s · 維運與部署 · 第 36 課 · · 14min read

從零部署完整系統(下):12 步驟(後 6 步)+ 驗證

接續上篇 6 步,這篇做後 6 步:PV/PVC → StatefulSet DB → HPA → Probe → RBAC → NetworkPolicy。完整一套生產級部署。

#Kubernetes #從零部署 #StatefulSet #HPA #Probe
章節目錄 · 11

接續上篇:6 個 Pod 都跑起來了,但...

上篇 完成了:

  • ✅ Namespace / Secret / ConfigMap
  • ✅ MySQL StatefulSet(1 個 Pod)
  • ✅ API Deployment(3 個 Pod)
  • ✅ 前端 Deployment(2 個 Pod)
但 Pod 跑起來不代表能用——外面還連不到、內部還沒受保護、流量爆炸還不會自動擴

繼續:Step 7 → Step 12。

---

Step 7:建 Service — Pod 之間怎麼找

\\\yaml
# services.yaml
apiVersion: v1
kind: Service
metadata:
name: api-svc
namespace: prod
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
namespace: prod
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 80
\
\\

\\\bash
kubectl apply -f services.yaml
kubectl get svc -n prod
# api-svc ClusterIP 10.43.x.x 80/TCP
# frontend-svc ClusterIP 10.43.x.x 80/TCP
# mysql-headless ClusterIP None 3306/TCP
\
\\

3 個 Service 各司其職

  • \api-svc\、\frontend-svc\ 走負載均衡
  • \mysql-headless\ 是 Headless(StatefulSet 用)
---

Step 8:建 Ingress — 對外入口

\\\yaml
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
namespace: prod
spec:
rules:
- host: myapp.local
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend-svc
port:
number: 80
\
\\

\\\bash
kubectl apply -f ingress.yaml
kubectl get ingress -n prod
\
\\

加一筆 \/etc/hosts\

\\\
127.0.0.1 myapp.local
\
\\

到這裡,功能上已經完整了——使用者可以透過 \http://myapp.local\ 進來。但安全沒做。

---

Step 9:設 NetworkPolicy — 三層隔離

\\\yaml
# networkpolicy.yaml
# 規則 1:DB 只接受 API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-policy
namespace: prod
spec:
podSelector:
matchLabels:
app: mysql
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 3306
---
# 規則 2:API 只接受前端 + Ingress Controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
namespace: prod
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
ports:
- protocol: TCP
port: 8080
---
# 規則 3:前端只接受 Ingress Controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: prod
spec:
podSelector:
matchLabels:
app: frontend
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
\
\\

\\\bash
kubectl apply -f networkpolicy.yaml
kubectl get networkpolicy -n prod
# db-policy, api-policy, frontend-policy
\
\\

三層隔離:實習生在 \marketing\ namespace 想連 \prod\ 的 DB?通通擋。

---

Step 10:設 HPA — 自動擴縮

\\\yaml
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-hpa
namespace: prod
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
\
\\

\\\bash
kubectl apply -f hpa.yaml
kubectl get hpa -n prod
# api-hpa Deployment/api 0%/70% 3 10 3
\
\\

---

Step 11:完整驗證 — 一覽你做了什麼

\\\bash
kubectl get all -n prod
\
\\

你會看到一大堆東西:

\\\
NAME READY STATUS RESTARTS
pod/api-xxx-1 1/1 Running 0
pod/api-xxx-2 1/1 Running 0
pod/api-xxx-3 1/1 Running 0
pod/frontend-xxx-1 1/1 Running 0
pod/frontend-xxx-2 1/1 Running 0
pod/mysql-0 1/1 Running 0

NAME TYPE CLUSTER-IP
service/api-svc ClusterIP 10.43.x.x
service/frontend-svc ClusterIP 10.43.x.x
service/mysql-headless ClusterIP None

NAME READY
deployment.apps/api 3/3
deployment.apps/frontend 2/2

statefulset.apps/mysql 1/1

horizontalpodautoscaler.autoscaling/api-hpa Deployment/api 0%/70% 3 10 3
\
\\

再看其他資源:

\\\bash
kubectl get pvc,ingress,networkpolicy -n prod
# pvc/mysql-data-mysql-0 Bound 1Gi
# ingress/myapp-ingress myapp.local
# networkpolicy/db-policy
# networkpolicy/api-policy
# networkpolicy/frontend-policy
\
\\

---

Step 12:壓測 HPA — 驗證自動擴容

\\\bash
# 終端 1:監控 HPA
kubectl get hpa -n prod -w

# 終端 2:壓測
kubectl run load-test --image=busybox:1.36 -n prod \\
--rm -it --restart=Never -- \\
sh -c "while true; do wget -qO- http://api-svc > /dev/null 2>&1; done"
\
\\

終端 1 你會看到 Pod 自動長:

\\\
api-hpa 125%/70% 3
api-hpa 125%/70% 5
api-hpa 85%/70% 7
api-hpa 65%/70% 7 ← 平衡
\
\\

按 Ctrl+C 停壓測,等 5 分鐘 Pod 自動縮回 3 個。

這就是生產就緒的完整樣貌——自動擴、自動縮、不用手動。

---

跟 Helm 比起來呢?

如果用 Helm 做這件事,可能就是:

\\\bash
helm install my-app ./my-chart -f prod-values.yaml
\
\\

一行。但前提是有人要先寫好 Chart——而那個寫 Chart 的人,就是你剛才做這 12 步的功夫

手寫 12 步 YAMLHelm Chart
第一次部署慢,要寫超多快,一行
多環境部署改 12 個 YAML換 values.yaml
升級回滾自己手動\helm rollback\
學習曲線高,每個概念都要懂低,會 install / upgrade
除錯直觀,YAML 看得到模板層多,要 \helm template\
先學手寫,再用 Helm。沒打過底,Helm 出事就只能擺爛。

---

清理

\\\bash
kubectl delete namespace prod
\
\\

一行刪光——這就是 Namespace 的好處。

重點整理

  • Step 7-8 開外部入口:Service + Ingress
  • Step 9 三層 NetworkPolicy:前端 → API → DB 逐層擋
  • Step 10 HPA:CPU > 70% 自動擴到 10 個 Pod
  • Step 11 \kubectl get all\ 一覽全貌
  • Step 12 壓測:實際看 HPA 動起來
  • 學完手寫 12 步,再用 Helm 才有底氣

下一步

12 步部署完成了——但真實的生產環境會出意外

  • HPA 真的會擴嗎?
  • 殺掉一個 Pod 真的會自動補嗎?
  • 流量翻 10 倍時,DB 連線池會不會爆?
下一篇:壓測 + 故障模擬 — 你的系統真的撐得住嗎?
📅 下一篇壓測 + 故障模擬:你的系統真的撐得住嗎?
hey 打壓測 + 模擬節點掛掉、Pod 被殺——驗證 HPA / probe / rolling update 真的工作。
📚 完整系列總覽K8s 系列教學首頁(共 40 課,按學習路徑順序排)