昨天建立了 todo-reader 和 cicd-deployer 兩個 ServiceAccount,分別設定對應的 Role 和 RoleBinding,控管不同角色的存取權限。今天會學 NetworkPolicy,控制 Pod 之間的網路流量 !
K8s 預設情況下,所有 Pod 都可以互相連線,不管在哪個 Namespace。
這意味著:
對 Todo App 來說,正確的網路存取應該是:
外部流量 → Ingress → todo-frontend
→ todo-api → MySQL
todo-frontend ✗ 不應該直接連 MySQL
其他 Pod ✗ 不應該連 MySQL
NetworkPolicy 讓你明確定義哪些 Pod 可以跟哪些 Pod 溝通。
NetworkPolicy 是 K8s 的網路防火牆規則,用 Label Selector 定義哪些 Pod 可以進來(Ingress)、哪些可以出去(Egress)。
重要:NetworkPolicy 是加法規則
Ingress:控制進入 Pod 的流量(誰可以連進來)
ingress:
- from:
- podSelector:
matchLabels:
app: todo-api # 只允許 todo-api 連進來
ports:
- port: 3306
Egress:控制從 Pod 出去的流量(Pod 可以連去哪裡)
egress:
- to:
- podSelector:
matchLabels:
app: mysql # 只允許連到 mysql
ports:
- port: 3306
今天目標:建立 NetworkPolicy 限制 MySQL 只接受 todo-api 的連線、todo-frontend 只能連 todo-api,並從不同 Pod 實際測試連線,驗證非預期的流量被擋住。
minikube stop
minikube delete
minikube start --network-plugin=cni --cni=calico
kubectl get pods -n kube-system | grep calico
# 等到兩個 Pod 都是 Running 才繼續
kubectl create namespace dev
kubectl config set-context --current --namespace=dev
kubectl apply -f .
kubectl create secret generic todo-api-secret \
--from-literal=DATABASE_URL=mysql+pymysql://user:password@mysql-service:3306/tododb \
-n dev
kubectl create secret generic mysql-secret \
--from-literal=MYSQL_ROOT_PASSWORD=rootpassword \
--from-literal=MYSQL_PASSWORD=password \
-n dev
kubectl get pods
mysql-network-policy.yaml:apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: mysql-allow-todo-api
namespace: dev
spec:
podSelector:
matchLabels:
app: mysql # 這條規則套用在 mysql Pod 上
policyTypes:
- Ingress # 只控制進入的流量
ingress:
- from:
- podSelector:
matchLabels:
app: todo-api # 只允許 todo-api 連進來
ports:
- protocol: TCP
port: 3306
kubectl apply -f mysql-network-policy.yaml
kubectl get networkpolicies

kubectl get pods
# 把 todo-api-xxxxx 換成你的實際 Pod 名稱
kubectl exec -it todo-api-xxxxx -- \
bash -c "apt-get install -y mysql-client 2>/dev/null; \
mysql -h mysql-service -u user -ppassword tododb -e 'SELECT 1'"
# 把 todo-frontend-xxxxx 換成你的實際 Pod 名稱
kubectl exec -it todo-frontend-xxxxx -- \
bash -c "apt-get install -y mysql-client 2>/dev/null; \
mysql -h mysql-service -u user -ppassword tododb -e 'SELECT 1'"
前者成功、後者超時或拒絕連線,代表 NetworkPolicy 生效了。
建立 frontend-network-policy.yaml:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-egress
namespace: dev
spec:
podSelector:
matchLabels:
app: todo-frontend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: todo-api # 只允許連到 todo-api
ports:
- protocol: TCP
port: 8000
- ports: # 允許 DNS 查詢,否則 Service 名稱解析會失敗
- protocol: UDP
port: 53
kubectl apply -f frontend-network-policy.yaml
設定 Egress 的時候記得允許 DNS(port 53),否則 Pod 無法解析 Service 名稱,連線會失敗。
# 把 todo-frontend-xxxxx 換成你的實際 Pod 名稱
kubectl exec -it todo-frontend-xxxxx -- \
sh -c "nc -zv -w 5 todo-api-service 80"

2. 從 todo-frontend 連 MySQL(應該失敗):
# 把 todo-frontend-xxxxx 換成你的實際 Pod 名稱
kubectl exec -it todo-frontend-xxxxx -- \
sh -c "nc -zv -w 5 mysql-service 3306"

前者成功、後者 timeout,代表 frontend-network-policy 生效了。
設定完成後,整個 Todo App 的網路流量限制如下:
外部
│
▼
Ingress Controller
│
├──▶ todo-frontend ──▶ todo-api ──▶ MySQL
│ ✗ 不能直接連 MySQL
│ ✗ 不能連其他服務
│
└──▶ todo-api ──▶ MySQL
✗ 不能連 todo-frontend
每個服務只能連它需要的服務,攻擊面大幅縮小。
NetworkPolicy 本身只是一份規則設定,真正執行規則的是叢集的 CNI(Container Network Interface)插件。
不是所有 CNI 都支援 NetworkPolicy:
在生產環境,選擇叢集 CNI 的時候要確認它支援 NetworkPolicy,不然寫了規則也沒有效果。
今天學了 NetworkPolicy:
到這裡,K8s 的核心元件都學完了——從 Pod、Deployment、Service、Ingress,到 ConfigMap、Secret、StatefulSet、PVC,再到 Probe、Resource、Namespace、HPA、RBAC、NetworkPolicy。
明天將介紹 K8s 除錯,建立一套系統化的問題排查流程。