解決 Cloud Service Mesh 中的工作負載啟動問題

本文說明常見的 Cloud Service Mesh 問題,以及如何解決這些問題。如需其他協助,請參閱「取得支援」。

公開具備權限的通訊埠時,閘道無法使用 Distroless Proxy 啟動

根據預設,無發行版代理程式會以非根權限啟動,在某些情況下,這可能會導致特殊權限通訊埠發生繫結失敗。如果在 Proxy 啟動期間看到類似下列的錯誤,則必須為閘道部署項目套用額外的 securityContext。

  Error adding/updating listener(s) 0.0.0.0_80: cannot bind '0.0.0.0:80': Permission denied

以下範例是輸出閘道部署作業的 YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: istio-egressgateway
spec:
  selector:
    matchLabels:
      app: istio-egressgateway
      istio: egressgateway
  template:
    metadata:
      annotations:
        # This is required to tell Anthos Service Mesh to inject the gateway with the
        # required configuration.
        inject.istio.io/templates: gateway
      labels:
        app: istio-egressgateway
        istio: egressgateway
    spec:
      containers:
      - name: istio-proxy
        image: auto # The image will automatically update each time the pod starts.
        resources:
          limits:
            cpu: 2000m
            memory: 1024Mi
          requests:
            cpu: 100m
            memory: 128Mi
      # Allow binding to all ports (such as 80 and 443)
      securityContext:
        sysctls:
        - name: net.ipv4.ip_unprivileged_port_start
          value: "0"
      serviceAccountName: istio-egressgateway 

連線至 Cloud Service Mesh 端點時遭到拒絕

叢集與端點 (例如 Memorystore Redis、Cloud SQL,或應用程式工作負載需要連線的任何外部服務) 通訊時,可能會間歇性發生連線遭拒 (ECONNREFUSED) 錯誤。

如果應用程式工作負載的啟動速度比 istio-proxy (Envoy) 容器快,並嘗試連線至外部端點,就可能發生這種情況。因為 istio-init (initContainer) 已在這個階段執行,所以 iptables 規則會將所有外送流量重新導向至 Envoy。由於 istio-proxy 尚未準備就緒,iptables 規則會將流量重新導向至尚未啟動的 Sidecar Proxy,因此應用程式會收到 ECONNREFUSED 錯誤。

請按照下列步驟操作,確認是否遇到這個錯誤:

  1. 使用下列篩選器檢查 stackdriver 記錄,找出有問題的 Pod。

    以下範例顯示典型的錯誤訊息:

    Error: failed to create connection to feature-store redis, err=dial tcp   192.168.9.16:19209: connect: connection refused
    [ioredis] Unhandled error event: Error: connect ECONNREFUSED
    
  2. 搜尋問題的發生次數。如果您使用的是舊版 Stackdriver,請改用 resource.type="container"。

    resource.type="k8s_container"
    textPayload:"$ERROR_MESSAGE$"
    
  3. 展開最新事件,取得 Pod 名稱,然後記下 pod_name 下方的 resource.labels。

  4. 取得該 Pod 的第一個問題:

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    輸出內容範例:

    E 2020-03-31T10:41:15.552128897Z
    post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
    connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
    connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb
    
  5. 請記下這個 Pod 的第一個錯誤時間戳記。

  6. 使用下列篩選器查看 Pod 啟動事件。

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    輸出內容範例:

    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Container image "docker.io/istio/proxyv2:1.3.3" already present on machine  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Created container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Started container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{APP-CONTAINER-NAME} Created container  spec.containers{APP-CONTAINER-NAME}
    W 2020-03-31T10:41:17Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:26Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:28Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:31Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:58Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    
  7. 使用錯誤和 istio-proxy 啟動事件的時間戳記,確認錯誤發生時 Envoy 尚未準備就緒。

    如果 istio-proxy 容器尚未準備就緒時發生錯誤,取得連線遭拒錯誤是正常現象。在前面的範例中,pod 嘗試在 2020-03-31T10:41:15.552128897Z 時連接到 Redis,但到 2020-03-31T10:41:58Z 時 istio-proxy 仍然無法進行就緒偵測。

    即使 istio-proxy 容器最先啟動,但它可能在應用程式嘗試連接到外部端點之前沒有足夠快地準備就緒。

    如果您遇到的問題是這樣的,請繼續按照以下故障排除步驟進行操作。

  8. 在 Pod 層級為設定加上註解。這項功能僅適用於 Pod 層級,不適用於全域層級。

    annotations:
    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  9. 修改應用程式程式碼,確保應用程式先檢查 Envoy 是否已準備就緒,再嘗試向外部服務發出任何其他要求。例如,在應用程式啟動時,啟動一個循環,向 istio-proxy 健康端點發出請求,並且只有在獲得 200 後才繼續。istio-proxy 健康狀態端點如下:

    http://localhost:15020/healthz/ready
    

