本文已發布超過一年。較舊的文章可能包含過時的內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

使用 KPNG 撰寫專門的 kube-proxier

這篇文章將向您展示如何使用 Kubernetes Proxy NG kpng 建立一個專業的服務 kube-proxy 風格網路代理程式,而不會干擾現有的 kube-proxy。kpng 專案旨在更新預設的 Kubernetes 服務實作「kube-proxy」。kpng 的一項重要功能是,它可以作為程式庫使用,在 K8s 之外建立代理程式。雖然這對於取代 kube-proxy 的 CNI 外掛程式很有用,但它也為任何人建立用於特殊用途的代理程式開啟了可能性。

定義使用專業代理程式的服務

apiVersion: v1
kind: Service
metadata:
  name: kpng-example
  labels:
    service.kubernetes.io/service-proxy-name: kpng-example
spec:
  clusterIP: None
  ipFamilyPolicy: RequireDualStack
  externalIPs:
  - 10.0.0.55
  - 1000::55
  selector:
    app: kpng-alpine
  ports:
  - port: 6000

如果定義了 service.kubernetes.io/service-proxy-name 標籤,則 kube-proxy 將忽略該服務。自訂控制器可以監看標籤設定為其自身名稱(在本範例中為「kpng-example」)的服務,並設定專業的負載平衡。

service.kubernetes.io/service-proxy-name 標籤並非新的,但到目前為止,編寫專業代理程式一直相當困難。

專業代理程式的常見用途被認為是處理 K8s 不支援的某些用例的外部流量。在這種情況下,不需要 ClusterIP,因此我們在本範例中使用「無頭」服務。

使用 kpng 的專業代理程式

基於 kpng 的代理程式由處理所有 K8s API 相關功能的 kpng 控制器,以及實作負載平衡的「後端」組成。後端可以與 kpng 控制器二進位檔連結,也可以是使用 gRPC 與控制器通訊的獨立程式。

kpng kube --service-proxy-name=kpng-example to-api

這會啟動 kpng 控制器,並告知它僅監看服務代理程式名稱為「kpng-example」的服務。「to-api」參數將為後端開啟 gRPC 伺服器。

您可以在叢集外部自行測試。請參閱以下範例。

現在我們啟動一個後端,該後端只會列印來自控制器的更新。

$ kubectl apply -f kpng-example.yaml
$ kpng-json | jq     # (this is the backend)
{
  "Service": {
    "Namespace": "default",
    "Name": "kpng-example",
    "Type": "ClusterIP",
    "IPs": {
      "ClusterIPs": {},
      "ExternalIPs": {
        "V4": [
          "10.0.0.55"
        ],
        "V6": [
          "1000::55"
        ]
      },
      "Headless": true
    },
    "Ports": [
      {
        "Protocol": 1,
        "Port": 6000,
        "TargetPort": 6000
      }
    ]
  },
  "Endpoints": [
    {
      "IPs": {
        "V6": [
          "1100::202"
        ]
      },
      "Local": true
    },
    {
      "IPs": {
        "V4": [
          "11.0.2.2"
        ]
      },
      "Local": true
    },
    {
      "IPs": {
        "V4": [
          "11.0.1.2"
        ]
      }
    },
    {
      "IPs": {
        "V6": [
          "1100::102"
        ]
      }
    }
  ]
}

真實的後端將使用某種機制來將來自外部 IP 的流量負載平衡到端點。

編寫後端

kpng-json 後端看起來像這樣

package main
import (
        "os"
        "encoding/json"
        "sigs.k8s.io/kpng/client"
)
func main() {
        client.Run(jsonPrint)
}
func jsonPrint(items []*client.ServiceEndpoints) {
        enc := json.NewEncoder(os.Stdout)
        for _, item := range items {
                _ = enc.Encode(item)
        }
}

(是的,這就是整個程式)

真實的後端當然會更複雜得多,但這說明了 kpng 如何讓您專注於負載平衡。

您可以將多個後端連接到一個 kpng 控制器,因此在開發或偵錯期間,讓類似 kpng-json 後端的東西與您的真實後端並行執行可能會很有用。

範例

完整的範例可以在這裡找到。

作為範例,我們實作一個「all-ip」後端。它將外部 IP 的所有流量導向到本機端點,而不管連接埠和上層協定為何。此功能有一個 KEP,而此範例是一個簡化得多的版本。

若要將來自外部位址的所有流量導向到本機 POD,僅需要一個 iptables 規則,例如;

ip6tables -t nat -A PREROUTING -d 1000::55/128 -j DNAT --to-destination 1100::202

如您所見,位址在對後端的呼叫中,而它要做的就是

  • 使用 Local: true 提取位址
  • ExternalIPs 設定 iptables 規則

這樣做的腳本可能看起來像

xip=$(cat /tmp/out | jq -r .Service.IPs.ExternalIPs.V6[0])
podip=$(cat /tmp/out | jq -r '.Endpoints[]|select(.Local == true)|select(.IPs.V6 != null)|.IPs.V6[0]')
ip6tables -t nat -A PREROUTING -d $xip/128 -j DNAT --to-destination $podip

假設上面的 JSON 輸出儲存在 /tmp/out 中(jq 是一個很棒的程式!)。

由於這是一個範例,因此我們透過使用上面 kpng-json 後端的次要變體,使其對我們來說非常簡單。程式不只是列印,而是呼叫程式,並且 JSON 輸出作為 stdin 傳遞到該程式。後端可以獨立測試

CALLOUT=jq kpng-callout

其中 jq 可以替換為您自己的程式或腳本。腳本可能看起來像上面的範例。如需更多資訊和完整範例,請參閱 https://github.com/kubernetes-sigs/kpng/tree/master/examples/pipe-exec

摘要

雖然 kpng 仍處於開發的早期階段,但這篇文章旨在展示您未來可以如何建置自己的專業 K8s 代理程式。您的應用程式唯一需要做的是在服務清單中新增 service.kubernetes.io/service-proxy-name 標籤。

將新功能加入 kube-proxy 是一個繁瑣的過程,而且它們很可能被拒絕,因此編寫專業代理程式可能是唯一的選擇。