すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Service Mesh:Istio サイドカープロキシ注入による予期しない WebSocket リターンコードの修正

最終更新日:Mar 11, 2026

Istio サイドカープロキシが WebSocket トラフィックをインターセプトすると、Google Chrome は期待されるデフォルトのリターンコード(1005)やカスタムリターンコードではなく、リターンコード 1006 を受信する場合があります。Firefox および Safari には影響ありません。この問題を解決するには、EnvoyFilter で delayed_close_timeout: 0s を設定します。

原因

Envoy サイドカープロキシの HTTP 接続マネージャーは、接続を正常に終了(グレースフルシャットダウン)するために delayed_close_timeout を使用します。WebSocket のクローズハンドシェイク中にこのタイムアウトが発生すると、Chrome がクローズフレームを受信する前に Envoy が接続をリセットしてしまうため、"wasclean": false およびリターンコード 1006 が返されます。他のブラウザでは接続タイミングの処理方法が異なり、正しいリターンコードが受信されます。

前提条件

開始する前に、以下の条件を満たしていることを確認してください。

ステップ 1:サンプル WebSocket アプリケーションのデプロイ

問題を再現するために WebSocket サーバーをデプロイします。以下のいずれかの方法を選択してください。

オプション A:事前ビルド済みの Alibaba Cloud イメージを使用

  1. 以下の内容で sample.yaml というファイルを作成します。

        apiVersion: apps/v1
        kind: Deployment
        metadata:
          name: websocket-test
          namespace: default
          labels:
            app: websocket-test
            version: current
        spec:
          replicas: 1
          selector:
            matchLabels:
              app: websocket-test
              version: current
          template:
            metadata:
              labels:
                app: websocket-test
                version: current
            spec:
              containers:
              - name: websocket-test
                image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/asm-websocketsample:v1
                imagePullPolicy: Always
                command: ["node", "ws.js"]
    
        ---
        apiVersion: v1
        kind: Service
        metadata:
          labels:
            app: websocket-test
          name: websocket-test
          namespace: default
        spec:
          ports:
          - name: http
            port: 80
            protocol: TCP
            targetPort: 9900
          selector:
            app: websocket-test
          type: ClusterIP
  2. マニフェストを default 名前空間に適用します。

    default 名前空間に対して自動サイドカープロキシ注入が有効化されている必要があります。詳細については、「自動サイドカープロキシ注入の有効化」をご参照ください。
        kubectl apply -f sample.yaml

オプション B:カスタム Docker イメージのビルド

  1. Node.js アプリケーション用の package.json ファイルを作成します。

        {
          "name": "wssample",
          "version": "0.0.1",
          "main": "ws.js",
          "license": "UNLICENSED",
          "scripts": {
            "start": "node --trace-warnings ./ws.js"
          },
          "dependencies": {
            "ws": "^8.0.0"
          }
        }
  2. WebSocket サーバーのロジックを含む ws.js ファイルを作成します。

        const WebSocket = require("ws");
        const http = require("http");
        const wss = new WebSocket.Server({ noServer: true });
    
        const server = http.createServer()
    
        server.on("upgrade", async (request, socket, head) => {
          const handleAuth = (ws) => {
            wss.emit("connection", ws, request);
          };
          wss.handleUpgrade(request, socket, head, handleAuth);
        })
    
        wss.on("connection", (conn, req) => {
          // デフォルトのクローズではリターンコード 1005 が送信されます。
          // サイドカープロキシ注入が有効な場合、Chrome では代わりに 1006 が報告されます。
          // conn.close()
    
          // カスタムリターンコード 4321。
          // サイドカープロキシ注入が有効な場合、Chrome では依然として 1006 が報告されます。
          // サイドカープロキシ注入が無効な場合、Chrome では正しく 4321 が報告されます。
          conn.close(4321, "test")
        });
    
        server.listen({ host: '0.0.0.0', port: 9900 });
  3. Dockerfile を作成します。

        FROM node:16.7.0-alpine3.14
        WORKDIR /root/app
        COPY . .
        RUN yarn install
  4. イメージをビルドし、オプション A と同様の Deployment および Service を使用してデプロイします。image フィールドをカスタムイメージのレジストリパスに置き換えます。

ステップ 2:ASM インスタンスの設定

イングレスゲートウェイを経由して WebSocket アプリケーションにトラフィックをルーティングするため、Gateway、DestinationRule、および VirtualService を作成します。

  1. ASM コンソール にログインします。

  2. 左側ナビゲーションウィンドウで、Service MeshMesh Management を選択します。

  3. Mesh Management ページで、対象の ASM インスタンスの名前をクリックするか、Actions 列の Manage をクリックします。

