Cloud Service Mesh でのプロキシの問題の解決

このドキュメントでは、Cloud Service Mesh の一般的な問題とその解決方法について説明します。さらにサポートが必要な場合は、サポートの利用をご覧ください。

Istio でエンドポイントにアクセスしたときに接続が拒否される

クラスタからエンドポイント(Memorystore Redis、CloudSQL、またはアプリケーション ワークロードがアクセスする必要がある外部サービスなど)に通信するときに、接続拒否(ECONNREFUSED)エラーが断続的に発生する場合があります。

これは、アプリケーション ワークロードが istio-proxy(Envoy)コンテナよりも先に開始され、外部エンドポイントにアクセスしようとした場合に発生する可能性があります。この段階で istio-init(initContainer)はすでに実行されているため、すべての送信トラフィックを Envoy にリダイレクトする iptables ルールが適用されています。istio-proxy の準備ができていないため、iptables ルールによって、まだ起動されていないサイドカー プロキシにトラフィックがリダイレクトされることになり、アプリケーションは 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 の名前を取得し、resource.labelspod_name をメモします。

  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 がまだ readiness プローブに失敗していました。

    istio-proxy コンテナのほうが先に起動されていた場合でも、アプリが外部エンドポイントへの接続を試みるまでに準備が完了しなかった可能性があります。

    これが発生した問題である場合は、次のトラブルシューティング手順を実施してください。

  8. Pod レベルで config にアノテーションを付けます。これは Pod レベルでのみ使用できます。グローバル レベルでは使用できません。

    annotations:
    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  9. 外部サービスに対する他のリクエストを試行する前に、Envoy の準備ができているかどうかを確認するように、アプリケーション コードを変更します。たとえば、アプリケーションの起動時に istio-proxy ヘルス エンドポイントにリクエストを送信するループを開始します。このループは、200 が取得された場合のみ続行されます。istio-proxy health エンドポイントは次のとおりです。

    http://localhost:15020/healthz/ready
    

Vault と Istio 間のサイドカー インジェクション中に競合状態になる

vault を secret management に使用すると、vaultistio の前にサイドカーを挿入することがあります。これにより、Pod が Init ステータスで停止する可能性があります。この場合、Deployment の再起動後または新たな 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 は両方ともサイドカーを挿入する競合状態が原因で発生します。最後の処理は Istio が行う必要があり、init コンテナ中に istio プロキシを実行することはできません。istio 初期コンテナは、すべてのトラフィックをプロキシにリダイレクトする iptables ルールを設定します。初期コンテナがまだ実行されていないため、このルールによるリダイレクトは行わず、すべてのトラフィックがブロックされます。そのため初期コンテナを最後に配置し、iptables ルールが設定された直後にプロキシを起動および実行する必要があります。残念ながら、順序は決定的ではないため、Istio を最初に挿入すると機能しなくなります。

この状態をトラブルシューティングするには、Vault IP に送信されるトラフィックがまだ準備できていない Envoy プロキシにリダイレクトされないように、vault の IP アドレスを許可してください。これにより、通信がブロックされます。これを実現するには、excludeOutboundIPRanges という名前の新しいアノテーションを追加する必要があります。

マネージド Cloud Service Mesh の場合は、Deployment レベルまたは Pod レベルで、spec.template.metadata.annotations においてのみ実行できます。次に例を示します。

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: ""

アノテーションを追加したら、ワークロードを再起動します。