iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
Kubernetes

不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA系列 第 23 篇

Day 23|NetworkPolicy 實戰:Pod 能互連,不代表所有 Pod 都應該互連

  • 分享至 

  • xImage
  •  

昨天在了解 CNI !今天要來進入 NetworkPolicy 的實戰!

目前我們的:

cka-lab Namespace

裡面,只要 Service、DNS 和網路都正常,Pod 基本上可以互相連線。

例如我們現在可能有:

frontend
api
redis
postgres

在學習環境裡,這非常方便,因為不用特別設定網路權限,大家就能互相溝通。

但到了 Production,這種「所有 Pod 都可以互連」其實風險很高。

正常的系統架構可能是:

Frontend
   │
   ▼
API
   │
   ├── Redis
   └── PostgreSQL

Frontend 只需要呼叫 API,真正需要存取 Redis 和 PostgreSQL 的應該是 API。

也就是說,我們真正希望的不是:

任何 Pod
   ↓
任何 Pod

而是:

只有需要連線的人
才能連線

Kubernetes 用來處理這件事情的功能,就叫做:

NetworkPolicy

什麼是 NetworkPolicy?

NetworkPolicy 可以把它理解成 Kubernetes 裡的「Pod 網路存取規則」。

它可以控制兩種方向:

Ingress

代表:

哪些流量可以「進入」Pod。

以及:

Egress

代表:

Pod 可以把流量「送到哪裡」。

這一篇我們先專心看:

Ingress

也就是控制:

誰可以連到 Redis?

不過這裡有一個非常重要的前提。

NetworkPolicy 本身只是 Kubernetes API 裡的一份「規則」,真正幫你攔截網路流量的是 CNI Plugin。

CNI,也就是:

Container Network Interface

它負責 Kubernetes Pod 的網路。

像我們前面安裝過的:

Calico

就支援 NetworkPolicy。

因此如果你的 Cluster 已經有 Calico:

kubectl get pods -n calico-system

看到 Calico Pod 正常 Running,就可以直接做今天的實驗,不需要再安裝其他東西。

如果 CNI 根本不支援 NetworkPolicy,就算:

kubectl apply -f networkpolicy.yaml

成功建立 Resource,流量也可能完全不會被擋住。


先確認:現在是不是誰都能連 Redis?

在還沒建立 NetworkPolicy 以前,我們先做一個對照實驗。

建立一個臨時 Pod,名字叫:

attacker

它不是真的在做攻擊,只是模擬:

一個原本不應該有權限連 Redis 的 Pod。

執行:

kubectl run attacker \
  -n cka-lab \
  --image=redis:7-alpine \
  --restart=Never \
  -- \
  sleep 3600

這裡使用:

redis:7-alpine

是因為這個 Image 裡面本身就有:

redis-cli

等等測試 Redis 會很方便。

sleep 3600 則只是讓這個 Pod 不要馬上結束,維持一個小時。

確認它正常:

kubectl get pods -n cka-lab

接著進入 attacker:

kubectl exec \
  -it attacker \
  -n cka-lab \
  -- sh

然後測試 Redis:

redis-cli \
  -h redis \
  ping

這裡的:

-h redis

代表連線到 hostname:

redis

如果你的 Redis Service 名字就是:

redis

Kubernetes DNS 就會幫我們解析到 Redis Service。

正常情況會看到:

PONG

https://ithelp.ithome.com.tw/upload/images/20260924/20168537ZHk0JZilDm.png

也就是:

attacker
   │
   ▼
 Redis
   ✅

這證明目前沒有任何 NetworkPolicy 限制 attacker。


建立第一個 NetworkPolicy

接下來我們要設定:

Redis 只能接受 app=api 的 Pod 連入 6379 Port。

建立:

vim k8s/redis-networkpolicy.yaml

內容:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy

metadata:
  name: redis-ingress
  namespace: cka-lab

spec:
  podSelector:
    matchLabels:
      app: redis

  policyTypes:
    - Ingress

  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: api

      ports:
        - protocol: TCP
          port: 6379

這份 YAML 最重要的是弄清楚兩個:

podSelector

分別在選誰。


