1. 程式人生 > >初試 Kubernetes 叢集中使用 Contour 反向代理_Kubernetes中文社群

初試 Kubernetes 叢集中使用 Contour 反向代理_Kubernetes中文社群

文章由作者:楊傳勝投稿;

在 Kubernetes 中執行大規模以 Web 為中心的工作負載,最關鍵的需求之一就是在 L7 層實現高效流暢的入口流量管理。自從第一批 Kubernetes Ingress Controller 開發完成以來,Envoy(由 Matt Klein 和 Lyft 團隊開發)已經成為雲原生生態系統中的新生力量。Envoy 之所以受到支援,因為它是一個 CNCF 託管的專案,與整個容器圈和雲原生架構有著天然的支援。

容器公司 Heptio 開源的專案 Contour 使用 Envoy 作為 Kubernetes 的 Ingress Controller 實現,為大家提供了一條新的 Kubernetes 外部負載均衡實現思路。

官方部落格介紹,Heptio Contour 可以為使用者提供以下好處:

  • 一種簡單的安裝機制來快速部署和整合 Envoy。
  • 與 Kubernetes 物件模型的整合。
  • Ingress 配置的動態更新,而無需重啟底層負載均衡器。
  • 專案成熟後,將允許使用 Envoy 一些強大的功能,如熔斷器、外掛式的處理器鏈,以及可觀測性和可除錯性。

下面我們就來試用一下。

1. 安裝步驟

首先克隆官方倉庫,進入 manifest 清單目錄:

$ git clone https://github.com/heptio/contour
$ cd contour/deployment/deployment-grpc-v2

修改 Deployment YAML 檔案:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: contour
  name: contour
  namespace: heptio-contour
spec:
  selector:
    matchLabels:
      app: contour
  replicas: 1
  template:
    metadata:
      labels:
        app: contour
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9001"
        prometheus.io/path: "/stats"
        prometheus.io/format: "prometheus"
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - "192.168.123.249"
      hostNetwork: true
      containers:
      - image: gcr.io/heptio-images/contour:master
        imagePullPolicy: Always
        name: contour
        command: ["contour"]
        args:
        - serve
        - --incluster
        - --envoy-http-port=80
        - --envoy-https-port=443
      - image: docker.io/envoyproxy/envoy-alpine:v1.7.0
        name: envoy
        ports:
        - containerPort: 80
          name: http
        - containerPort: 443
          name: https
        command: ["envoy"]
        args:
        - --config-path /config/contour.yaml
        - --service-cluster cluster0
        - --service-node node0
        - --log-level info
        - --v2-config-only
        volumeMounts:
        - name: contour-config
          mountPath: /config
      initContainers:
      - image: gcr.io/heptio-images/contour:master
        imagePullPolicy: Always
        name: envoy-initconfig
        command: ["contour"]
        args: ["bootstrap", "/config/contour.yaml"]
        volumeMounts:
        - name: contour-config
          mountPath: /config
      volumes:
      - name: contour-config
        emptyDir: {}
      dnsPolicy: ClusterFirst
      serviceAccountName: contour
      terminationGracePeriodSeconds: 30

總共修改以下幾處:

  1. 將例項數修改為 1。
  2. 將網路模式改為 hostNetwork。
  3. 刪除 Pod 反親和性配置,新增 Node 親和性配置,排程到指定的節點。
  4. 修改 contour 啟動引數,將 Listener 埠改為 80 和 443。
  5. 將 Envoy 容器的暴露埠改為 80 和 443。

部署:

$ kubectl apply -f ./

namespace "heptio-contour" created
serviceaccount "contour" created
customresourcedefinition.apiextensions.k8s.io "ingressroutes.contour.heptio.com" created
deployment.extensions "contour" created
clusterrolebinding.rbac.authorization.k8s.io "contour" created
clusterrole.rbac.authorization.k8s.io "contour" created
service "contour" created

2. Ingress 測試

安裝結束後,我們就可以來測試 Ingress 了。在 deployment 目錄下包含一個示例應用,可以直接使用:

$ kubectl apply -f ../example-workload/kuard.yaml

檢視建立好的資源:

$ kubectl get po,svc,ing -l app=kuard

