DockOne微信分享(一三三):深入理解Kubernetes網路策略

貓飯先生發表於2017-10-11
本文講的是DockOne微信分享(一三三):深入理解Kubernetes網路策略【編者的話】當我們逐漸向著微服務、雲原生邁進的時候,傳統靜態的、相對簡單的網路安全策略開始顯得吃力。 Kubernetes 的 Network Policy 特性正是來解決這個問題的。在剛剛出爐不久的1.7版本中,該特性也被扶正成為GA。讓我們來一起看看 Network Policy 是什麼,能為我們做什麼,以及是如何實現的。

【3 天燒腦式基於Docker的CI/CD實戰訓練營 | 北京站】本次培訓圍繞基於Docker的CI/CD實戰展開,具體內容包括:持續整合與持續交付(CI/CD)概覽;持續整合系統介紹;客戶端與服務端的 CI/CD 實踐;開發流程中引入 CI、CD;Gitlab 和 CI、CD 工具;Gitlab CI、Drone 的使用以及實踐經驗分享。

CNI

Kubernetes 對網路做了較好的抽象。它將對網路的需求交給外部的元件完成,也就是 CNI driver。

Pod 的網路必須滿足以下三個需求:

  1. 所有 Pod 之間無需 NAT 即可互通
  2. 主機和 Pod 之間無需 NAT 即可互通
  3. Pod 自省的 IP 地址和之外部看到該 Pod 的地址一致


CNI 對網路的實現做了詳細的定義。CNI 的實現可以被分成三種:

  1. 3 層路由實現
  2. Overlay 實現
  3. 2 層交換實現


現在比較常用的 CNI 實現有:Flannel、Calico、Weave。 Flannel 通過 VXLan Overlay 來實現跨主機 Pod 網路, Calico 則完全通過 3 層路由來實現了跨主機的容器網路,Weave也是 Overlay 的實現。

什麼是Network Policy

隨著業務邏輯的複雜化,微服務的流行,越來越多的雲服務平臺需要大量模組之間的網路呼叫。

傳統的單一外部防火牆,或依照應用分層的防火牆的做法漸漸無法滿足需求。在一個大的叢集裡面,各模組,業務邏輯層,或者各個職能團隊之間的網路策略的需求越來越強。

Kubernetes 在 1.3 引入了 Network Policy 這個功能來解決這個問題。這些 Policy 允許使用者在同一個 Cluster 內實現網路的隔離。也就是在某些需要的 Pod 之間架起防火牆。可以簡單的理解為各個微服務之間的動態防火牆。也有人把這叫做分散式防火牆。

並非所有的網路驅動都支援這個功能。比如大家比較熟悉的,比較流行的 Flannel 就還沒有加入對 Network Policy 的支援。Calico 和 Weave 都各自實現了 NPC(network policy controller)。雖然 Flannel 不支援 Network Policy。但是,可以使用 Flannel 提供網路方案,同時使用 Calico 或者Weave 的 NPC 元件來共同完成。Canal 就提供了將 Flannel 和 Calico NPC 組合的方案。

DEMO

下面我們就以Calico為基礎,給大家做一個demo。

這裡我已經搭好了一個 Kubernetes 的簡單的叢集:只有一個 master 和兩個 minion。我們先來部署 Calico。這裡要注意的是,由於我們要 demo 的是最新的 Kubernetes API,我們必須使用 Kubernetes 1.7 和 Calico 2.3,並且,Calico只能配置成 Kubernetes Datastore 的模式。在這種模式下,Calico 對網路狀態的控制是通過 Kubernetes API 來完成的。另外還有一種模式是通過 etcd 叢集。那種模式暫時還不支援最新的API。Kubernetes Datastore 這種方式有時也叫做KDD — Kubernetes datastore driver。

在進行 KDD 模式的 Calico 安裝時要注意以下這麼幾點:

  1. IPAM 要使用 host-local
  2. 通過 controller manager 來分配 CIDR


細節請點選:http://docs.projectcalico.org/ … tore/

配置好 Calico之後,我們來看一個簡單的 demo,實際操作一下 Network Policy:

為了簡單起見,我們會直接使用 default namespace。如果你在一個現有的環境裡面, 可以將以下的命令在一個獨立的 namespace 裡面執行。

建立 namespace 使用這個命令:

kubectl create ns np-demo


接下來執行一個簡單的 nginx deployment 並用80埠暴露服務:

kubectl run nginx --replicas=2 --image=nginx
  deployment "nginx" created
kubectl expose deploy nginx --port=80
  service "nginx" exposed


現在我們還沒有做任何的限制,所以 Kubernetes 預設情況是所有 Pod 都是開放的。

我們來用一個 BusyBox 的 Pod 驗證一下:

kubectl run busy --rm -ti --image busybox /bin/sh
  If you don`t see a command prompt, try pressing enter.
/ # wget -q nginx -O - | head -4


上面的 Wget 命令是在 BusyBox 這個 Pod 裡面執行的。-q 和 -O - 的組合會使這個命令在命令列輸出 nginx 的預設首頁,這表明我們的驗證是成功的。用 Ctl-D 退出容器。

接下來我們就要加入限制了。我們的做法是先限制對所有 Pod 的訪問,然後建立白名單。 kubectl apply下面的 YAML 檔案就可以限制所有訪問:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: default-deny 
spec:
  podSelector: 


注意我們提供了一個空的 podSelector。

我們再試著用之前的 BusyBox 的方式來訪問一下:

kubectl run busy --rm -ti --image busybox /bin/sh
/ # wget -q --timeout=5 nginx -O -



這此我們設定了 5 秒的超時。因為訪問被拒接,所以確實會超時:

wget: download timed out 


好,我們的第一個 Network Policy 已經生效了。然而,限制對所有 Pod 的訪問顯然是沒有意義的。接下來我們建立一個白名單 . apply 下面的 YAML 檔案:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-nginx
spec:
  podSelector:
    matchLabels:
      run: nginx
  ingress:
    - from:
      - podSelector:
          matchLabels:
            run: access


這個 Network Policy 的意思是:標籤 run:access 選中的 Pod 可以訪問標籤 run:nginx 的 Pod,也就是我們 demo 開始時建立的 Nginx 的 Pod。這些 label 都是 kubectl run 命令自動 新增的。 

接下來我們試試看能否成功地訪問了:

kubectl run access --rm -ti --image busybox /bin/sh
wget -q nginx -O - 


我們依然會看到熟悉的 Nginx 預設首頁。如果我們執行一個不符合上述 selector 的 Pod,就無法訪問。這個留給有興趣的同學自己回去驗證。

如果你接觸過 1.7 之前的 Network Policy 的話,你可能會發現這裡的 API 有些不同。Kubernetes 1.7 已經將 Network Policy 正式提升到 GA。

正式的 API 和之前的 API 區別有:

  1. 不再使用 Annotation 來表達 default-deny 等這樣的規則
  2. API version 從 extension/beta1 升級到了 networking.k8s.io/v1


實現

Calico 的實現是基於 iptables 的。在每個 chain 的頂端,Calico 都插入了一條定製的 chain,從而使得 packet 優先經過 Calico 定義的規則。我們在其中一個 minion 上面執行 iptables-save -c 就可以檢查這些規則。可以看出 kube-proxy 和 Calico 都定義了大量的 iptable 規則。 

這裡細節很多,我們只需要關注這幾點:

Calico 使用 conntrack 來優化。就是說,一旦一個連線已經建立,之後的packet都會直接被允許通過。比如:

-A cali-fw-cali7af3f94d3a1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 
-A cali-fw-cali7af3f94d3a1 -m conntrack --ctstate INVALID -j DROP


名為 cali-pi-xxxx 的規則是負責 Network Policy Ingress 的。我們可以想見,如果我們定義了很多 Policy,一個一個單獨定義的規則會導致效能下降。這裡 Calico 利用了 iptables 的 ipset 特性。使得一個 rule 可以通過 hash 表來匹配多種地址。

-A cali-pi-_G7e-YAvXRsfDoqGDf36 -m set --match-set cali4-s:2I5R46OBA_TBIUlpH0dCd_n src -j MARK --set-xmark 0x1000000/0x1000000 
-A cali-pi-_G7e-YAvXRsfDoqGDf36 -m mark --mark 0x1000000/0x1000000 -j RETURN


Weave 的實現也類似。底層還是使用了 iptables 和 netfilter。Weave 也建立了自定義的 chain。但由於一個是 Overlay 一個是路由,規則還是有些不同的。

另外一個細微的不同是,Weave使用了 -m state 而不是 -m conntrack。conntrack 是比較新的語法,但實際使用中功能是一樣的。下面是幾個 Weave 安裝的 iptables rules 的例子:

FORWARD chain:
    -o weave -j WEAVE-NPC
    -o weave -j DROP
WEAVE_NPC chain:
    -m state --state RELATED,ESTABLISHED -j ACCEPT
    -m state --state NEW -j WEAVE-NPC-DEFAULT
    -m state --state NEW -j WEAVE-NPC-INGRESS
    -m set --match-set weave-v/q_G.;Q?uK]BuDs2 dst -j ACCEPT
    -m set --match-set weave-k?Z;25^M}|1s7P3|H dst -j ACCEPGo


Q&A

Q:Calico 和 Weave 從 Policy 處理效能來看,兩者哪個更優?

A:兩者在 iptables 層面上的實現原理是一樣的。都用了-m
state 和 ipset 優化,效能差別不大。

Q:Calico 結合 Kubernetes 怎麼實現多租戶,比如網路隔離之類的?

A:可以考慮用 namespace 來隔離。沒有 Network Policy 的情況下當然是互通的。但是 Kubernetes 的 Network Policy 支援 namespaceSelector,可以輕鬆搞定。

Q:Weave、Calico、Flannel 比較,適用場景和優缺點是什麼,Flannel out了麼?

A:各有各的市場 :-)。
Flannel 比較簡單,資源消耗也會小些。Flannel 不能算 out 了。Cannel 的出現將 Flannel 和 Calico 整合了起來。

Q:NPC 必須用 iptables 實現嗎?在某些情況下,Pod 出向流量並不會由主機協議棧,這樣 iptables 就用不了,這種情況下 NPC 怎麼實現呢 ?

A:Calico、Weave、Romana 的 NPC 都是通過 iptables 實現的。Pod
egress 不通過主機協議棧也會通過 netfilter。

以上內容根據2017年07月20日晚微信群分享內容整理。分享人樑文智:睿雲智合研發顧問。 DockOne每週都會組織定向的技術分享,歡迎感興趣的同學加微信:liyingjiesa,進群參與,您有想聽的話題或者想分享的話題都可以給我們留言。

原文釋出時間為:2017-07-21

本文作者:樑文智

本文來自雲棲社群合作伙伴Dockerone.io,瞭解相關資訊可以關注Dockerone.io。

原文標題:DockOne微信分享(一三三):深入理解Kubernetes網路策略


相關文章