第一個 podSelector:誰要被保護?

先看:

spec:
  podSelector:
    matchLabels:
      app: redis

很多人第一次看會誤解成:

app=redis 的 Pod 可以連進來。

不是。

這一段是在回答:

這份 NetworkPolicy 要套用到哪些 Pod?

答案是:

app=redis

因此它代表:

誰被這份 Policy 保護?
        ↓
     Redis Pod

你可以先確認 Redis 的 Label:

kubectl get pods \
  -n cka-lab \
  --show-labels

可以看到:
https://ithelp.ithome.com.tw/upload/images/20260924/20168537b7NMlBTeth.png

如果你的 Redis Pod 根本沒有:

app=redis

那這份 NetworkPolicy 就選不到它,自然也不會產生效果。


第二個 podSelector:誰可以進來?

接下來看:

ingress:
  - from:
      - podSelector:
          matchLabels:
            app: api

這裡才是在回答:

誰可以連到 Redis?

答案是:

app=api

的 Pod。

因此整份 Policy 可以直接翻譯成人話:

保護:
app=redis

允許來源:
app=api

允許 Port:
TCP 6379

最後:

ports:
  - protocol: TCP
    port: 6379

表示即使來源符合:

app=api

也只允許它連 Redis 的:

TCP 6379

而不是 Redis Pod 上的所有 Port。

另外,由於這裡只有:

podSelector:

沒有 namespaceSelector,所以這個 app=api 指的是:

同一個 Namespace,也就是 cka-lab 裡的 app=api Pod。


套用 NetworkPolicy

執行:

kubectl apply \
  -f k8s/redis-networkpolicy.yaml

確認:

kubectl get networkpolicy \
  -n cka-lab

也可以使用簡寫:

kubectl get netpol \
  -n cka-lab

就會看到:

redis-ingress

https://ithelp.ithome.com.tw/upload/images/20260924/20168537jgDzA2U7ep.png


再測一次 attacker

重新進入 attacker:

kubectl exec \
  -it attacker \
  -n cka-lab \
  -- sh

再次執行:

redis-cli \
  -h redis \
  ping

這次通常不會再收到:

PONG

https://ithelp.ithome.com.tw/upload/images/20260924/20168537hhsix9TGAN.png
而是卡住一段時間,最後 Timeout。

原因很簡單。

attacker 沒有:

app=api

所以:

attacker
   │
   X
 Redis

現在變成:

attacker
↓
Redis
❌

這就表示 NetworkPolicy 已經開始作用。


那 API 還能不能連 Redis?

現在 Policy 規定:

app=api

可以連 Redis。

因此先確認 API Label:

kubectl get pods \
  -n cka-lab \
  --show-labels

https://ithelp.ithome.com.tw/upload/images/20260924/20168537bd61alzrDq.png

API Pod 應該包含:

app=api

如果你的 FastAPI 本身有某個 endpoint 會真的讀寫 Redis,可以直接呼叫那個 endpoint:

curl localhost:8080

但這裡要注意:

單純看到 API 回傳 HTTP 200,不一定就能證明 API → Redis 正常。

因為那個 API Endpoint 必須真的有使用 Redis 才算。

更直接的思考應該是:

API Pod
   │
   ▼
 Redis
   ✅

只要來源 Pod 符合:

app=api

而且連的是:

TCP 6379

NetworkPolicy 就會允許。


NetworkPolicy 不是「第一條符合就結束」

NetworkPolicy 有一個非常重要的特性:

Additive

Additive 的意思就是:

多份 NetworkPolicy 的「允許條件」會累加在一起。

它不像某些傳統 Firewall:

Rule 1
Rule 2
Rule 3

找到第一個 Match
↓
停止

NetworkPolicy 不是這種:

First Match Wins

的模式。

假設 Redis 同時被兩份 Policy 選到:

Policy A
允許 API

Policy B
允許 Monitoring

最後的結果會是:

API
      ┐
      ├──→ Redis
Monitor
      ┘

兩邊都可以。

也就是允許集合會合併。


更接近 Production:Default Deny

剛剛我們只保護了:

Redis

