k8s之網絡

Kubernetes 作爲編排引擎管理着分佈在不同節點上的容器和 Pod。Pod、Service、外部組件之間需要一種可靠的方式找到彼此並進行通信,Kubernetes 網絡則負責提供這個保障。本章包括如下內容:

  1. Kubernetes 網絡模型
  2. 各種網絡方案
  3. Network Policy
Kubernetes 網絡模型

Kubernetes 採用的是基於扁平地址空間的網絡模型,集羣中的每個 Pod 都有自己的 IP 地址,Pod 之間不需要配置 NAT 就能直接通信。另外,同一個 Pod 中的容器共享 Pod 的 IP,能夠通過 localhost 通信。

這種網絡模型對應用開發者和管理員相當友好,應用可以非常方便地從傳統網絡遷移到 Kubernetes。每個 Pod 可被看作是一個個獨立的系統,而 Pod 中的容器則可被看做同一系統中的不同進程。

下面討論在這個網絡模型下集羣中的各種實體如何通信。知識點前面都已經涉及,這裏可當做複習和總結。

Pod 內容器之間的通信

當 Pod 被調度到某個節點,Pod 中的所有容器都在這個節點上運行,這些容器共享相同的本地文件系統、IPC 和網絡命名空間。

不同 Pod 之間不存在端口衝突的問題,因爲每個 Pod 都有自己的 IP 地址。當某個容器使用 localhost 時,意味着使用的是容器所屬 Pod 的地址空間。

比如 Pod A 有兩個容器 container-A1 和 container-A2,container-A1 在端口 1234 上監聽,當 container-A2 連接到 localhost:1234,實際上就是在訪問 container-A1。這不會與同一個節點上的 Pod B 衝突,即使 Pod B 中的容器 container-B1 也在監聽 1234 端口。

Pod 之間的通信

Pod 的 IP 是集羣可見的,即集羣中的任何其他 Pod 和節點都可以通過 IP 直接與 Pod 通信,這種通信不需要藉助任何的網絡地址轉換、隧道或代理技術。Pod 內部和外部使用的是同一個 IP,這也意味着標準的命名服務和發現機制,比如 DNS 可以直接使用。

Pod 與 Service 的通信

Pod 間可以直接通過 IP 地址通信,但前提是 Pod 得知道對方的 IP。在 Kubernetes 集羣中, Pod 可能會頻繁的銷燬和創建,也就是說 Pod 的 IP 不是固定的。爲了解決這個問題,Service 提供了訪問 Pod 的抽象層。無論後端的 Pod 如何變化,Service 都作爲穩定的前端對外提供服務。同時,Service 還提供了高可用和負載均衡功能,Service 負責將請求轉發給正確的 Pod。

外部訪問

無論是 Pod 的 IP 還是 Service 的 Cluster IP,它們只能在 Kubernetes 集羣中可見,對集羣之外的世界,這些 IP 都是私有的。

Kubernetes 提供了兩種方式讓外界能夠與 Pod 通信:

  1. NodePort
    Service 通過 Cluster 節點的靜態端口對外提供服務。外部可以通過 <NodeIP>:<NodePort> 訪問 Service。

  2. LoadBalancer
    Service 利用 cloud provider 提供的 load balancer 對外提供服務,cloud provider 負責將 load balancer 的流量導向 Service。目前支持的 cloud provider 有 GCP、AWS、Azur 等。
k8s網絡方案

網絡模型有了,如何實現呢?

爲了保證網絡方案的標準化、擴展性和靈活性,Kubernetes 採用了 Container Networking Interface(CNI)規範。

CNI 是由 CoreOS 提出的容器網絡規範,它使用了插件(Plugin)模型創建容器的網絡棧。

CNI 的優點是支持多種容器 runtime,不僅僅是 Docker。CNI 的插件模型支持不同組織和公司開發的第三方插件,這對運維人員來說很有吸引力,可以靈活選擇適合的網絡方案。

