理解Kubernetes系列文章:
1. 通过 Pod 的 IP 地址访问应用
1.1 Pod 的IP地址
每个Pod 都会被分配一个IP地址,比如下面这儿pod的IP地址是 10.1.79.11.
root@kub-node-0:/home/ubuntu# kubectl get pod -o wideNAME READY STATUS RESTARTS AGE IP NODEmy-nginx8-b77fdd7bc-4t2kb 1/1 Running 0 3d 10.1.79.11 172.23.100.6
该 Pod 在某个 node 上有两个容器:
root@kub-node-2:/home/ubuntu# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESd199cb090c13 sammyliu8/nginx "nginx -g 'daemon of…" 3 days ago Up 3 days k8s_my-nginx8_my-nginx8-b77fdd7bc-4t2kb_default_cd1425fd-f48a-11e7-a605-fa163e9a22a6_00c647fb76c0b kubernetes/pause "/pause" 3 days ago Up 3 days k8s_POD_my-nginx8-b77fdd7bc-4t2kb_default_cd1425fd-f48a-11e7-a605-fa163e9a22a6_0
dockerd 会给 pause 容器分配一个ip,这个ip 就是 pod 的ip:
root@kub-node-2:/home/ubuntu# ip netns exec pause0b ip addr77: eth0@if78:mtu 1400 qdisc noqueue state UP group default link/ether 02:42:0a:01:4f:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.1.79.11/24 brd 10.1.79.255 scope global eth0 valid_lft forever preferred_lft forever
而 nginx 容器是跟 pause 容器共享 network namespace 的:
root@kub-node-2:/home/ubuntu# ip netns exec nginx13 ip addr77: eth0@if78:mtu 1400 qdisc noqueue state UP group default link/ether 02:42:0a:01:4f:0b brd ff:ff:ff:ff:ff:ff link-netnsid 0 inet 10.1.79.11/24 brd 10.1.79.255 scope global eth0 valid_lft forever preferred_lft forever
IP 所在的网段是在 dockerd 的启动参数中给的:
root 11183 1 1 2017 ? 03:04:26 /usr/bin/dockerd -g /data/docker --bip=10.1.79.1/24 --mtu=1400
1.2 Pod IP 地址的由来
(1)管理员配置 flannel 使用的 network,并将配置保存在 etcd 中
/opt/bin/etcdctl --endpoints="http://172.23.100.4:2379,http://172.23.100.5:2379,http://172.23.100.4:2379" mk /coreos.com/network/config \ '{"Network":"10.1.0.0/16", "Backend": {"Type": "vxlan"}}'
(2)在每个 minion 节点上,flannel 启动。它从 etcd 中获取 network 配置,并为本节点产生一个 subnet,也保存在 etcd 中。并且产生 /run/flannel/subnet.env 文件:
FLANNEL_NETWORK=10.1.0.0/16 #这是全局的 falnnel networkFLANNEL_SUBNET=10.1.1.1/24 #这是本节点上 falnnel subnetFLANNEL_MTU=1400 #本节点上 flannel mtuFLANNEL_IPMASQ=true
(3)flannel deamon 还会创建 flannel.1 的 vxlan vtep 端点:
root@kub-node-1:/opt/bin# ifconfig flannel.1flannel.1 Link encap:Ethernet HWaddr 0a:6e:a6:6f:95:04 inet addr:10.1.1.0 Bcast:0.0.0.0 Mask:255.255.255.255root@kub-node-1:/opt/bin# ip link show dev flannel.13: flannel.1:mtu 1400 qdisc noqueue state UNKNOWN mode DEFAULT group default link/ether 0a:6e:a6:6f:95:04 brd ff:ff:ff:ff:ff:ff
其 mtu 正好是 1400. 该端点会负责接收和发送 vxlan 数据包。
(3)使用 subnet.env 中的变量,启动 dockerd 进程,它的 bip 对应 FLANNEL_SUBNET,mtu 对应 FLANNEL_MTU。
/usr/bin/dockerd -g /data/docker --bip=10.1.1.1/24 --mtu=1400
(4)dockerd deamon 会创建 docker0 网桥
root@kub-node-1:/opt/bin# ifconfig docker0docker0 Link encap:Ethernet HWaddr 02:42:30:ef:ef:18 inet addr:10.1.1.1 Bcast:10.1.1.255 Mask:255.255.255.0
本节点上的容器都会使用一个 veth 设备挂接在该网桥上比如:
root@kub-node-1:/opt/bin# brctl show docker0bridge name bridge id STP enabled interfacesdocker0 8000.024230efef18 no veth295ded4 veth5459316
(5)对每一个被调度到本节点上的 POD,kubelet 都会创建一个 pause 容器,该容器会通过一个 veth 设备挂接到 docker0 上;同时,POD 中的其他容器会共享 pause 容器的 network namespace。
1.3 POD 间通信
首先看下本节点上的路由表:
root@kub-node-1:/opt/bin# route -nKernel IP routing tableDestination Gateway Genmask Flags Metric Ref Use Iface0.0.0.0 172.23.100.1 0.0.0.0 UG 0 0 0 ens310.1.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.1 #flannel 网络内跨节点的通信会交给 flannel.1 处理10.1.1.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0 #flannel 网络内节点内的通信会走 docker0169.254.169.254 172.23.100.1 255.255.255.255 UGH 0 0 0 ens3172.23.100.0 0.0.0.0 255.255.255.0 U 0 0 0 ens3
如果是同一个 deployment 的位于同一个 minon 上的两个 POD 之间通信,那实际上是同一个 subnet 间通信,此时经过 docker0 网桥即可;
如果是同一个 deployment 的位于两个 minon 上的两个 POD 之间通信,另一个POD 的IP 在 10.1.79.0 子网上,那么首先到 flannel.1, flannel 查询 etcd 获取对方容器所在的节点的IP地址,然后封装为 vxlan 的 udp 包,发到 ens3,走物理机网络达到对方节点。此时,完整的路径是:
containerA --> docker0 --> flannel.1 --> NodeA --> (IP Address) --> NodeB --> flannel.1 --> docker0 --> containerB
根据subnet 从 etcd 中获取 node ip:
root@kub-node-0:/home/ubuntu/kub# /opt/bin/etcdctl --endpoints="http://172.23.100.4:2379,http://172.23.100.5:2379,http://172.23.100.4:2379" get /coreos.com/network/subnets/10.1.79.0-24{ "PublicIP":"172.23.100.6","BackendType":"vxlan","BackendData":{ "VtepMAC":"6e:10:b3:53:1e:f4"}}
1.4 通过POD IP 访问应用
POD 的 IP 地址是 docker 分配的,在 falnnel 网络范围内,也就是 K8S 集群范围内,都可以使用 POD 的 IP 访问 POD 中的应用。比如:
root@kub-node-0:/home/ubuntu/kub# kubectl get pods -o wideNAME READY STATUS RESTARTS AGE IP NODEmy-nginx8-b77fdd7bc-4t2kb 1/1 Running 0 4d 10.1.79.11 172.23.100.6root@kub-node-0:/home/ubuntu/kub# curl 10.1.79.11:80Welcome to nginx!
If you see this page, the nginx web server is successfully installed andworking. Further configuration is required.
For online documentation and support please refer tonginx.org.
Commercial support is available atnginx.com.Thank you for using nginx.
而在集群之外,是无法访问POD的IP的。
2. 通过 Cluster Service 的IP地址访问应用
2.1 Service 的 Cluster IP
从上面内容可以看出,每个POD的IP 跟所在节点的配置有关,因此,随着POD在不同节点上的生生死死,其IP地址会发生变化。为了解决这个问题,K8S 提供了 Cluster 类型的 service。
创建一个 Cluster 类型的 service:
root@kub-node-0:/home/ubuntu/kub# kubectl expose deployment my-nginx8 --type ClusterIP --name nginx-cluster-svcservice "nginx-cluster-svc" exposed
该 service 由 kube-apiserver 服务分配了虚拟的 IP 地址 192.1.47.211:
root@kub-node-0:/home/ubuntu/kub# kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 192.1.0.1443/TCP 13dnginx-cluster-svc ClusterIP 192.1.47.211 80/TCP 1m
而这些 IP 地址的区间则是其启动参数 service-cluster-ip-range 指定的。在当前测试环境中,其值是 --service-cluster-ip-range=192.1.0.0/16。
2.2 Service Cluster IP 地址的由来
只能在 minon 节点上,而且只能使用 IP:Port/协议 才能访问 service。其它地方或者其它方式都不可以。
root@kub-node-1:/home/ubuntu# iptables -S -t nat | grep KUBE-SERVICES-N KUBE-SERVICES-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES-A KUBE-SERVICES -d 192.1.47.211/32 -p tcp -m comment --comment "default/nginx-cluster-svc: cluster IP" -m tcp --dport 80 -j KUBE-SVC-XQHY67DURS27F2QJ-A KUBE-SERVICES -d 192.1.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y-A KUBE-SERVICES -d 192.1.233.219/32 -p tcp -m comment --comment "kube-system/kubernetes-dashboard: cluster IP" -m tcp --dport 443 -j KUBE-SVC-XGLOHA7QRQ3V22RZ-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS root@kub-node-1:/home/ubuntu# iptables -S -t nat | grep KUBE-SVC-XQHY67DURS27F2QJ-N KUBE-SVC-XQHY67DURS27F2QJ-A KUBE-SERVICES -d 192.1.47.211/32 -p tcp -m comment --comment "default/nginx-cluster-svc: cluster IP" -m tcp --dport 80 -j KUBE-SVC-XQHY67DURS27F2QJ-A KUBE-SVC-XQHY67DURS27F2QJ -m comment --comment "default/nginx-cluster-svc:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-FO4LSVXACD7WOMVW-A KUBE-SVC-XQHY67DURS27F2QJ -m comment --comment "default/nginx-cluster-svc:" -j KUBE-SEP-DCSHEKS5WGS4LYVT root@kub-node-1:/home/ubuntu# iptables -S -t nat | grep KUBE-SEP-FO4LSVXACD7WOMVW-N KUBE-SEP-FO4LSVXACD7WOMVW-A KUBE-SEP-FO4LSVXACD7WOMVW -s 10.1.1.4/32 -m comment --comment "default/nginx-cluster-svc:" -j KUBE-MARK-MASQ-A KUBE-SEP-FO4LSVXACD7WOMVW -p tcp -m comment --comment "default/nginx-cluster-svc:" -m tcp -j DNAT --to-destination 10.1.1.4:80
root@kub-node-1:/home/ubuntu# iptables -S -t nat | grep KUBE-SEP-DCSHEKS5WGS4LYVT
-N KUBE-SEP-DCSHEKS5WGS4LYVT -A KUBE-SEP-DCSHEKS5WGS4LYVT -s 10.1.79.11/32 -m comment --comment "default/nginx-cluster-svc:" -j KUBE-MARK-MASQ -A KUBE-SEP-DCSHEKS5WGS4LYVT -p tcp -m comment --comment "default/nginx-cluster-svc:" -m tcp -j DNAT --to-destination 10.1.79.11:80从以上 iptables 规则可以看出,在该节点上通过 192.1.47.211:80 访问 cluster service,最终结果是以50% 和 50% 的概率发到了 10.1.1.4:80 和 10.1.79.11:80 上,而这两个的地址正是该service 的 两个 POD 的 IP 地址。
所以主要流程是:
- kube-proxy 根据 cluster service 的CRUD 来在每个节点上生成对应的 iptables 规则
- 当通过 cluster ip 访问时,这些 iptables 规则会通过 NAT 方式把流量导向 service的 POD
3. 通过 NodePort service 的 IP 访问应用
Cluster service 的 IP 地址是虚拟的,因此,只能从minon 节点上使用该IP 地址访问应用。为了从集群外访问应用,K8S 提供了使用 minon 节点的IP 地址访问应用的方式。
3.1 NodePort Service
创建一个 NodePort 类型的 service,系统会自动创建一个 cluster-ip,同时还多了一个 port。下图中是 31295. 该端口号的范围是 kube-apiserver 的启动参数 --service-node-port-range指定的,在当前测试环境中其值是 30000-32767。
root@kub-node-0:/home/ubuntu/kub# kubectl expose deployment my-nginx8 --type NodePort --name nginx-nodeport-svcservice "nginx-nodeport-svc" exposedroot@kub-node-0:/home/ubuntu/kub# kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 192.1.0.1443/TCP 13dnginx-nodeport-svc NodePort 192.1.88.35 80:31295/TCP 5s
通过每个 minon 节点的IP 和 31295 端口可以访问该service。
3.2 Service NodePort 的由来
root@kub-node-1:/var/log/kubernetes# iptables -S -t nat | grep KUBE-NODEPORTS-N KUBE-NODEPORTS-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx-nodeport-svc:" -m tcp --dport 31295 -j KUBE-MARK-MASQ-A KUBE-NODEPORTS -p tcp -m comment --comment "default/nginx-nodeport-svc:" -m tcp --dport 31295 -j KUBE-SVC-YN4D7LGVMIQA3S2Yroot@kub-node-1:/var/log/kubernetes# iptables -S -t nat | grep KUBE-SVC-YN4D7LGVMIQA3S2Y-N KUBE-SVC-YN4D7LGVMIQA3S2Y-A KUBE-SVC-YN4D7LGVMIQA3S2Y -m comment --comment "default/nginx-nodeport-svc:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-H7QSASJ4XBTCRED7
root@kub-node-1:/var/log/kubernetes# iptables -S -t nat | grep KUBE-SEP-H7QSASJ4XBTCRED7-N KUBE-SEP-H7QSASJ4XBTCRED7-A KUBE-SEP-H7QSASJ4XBTCRED7 -s 10.1.1.4/32 -m comment --comment "default/nginx-nodeport-svc:" -j KUBE-MARK-MASQ-A KUBE-SEP-H7QSASJ4XBTCRED7 -p tcp -m comment --comment "default/nginx-nodeport-svc:" -m tcp -j DNAT --to-destination 10.1.1.4:80-A KUBE-SVC-YN4D7LGVMIQA3S2Y -m comment --comment "default/nginx-nodeport-svc:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-H7QSASJ4XBTCRED7root@kub-node-1:/var/log/kubernetes# iptables -S -t nat | grep KUBE-SEP-YLSXRQMQKCP2ZZ6B-N KUBE-SEP-YLSXRQMQKCP2ZZ6B-A KUBE-SEP-YLSXRQMQKCP2ZZ6B -s 10.1.79.11/32 -m comment --comment "default/nginx-nodeport-svc:" -j KUBE-MARK-MASQ-A KUBE-SEP-YLSXRQMQKCP2ZZ6B -p tcp -m comment --comment "default/nginx-nodeport-svc:" -m tcp -j DNAT --to-destination 10.1.79.11:80-A KUBE-SVC-YN4D7LGVMIQA3S2Y -m comment --comment "default/nginx-nodeport-svc:" -j KUBE-SEP-YLSXRQMQKCP2ZZ6B
可见,同样地,通过 iptables 规则,通过 nodeport 访问service,最终转到了对该 servide 的所有 pod 轮流地访问。
即使某个 node 上没有service 的 pod,这些规则也会被创建,也就是说,NodePort 模式会在每个节点上开起一个端口,然后转发到内部 Pod IP 。这样可以保证使用任何一个 node 的ip,结合 nodeport,都可以访问到 service。
4. 采用云 load balancer
NodePort 方式虽然可以被数据中心内部访问,但是它无法被互联网访问。为了解决这个问题,某些公有云平台向 Kubernetes 集群提供了负载均衡,产生了一个可以在公网上访问到的虚拟IP。示意图如下:
等将来有机会再试试。