但 Production 常見的安全思維是反過來:

一開始全部都不准,再逐一開放真正需要的連線。

這叫做:

Default Deny

建立:

vim k8s/default-deny-ingress.yaml

內容:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy

metadata:
  name: default-deny-ingress
  namespace: cka-lab

spec:
  podSelector: {}

  policyTypes:
    - Ingress

這裡最重要的是:

podSelector: {}

空的 Selector 代表:

cka-lab Namespace 裡的所有 Pod。

而這份 Policy 又宣告:

policyTypes:
  - Ingress

但完全沒有:

ingress:

允許規則。

因此意思就是:

cka-lab 裡所有 Pod
↓
Ingress 預設全部拒絕

套用:

kubectl apply \
  -f k8s/default-deny-ingress.yaml

這時你就不能再假設:

Frontend → API
API → Redis
API → PostgreSQL

會自動成功。

而是要一條一條建立:

Frontend → API
✅

API → Redis
✅

API → PostgreSQL
✅

其他沒有明確需要的流量
❌

這種安全原則叫:

Least Privilege

也就是:

最小權限原則。

一個 Component 只取得完成工作所需要的最低權限。

所以 Production 常見的網路安全設計就是:

Default Deny
+
明確 Allow Rule
+
Least Privilege

為什麼 NetworkPolicy 很容易讓人以為 Application 壞掉?

導入 NetworkPolicy 之後,Kubernetes Networking Troubleshooting 就會多一層。

有一天你可能看到:

API Pod
Running

Redis Pod
Running

Service
正常

DNS
正常

EndpointSlice
正常

但 Application 還是:

Connection Timeout

以前你可能只會一直查:

kubectl get svc
kubectl get pods
kubectl get endpointslice

但現在還要想到:

kubectl get netpol \
  -n cka-lab

因為很可能:

Application 沒壞
Service 沒壞
DNS 也沒壞

只是 NetworkPolicy 不讓你過。

這也是為什麼 NetworkPolicy 設定錯誤時,常常看起來很像應用程式本身出了問題。


Kubernetes Networking 的 Troubleshooting 思路

學到這裡之後,遇到:

Pod A 連不到 Pod B

可以開始按照這個順序思考:

DNS 能不能解析?
        ↓
Service 設定正不正確?
        ↓
EndpointSlice 有沒有 Backend?
        ↓
Backend Pod 是否 Running / Ready?
        ↓
NetworkPolicy 是否允許這個流量?

這會比看到:

Connection Timeout

就直接猜 Application 壞掉有效率很多。


Day 23 小結

今天 NetworkPolicy 最重要的不是把 YAML 背起來,而是先建立一個網路安全觀念。

原本 Kubernetes 是:

Pod
 ↕
Pod

彼此可以自由溝通。

加入 NetworkPolicy 之後,我們可以變成:

Frontend
   │
   ▼
 API
  │  │
  ▼  ▼
Redis PostgreSQL

只有真正需要的網路路徑才被允許。

記住 NetworkPolicy YAML 最重要的閱讀順序:

spec.podSelector
↓
這份 Policy 保護誰?

ingress.from
↓
誰可以進來?

ports
↓
可以連哪個 Port?

以及一個實務上很重要的前提:

NetworkPolicy
只是規則

CNI,例如 Calico
才是真正執行規則的人

因此今天我們學到的是:

控制:
Pod → Pod

下一個很自然的問題則是:

誰可以操作 Kubernetes?

例如:

誰可以 get Pod?
誰可以 delete Pod?
誰可以修改 Deployment?

這就不再是網路權限,而是:

Identity
↓
Kubernetes API

所以下一篇就可以進入 Kubernetes 另一個非常重要的權限控制機制:

RBAC

也就是:

Role-Based Access Control

「以角色為基礎的存取控制」。
我們明天見!


上一篇
Day 22|CNI 到底是什麼?砍掉整個 Cluster,再用 Calico 重建一次
下一篇
Day 24|RBAC:不要讓每個人都變成 Kubernetes 管理員
系列文
不是背 YAML!30 天從零打造 Kubernetes 微服務:從本機實戰一路到 CKA 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言