一、容器與容器之間的通信
同一個pod中的容器,共用同一個網絡命名空間,直接使用localhost或127.0.0.1加上端口訪問就好了。可以看到這兩個容器的ip地址,路由表,以及暴露的端口號等都是一樣的,因為他們使用的是同一個網絡命名空間,同一個網絡棧。
【注意】:他們只是共用網絡命名空間(network namespace),但是進程命名空間(PID namespace)、用戶命名空間(user namespace)等其他的命名空間是相互隔離的。
二、POD與POD之間的通信
1、同一個Node上的pod之間的通信

每個pod都有唯一一個ip(屬於docker0網橋上的網段),每個pod的網絡基礎設施容器都是通過veth設備對直接連在docker0網橋上的:
本機docker0網橋的地址的數據包都會通過容器內的eth0(veth設備)直接轉給Kernel,路由的范圍是link(與本設備直接連接),因為veth設備都是成對存在的,另一端連在docker0網橋上,所以會由Kernel直接轉發給docker0網橋上的另一個veth設備。
宿主機將數據包通過docker0發給Kernel,最終docker0網橋會將數據包轉發給目的pod。
2、不同Node上的pod之間的通信
要支持不同Node節點上的pod通信需要滿足的要求:
1)整個kubernetes集群中對每個POD分配一個唯一的IP:在部署Kubernetes時,對每個Node節點的docker0網橋的網段重新划分,用戶設定一個大的網段(eg:10.20.0.0/16),存在etcd中,每個節點的flanneld會去etcd中查找這個值,然后,flanneld隨機生成一個屬於大網段的,且不沖突的子網(eg: 10.20.37.0/24; 10.20.92.0/24; 10.20.0.53/24)並將該值寫回etcd中,這樣就保證了每個pod的IP都會在10.20.0.0/16這個網段內;
2)Node節點之間需要架設一個overlay網絡(一般通過flannel實現),保證pod可以互相訪問。
三、Pod與Service之間的通信
為了支持水平擴展和高可用,實際使用中並不會直接使用POD的IP來訪問POD中的服務,而是定義service來訪問一組POD中的服務。service是對一組POD的抽象,會根據訪問策略(負載均衡)來訪問這組POD。
Kubernetes會在創建service時隨機分配一個ClusterIP,這是一個虛IP(VIP),不能與docker0網橋的網段沖突,比如:10.10.0.0/16這個網段(在 kube-apiserver的--service-cluster-ip-range=10.10.0.0/16配置項中指定)。
訪問POD中服務是只需要訪問這個VIP就好了,而service會將請求經過負載均衡之后轉發到后端POD中,這些工作都是運行在每個Node節點的kube-proxy進程管理的,它的核心功能是:將對某個Service的訪問經過負載均衡(Round Robin算法)之后轉發到某一個后端POD:
1、Service的類型
1、ClusterIP:默認類型,自動分配一個僅cluster內部可以訪問的虛擬IP;
2、NodePort:在ClusterIP基礎上為Service在每台機器上綁定一個端口,這樣就可以通過<NodeIP>:NodePort來訪問改服務;
3、LoadBalancer:在NodePort的基礎上,借助cloud provider創建一個外部的負載均衡器,並將請求轉發到<NodeIP>:NodePort
無論是ClusterIP+TargetPort的方式,還是NodeIP+NodePort的方式訪問服務,都會被節點的iptables規則重定向到kube-proxy監聽Service的服務代理端口,kube-proxy接受到Service請求之后,會使用Round-Robin負載均衡算法按照Service維護的Endpoint對象中選擇一個endpoint(其實就是POD)。
2、ClusterIP類型的service需要的iptables規則
任何數據包進入宿主機后,首先進入PREROUTING鏈:
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
kube-proxy進程在啟動時或者監聽到Service和Endpoint發生變化之后,會為每個endpoint修改本機Iptables的NAT表中的4條規則鏈:
-A KUBE-SEP-6XCC4VJACKOMSYVV -s 10.20.92.11/32 -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo" -j KUBE-MARK-MASQ -A KUBE-SEP-6XCC4VJACKOMSYVV -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo" -m tcp -j DNAT --to-destination 10.20.92.11:3306 -A KUBE-SERVICES -d 10.10.147.26/32 -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo cluster IP" -m tcp --dport 13306 -j KUBE-SVC-DM5EMDBLORDRGWLL -A KUBE-SVC-DM5EMDBLORDRGWLL -m comment --comment "hyjx--hyjx-group3/mysql-demo:mysqld-demo" -j KUBE-SEP-6XCC4VJACKOMSYVV
3、NodePort類型的Service需要的iptables規則
-A KUBE-NODEPORTS -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -m tcp --dport 30016 -j KUBE-MARK-MASQ -A KUBE-NODEPORTS -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -m tcp --dport 30016 -j KUBE-SVC-NVNGTMQMPFTQA6YS -A KUBE-SEP-7MWHP37XXE3W6QI4 -s 10.20.92.11/32 -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -j KUBE-MARK-MASQ -A KUBE-SEP-7MWHP37XXE3W6QI4 -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -m tcp -j DNAT --to-destination 10.20.92.11:3306 -A KUBE-SERVICES -d 10.10.95.1/32 -p tcp -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2 cluster IP" -m tcp --dport 23306 -j KUBE-SVC-NVNGTMQMPFTQA6YS -A KUBE-SVC-NVNGTMQMPFTQA6YS -m comment --comment "hyjx--hyjx-group3/mysql-demo2:mysqld-demo2" -j KUBE-SEP-7MWHP37XXE3W6QI4
NodePort類型的Service比ClusterIP類型就多了兩條規則KUBE-NODEPORTS,剩下的4條規則是用來處理使用ClusterIP的方式訪問服務的請求,因為NodePort類型的服務,同樣可以使用ClusterIP的方式訪問。
1)、第一條規則,實現了IP Masquerade功能 ;
2)、第二條規則,如果是使用NodeIP+NodePort的方式,目的端口為30016,處理方法和直接使用ClusterIP類型的服務訪問一樣。
4、更近一步,直接使用服務的名字訪問
使用服務的VIP的方式還是不太友好,因為只有創建好Service之后,系統才會隨機分配一個VIP,運行在容器中的應用無法提前知道這個VIP,動態修改鏡像是不現實的。最終的解決方案是直接使用服務的名稱來訪問服務。而,服務的名稱到服務的VIP的解析是由kube-dns來完成的。
kubelet在啟動容器的時候,會修改容器的/etc/resolv.conf,設置容器的DNS服務器為kube-dns(k8s-master節點IP),每個服務都有唯一一個域名。
在容器中無法ping通服務mysql-demo的VIP,但是使用服務的名稱,kube-dns可以正確地返回服務對應的VIP
四、k8s集群外部到k8s集群內部的訪問
設置service的類型為NodePort,並為服務指定一個不沖突的端口(30000-32767)就行了,訪問時直接使用任意一個Node節點的IP加上這個NodePort