Gateway の作成

  1. 左側ナビゲーションウィンドウで、ASM GatewaysGateway を選択し、Create from YAML をクリックします。

  2. Namespacedefault に設定し、シナリオテンプレートを選択して、以下の YAML を貼り付け、Create をクリックします。

        apiVersion: networking.istio.io/v1beta1
        kind: Gateway
        metadata:
          name: websocket-test
          namespace: default
        spec:
          selector:
            istio: ingressgateway
          servers:
            - hosts:
                - '*'
              port:
                name: http
                number: 80
                protocol: HTTP

DestinationRule の作成

  1. 左側ナビゲーションウィンドウで、Traffic Management CenterDestinationRule を選択し、Create from YAML をクリックします。

  2. Namespacedefault に設定し、シナリオテンプレートを選択して、以下の YAML を貼り付け、Create をクリックします。

        apiVersion: networking.istio.io/v1alpha3
        kind: DestinationRule
        metadata:
          name: websocket-test
          namespace: default
        spec:
          host: websocket-test
          subsets:
          - name: current
            labels:
              version: current

VirtualService の作成

  1. 左側ナビゲーションウィンドウで、Traffic Management CenterVirtualService を選択し、Create from YAML をクリックします。

  2. Namespacedefault に設定し、シナリオテンプレートを選択して、以下の YAML を貼り付け、Create をクリックします。

        apiVersion: networking.istio.io/v1alpha3
        kind: VirtualService
        metadata:
          name: websocket-test
          namespace: default
        spec:
          gateways:
          - websocket-test
          hosts:
          - '*'
          http:
          - name: default
            route:
            - destination:
                host: websocket-test
                subset: current

ステップ 3:問題の再現

シンプルな HTML クライアントを使用して、Chrome が期待されるリターンコードではなく 1006 を返すことを確認します。

  1. 以下の内容で client.html ファイルを作成します。<ingress-gateway-ip> をご利用のイングレスゲートウェイの IP アドレスに置き換えてください。

        <!DOCTYPE html>
        <html>
        <head>
            <title>WebSocket の例</title>
        </head>
        <body>
            <script>
                var ws = new WebSocket('ws://<ingress-gateway-ip>');
    
                ws.onopen = function (ev) {
                    console.log(ev)
                };
                ws.onmessage = function (ev) {
                    console.log("on msg", ev)
                };
                ws.onclose = function (ev) {
                    console.log("on close", ev)
                };
                ws.onerror = function (ev) {
                    console.log("on error", ev)
                };
            </script>
        </body>
        </html>
  2. Google Chrome で client.html を開き、F12 を押して開発者ツールを開きます。

  3. ページを更新し、Console タブを確認します。期待されるカスタムリターンコード 4321 ではなく、リターンコード 1006 が表示されます。

    Return code 1006 without EnvoyFilter

ステップ 4:EnvoyFilter による修正の適用

Envoy HTTP 接続マネージャーの delayed_close_timeout0s に設定することで、サイドカープロキシが WebSocket クローズフレームに干渉するのを防ぎます。

  1. 以下の内容で EnvoyFilter を作成します。この EnvoyFilter は、すべての Envoy サイドカー(プロキシバージョン ^1.*.*)に対し、HTTP 接続マネージャーのネットワークフィルターに delayed_close_timeout: 0s 設定をマージするパッチを適用します。

        apiVersion: networking.istio.io/v1alpha3
        kind: EnvoyFilter
        metadata:
          labels:
            asm-system: 'true'
            provider: asm
          name: hack-to-fix-delayedclosetimeout-istio-upper-version
          namespace: istio-system
        spec:
          configPatches:
            - applyTo: NETWORK_FILTER
              match:
                listener:
                  filterChain:
                    filter:
                      name: envoy.filters.network.http_connection_manager
                proxy:
                  proxyVersion: ^1.*.*
              patch:
                operation: MERGE
                value:
                  typed_config:
                    '@type': >-
                      type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                    delayed_close_timeout: 0s
  2. EnvoyFilter を対象のワークロードまたは名前空間にバインドします。詳細については、「ワークロードまたは名前空間への Envoy フィルターテンプレートのバインド」をご参照ください。

ステップ 5:修正の検証

  1. Google Chrome で client.html を開き、F12 キーを押してよく使うツールを開きます。

  2. ページを更新し、Console タブを確認します。リターンコードが WebSocket サーバーで設定したカスタムコード 4321 と一致するようになりました。

    Return code 4321 with EnvoyFilter applied