NAME                       READY     STATUS    RESTARTS   AGE
kuard-bcc7bf7df-6h55x      1/1       Running   0          4m
kuard-bcc7bf7df-9sdnr      1/1       Running   0          4m
kuard-bcc7bf7df-ws57j      1/1       Running   0          4m

NAME        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
svc/kuard   10.254.85.181   <none>        80/TCP    4m

NAME        HOSTS     ADDRESS           PORTS     AGE
ing/kuard   *         192.168.123.249   80        4m

現在在瀏覽器中輸入 Contour 執行節點的 IP 地址或 DNS 域名來訪問示例應用程式了。

3. Contour 工作原理

Contour 同時支援 Ingress 資源物件和 IngressRoute 資源物件(通過 CRD 建立),這些物件都是為進入叢集的請求提供路由規則的集合。這兩個物件的結構和實現方式有所不同,但它們的核心意圖是相同的,都是為進入叢集的請求提供路由規則。如不作特殊說明,後面當我們描述 “Ingress” 時,它將同時適用於 Ingress 和 IngressRoute 物件。

通常情況下,當 Envoy 配置了 CDS 端點時,它會定期輪詢端點,然後將返回的 JSON 片段合併到其執行配置中。如果返回到 Envoy 的叢集配置代表當前的 Ingress 物件的集合,則可以將 Contour 視為從 Ingress 物件到 Envoy 叢集配置的轉換器。隨著 Ingress 物件的新增和刪除,Envoy 會動態新增並刪除相關配置,而無需不斷重新載入配置。

在實踐中,將 Ingress 物件轉換為 Envoy 配置更加微妙,需要將 Envoy 中的 xDS 配置(包括 CDS,SDS 和 RDS)對映到 Kubernetes 中。Contour 至少需要觀察 Ingress、Service 和 Endpoint 這幾個資源物件以構建這些服務的響應,它通過 client-go 的 cache/informer 機制免費獲得這些 watchers。這些機制提供新增,更新和刪除物件的邊緣觸發通知,以及通過 watch API 執行查詢物件的本地快取的列表機制。

Contour 將收集到的這些物件處理為虛擬主機及其路由規則的有向非迴圈圖(DAG),這表明 Contour 將有權構建路由規則的頂級檢視,並將群集中的相應服務和TLS祕鑰連線在一起。一旦構建了這個新的資料結構,我們就可以輕鬆實現 IngressRoute 物件的驗證,授權和分發。改資料結構匯出的 png 圖片如下圖所示:

Envoy API 呼叫和 Kubernetes API 資源之間的對映關係如下:
  • CDS : 叢集發現服務。對映為 Kubernetes 中的 Service 以及一部分 Ingress 物件的 TLS 配置。
  • SDS : 服務發現服務。對映為 Kubernetes 中的 Endpoint。Envoy 使用 SDS 自動獲取 Cluster 成員,這與 Endpoint 物件中包含的資訊非常匹配。Envoy 使用 Contour 在 CDS 響應中返回的名稱查詢 SDS。
  • RDS : 路由發現服務。對映為 Kubernetes 中的 Ingress。提供了虛擬主機名和字首路由資訊的 RDS 與 Ingress 匹配得更好。

4. 對映關係詳情

CDS

CDS 更像是 Kubernetes 中的 Service 資源,因為 Service 是具體 Endpoint(Pods)的抽象,Envoy Cluster 是指 Envoy 連線到的一組邏輯上相似的上游主機(參考下文的 RDS)。其中 TLS 配置也是 CDS 的一部分,而 Kubernetes 中的 TLS 資訊由 Ingress 提供,所以這部分之間的對映關係會有些複雜。

SDS

SDS 更像是 Kubernetes 中的 Endpoint 資源,這部分對映關係的實現最簡單。Contour 將 Endpoint 的響應物件轉換為 SDS 的 { hosts: [] } json 配置塊。

RDS

RDS 更像是 Kubernetes 中的 Ingress 資源。RDS 將字首,路徑或正則表示式之一路由到 Envoy 群集。Envoy 叢集的名稱可以從 Ingress 的 IngressSpec 的配置項中獲取(比如:namespace/serviceName_servicePort),因為這是一個選擇器,它會匹配 Service 物件被轉換後返回的 CDS 物件。

