1 Kubernetes網絡模型
基本原則:每個Pod都擁有一個獨立IP,而且假定所有Pod都在一個可以直接連通的、扁平的網絡空間中。
- 基於基本原則,用戶不需要額外考慮如何建立Pod之間的連接,也不需要考慮容器端口映射到主機端口等問題
- 同一個Pod內部的所有容器共享一個網絡堆棧即網絡命名空間,Pod內的所有容器的端口是共享的
- Kubernetes對集群網絡要求:
- 所有容器都可以在不用NAT的方式下同別的容器通信
- 所有節點都可以在不用NAT的方式下同所有容器通信,反之亦然
- 容器的地址和別人看到的地址是同一個
2 Docker的網絡基礎
2.1 網絡命名空間
- Linux在網絡棧中引入網絡命名空間(Network Namespace)為了支持網絡協議棧的多個實例,處於不同命名空間的網絡棧是完全隔離的
- Linux的網絡命名空間內可以有自己獨立的路由表及獨立的Iptables/Netfilter設置來提供轉發、NAT及IP包過濾等功能
- 需要納入命名空間的元素有進程、套接字、網絡設備等
2.1.1 網絡命名空間的實現
- linux實現網絡命名空間的核心是讓網絡棧的全局變量變成一個Net Namespace變量的成員,然后協議棧的函數調用加入一個Namespace的參數
- 網絡空間鏈表連接所有網絡空間,所有網絡棧變量都放入到網絡空間
- 所有的網絡設置都只能屬於一個命名空間,通常物理設置只能關聯到root這個命名空間
- 網絡命名空間代表是一個獨立的協議棧,所以他們之間是相互隔離的彼此無法通信協議棧內部看不到對方
2.1.2 網絡命名空間的操作
使用iproute2系列配置工具中的ip命令操作
ip netns add <name> #創建一個命名空間
ip netns exec <name> <command> #在命名空間內執行命令
ip netns exec <name> bash #執行多個命令先進入sh
2.2 Veth設備對
Veth設備都是成對出現的,可以實現不同網絡命名空間之間進行通信
- 在Veth設置的一端發送數據時它會將數據直接發送到另一端,並觸發另一端的接收操作
2.2.1 Veth操作命令
ip link add veth0 type veth peer name veth1 #創建veth設備對
ip link show #查看所有網絡接口
ip link set veth1 netns netns1 #將veth1掛載到netns1命名空間下
ip link set dev veth1 up #啟動veth1設備
2.2.2 Veth設備對如何查看對端
使用ethtool工具查看
ip netns exec netns1 ethtool -S veth1 #查詢netns1下的veth1的對端,看peer_ifindex
ip netns exec netns2 ip link #查看peer_ifindex對應的值
2.3 網橋
- 網橋是一個二層網絡設備,可以解析收發報文,讀取目標MAC地址信息和自己記錄的MAC表結合,來決策報文轉發端口
- Linux中多網卡可以通過網橋提供這些設備之間相互轉發數據
- 網橋處理轉發和丟棄報文外還可以將報文發送到上層(網絡層)
2.3.1 Linux網橋的實現
- Linux內核通過一個虛擬網橋設備(Net Device)來實現橋接,其可以綁定若干個以太網接口設備,從而將它們橋接起來
- Net Device可以有一個IP
3 Docker的網絡實現
docker的4類網絡模式:
1. host
2. container
3. none
4. bridge,默認設置
只介紹bridge模式
- Docker Daemon第一次啟動時將創建一個虛擬網橋默認名為docker0,同時給其分配一個子網
- 針對Docker中的容器都會創建一個Veth設備對,一端關聯網橋一端使用Linux的網絡命名空間映射到容器內的eth0設備,然后從網橋的地址段內給eth0分配一個IP
- 為了讓容器跨主機通信就必須在主機的地址上分配端口,然后通過這個端口路由或代理到容器上
4 Kubernetes的網絡實現
4.1 容器到容器的通信
- 同一個Pod內的容器共享同一個網絡命名空間,直接使用localhost即可
4.2 Pod之間的通信
- 每個Pod都有一個真實的全局IP,每個Node內的不同Pod之間可以直接采用對方Pod的Ip通信
4.2.1 同一Node內Pod通信
- 同一Node內的Pod通過Veth設備對連接到同一個docker0網橋上,Pod的IP也都是該網橋分配的
- Pod內的默認路由都是docker0的地址,所有數據都通過docker0轉發
4.2.2 不同Node的Pod通信
- Pod的地址是與docker0在同一個網段內,而docker0網段與宿主機網卡的IP網段完全不同,而通信只能走宿主機的網卡,因此通信必須通過宿主機IP來進行尋址和通信
- Kubernetes會記錄所有正在運行Pod的IP分配信息,並將其寫入etcd作為Service的Endpoint
- 支持不同Node之間Pod的通信條件:
- 整個Kubernetes集群中Pod的IP不能沖突
- 能將Pod的IP與Node的IP關聯起來,通過這個關聯實現相互訪問
- 解決:
- Kubernetes部署時多docker0的IP地址進行規划保證每個Node上的docker0地址不沖突
- 通信時先找到Node的IP將數據發送到這個網卡上,然后讓宿主機將相應數據轉到具體docker0上
4.3 Pod到Service的通信
- k8s在創建服務時為服務分配一個虛擬IP,客戶端通過該IP訪問服務,服務則負責將請求轉發到后端Pod上
- Service是通過kube-proxy服務進程實現,該進程在每個Node上均運行可以看作一個透明代理兼負載均衡器
- 對每個TCP類型Service,kube-proxy都會在本地Node上建立一個SocketServer來負責接受請求,然后均勻發送到后端Pod默認采用Round Robin負載均衡算法
- Service的Cluster IP與NodePort等概念是kube-proxy通過Iptables的NAT轉換實現,kube-proxy進程動態創建與Service相關的Iptables規則
- kube-proxy通過查詢和監聽API Server中Service與Endpoints的變化來實現其主要功能,包括為新創建的Service打開一個本地代理對象,接收請求針對針對發生變化的Service列表,kube-proxy會逐個處理,處理流程:
- 如果該Service沒設置ClusterIP,則不做處理,否則獲取該Service的所有端口定義列表
- 逐個讀取端口定義列表,根據端口名、Service名和Namespace對其進行修改
- 更新負載均衡組件中對應Service的轉發Service的轉發地址列表
- 對於已刪除的Service則進行清理
5 開源的網絡組件
- Kubernetes的網絡模型假定所有Pod都在一個可以直接連通的扁平網絡空間,因此需要對網絡進行設置
- 目前常見的模型有Flunnel、Open vSwitch及直接路由的方式