從0開始使用Kubernetes系列(七):網絡

本文是從 0 到 1 使用 Kubernetes 系列第七篇,上一篇《從0到1使用Kubernetes系列(六):數據持久化實戰》 介紹了Kubernetes中的幾種常用儲存類型,本文將介紹K8S網絡相關的內容。

不同宿主機上運行的容器並不能通過 IP 相互訪問,那麼 Kubernetes 是如何實現不同節點上 Pod 的互通?Pod 有生命週期,它的 IP 會隨着動態的創建和銷燬而動態變化,Kubernetes 又是怎樣對外提供穩定的服務?今天就爲大家一一解答這些疑問。

Docker 網絡

先來看一下 Docker 中的網絡。在啓動 Docker 服務後,默認會創建一個 docker0 網橋(其上有一個 docker0 內部接口),它在內核層連通了其他的物理或虛擬網卡,這就將所有容器和本地主機都放到同一個物理網絡。

Docker 默認指定了 docker0 接口 的 IP 地址和子網掩碼,讓主機和容器之間可以通過網橋相互通信,它還給出了 MTU(接口允許接收的最大傳輸單元),通常是 1500 Bytes,或宿主主機網絡路由上支持的默認值,這些值都可以在服務啓動的時候進行配置。

root@ubuntu:/root# ifconfig
...
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
        ether 02:42:d2:00:10:6c  txqueuelen 0  (Ethernet)
...
root@ubuntu:/root# docker inspect busybox
···
"IPAddress": "172.17.0.2",
···

爲了實現上述功能,Docker 主要用到了 linux 的 BridgeNetwork NamespaceVETH

  • Bridge相當於是一個虛擬網橋,工作在第二層網絡。也可以爲它配置IP,工作在三層網絡。docker0網關就是通過Bridge實現的。
  • Network Namespace是網絡命名空間,通過Network Namespace可以建立一些完全隔離的網絡棧。比如通過docker network create xxx就是在建立一個Network Namespace
  • VETH是虛擬網卡的接口對,可以把兩端分別接在兩個不同的Network Namespace中,實現兩個原本隔離的Network Namespace的通信。

所以總結起來就是:Network Namespace做了容器和宿主機的網絡隔離,Bridge分別在容器和宿主機建立一個網關,然後再用VETH將容器和宿主機兩個網絡空間連接起來。但這都是在同一個主機上的網絡實現,如果想要在多臺主機上進行網絡就得看看下面介紹的 Kubernetes 網絡。

Kubernetes 網絡

Kubernetes 爲了解決容器的“跨主通信”問題,提出了很多解決方案。常見思路有兩種:

  • 直接在宿主機上建立不同宿主機上子網的路由規則;
  • 通過特殊的網絡設備封裝二層數據幀,根據目標 IP 地址匹配到對應的子網找到對應的宿主機 IP 地址,最後將轉發 IP 包,目的宿主機上同樣的特殊網絡設備完成解封並根據本機路由錶轉發。

Flannel

大家所熟知的 Flannel 項目是 CoreOS 公司推出的容器網絡解決方案。它本身只是一個框架,爲開發者提供容器網絡功能的是 Flannel 的後端實現。目前有如下三種具體實現:

  • UDP
  • VXLAN
  • host-gw

下面的三層網絡指的是七層網絡模型中的底部的三層:網絡層、數據鏈路層和物理層。

UDP 模式是最早支持,性能最差,但最容易理解和實現的容器跨主網絡方案。Flannel UDP 模式提供的是一個三層的覆蓋網絡:首先對發出端的IP包進行 UDP 封裝,然後在接受端進行解封拿到原始的IP包,進而把這個包轉發給目標容器。它相當於在兩個容器之間打通一條“隧道”,使得兩個容器可以直接使用 IP 通信,而不關心容器和宿主機的分佈情況。

因爲 Flannel 進行 UDP 封裝和解封都是在用戶態完成,而在 Linux 系統中上下文切換和用戶態操作的代價非常大,這就是它性能不好的主要原因。

