暴露 Istio Service Mesh 中的 Gateway

米開朗基楊發表於2019-03-31

在之前的文章 Istio 服務網格中的閘道器 中,我已經介紹了簡單的暴露 Ingress Gateway 的方案。當時的方案只是用於臨時測試,不適合在大規模場景下使用,本文將探討更加優化的暴露 Ingress Gateway 的方案。

HostNetwork

第一種方法比較簡單,可以直接使用 HostNetwork 模式執行 Ingress Gateway。但你會發現無法啟動 ingressgateway 的 Pod,因為如果 Pod 設定了 HostNetwork=true,則 dnsPolicy 就會從 ClusterFirst 被強制轉換成 Default。而 Ingress Gateway 啟動過程中需要通過 DNS 域名連線 pilot 等其他元件,所以無法啟動。

我們可以通過強制將 dnsPolicy 的值設定為 ClusterFirstWithHostNet 來解決這個問題,詳情參考:Kubernetes DNS 高階指南

修改後的 ingressgateway deployment 配置檔案如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: istio-ingressgateway
  namespace: istio-system
  ...
spec:
  ...
  template:
    metadata:
    ...
    spec:
      affinity:
        nodeAffinity:
          ...
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - 192.168.123.248   # 比如你想排程到這臺主機上
      ...
      dnsPolicy: ClusterFirstWithHostNet
      hostNetwork: true
      restartPolicy: Always
      ...
複製程式碼

接下來我們就可以在瀏覽器中通過 Gateway 的 URL 來訪問服務網格中的服務了。

但是作為服務網格的流量接入層,Ingress Gateway 的高可靠性顯得尤為重要,高可靠性首先要解決的就是單點故障問題,一般常用的是採用多副本部署的方式。而上述方案只適用於單例項(Deployment 的副本數為 1)的情況,為了適應多節點部署架構,需要尋求更好的暴露方案。

使用 Envoy 作為前端代理

我們已經知道,Ingress Gateway 實際上內部執行的是 Envoy 代理,我們可以在 Ingress Gateway 前面再加一層代理,這樣就解決了高可用問題,你可以將 Ingress Gateway 的副本數擴充套件為多個,前端代理只需要通過 Service Name 來連線後端的 Gateway 就行了。同時建議採用獨佔節點的方式部署前端代理,以避免業務應用與前端代理服務發生資源爭搶。

前端代理可以使用一般的負載均衡軟體(如 HaproxyNginx 等),也可以使用 Envoy。由於 Envoy 是 Istio Service Mesh 中預設的 data plane,所以這裡推薦使用 Envoy。

Envoy 官方提供了一組 Envoy 的用例,我們只需要用到其中的 Dockerfile。首先克隆 Envoy 的程式碼倉庫並轉到 examples/front-proxy 目錄:

$ git clone https://github.com/envoyproxy/envoy
$ cd envoy/examples/front-proxy
複製程式碼

修改 front-envoy.yaml 配置檔案,修改後的內容如下:

static_resources:
  listeners:
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 80
    filter_chains:
    - filters:
      - name: envoy.tcp_proxy  
        config:
          stat_prefix: ingress_tcp
          cluster: ingressgateway
          access_log:
            - name: envoy.file_access_log
              config:
                path: /dev/stdout
  - address:
      socket_address:
        address: 0.0.0.0
        port_value: 443
    filter_chains:
    - filters:
      - name: envoy.tcp_proxy
        config:
          stat_prefix: ingress_tcp
          cluster: ingressgateway_tls
          access_log:
            - name: envoy.file_access_log
              config:
                path: /dev/stdout
  clusters:
  - name: ingressgateway
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    hosts:
    - socket_address:
        address: istio-ingressgateway.istio-system  
        port_value: 80
  - name: ingressgateway_tls
    connect_timeout: 0.25s
    type: strict_dns
    lb_policy: round_robin
    http2_protocol_options: {}
    hosts:
    - socket_address:
        address: istio-ingressgateway.istio-system
        port_value: 443
admin:
  access_log_path: "/dev/null"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001
複製程式碼
  • envoy.tcp_proxy 表示要例項化的過濾器的名稱。該名稱必須與內建支援的過濾器匹配,也就是說,該欄位的值不可隨意填寫,必須使用指定的幾個值。這裡 envoy.tcp_proxy 表示使用 TCP 代理。詳情參考:listener.Filter
  • istio-ingressgateway.istio-system 表示 Ingress Gateway 在叢集內部的 DNS 域名。

其他配置解析請參考:Envoy 的架構與基本術語

接下來通過 Dockerfile-frontenvoyfront-envoy.yaml 來構建 Docker 映象,我們來看下該 Dockerfile 的內容。

FROM envoyproxy/envoy:latest

RUN apt-get update && apt-get -q install -y \
    curl
CMD /usr/local/bin/envoy -c /etc/front-envoy.yaml --service-cluster front-proxy
複製程式碼

其中 /etc/front-envoy.yaml 是本地的 front-envoy.yaml 掛載進去的。在 Kubernetes 中可以通過 ConfigMap 來掛載,所以我們還要建立一個 ConfigMap:

$ kubectl -n istio-system create cm front-envoy --from-file=front-envoy.yaml
複製程式碼

你可以將構建好的映象 push 到私有映象倉庫中或者公共倉庫中,也可以使用我已經上傳好的映象。

最後我們就可以通過該映象來部署前端代理了,需要建立一個 Deployment,配置檔案 front-envoy-deploy.yaml 內容如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: front-envoy
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: front-envoy
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/hostname
                operator: In
                values:
                - 192.168.123.248 # 比如你想排程到這臺主機上
      containers:
      - name: front-envoy
        image: yangchuansheng/front-envoy
        ports:
        - containerPort: 80
        volumeMounts:
        - name: front-envoy
          mountPath: /etc/front-envoy.yaml
          subPath: front-envoy.yaml
      hostNetwork: true
      volumes:
        - name: front-envoy
          configMap:
            name: front-envoy
複製程式碼

你可以將映象換成你自己的映象,然後通過該 yaml 檔案來部署:

$ kubectl -n istio-system create -f front-envoy-deploy.yaml
複製程式碼

接下來我們就可以在瀏覽器中通過前端代理所在節點的 URL 來訪問服務網格中的服務了。

更一般的場景,我們還可以配置前端代理的高可用。對於 Kubernetes 叢集以外只暴露一個訪問入口,可以使用 keepalived 排除單節點問題。具體實現方式與 Ingress 的高可用類似,可以參考 Ingress 的高可用方案。

相關文章