深刻理解Kubernetes網絡策略

【編者的話】當咱們逐漸向着微服務、雲原生邁進的時候,傳統靜態的、相對簡單的網絡安全策略開始顯得吃力。 Kubernetes 的 Network Policy 特性正是來解決這個問題的。在剛剛出爐不久的1.7版本中,該特性也被扶正成爲GA。讓咱們來一塊兒看看 Network Policy 是什麼,能爲咱們作什麼,以及是如何實現的。nginx

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

CNI

Kubernetes 對網絡作了較好的抽象。它將對網絡的需求交給外部的組件完成,也就是 CNI driver。安全

Pod 的網絡必須知足如下三個需求:bash

  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 的實現。app

什麼是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

細節請點擊:docs.projectcalico.org/v2.3/gettin…

配置好 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。

原文連接

相關文章
相關標籤/搜索