今兒我們來念叨念叨 AZURE NETWROK 在K8S下的實戰,有童鞋可能說這東西有啥可說的對容器來說不都是透明的嘛,這么說大體上也沒啥毛病,但是捋清楚它的實現會有助於我們來做一些有意思的事情,這里先賣個關子。那我們先來看看要容器網絡何用?
1. 容器網絡的由來
容器單機也能玩,CLUSTER下也能玩,跟虛擬化的思路是差不多的。那么問題就來的單節點內部解決通信問題容易,集群下解決跨節點通信問題就得權衡一下了。先來看看在沒有類 K8S 這種控制器出現之前,單節點上不同容器之間是如何通信的。假設目前有三個容器Container-1,Container-2, Container-3,Container-1 和 Container-2 在 Node-1 上,Container-3 在 Node-2 上。原生 Docker 實現是將 Container 的網卡橋接在宿主機(Node)的 Bridge 設備上,Container 像直接接入到網絡上一樣,本節點(Node)內,不同 Container 在一個 L2 的域內,可以直接互相通信(即 Container-1,Container-2 不需要額外配置即可進行通信)。跨節點通信(Container-2 和 Container-3 通信)就是不是那么簡單了,第一我們並不一定能夠滿足 Node-1 和 Node-2 之間穿過的網絡在一個 L2 的域內,其次每個節點(Node)上 Container 地址分配上如何避免沖突。從設計思路上講 Docker 是一個獨立的管理域,從設計初衷上一定是 Day-0 去適配各種物理網絡(不對物理網絡配置提出要求),所以 Docker 在原生的解決方案中提供的跨節點(Node)的通信方法是通過端口映射轉發的方式實現的,即 Container-2 要訪問 Container-3 時,目的訪問地址並非 Container-3 本身的地址,而是 Container-3 所在 Node 的節點IP 地址,這樣一來,用戶只需要滿足在 Cluster 下的所有 Node 的節點 IP 地址之間可以互聯互通即可。但上述方案的弊端也是顯而易見的,第一,由於在節點(Node)上 運行的所有 Container 如果對其他節點發布自己的訪問地址都需要復用該節點(Node)的 IP 地址,這里為服務端口管理帶來了極高的復雜度,而且由於避免沖突的問題喪失了極大的靈活性;第二,在 Cluster 下從 Container 的創建到服務發現需要一整套的自動化管理工具,手工干預是不現實的,基於上述方案實現復雜度太高。
=======Example Container-1 to Container-2==========
Container-1 IP address --- Node-1 docker0 bridge --- Container-2 IP address √
=======Example Container-2 to Container-3==========
S: Container-2 IP address, D: Node-2 IP address --- Node-1 Docker0 Bridge --- SNAT to Node-1 IP address --- DNAT to Container-3 IP address --- Node-2 Docker0 Bridge --- S: Node-1 IP address, D: Container-3 IP address √
K8S 作為容器集群的控制器 Day 1 設計的時候就將上述問題考慮在內,提出了幾點設計要求:
1. 所有 Cluster 內的 Container 可以互相通信(跨節點或節點內)且不需要 NAT ;
2. 所有 Cluster 內的節點(Node)可以與 Cluster 內的 Container 互相通信且不需要 NAT ;
3. 所有 Container 自己的真實地址與其他 Container 訪問它所用地址相同 ;
其實上述要求的實現核心問題就是,Node IP 地址這個平面,Container IP 地址這個平面,可以互聯互通(適配各種物理網絡環境)
上述要求實現可以通過下述幾種方法實現:
1). Overlay :這個其實是設計起來最清晰以及最容易實現的方法,將上述所有互聯互通的需求都封裝在一個 Full Control 的邏輯網絡中,說白就是想怎么通怎么通,對底層物理網絡啥依賴也沒有。通常的實現通過隧道技術如 GRE,IPinIP,VXLAN 等;
2). Underlay :把上述的 Node IP 和 Container IP 地址平面和物理網絡 L2 打通,扁平化實現,Container 如同直接連接在物理網絡上一樣,如果部署在物理IDC中擴展性性受制於傳統網絡在 L2 Domian 的一些限制,如果部署在雲中受制於單網卡支持多 IP 的數量;
3). Native L3 Routing :類似 Underlay 方案,最大區別是將 L2 Domian 限制在 Node 內部,支持良好的擴展性,反之如果部署在物理IDC中,對物理網絡有相應要求,物理網路需要擁有所有 Container IP 地址平面的路由,如果部署在雲中,通常的方式是通過客戶自定義路由的方式來模擬物理網絡的路由表要求,所以擴展性受制於支持UDR的數量;
除了上述的基本的容器網絡內互聯互通的要求外,容器網絡中還有一個很重好的需求就是混合部署互通,有些有些資源是部署在容器中,有些資源是部署在虛擬機中,這個時候,容器網絡外部虛擬機如何和虛擬機通信,這個需求的滿足上其實 Underlay 模式是有先天優勢的,因為容器其實無異於虛擬機。另外就是如何在容器網絡中實現今天物理網絡中的安全隔離策略,在安全需求中,策略的一致性是非常重要的,如何實現容器網絡內部外部的策略一致性管理,舉例在雲上,有些資源是部署在容器中,有些資源是部署在虛擬機中,如何做到安全策略的統一管理而不是兩個孤島。
2. K8S Network on AZURE 實現
了解實現之前,需要先問清楚自己想獲得什么目標,因為第一在 AZURE 上 K8S 部署有不同的方式,第二在不同的部署方式上 Network 的實現各有不同,並且在不同的 Network 實現下支持的功能也各有不同。
先來看一下 K8S on Azure 的部署選項:
1). ACS(Azure Container Service) 服務,Azure 第一代托管的 K8S 容器服務,K8S Cluster 的生命周期管理由 Azure 平台完成;
2). ACS-Engine, 類似ACS服務,不同在於把更多的 K8S Cluster 的管理控制權給到用戶,MS 通過在 Github (https://github.com/Azure/acs-engine) 上的 ACS-Engine 工具幫助用戶生成一個 K8S Cluster的 AZURE ARM(Azure Resource Management)部署模板,暴露給用戶更多的集群可配置參數,並且在沒有 ACS 服務的地區可以通過 ACS-Engine 獲得類 ACS 服務;
3). AKS (Azure Kubernetes Service)服務,Azure 第二代托管的 K8S 容器服務,和第一代的區別 K8S Cluster 的 Master 節點由 Azure 平台托管,客戶看不到 Master 節點 VM 且不會收取 Master 節點費用;
再來看一下 K8S Network 在 Azure 上的實現由哪些:
1). Kubenet :配合 AZURE 上 UDR 自定義路由實現,可以理解為上述的 Native L3 Routing 模式, K8S 為每個 Node 分配一個地址空間,該地址空間內的地址用來為生根在該 Node 上的所有 POD (Container)分配地址,舉例兩個 Node 場景,為 Node-1 分配地址空間 (10.244.0.0/24),為 Node-2 分配地址空間 (10.244.1.0/24),所有生根在 Node-1 的 POD (Container)從 地址空間 (10.244.0.0/24)中獲取可用地址。由於 Node 上 POD (Container)地址空間對 Vnet 是無感的,所以當需要跨節點 (Node)實現 POD (Container)互通時候,Vnet 路由是不知道該將該數據報文轉發給哪一個 Node 的,所以通過配置 UDR 自定義路由實現 POD 地址空間和 Node 的對應關系。這種方案的擴展性受制於 Azure 下 UDR 支持的路由條目數,默認100條路由最大可支持400,看起來還不錯,比 AWS 還是多了很多的 ( AWS 自定義路由表最大路由條目數50)。
2). Azure:將 POD (Container)直接橋接在 Vnet 網絡上,POD (Container)和 VM 是一樣的,可以獲得所有 Vnet 內的功能。通過在所有 Node 節點網卡上關聯多個 Vnet Private IP ,在創建 POD (Container)時將 Vnet Private IP 直接給到 POD (Container)。擴展性的限制,目前 VM 單網支持的 Private IP 數量為默認256最大1024,所以單節點上支持最大的 POD 數量默認為256最大為1024,集群下 VNET 內支持的 Private IP 數量默認為4096最大支持8192。AWS 類似方案基本不太可行,因為每個 ENI 上支持的 IP 地址數量太少 (依照機器規格大小不同單個 ENI 支持的 IP 數量不同,最大支持50個)
3). Calico:Calico 支持通過 UDR 方式實現 Native L3 Routing,也支持 Overlay 方式(由於 Azure Vnet 內不支持 IPinIP 封裝,所以原生的 Overlay 方式不支持,可以通過 Canal -- Calico + Flannel 方式實現)。該方案的優勢是支持 K8S 的 Network Policies,對於用戶由 Micro Segment 需求的用戶這個是個不錯的選擇。
K8S on Azure Network Support Matrix
Kubenet | Azure | Calico | |
ACS | Supported | Supported | Supported |
ACS Engine | Supported | Supported(as default) | Supported |
AKS | Supported (as default) | N/A | On Roadmap |
3. K8S on Azure 通過 ACS-Engine 部署使能 Azure Network CNI Plug-IN
本部分和大家一個來看一下如何通過 ACS-Engine 部署使能 AZURE CNI 網絡的 K8S 集群。
ACS-Engine 部署 K8S 的詳細方法及操作步驟,大家可參閱如下連接:https://github.com/Azure/acs-engine/blob/master/docs/kubernetes.md 。這里不做贅述,下面主要針對在部署中影響 Network 的幾部分着重介紹。ACS-Engine 通過用戶定義的 Kubernetes Cluster Json 描述文件來生成 Azure 部署的 ARM 模板,ACS-Engine 從 0.11.0 版本開始默認采用 Azure CNI 進行網絡實現,所以如果用戶使用 0.11.0 或者之后的版本則無需額外的配置。對於 0.11.0 版本之前的 ACS-Engine,需要在生成的 ARM 參數模板中進行指定,可參考如下模板示例:
1 #azuredeploy.parameters.json 2 { 3 "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", 4 "contentVersion": "1.0.0.0", 5 "parameters": { 6 ... 7 "networkPolicy": { 8 "value": "azure" 9 }, 10 ... 11 } 12 }
除此之外 ACS-Engine 所使用的 Kubernetes Cluster Json 描述文件中需要定義單個 Compute Node 上配置多個 Private IP,以滿足 POD (Container)創建時候使用,因為目前該網絡插件的實現還不支持動態創建分配,所以需要進行與配置。
#kubernetes.json
"masterProfile": {
"count": 1,
"ipAddressCount": 64, # Master 節點配置64個 Private IP
...
},
"agentPoolProfiles": [
{
"name": "agentpool1",
"count": 8,
...
"ipAddressCount": 150, # Agent 節點配置150個 Private IP
...
}
],
通過 ACS-Engine 生成的 Azure ARM 部署完 K8S 集群后,登入 K8S Master 節點檢查部署情況:
/var/log/azure 路徑下查看 cluster-provision.log 中,有如下日志
+ configNetworkPolicy
+ [[ azure = \a\z\u\r\e ]]
+ configAzureNetworkPolicy
+ CNI_CONFIG_DIR=/etc/cni/net.d
進入 /etc/cni/net.d 目錄,確認有 10-azure.conf 文件存在。
進入 /opt/cni/bin 目錄,確認有 azure-vnet, azure-vnet-ipam, loopback 三個 bin 文件存在。
登入 Agent 節點檢查基礎網絡情況,存在 bridge azure0,該設備由 azure cni 創建,
adminuser@k8s-agentpool1-26563307-1:~$ ip link show type bridge
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
link/ether 02:42:5a:0c:b0:2a brd ff:ff:ff:ff:ff:ff
6: azure0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
link/ether 00:17:fa:00:f7:e7 brd ff:ff:ff:ff:ff:ff
=====================================================================================================
adminuser@k8s-agentpool1-26563307-1:~$ ip link show master azure0
2: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc mq master azure0 state UP mode DEFAULT group default qlen 1000
link/ether 00:17:fa:00:f7:e7 brd ff:ff:ff:ff:ff:ff
創建一個 stateless deployment 驗證 POD 地址分配
#nginxdeployment.yaml
apiVersion: apps/v1beta1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template: # create pods using pod definition in this template
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
查看 POD (Container)IP 地址, 可以看到 POD 分配的 IP 地址在 VNET Subnet 內,並且所用地址為 Agent 節點網卡的輔助 Private IP
adminuser@k8s-master-26563307-0:~$ kubectl get pod -o wide
2018-02-05 15:13:55.571574 I | proto: duplicate proto type registered: google.protobuf.Any
2018-02-05 15:13:55.571653 I | proto: duplicate proto type registered: google.protobuf.Duration
2018-02-05 15:13:55.571677 I | proto: duplicate proto type registered: google.protobuf.Timestamp
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-431080787-f3pf6 1/1 Running 1 5d 10.240.0.15 k8s-agentpool1-26563307-1
nginx-deployment-431080787-wp4fc 1/1 Running 0 5d 10.240.0.7 k8s-agentpool1-26563307-0
登入 POD 查看 POD 內地址與路由表, POD 內地址與在 NODE 中所查看 POD 地址一致,且 POD 中路由表默認網關為 VNET Subnet 網關
weikang@k8s-master-26563307-0:~$ kubectl exec -i -t nginx-deployment-431080787-f3pf6 -- /bin/bash
2018-02-05 15:17:40.701659 I | proto: duplicate proto type registered: google.protobuf.Any
2018-02-05 15:17:40.701730 I | proto: duplicate proto type registered: google.protobuf.Duration
2018-02-05 15:17:40.701886 I | proto: duplicate proto type registered: google.protobuf.Timestamp
root@nginx-deployment-431080787-f3pf6:/# ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
16: eth0@if15: <BROADCAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state UP qlen 1000
link/ether 06:9f:93:f2:69:32 brd ff:ff:ff:ff:ff:ff
inet 10.240.0.15/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::49f:93ff:fef2:6932/64 scope link
valid_lft forever preferred_lft forever
=======================================================================================
root@nginx-deployment-431080787-f3pf6:/# ip route show
default via 10.240.0.1 dev eth0 proto static
10.240.0.0/16 dev eth0 proto kernel scope link src 10.240.0.15
在 Agent 節點上查看 ip link show type veth,可以看到很多 veth 設備存在,這些 veth 和 POD 相對應,實現了 POD 的網卡橋接到 azure0 網橋上,並通過 azure0 網橋的上聯接口 eth0 接入到 Vnet 中。
adminuser@k8s-agentpool1-26563307-1:~$ ip link show type veth
7: azveth9d2d732@if8: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master azure0 state UP mode DEFAULT group default qlen 1000
link/ether 06:72:7e:d3:72:30 brd ff:ff:ff:ff:ff:ff link-netnsid 0
9: azveth76bb202@if10: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master azure0 state UP mode DEFAULT group default qlen 1000
link/ether 7e:e6:03:38:8a:e6 brd ff:ff:ff:ff:ff:ff link-netnsid 1
11: azveth78715da@if12: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master azure0 state UP mode DEFAULT group default qlen 1000
link/ether 02:08:c2:de:79:44 brd ff:ff:ff:ff:ff:ff link-netnsid 2
13: azvethb9e59f4@if14: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master azure0 state UP mode DEFAULT group default qlen 1000
link/ether 2e:7f:1c:ba:0c:bd brd ff:ff:ff:ff:ff:ff link-netnsid 3
15: azvethc1ed481@if16: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master azure0 state UP mode DEFAULT group default qlen 1000
link/ether 0a:32:5a:b1:98:ec brd ff:ff:ff:ff:ff:ff link-netnsid 4
Note:如何獲取 veth 和 POD 的對應關系
Docker 為每個 POD 創建獨立的 namespace,進入 /var/run/docker/netns 目錄可以看到多個已生成 netns。通過 sudo ln -s /var/run/docker/netns /var/run/netns 將 docker 的 ns 目錄鏈接到 agent 節點的 ns 目錄, 執行 sudo ip netns exec namespace_name ip addr show
adminuser@k8s-agentpool1-26563307-0:~$ sudo ip netns exec c657dc01301f ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
12: eth0@if11: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether d2:af:35:15:c1:ad brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.240.0.7/16 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::d0af:35ff:fe15:c1ad/64 scope link
valid_lft forever preferred_lft forever
xx:eth0@ifyy 字樣代表 POD 的接口 index 為xx,NODE 節點與之成對的 veth index 為yy,在 NODE 節點上執行 ip link show type veth
adminuser@k8s-agentpool1-26563307-0:~$ ip link show type veth
9: azvethca929c7@if10: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master azure0 state UP mode DEFAULT group default qlen 1000
link/ether d6:d4:04:b6:40:81 brd ff:ff:ff:ff:ff:ff link-netnsid 0
11: azveth547dc55@if12: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master azure0 state UP mode DEFAULT group default qlen 1000
link/ether c6:a7:78:5e:ae:56 brd ff:ff:ff:ff:ff:ff link-netnsid 1
與前面獲得的 POD 信息進行比較,可以看到信息完全一致。
adminuser@k8s-master-26563307-0:~$ kubectl get pod -o wide
2018-02-05 15:13:55.571574 I | proto: duplicate proto type registered: google.protobuf.Any
2018-02-05 15:13:55.571653 I | proto: duplicate proto type registered: google.protobuf.Duration
2018-02-05 15:13:55.571677 I | proto: duplicate proto type registered: google.protobuf.Timestamp
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-431080787-f3pf6 1/1 Running 1 5d 10.240.0.15 k8s-agentpool1-26563307-1
nginx-deployment-431080787-wp4fc 1/1 Running 0 5d 10.240.0.7 k8s-agentpool1-26563307-0
4. 總結
通過上述的介紹,相信大家對 K8S on Azure 的 AZURE CNI 實現已經有了一定了解,目前 AZURE CNI 的實現還是為用戶帶來了不少便利,讓 POD (Container)與 VM 一樣直接接入到 AZURE Vnet 中,享受所有 Vnet 內的功能。但是 AZURE CNI 也還有需要繼續完善的地方,容器網絡安全是很多客戶非常迫切的需求,目前 AZURE CNI 還不支持 K8S Network Policies,無法實現 micro segment 的安全隔離,如果有這部分需求,可以考慮 Calico 的方案。容器網絡是一個非常大的話題,本文希望大家深入淺出,快速入門,最后為大家分享一些學習資料,感興趣的同學可以擴展閱讀。
1. K8S 網絡解決方案介紹:https://kubernetes.io/docs/concepts/cluster-administration/networking/ 這里面列出的比較全面的解決方案,可以逐個深入了解
2. Calico 網絡組件介紹:https://docs.projectcalico.org/master/introduction/ 個人認為實現最完整的容器網絡組件,從連通性到可擴展性再到安全性
3. AZURE Container Network Github:https://github.com/Azure/azure-container-networking 詳細的 AZURE CNI 實現說明
萬變不離其宗,小伙伴們在花些時間揣摩 Overlay, Underlay, Native L3 Routing 三種方式的意義,相信一定會有更深刻的理解。