Kubernetes 網絡:Pod 和 container 的那點貓膩



1. Kubernetes 網絡模型

在 Kubernetes 的網絡模型中,最小的網絡單位是 Pod。Pod 的網絡設計原則是 IP-per-Pod,即 Pod 中 container 共享同一套網絡協議棧,具有相同的網絡命名空間。Pod 內的 container 通過 localhost + port 訪問,類似於 Liunx 中進程訪問的方式。構建 Kubernetes 的 Pod 網絡模型如下:
 
Kubernetes 對集群網絡的設計原則有:
  • 所有容器都可以不用 NAT 和別的容器通信。
  • 所有節點都可以不用 NAT 和所有容器通信,反之亦然。
  • 容器的地址和外部看到的地址是同一個地址。
 
回顧前面文章,容器在 Docker 環境下訪問外網是通過 NAT 的,NAT 會增加端口管理的復雜性,而且在外部看不到實際容器的 ip。這里的設計原則避開了這些點,它使得 Pod 可以被看作獨立的“虛擬機”,從而較好的進行服務發現,域名解析,負載均衡等。

2. 單節點 Pod 訪問

單節點構建 pod,實現 pod 內 container 的相互訪問。
 
創建 pod:
$ cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web_server
  template:
    metadata:
      labels:
        app: web_server
    spec:
      containers:
      - name: httpd-test
        image: httpd
      - name: bootcamp-test
        image: bootcamp:v1
 
$ kubectl apply -f deployment.yaml
deployment.apps/deployment created
 
$ kubectl get pods
NAME                          READY   STATUS    RESTARTS   AGE
deployment-595bb7bd6c-6jxwr   2/2     Running   0          44s
 
pod 包括兩個 container httpd 和 bootcamp。其中,httpd 監聽在 80 端口,bootcamp 監聽在 8080 端口:
$ netstat -ntlp | grep 80
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      10204/httpd
tcp6       0      0 :::8080                 :::*                    LISTEN      10077/node
 
$ ps aux | grep 10077 | grep -v grep
root     10077  0.0  0.9 708220 23720 ?        Sl   09:32   0:00 node server.js
$ ps aux | grep 10204 | grep -v grep
root     10204  0.0  0.1   5940  4360 ?        Ss   09:32   0:00 httpd -DFOREGROUND
 
進入 pod ,從 pod 中訪問 container:
$ kubectl exec -it deployment-595bb7bd6c-6jxwr /bin/bash
Defaulting container name to bootcamp-test.
Use 'kubectl describe pod/deployment-595bb7bd6c-6jxwr -n default' to see all of the containers in this pod.
root@deployment-595bb7bd6c-6jxwr:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:12:00:05 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.5/24 brd 172.18.0.255 scope global eth0
       valid_lft forever preferred_lft forever
 
root@deployment-595bb7bd6c-6jxwr:/# curl 172.18.0.5:8080
Hello Kubernetes bootcamp! | Running on: deployment-595bb7bd6c-6jxwr | v=1
root@deployment-595bb7bd6c-6jxwr:/# curl 172.18.0.5:80
<html><body><h1>It works!</h1></body></html>
可以看到,進入 pod 實際上進入的是 pod 內的 container,在 continer 內通過 ip + port 實現 httpd 和 bootcamp 的訪問。 
 
同理,直接在 node 上訪問 pod:
$ curl 172.18.0.5:8080
Hello Kubernetes bootcamp! | Running on: deployment-595bb7bd6c-6jxwr | v=1
$ curl 172.18.0.5:80
<html><body><h1>It works!</h1></body></html>
$ curl 172.18.0.5:8081
curl: (7) Failed to connect to 172.18.0.5 port 8081: Connection refused

3. Pod 網絡模型

上節實現了 pod 內 container 的相互訪問,那么 container 是怎么訪問到“外網”的呢?
 
