iT邦幫忙

2026 iThome 鐵人賽

DAY 20
0

昨天建立了 todo-reader 和 cicd-deployer 兩個 ServiceAccount,分別設定對應的 Role 和 RoleBinding,控管不同角色的存取權限。今天會學 NetworkPolicy,控制 Pod 之間的網路流量 !


K8s 預設網路的風險

K8s 預設情況下,所有 Pod 都可以互相連線,不管在哪個 Namespace。

這意味著:

  • todo-frontend 可以直接連 MySQL
  • 任何 Pod 都可以對 MySQL 發查詢
  • 某個 Pod 被入侵後,攻擊者可以從那個 Pod 連到叢集裡的任何其他服務

對 Todo App 來說,正確的網路存取應該是:

外部流量  →  Ingress  →  todo-frontend
                      →  todo-api  →  MySQL

todo-frontend  ✗  不應該直接連 MySQL
其他 Pod       ✗  不應該連 MySQL

NetworkPolicy 讓你明確定義哪些 Pod 可以跟哪些 Pod 溝通。


NetworkPolicy 是什麼

NetworkPolicy 是 K8s 的網路防火牆規則,用 Label Selector 定義哪些 Pod 可以進來(Ingress)、哪些可以出去(Egress)。

重要:NetworkPolicy 是加法規則

  • 沒有任何 NetworkPolicy 的 Pod:允許所有流量(預設)
  • 一旦有任何 NetworkPolicy 選中這個 Pod:預設行為變成「拒絕所有」,只有規則中明確允許的流量才能通過
  • 因此,多條 NetworkPolicy 同時選中同一個 Pod 時,效果是聯集——只要任一條規則允許,該流量就放行

Ingress 和 Egress 規則

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 實際測試連線,驗證非預期的流量被擋住。

前置:確認 CNI 支援 NetworkPolicy

  1. minikube 預設 CNI 不支援 NetworkPolicy,需要先切換成 Calico:
minikube stop
minikube delete
minikube start --network-plugin=cni --cni=calico
  1. 確認 Calico 啟動成功:
kubectl get pods -n kube-system | grep calico
# 等到兩個 Pod 都是 Running 才繼續
  1. 重新建立 namespace 並部署所有資源:
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
  1. 確認所有 Pod 正常運行:
kubectl get pods

Step1: 限制 MySQL 只接受 todo-api 的連線

  1. 建立 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
  1. 確認 NetworkPolicy 建立成功:
kubectl get networkpolicies

https://ithelp.ithome.com.tw/upload/images/20260922/20183863pTV3afeIsZ.png

Step2: 驗證 NetworkPolicy 生效

  1. 先查出 Pod 名稱:
kubectl get pods
  1. 從 todo-api Pod 連 MySQL(應該成功):
# 把 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'"
  1. 從 todo-frontend Pod 連 MySQL(應該失敗):
# 把 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 生效了。

Step3: 限制 todo-frontend 只能連 todo-api

建立 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 名稱,連線會失敗。

Step4: 驗證 frontend-network-policy 生效:

  1. 從 todo-frontend 連 todo-api(應該成功):
# 把 todo-frontend-xxxxx 換成你的實際 Pod 名稱
kubectl exec -it todo-frontend-xxxxx -- \
  sh -c "nc -zv -w 5 todo-api-service 80"

https://ithelp.ithome.com.tw/upload/images/20260922/20183863IlBh35BYEv.png
2. 從 todo-frontend 連 MySQL(應該失敗):

# 把 todo-frontend-xxxxx 換成你的實際 Pod 名稱
kubectl exec -it todo-frontend-xxxxx -- \
  sh -c "nc -zv -w 5 mysql-service 3306"

https://ithelp.ithome.com.tw/upload/images/20260922/20183863JQxNwLGlOd.png

前者成功、後者 timeout,代表 frontend-network-policy 生效了。

完整的 Todo App NetworkPolicy 架構

設定完成後,整個 Todo App 的網路流量限制如下:

外部
  │
  ▼
Ingress Controller
  │
  ├──▶ todo-frontend  ──▶  todo-api  ──▶  MySQL
  │         ✗ 不能直接連 MySQL
  │         ✗ 不能連其他服務
  │
  └──▶ todo-api  ──▶  MySQL
            ✗ 不能連 todo-frontend

每個服務只能連它需要的服務,攻擊面大幅縮小。


延伸筆記:NetworkPolicy 需要 CNI 支援

NetworkPolicy 本身只是一份規則設定,真正執行規則的是叢集的 CNI(Container Network Interface)插件。

不是所有 CNI 都支援 NetworkPolicy:

  • 支援:Calico、Cilium、Weave Net
  • 不支援:Flannel(最常見的 CNI 之一,但不支援 NetworkPolicy)

在生產環境,選擇叢集 CNI 的時候要確認它支援 NetworkPolicy,不然寫了規則也沒有效果。


小結

今天學了 NetworkPolicy:

  • K8s 預設所有 Pod 都可以互連,沒有任何隔離
  • NetworkPolicy 是加法規則:一旦有規則選中 Pod,沒有明確允許的流量一律拒絕
  • Ingress:控制進入 Pod 的流量;Egress:控制從 Pod 出去的流量
  • 設定 Egress 記得允許 DNS(port 53)
  • 需要 CNI 支援,Flannel 不支援,Calico、Cilium 支援

到這裡,K8s 的核心元件都學完了——從 Pod、Deployment、Service、Ingress,到 ConfigMap、Secret、StatefulSet、PVC,再到 Probe、Resource、Namespace、HPA、RBAC、NetworkPolicy。

明天將介紹 K8s 除錯,建立一套系統化的問題排查流程。


上一篇
Day 19|RBAC
下一篇
Day 21|K8s 除錯
系列文
從零學 K8s|30 天核心概念 × 實作,新手也能真正掌握 Kubernetes 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言