目前已有多種支持 Kubernetes 的網絡方案,比如 Flannel、Calico、Canal、Weave Net 等。因爲它們都實現了 CNI 規範,用戶無論選擇哪種方案,得到的網絡模型都一樣,即每個 Pod 都有獨立的 IP,可以直接通信。區別在於不同方案的底層實現不同,有的採用基於 VxLAN 的 Overlay 實現,有的則是 Underlay,性能上有區別。再有就是是否支持 Network Policy。

Network Policy

Network Policy 是 Kubernetes 的一種資源。Network Policy 通過 Label 選擇 Pod,並指定其他 Pod 或外界如何與這些 Pod 通信。

默認情況下,所有 Pod 是非隔離的,即任何來源的網絡流量都能夠訪問 Pod,沒有任何限制。當爲 Pod 定義了 Network Policy,只有 Policy 允許的流量才能訪問 Pod。

不過,不是所有的 Kubernetes 網絡方案都支持 Network Policy。比如 Flannel 就不支持,Calico 是支持的。我們接下來將用 Canal 來演示 Network Policy。Canal 這個開源項目很有意思,它用 Flannel 實現 Kubernetes 集羣網絡,同時又用 Calico 實現 Network Policy。

部署 Canal

部署 Canal 與部署其他 Kubernetes 網絡方案非常類似,都是在執行了 kubeadm init 初始化 Kubernetes 集羣之後通過 kubectl apply 安裝相應的網絡方案。也就是說,沒有太好的辦法直接切換使用不同的網絡方案,基本上只能重新創建集羣。

要銷燬當前集羣,最簡單的方法是在每個節點上執行 kubeadm reset。然後初始化集羣。

kubeadm init --apiserver-advertise-address 192.168.56.105 --pod-network-cidr=10.244.0.0/16

然後按照文檔 https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/ 安裝 Canal。文檔列出了各種網絡方案的安裝方法:

執行如下命令部署 Canal

kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/canal.yaml

部署成功後,可以查看到 Canal 相關組件:

Canal 作爲 DaemonSet 部署到每個節點,地屬於 kube-system 這個 namespace。

Canal 準備就緒,下節我們將實踐 Network Policy。

實踐 Network Policy

爲了演示 Network Policy,我們先部署一個 httpd 應用,其配置文件 httpd.yaml 爲:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: httpd
spec:
  replicas: 3
  template:
    metadata:
      labels:
        run: httpd
    spec:
      containers:
      - name: httpd
        image: httpd:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  name: httpd-svc
spec:
  type: NodePort
  selector:
    run: httpd
  ports:
  - protocol: TCP
    nodePort: 30000
    port: 8080
    targetPort: 80

httpd 有三個副本,通過 NodePort 類型的 Service 對外提供服務。部署應用:

[root@k8s-master ~]# kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP           NODE        NOMINATED NODE   READINESS GATES
httpd-64554b6c67-8sx9d   1/1     Running   0          63s   10.244.2.3   k8s-node2   <none>           <none>
httpd-64554b6c67-csbzd   1/1     Running   0          63s   10.244.2.2   k8s-node2   <none>           <none>
httpd-64554b6c67-kkmdg   1/1     Running   0          63s   10.244.1.3   k8s-node1   <none>           <none>
[root@k8s-master ~]# kubectl get service httpd-svc 
NAME        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
httpd-svc   NodePort   10.108.126.45   <none>        8080:30000/TCP   80s

當前沒有定義任何 Network Policy,驗證應用可以被訪問:

啓動一個 busybox Pod,可以訪問 Service,也可以 Ping 到副本 Pod。

[root@k8s-master ~]# kubectl run busybox --rm -it --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget httpd-svc:8080
Connecting to httpd-svc:8080 (10.108.126.45:8080)
saving to 'index.html'
index.html           100% |**********************************************************************************************************|    45  0:00:00 ETA
'index.html' saved
/ # ping 10.244.2.3
PING 10.244.2.3 (10.244.2.3): 56 data bytes
64 bytes from 10.244.2.3: seq=0 ttl=62 time=1.316 ms
64 bytes from 10.244.2.3: seq=1 ttl=62 time=0.862 ms
64 bytes from 10.244.2.3: seq=2 ttl=62 time=1.204 ms
^C
--- 10.244.2.3 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.862/1.127/1.316 ms
/ # exit