帶着這個問題我們查看和 deployment 相關的 container:
$ docker ps | grep deployment
24adb2a738f4        httpd                  "httpd-foreground"       Up 6 minutes    k8s_httpd-test_deployment-..
1339d17223d8        8fafd8af70e9           "/bin/sh -c 'node se…"   Up 7 minutes    k8s_bootcamp-test_deployment-..
c86a97fff88b        k8s.gcr.io/pause:3.1   "/pause"                 Up 7 minutes    k8s_POD_deployment-...
 
和期望的不一樣,多了一個“pause”類型的 container。查看這三個容器的網絡命名空間:
$ mkdir -p /var/run/netns
$ docker inspect --format '{{ .State.Pid }}' 24adb2a738f4
8349
$ docker inspect --format '{{ .State.Pid }}' 1339d17223d8
8189
$ docker inspect --format '{{ .State.Pid }}' c86a97fff88b
8100
 
$ ln -s /proc/8349/ns/net /var/run/netns/httpd
$ ln -s /proc/8189/ns/net /var/run/netns/bootcamp
$ ln -s /proc/8100/ns/net /var/run/netns/pause
 
$ ip netns list
pause (id: 3)
bootcamp (id: 3)
httpd (id: 3)
 
$ ip netns exec httpd ls -la /proc/self/ns/
lrwxrwxrwx 1 root root 0 Jan 17 08:37 net -> 'net:[4026532636]'
...
$ ip netns exec bootcamp ls -la /proc/self/ns/
lrwxrwxrwx 1 root root 0 Jan 17 08:37 net -> 'net:[4026532636]'
...
$ ip netns exec pause ls -la /proc/self/ns/
lrwxrwxrwx 1 root root 0 Jan 17 08:37 net -> 'net:[4026532636]'
...
 
它們具有相同的網絡命名空間,進一步的查看容器對應的網絡類型:
$ docker inspect 24adb2a738f4 | grep NetworkMode
            "NetworkMode": "container:c86a97fff88bd73d221dfbe56e209e868f4479671bca9d01c14fd07ba1f03a4d",
$ docker inspect 1339d17223d8 | grep NetworkMode
            "NetworkMode": "container:c86a97fff88bd73d221dfbe56e209e868f4479671bca9d01c14fd07ba1f03a4d",
$ docker inspect c86a97fff88b | grep NetworkMode
            "NetworkMode": "default"
 
到這里我們大體知道了,容器 pause 的網絡模式是默認(網橋)模式,容器 httpd 和 bootcamp 是 container 模式。pause 容器是作為 httpd 和 bootcamp 的“掛載點”存在的,它的存在保證了多個容器可以使用同一個 ip,同一個網絡協議棧,即滿足 IP-per-Pod 模型。
 
那么,我們查看網絡命名空間的 veth 設備即能知道 Pod 的網絡結構了。
$ brctl show
bridge name     bridge id               STP enabled     interfaces
docker0         8000.0242ed103e29       no              vethd2c436f
 
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
11: veth4132daf@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
    link/ether 7a:db:25:ff:27:85 brd ff:ff:ff:ff:ff:ff link-netnsid 3
    inet6 fe80::78db:25ff:feff:2785/64 scope link
       valid_lft forever preferred_lft forever
 
root@deployment-595bb7bd6c-6jxwr:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:ac:12:00:05 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.5/24 brd 172.18.0.255 scope global eth0
       valid_lft forever preferred_lft forever
 
構建網絡示意圖如下:
 
 
那么,如果網絡容器 pause 因故障銷毀了會出現什么情況呢?
首先,綁定到網絡容器中的原 network namespace 還是會存在的。當網絡容器銷毀時 replicaSet 會觸發 kubelet 對網絡容器進行重建,重建的網絡容器歸屬於新的 network namespace,而相應的原來“橋接”在網絡容器上的容器也被重建,並且“橋接”到新的網絡容器上。
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM