Kubernetes技術分析之網路
概述
Docker的流行激活了一直不溫不火的PaaS,隨著而來的是各類Micro-PaaS的出現,Kubernetes是其中最具代表性的一員,它是Google多年大規模容器管理技術的開源版本。本系列文章將逐一分析Kubernetes,本文說明 Kubernetes網路模型的特點和實現方式。
Kubernetes網路
Kubernetes採用扁平化的網路模型,每個Pod都有一個全域性唯一的IP(IP-per-pod),Pod之間可以跨主機通訊,相比於Docker原生的NAT方式來說,這樣使得容器在網路層面更像虛擬機器或者物理機,複雜度整體降低,更加容易實現服務發現,遷移,負載均衡等功能。
為了實現這個目標,Kubernetes中需要解決4個問題:
容器間通訊
Pod是容器的集合,Pod包含的容器都執行在同一個Host上,並且擁有同樣的網路空間。現在建立一個Pod,包含2個Container:
web-pod.yaml:
apiVersion: v1
kind: Pod
metadata:
name: webpod
labels:
name: webpod
spec:
containers:
- name: webpod80
image: jonlangemak/docker:web_container_80
ports:
- containerPort: 80
hostPort: 80
- name: webpod8080
image: jonlangemak/docker:web_container_8080
ports:
- containerPort: 8080
hostPort: 8080
Pod執行成功後,在其所在的Node上查詢容器:
$ docker ps
CONTAINER ID IMAGE PORTS
63 dc7e032ab6 jonlangemak/docker:web_container_8080
4ac1a5156a04 jonlangemak/docker:web_container_80
b77896498f8f gcr.io/google_containers/pause:0.8.0 0.0.0.0:80->80/tcp, 0.0.0.0:8080->8080/tcp
可以看到運行了3個容器,其中2個這是Pod定義好的,第3個執行的容器映象 是gcr.io/google_containers/pause,它是Netowrk Container,它不做任何事情,只是用來接管Pod的網路。
通過docker inspect檢視著幾個容器的資訊,可以看出Pod中定義的容器的網路設定都集中配置在了Netowrk Container上,然後再加入Netowrk Container的網路中。這樣的好處是避免服務容器之間產生依賴,用一個簡單的容器來統一管理網路。
Pod間通訊
Pod間通訊是使用一個內部IP,這個IP即使Netowrk Container的IP。
以web-pod為例:
$ kubectl describe pod webpod
Name: webpod
Namespace: default
IP: 10.1.14.51
...
$ docker inspect b77896498f8f |grep IPAddress
"IPAddress": "10.1.14.51",
對應用來說,這個IP是應用能看到,並且是可以對外宣稱的(服務註冊,服務發現);而NAT方式,應用能看到的IP是不能對外宣稱的(必須要使用主機IP+port方式),埠本身就是稀缺資源,並且ip+port的方式,無疑增加了複雜度,這就是IP-per-pod的優勢所在。
那麼第一個問題就是,如何保證Pod的IP是全域性唯一的。其實做法也很簡單,因為Pod的IP是docker bridge分配的,不同Node之間docker bridge配置成不同的網段。
- node1: docker -d –bip=10.1.79.1/24 …
- node2: docker -d –bip=10.1.14.1/24 …
- node3: docker -d –bip=10.1.58.1/24 …
同一個Node上的Pod原生能通訊,但是不同Node之間的Pod如何通訊的,這就需要對Docker進行增強,現有的方案有Flannel,OpenVSwitch,Weave等。本文Kubernetes環境是採用Flannel。
- Flannel
由CoreOS團隊針對Kubernetes設計的一個覆蓋網路工具,Flannel 通過在叢集中建立一個覆蓋網路為主機設定一個子網。通過隧道協議(支援udp,vxlan)封裝容器之間的通訊報文,實現跨主機通訊。
Pod到Service通訊
Pod本身是變化的,比如當Pod發生遷移,那麼Pod的IP是變化的, 那麼Service的就是在Pod之間起到中轉和代理的作用,Service會生成一個虛擬IP, 這個虛擬IP負載均衡到後端的Pod的IP。現在為上面的web-pod建立service:
web-service.yaml:
apiVersion: v1
kind: Service
metadata:
name: webservice
labels:
name: webservice
spec:
ports:
- name: web-80
port: 80
containerPort: 80
- name: web-8080
port: 8080
containerPort: 8080
selector:
name: webpod
然後查詢Service的VIP和後端Pod的IP:
$ kubectl describe service webservice
Name: webservice
Namespace: default
Labels: name=webservice
Selector: name=webpod
Type: ClusterIP
IP: 10.254.85.33
Port: web-80 80/TCP
Endpoints: 10.1.14.51:80
Port: web-8080 8080/TCP
Endpoints: 10.1.14.51:8080
Session Affinity: None
No events.
那麼訪問web-pod則可以通過10.254.85.33。這主要是Kube-Proxy在其作用,在每臺Node上都會部署一個Kube-Proxy,Kube-Proxy對於每個Service會啟用埠監聽,並配合iptables定向到該埠,現在在一臺Node上查詢:
$ iptables-save
...
-A KUBE-PORTALS-HOST -d 10.254.85.33/32 -p tcp -m comment --comment "default/webservice:web-80" -m tcp --dport 80 -j DNAT --to-destination 192.168.3.146:56610
-A KUBE-PORTALS-HOST -d 10.254.85.33/32 -p tcp -m comment --comment "default/webservice:web-8080" -m tcp --dport 8080 -j DNAT --to-destination 192.168.3.146:50871
對於Web-Service,Kube-Proxy建立2條iptables規則:
1.目的IP為10.254.85.33,目的埠為80的報文DNAT到192.168.3.146:56610
2.目的IP為10.254.85.33,目的埠為8080的報文DNAT到192.168.3.146: 50871
$ lsof -i:56610
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
kube-prox 2537 root 16u IPv6 26353463 0t0 TCP *:56610 (LISTEN)
$ lsof -i:50871
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
kube-prox 2537 root 17u IPv6 26353485 0t0 TCP *:50871 (LISTEN)
可以看到Kube-Proxy監聽在56610和50871,對於訪問的報文將轉發到後端Pod。對於Pod的變化,Kube-Proxy會及時重新整理。
還需要注意的是,當前Kube-Proxy只是3層”(TCP/UDP over IP) 轉發,當後端有多個Pod的時候,Kube-Proxy預設採用輪詢方式進行選擇,也可以設定成基於CLientIP的會話保持。
外網到內網的通訊
目前為止,以上部分都是在討論Kubernetes內部網路的通訊,但是外網如何訪問到Kubernetes上的應用,因為內網的IP是無法直接訪問的(需要KubeProxy和Flannel的工作),這時候就需要一個外部路由模組來連線外網和內網。
參考
作者簡介
吳龍輝,現任網宿科技高階運營工程師,致力於雲端計算PaaS的研究和實踐,活躍於CloudFoundry,Docker,Kubernetes等開源社群,貢獻程式碼和撰寫技術文件。
郵箱:[email protected]/[email protected]