集羣節點可以訪問 Service, 也可以 Ping 到副本 Pod。

[root@k8s-node1 ~]# curl 10.108.126.45:8080
<html><body><h1>It works!</h1></body></html>
[root@k8s-node1 ~]# ping -c 3 10.244.2.3
PING 10.244.2.3 (10.244.2.3) 56(84) bytes of data.
64 bytes from 10.244.2.3: icmp_seq=1 ttl=63 time=0.732 ms
64 bytes from 10.244.2.3: icmp_seq=2 ttl=63 time=0.824 ms
^C
--- 10.244.2.3 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.732/0.778/0.824/0.046 ms

集羣外(192.168.56.1)可以訪問 Service。

現在創建如下 Network Policy:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: access-httpd
spec:
  podSelector:
    matchLabels:
      run: httpd
  ingress:
  - from:
    - podSelector:
        matchLabels:
          access: "true"
    ports:
    - protocol: TCP
      port: 80

① 定義將此 Network Policy 中的訪問規則應用於 label 爲 run: httpd 的 Pod,即 httpd 應用的三個副本 Pod。

② ingress 中定義只有 label 爲 access: "true" 的 Pod 才能訪問應用。

③ 只能訪問 80 端口。

通過 kubectl apply 創建 Network Policy。

[root@k8s-master ~]# kubectl apply -f policy.yml
networkpolicy.networking.k8s.io/access-httpd created
[root@k8s-master ~]# kubectl get networkpolicies.
NAME           POD-SELECTOR   AGE
access-httpd   run=httpd      32s

驗證 Network Policy 的有效性:

busybox Pod 已經不能訪問 Service。

[root@k8s-master ~]# kubectl run busybox --rm -it  --image=busybox /bin/sh                      
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget httpd-svc:8080 --timeout=5
Connecting to httpd-svc:8080 (10.108.126.45:8080)
wget: download timed out
/ # exit

如果 Pod 添加了 label access: "true" 就能訪問到應用,但 Ping 已經被禁止。

[root@k8s-master ~]# kubectl run busybox --rm -it --labels="access=true" --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # wget httpd-svc:8080
Connecting to httpd-svc:8080 (10.108.126.45:8080)
saving to 'index.html'
index.html           100% |**********************************************************************************************************|    45  0:00:00 ETA
'index.html' saved
/ # ping  10.244.2.3
PING 10.244.2.3 (10.244.2.3): 56 data bytes
^C
--- 10.244.2.3 ping statistics ---
68 packets transmitted, 0 packets received, 100% packet loss
/ # exit

集羣節點已經不能訪問 Service, 也 Ping 不到副本 Pod。

[root@k8s-node1 ~]# curl 10.108.126.45:8080
^C
[root@k8s-node1 ~]# ping -c 3 10.244.2.3   
PING 10.244.2.3 (10.244.2.3) 56(84) bytes of data.
^C
--- 10.244.2.3 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 1999ms

集羣外(192.168.56.1)已經不能訪問 Service。

[root@k8s-node1 ~]# curl 192.168.77.10:30000 --connect-timeout 5
curl: (28) Connection timed out after 5001 milliseconds

如果希望讓集羣節點和集羣外(192.168.56.1)也能夠訪問到應用,可以對 Network Policy 做如下修改:

應用新的 Network Policy:

現在,集羣節點和集羣外(192.168.56.1)已經能夠訪問了:

除了通過 ingress 限制進入的流量,也可以用 egress 限制外出的流量。大家可以參考官網相關文檔和示例,這裏就不贅述了。

小結
Kubernetes 採用的是扁平化的網絡模型,每個 Pod 都有自己的 IP,並且可以直接通信。

CNI 規範使得 Kubernetes 可以靈活選擇多種 Plugin 實現集羣網絡。

Network Policy 則賦予了 Kubernetes 強大的網絡訪問控制機制。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章