Vault 和 Cloud Service Mesh 之間邊車注入期間的競爭情況

使用 vault 管理密鑰時,vault 有時會在 istio 前注入 Sidecar,導致 Pod 停滯在 Init 狀態。發生這種情況時,重新啟動或部署新的 Deployment 後,建立的 Pod 會停留在 Init 狀態。例如:

E 2020-03-31T10:41:15.552128897Z
post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb

這個問題是由競爭狀況所致,Istio 和 vault 都會插入 Sidecar,但 Istio 必須是最後一個執行這項作業的服務,且 istio Proxy 不會在 Init 容器期間執行。istio 初始化容器設定 iptables 規則,將所有流量重新導向至代理程式。由於尚未執行,這些規則不會重新導向任何內容,因此會封鎖所有流量。因此,初始化容器必須是最後一個,這樣在設定 iptables 規則後,Proxy 就能立即啟動並運作。很抱歉,由於順序不確定,如果 Istio 先注入,就會發生中斷。

如要排解這個情況,請允許 vault 的 IP 位址,這樣前往 Vault IP 的流量就不會重新導向至尚未準備就緒的 Envoy Proxy,進而封鎖通訊。為此,需要新增一個名為 excludeOutboundIPRanges 的新註解。

如果是代管 Cloud Service Mesh,只能在 spec.template.metadata.annotations 下的 Deployment 或 Pod 層級執行這項操作,例如:

apiVersion: apps/v1
kind: Deployment
...
...
...
spec:
  template:
    metadata:
      annotations:
        traffic.sidecar.istio.io/excludeOutboundIPRanges:

對於叢集內 Cloud Service Mesh,可以選擇將其設定為全域服務網格,並在 spec.values.global.proxy.excludeIPRanges 下使用 IstioOperator,例如:

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      proxy:
        excludeIPRanges: ""

新增註解後,請重新啟動工作負載。

找出叢集中使用的 Proxy 圖片類型

如要判斷特定 Pod 使用的 Proxy 圖片類型 (default 或 distroless),可以檢查 Pod 規格。

執行下列指令,檢查 istio-proxy 容器使用的映像檔:

kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.spec.containers[?(@.name=="istio-proxy")].image}'
  • 如果圖片路徑在代碼或後置字串中不含 -distroless,則會使用 default 圖片。
  • 如果圖片路徑在標記或後置字元中包含 -distroless,則會使用 distroless 圖片。

驗證設定意圖

您可以透過兩種方式設定圖片類型 (註解優先於 MeshConfig):

  1. Pod 註解:檢查 Pod 是否有 sidecar.istio.io/proxyImageType 註解。

    kubectl get pod POD_NAME -n NAMESPACE -o jsonpath='{.metadata.annotations["sidecar.istio.io/proxyImageType"]}'
    
  2. MeshConfig:檢查 istio-system 命名空間中 istio-RELEASE_CHANNEL ConfigMap 的 defaultConfig.image.imageType 設定。

注意:如果是使用代管 TRAFFIC_DIRECTOR 控制層的叢集:

  • 如果是直接使用受管理 TRAFFIC_DIRECTOR 控制層佈建的叢集,則僅支援 distroless 映像檔。系統會忽略其他圖片類型的覆寫。
  • 對於遷移至 TRAFFIC_DIRECTOR 控制層的叢集 (例如從 CSM-ISTIOD 遷移的叢集),映像檔類型預設為 default 映像檔,但您可以使用 MeshConfig 或 Pod 註解選擇加入 distroless。系統不支援覆寫其他圖片類型 (例如 debug)。