5. Contour 架構分析

Contour Ingress controller 由兩個元件組成:

  • Envoy : 提供高效能反向代理。
  • Contour : 充當 Envoy 的控制平面,為 Envoy 的路由配置提供統一的來源。

這些容器以 Sidecar 的形式部署在同一個 Pod 中,當然也包括了一些其他的配置。

在 Pod 初始化期間,Contour 作為 Init 容器執行,並將載入程式配置寫入一個 temporary volume。該 Volume 被傳遞給 Envoy 容器並告訴 Envoy 將其 Sidecar Contour 容器視為控制平面。

初始化完成後,Envoy 容器啟動,檢索 Contour 寫入的載入程式配置,並開始輪詢 Contour 以熱更新配置。如果控制平面無法訪問,Envoy 將會進行優雅重試。

Contour 相當於 Kubernetes API 的客戶端。它監視 Ingress,Service 和 Endpoint 物件,並通過將其物件快取轉換為相關的 JSON 欄位來充當其 Envoy 的控制平面。

從 Kubernetes 到 Contour 的資訊轉換是通過 SharedInformer 框架 watching API 來完成的;而從 Contour 到 Envoy 的資訊轉換是通過 Envoy 定期輪詢來實現的。

6. IngressRoute 介紹

Ingress 物件從 Kubernetes 1.1 版本開始被引進,用來描述進入叢集的請求的 HTTP 路由規則。但迄今為止 Ingress 物件還停留在 beta 階段,不同的 Ingress Controller 外掛為了新增 HTTP 路由的額外屬性,只能通過新增大量的 annotation 來實現,而且每個外掛的 annotation 都不一樣,非常混亂。

IngressRoute CRD 的目標就是擴充套件 Ingress API 的功能,以便提供更豐富的使用者體驗以及解決原始設計中的缺點。

目前 Contour 是唯一支援 IngressRoute CRD 的 Kubernetes Ingress Controller。下面就來看看它與 Ingress 相比的優點:

  • 安全地支援多團隊 Kubernetes 叢集,能夠限制哪些名稱空間可以配置虛擬主機和 TLS 憑據。
  • 允許將路徑或域名的路由配置分發給另一個名稱空間。
  • 接受單個路由中的多個服務,並對它們之間的流量進行負載均衡。
  • 無需通過新增 annotation 就可以定義服務權重和負載均衡策略。
  • 在建立時驗證 IngressRoute 物件,併為建立後報告驗證是否有效。

從 Ingress 到 IngressRoute

一個基本的 Ingress 物件如下所示:

# ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: basic
spec:
  rules:
  - host: foo-basic.bar.com
    http:
      paths:
      - backend:
          serviceName: s1
          servicePort: 80

這個 Ingress 物件名為 basic,它將傳入的 HTTP 流量路由到標頭檔案中 Host: 欄位值為 foo-basic.bar.com 且埠為 80 的 s1 服務。該路由規則通過 IngressRoute 來實現如下:

# ingressroute.yaml
apiVersion: contour.heptio.com/v1beta1
kind: IngressRoute
metadata:
  name: basic
spec:
  virtualhost:
    fqdn: foo-basic.bar.com
  routes:
    - match: /
      services:
        - name: s1
          port: 80

對應關係很簡單,我就不再詳細介紹了,更多功能配置可以參考官方倉庫的文件:IngressRoute

視覺化 Contour 的內部有向非迴圈圖

Contour 使用 DAG 對其配置進行建模,可以通過以 DOT 格式輸出 DAG 的除錯端點對其進行視覺化,當然需要先在系統中安裝 graphviz:

$ yum install -y graphviz

下載圖表並將其另存為 PNG:

# Port forward into the contour pod
$ CONTOUR_POD=$(kubectl -n heptio-contour get pod -l app=contour -o jsonpath='{.items[0].metadata.name}')
# Do the port forward to that pod
$ kubectl -n heptio-contour port-forward $CONTOUR_POD 6060
# Download and store the DAG in png format
$ curl localhost:6060/debug/dag | dot -T png > contour-dag.png

我自己儲存的 PNG 圖片如下所示:

7. 參考

歡迎關注作者微信公眾號