VXLAN 即 Virtual Extensible LAN(虛擬可擴展局域網),是 Linux 內核本身就支持的一種網絡虛擬化技術。VXLAN 在內核態就完成了上面的封裝和解封工作,通過與 UDP 模式類似的“隧道”機制,構建出覆蓋網絡(Overlay Network),使得連接在這個 VXLAN 二層網絡的“主機”可以像在局域網自由通信。

host-gw 模式的工作原理是將每一個 Flannel 子網的下一跳設置爲該子網對應的宿主機 IP 地址。

也就是說,這臺“主機”(host)會充當這條容器通信路徑裏的“網關”(Getway)。Flannel host-gw 模式必須要求集羣宿主機之間是二層連通的

Calico

Calico 項目提供的網絡解決方案與 Flannel Host-gw 模式同理。但是不同於 Flannel 通過 Etcd 和宿主機的 flanneld 來維護路由信息得做法,Calio 項目使用BGP(邊界網關協議) 來自動的在整個集羣中分發路由消息。它由三部分組成:

Calico 的 CNI 插件:這是 Calico 與 Kubernetes 對接的部分。 Felix:它是一個 DaemonSet,負責在宿主機插入路由規則,以及維護Calico所需的網絡設備等。 BIRD:它是 BGP 的客戶端,負責在集羣裏分發路由規則信息。

除了對路由信息的維護方式之外,Calico 項目和 Flannel 的 host-gw 另一個不同是它不會在宿主機上創建任何網橋設備。

CNI(容器網絡接口)

CNI)是CNCF旗下的一個項目,由一組用於配置Linux容器的網絡接口的規範和庫組成,同時還包含了一些插件。CNI僅關心容器創建時的網絡分配,和當容器被刪除時釋放網絡資源。其基本思想爲: Kubernetes 在啓動 Infra 容器之後,就可以直接調用 CNI 網絡插件,爲這個 Infra 容器的 Network Namespace 配置符合預期的網絡棧。

Kubernetes 使用 CNI 接口,維護一個單獨的網橋來代替 docker0。這個網橋就叫做 CNI 網橋,它在宿主機上的默認名稱是:cni0。以 Flannel 的 VXLAN 模式爲例,在 Kubernetes 環境裏,它的工作方式沒有變化,只是 docker0 網橋替換成了 CNI 網橋。CNI 網橋只是接管所有 CNI 插件負責的,即 Kuberntes 創建的容器(Pod)。

Service

Kubernetes 中 Pod 有生命週期,它的 IP 會隨着動態的創建和銷燬而動態變化,不能穩定的提供服務。Kubernetes Service 定義這樣一種抽象:一個 Pod 的邏輯分組,一種可以訪問它們的策略。開發者可以通過一個 Service 的入口地址訪問其背後的一組 Pod。一旦 Service 被創建,Kubernetes 就會自動爲它分配一個可用的 Cluster IP,在 Service 的整個生命週期中它的 Cluster IP 都不會發生改變。這樣就解決了分佈式集羣的服務發現。

一個典型的 Service 定義如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  ports:
  - nmae: dafault
    protocol: TCP
    port: 8000
    targetPort: 80

在這個 Service 例子中,筆者使用 selector 字段聲明這個 Service 只代理 app=nginx 標籤的 pod。這個 Service 的 8000 端口代理 Pod 的 80 端口。

然後定義應用 Delpoyment 如下:

apiVersion: v1
kind: Delpoyment
metadata:
  name: nginx
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    meatdata:
    lalels:
      app: nginx
    spec:
    containers:
    - name: nginx
      image: nginx
      ports:
      - containers: 80
        protocol: TCP

被 selector 選中的 Pod,就被稱爲 Serivce 的 Endpoints,你可以使用 kubectl get ep 查看它們,如下所示:

$ kubectl get endpoints nginx
NAME    ENDPOINTS                                      AGE
nginx   172.20.1.16:80,172.20.2.22:80,172.20.2.23:80   1m

通過該 Service 的 VIP 10.68.57.93 地址,就可以訪問到它所代理的 Pod:

$ kubectl get svc nginx
NAME    TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.68.57.93   <none>        80/TCP    1m

$ curl 10.68.57.93
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
......
<h1>Welcome to nginx!</h1>
......
</html>

這個 VIP 地址是 Kubernetes 自動爲 Service 分配的。訪問 Service 的 VIP 地址和代理的 80 端口,它就爲我們返回了默認的 nginx 頁面,這種方式稱爲:Cluster IP 模式的 Service。

集羣外訪問 Service

Servcie 的訪問信息在 kubernetes 集羣外無效,因爲所謂的 Service 的訪問接口,實際上是每臺宿主機上由 kube-proxy 生成的 iptables 規則,以及 kube-dns 生成的 DNS 記錄。

解決外部訪問 Kubernetes 集羣裏創建的 servcie有以下幾種方法:

  • NodePort
  • LoadBalancer

NodePort 方法

下面是 NodePort 的例子:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - name: http
    nodePort: 30080
    port: 8080
    targetPort: 80
    protocol: TCP

在這個 Service 定義中,聲明它的類型爲 type=NodePort。此時在 ports 字段中聲明瞭 Service 的 8080 端口代理 Pod的80端口。

如果你不顯示聲明 nodePort 字段,Kubernetes 會爲你隨機分配可用端口來設置代理,這個端口的範圍默認爲:30000-32767。這裏設置爲 30080。

這裏就可以如此訪問這個 service:

<任何一臺宿主機 IP 地址>:30080

LoadBalancer

這種方法適用於公有云上的 Kubernetes 服務,通過指定一個 LoadBalancer 類型的 Service 實現。

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  ports:
  - port: 8765
    targetPort: 9379
  selector:
    app: example
  type: LoadBalancer

創建 Service 時,你可以選擇自動創建雲網絡負載均衡器。這提供了一個外部可訪問的IP地址,只要您的羣集在受支持的雲環境中運行,就可以將流量發送到羣集節點上的正確端口。

Ingress

爲代理不同後端 Service 而設置的路由規則集合就是 Kubernetes 裏的 Ingress。

舉一個例子,這裏有一個訂閱系統,它的域名是:https://wwww.example.com 。其中 http://www.example.com/book 是訂書系統,https://www.example.com/food 是訂餐系統。這兩個系統分別由 book 和 food 兩個 Deployment 來提供服務。

apiVersion: v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  tls:
  - hosts:
    - www.example.com
    secretName: example-secret
  rules:
  - host: www.example.com
    http:
      paths:
      - path: book
        backend:
          serviceName: book-svc
          servicePort: 80
      - path: /food
        backend:
          serviceName: food-svc
          servicePort: 80

這個 yaml 文件值得關注的 rules 字段,它叫作:IngressRules。

IngressRule 的 Key 就是 host,它必須是一個標準域名格式的字符串,不能是 IP 地址。

host 字段定義的值就是 Ingress 的入口,也就是說當用戶訪問 www.example.com 的時候,實際上訪問到的是這個 Ingress 對象。Kubernetes就能根據 IngressRule 進行下一步轉發,這裏定義兩個 path,它們分別對應 book 和 food 這個兩個 Deployment 的 Service。

由此不難看出,Ingress 對象其實就是 Kubernetes 項目對“反向代理”的一種抽象。

總結

今天這篇文主要介紹了 Kubernetes 集羣實現容器跨主機通信的幾種方式的原理,並且介紹瞭如何使用 Service 對外界提供服務。

更多Kubernetes相關文章,點擊藍字可閱讀:


本文由豬齒魚技術團隊原創,轉載請註明出處:豬齒魚官網

關於豬齒魚

豬齒魚Choerodon全場景效能平臺,提供體系化方法論和協作、測試、DevOps及容器工具,幫助企業拉通需求、設計、開發、部署、測試和運營流程,一站式提高管理效率和質量。從團隊協同到DevOps工具鏈、從平臺工具到體系化方法論,豬齒魚全面滿足協同管理與工程效率需求,貫穿端到端全流程,助力團隊效能更快更強更穩定。戳此處試用豬齒魚

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