本篇已加入《.NET Core on K8S學習實踐系列文章索引》,可以點擊查看更多容器化技術相關系列文章。
一、Kubernetes網絡模型
我們都知道Kubernetes作為容器編排引擎,它有一個強大又復雜的網絡模型,也牽引出了Pod網絡、Service網絡、ClusterIP、NodePort、Ingress等多個概念。這里我們采用楊波老師(架構師楊波)模仿TCP/IP協議棧總結的一個K8S網絡模型圖來看看K8S的四個抽象層次,從而了解一下K8S的網絡。本小節的文字主要引用自楊波老師關於K8S網絡模型的文章及CloudMan的《每天5分鍾玩轉Kubernetes》一書。

K8S網絡層次模型圖 (From 波波老師)
根據上圖模型中展示的四個層次,從0到3,除了第0層,每一層都是構建於前一層之上。
(1)第0層:節點主機互通互聯
主要保證K8S節點(物理或虛擬機)之間能夠正常IP尋址和互通的網絡,這個一般由底層(公有雲或數據中心)網絡基礎設施支持,這里我們無需過多關心。
(2)第1層:Pod虛擬機互聯
在一個Pod中可以運行一個或多個容器,且Pod中所有容器使用同一個網絡namespace,即相同的IP和端口空間,可以直接用localhost通信,而且還可以共享存儲(本質是通過將Volume掛載到Pod中的每個容器)。

Pod網絡模型圖 (From 波波老師)
(3)第2層:服務發現和負載均衡
在K8S集群中,Pod的IP並不是固定的,可能會頻繁地銷毀和創建實例,為了解決此問題,Service提供了訪問Pod的抽象層。即無論后端Pod如何變化,Service都作為穩定的前端對外提供服務。此外,Service還提供了高可用和負載均衡的功能,它負責將請求轉發給正確的Pod。

Service網絡模型圖 (From 波波老師)
(4)第3層:外部流量接入
K8s的Service網絡只是一個集群內部網絡,集群外部是無法直接訪問的。為此,想要將應用暴露出去讓公網能夠訪問,K8S提供了兩種方式:
① NodePort:使Service通過Cluster節點的靜態端口對外提供服務,外部可以通過 NodeIP:NodePort 來訪問Service。

Node Port方式示意圖 (From 波波老師)
② LoadBalancer:使Service利用Cloud Provider提供的Load Balancer對外提供服務,Cloud Provider負責將Load Balancer的流量導向Service。目前支持的Cloud Provider包括AWS、Azure、阿里雲、騰訊雲等。

Load Balancer方式示意圖 (From 波波老師)
More:關於K8S網絡的更多基本原理與講解,強力推薦閱讀波波老師的以下文章:
- Kubernetes網絡三部曲-Pod網絡(From 楊波老師)
- Kubernetes網絡三部曲-Service網絡(From 楊波老師)
- Kubernetes網絡三部曲-外部接入網絡(From 楊波老師)
二、傳說中的CNI規范
為了保證網絡方案的標准化、擴展性和靈活性,K8S采用了CNI(Container Networking Interface)規范。CNI是一個Pod網絡集成標准,簡化了K8S和不同Pod網絡實現技術的集成。CNI最大的優點就是支持多種容器runtime,而不僅僅是Docker。目前已經有多種支持K8S的網絡方案,包括 Flannel、Calico、Canal等,它們都實現了CNI規范,因此無論我們選擇哪種具體方案,它們的網絡模型都是一致的。

CNI模型圖
More:關於CNI的更多基本原理與講解,推薦閱讀陳Sir的文章《K8S網絡詳解:CNI與CNI網絡模型》
三、Network Policy
3.1 關於Network Policy
Network Policy是K8S的一種資源,它使K8S可以通過Label選擇Pod,並指定其他Pod或外界如何與這些Pod通信。換句話說,當Pod被定義了Network Policy時,只有Policy允許的流量才能訪問Pod(默認情況下,任何來源的流量都可以訪問Pod,是沒有限制的)即幫助K8S實現更為精細的流量控制,實現租戶隔離機制。
But,並不是所有K8S網絡方案都支持Network Policy,比如Flannel就不支持,而Calico是支持的。
3.2 Network Policy實踐
3.2.1 部署Canal
想要部署Canal,需要切換網絡方案,這里我們使用最簡單粗暴的方式:重建當前K8S集群
kubeadm reset # 在每個節點上執行一次
然后,重新對Master節點進行初始化:
kubeadm init \ --apiserver-advertise-address=192.168.2.100 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.13.3 \ --service-cidr=10.1.0.0/16 \ --pod-network-cidr=10.244.0.0/16
在兩個Node節點上執行以下命令重新加入集群:(注意這里的token請填寫你的Master節點初始化后的輸出結果)
kubeadm join 192.168.2.100:6443 --token ekqxk2.iiu5wx5bbnbdtxsw --discovery-token-ca-cert-hash \ sha256:c50bb83d04f64f4a714b745f04682b27768c1298f331e697419451f3550f2d05
最后,通過以下命令部署Canal:(參考自K8S官方文檔)
kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/canal.yaml
此時,再次令驗證的集群結果如下:
(1)集群節點狀態

