理解Kubernetes(2): 應用的各類訪問方式

理解Kubernetes系列文章:html

  1. 手工搭建環境
  2. 應用的各類訪問方式

 

1. 經過 Pod 的 IP 地址訪問應用

1.1 Pod 的IP地址

每一個Pod 都會被分配一個IP地址,好比下面這兒pod的IP地址是 10.1.79.11.node

root@kub-node-0:/home/ubuntu# kubectl get pod -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP           NODE
my-nginx8-b77fdd7bc-4t2kb   1/1       Running   0          3d        10.1.79.11   172.23.100.6

該 Pod 在某個 node 上有兩個容器:nginx

root@kub-node-2:/home/ubuntu# docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS               NAMES
d199cb090c13        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_0
0c647fb76c0b        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:web

root@kub-node-2:/home/ubuntu# ip netns exec pause0b ip addr77: eth0@if78: <BROADCAST,MULTICAST,UP,LOWER_UP> 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 的:docker

root@kub-node-2:/home/ubuntu# ip netns exec nginx13 ip addr
77: eth0@if78: <BROADCAST,MULTICAST,UP,LOWER_UP> 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 的啓動參數中給的:ubuntu

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 中api

/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 network
FLANNEL_SUBNET=10.1.1.1/24   #這是本節點上 falnnel subnet
FLANNEL_MTU=1400             #本節點上 flannel mtu
FLANNEL_IPMASQ=true

(3)flannel deamon 還會建立 flannel.1 的 vxlan vtep 端點:負載均衡

root@kub-node-1:/opt/bin# ifconfig flannel.1
flannel.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.255

root@kub-node-1:/opt/bin# ip link show dev flannel.1
3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> 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 數據包。dom

(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 docker0
docker0   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 docker0
bridge name    bridge id        STP enabled    interfaces
docker0        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 -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         172.23.100.1    0.0.0.0         UG    0      0        0 ens3
10.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 網絡內節點內的通訊會走 docker0 169.254.169.254 172.23.100.1    255.255.255.255 UGH   0      0        0 ens3
172.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 wide
NAME                        READY     STATUS    RESTARTS   AGE       IP           NODE
my-nginx8-b77fdd7bc-4t2kb   1/1       Running   0          4d        10.1.79.11   172.23.100.6
root@kub-node-0:/home/ubuntu/kub# curl 10.1.79.11:80
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

而在集羣以外,是沒法訪問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-svc
service "nginx-cluster-svc" exposed

該 service 由 kube-apiserver 服務分配了虛擬的 IP 地址 192.1.47.211:

root@kub-node-0:/home/ubuntu/kub# kubectl get svc
NAME                TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
kubernetes          ClusterIP   192.1.0.1      <none>        443/TCP   13d
nginx-cluster-svc   ClusterIP   192.1.47.211   <none>        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-svc
service "nginx-nodeport-svc" exposed
root@kub-node-0:/home/ubuntu/kub# kubectl get svc
NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
kubernetes           ClusterIP   192.1.0.1     <none>        443/TCP        13d
nginx-nodeport-svc   NodePort    192.1.88.35   <none>        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-YN4D7LGVMIQA3S2Y 
root@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-H7QSASJ4XBTCRED7

root@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。示意圖以下:

等未來有機會再試試。

相關文章
相關標籤/搜索