(2)Pod狀態

3.2.2 部署測試應用
這里通過一個httpd應用來演示Network Policy,該應用的yaml定義如下:
apiVersion: apps/v1 kind: Deployment metadata: name: httpd spec: replicas: 3 selector: matchLabels: name: networkpolicy-demo template: metadata: labels: name: networkpolicy-demo spec: containers: - name: httpd image: httpd:latest ports: - containerPort: 80 imagePullPolicy: IfNotPresent --- kind: Service apiVersion: v1 metadata: name: httpd-svc spec: type: NodePort ports: - protocol: TCP nodePort: 31000 port: 8080 targetPort: 80 selector: name: networkpolicy-demo
通過kubectl將其部署到K8S集群:
kubectl apply -f httpd-demo.yaml
這時候三個httpd Pod已經成功Running:

由於定義的是NodePort方式暴露服務,這里我們在集群外部訪問Service看看:

由於當前並沒有創建任何Network Policy,這里我們可以通過創建一個Pod應用(我們熟悉的busybox)來驗證一下是否可以在K8S集群內部隨意訪問該httpd應用:
kubectl run busybox --rm -it --image=busybox /bin/sh


從上圖可以知道,它可以正常訪問到Service,也可以正常ping到Pod節點。
3.2.3 測試Network Policy有效性
現在我們創建一個Network Policy,其配置文件yaml如下:
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access-httpd spec: podSelector: matchLabels: name: networkpolicy-demo ingress: - from: - podSelector: matchLabels: access: "true" ports: - protocol: TCP port: 80
該Network Policy定義了如下規則:
(1)應用於所有 label 為 name : networkpolicy-demo 的Pod,這里即剛剛創建的三個httpd pod。
(2)ingress中定義了只有 label 為 access : "true" 的Pod才能訪問應用。
(3)即使通過Policy也只能訪問80端口
通過kubectl將其應用到K8S集群中:
kubectl apply -f networkpolicy.yaml
下面再次在busybox pod中驗證Network Policy的有效性:


從上圖中可以看到,已經無法再成功訪問Service,也無法再ping通三個Pod節點。
這個時候,集群外也無法再通過NodePort訪問到Service:

如果想要讓測試Pod(busybox)能訪問到應用了Network Policy的httpd應用,我們可以對busybox pod加一個label就可以:
kubectl run busybox --rm -it --image=busybox --labels="access=true" /bin/sh
運行后的驗證結果如下,可以訪問到Service,但Ping卻被禁止:

但是,此時集群節點(k8s-master與兩個node)與集群仍然無法訪問到應用了Network Policy的httpd應用,如果想要讓它們也訪問到,則需要修改Network Policy做一個類似於開防火牆白名單的操作(注意下面的ipBlock配置):
apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: access-httpd spec: podSelector: matchLabels: name: networkpolicy-demo ingress: - from: - podSelector: matchLabels: access: "true" - ipBlock: cidr: 192.168.2.0/24 ports: - protocol: TCP port: 80
再次應用到K8S集群后,再來通過集群外部的訪問者瀏覽器試試:

可以看到,已經可以正常訪問啦!
四、小結
本文簡單介紹了Kubernetes的4層網絡模型、CNI 容器網絡接口規范 以及 Network Policy,並通過改造K8S集群的網絡配置從Flannel到Canal來驗證Network Policy的有效性。對於Kubernetes的網絡模型的原理與介紹,強烈推薦閱讀楊波老師的《Kubernetes網絡三部曲》,它的傳送門位於下方的參考資料列表中。最后,希望能夠對初學者的你有所幫助!
參考資料
(1)CloudMan,《每天5分鍾玩轉Kubernetes》
(2)李振良,《一天入門Kubernets教程》
(3)馬哥(馬永亮),《Kubernetes快速入門》
(4)Liang,《K8S CNI網絡最強對比》
(5)楊波,《K8S網絡三部曲》
(6)陳Sir,《K8S網絡詳解:CNI與CNI網絡模型》
