當使用Firefox和Safari時,關閉WebSocket可以正常工作,但Istio和Chrome的組合會導致WebSocket關閉時返回"wasclean": false和返回碼1006。本文介紹如何通過EnvoyFilter解決Websocket關閉時返回碼不一致的問題。
前提條件
已建立Kubernetes託管版叢集。具體操作,請參見建立ACK託管叢集。
已建立一個ASM執行個體,並已將ACK叢集添加到ASM執行個體中。具體操作,請參見建立ASM執行個體和添加叢集到ASM執行個體。
已部署至少1個入口網關服務。具體操作,請參見建立入口網關。
已部署應用到ASM執行個體關聯的叢集。具體操作,請參見在ASM執行個體關聯的叢集中部署應用。
通過kubectl串連ACK叢集和ASM執行個體。具體操作,請參見通過kubectl工具串連叢集和通過控制面kubectl訪問Istio資源。
步驟一:部署樣本應用
您可以使用本樣本鏡像或者自行構建Docker鏡像部署應用,具體操作如下:
方式一:使用阿里雲鏡像部署應用
使用以下內容,建立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執行以下命令,在default命名空間下部署WebSocket服務端樣本應用。
命名空間default需啟用Sidecar自動注入,具體操作,請參見啟用自動注入。
kubectl apply -f sample.yaml
方式二:自行構建Docker鏡像部署應用
此處以Node應用為例,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" } }執行以下代碼,部署WebSocket服務端樣本應用。
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,但是有Sidecar的情況下,用戶端收到的是1006。 // conn.close() // 自訂的返回碼4321。 // 但是有Sidecar的情況下, 用戶端收到的仍然是1006。 // 當沒有Sidecar 時, 用戶端收到4321。 conn.close(4321, "test") }); server.listen({ host: '0.0.0.0', port: 9900 });使用以下內容,構建鏡像的Dockerfile。
FROM node:16.7.0-alpine3.14 WORKDIR /root/app COPY . . RUN yarn install
步驟二:配置網格規則
登入ASM控制台。
在左側導覽列,選擇。
在網格管理頁面,找到待配置的執行個體,單擊執行個體的名稱或在操作列中單擊管理。
建立網關規則。
在網格詳情頁面左側導覽列,選擇,然後在右側頁面,單擊使用YAML建立。
設定命名空間為default,選擇任意情境模板,將以下內容粘貼到YAML文字框,然後單擊建立。
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
建立目標規則。
在網格詳情頁面左側導覽列,選擇,然後在右側頁面,單擊使用YAML建立。
設定命名空間為default,選擇任意情境模板,將以下內容粘貼到YAML文字框,然後單擊建立。
apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: websocket-test namespace: default spec: host: websocket-test subsets: - name: current labels: version: current
建立虛擬服務。
在網格詳情頁面左側導覽列,選擇,然後在右側頁面,單擊使用YAML建立。
設定命名空間為default,選擇任意情境模板,將以下內容粘貼到YAML文字框,然後單擊建立。
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
步驟三:訪問WebSocket
本文以直接存取WebSocket和使用EnvoyFilter訪問WebSocket兩種方式進行對比,通過返回碼(本文以自訂返回碼4321為例)驗證WebSocket的訪問效果。
方式一:直接存取WebSocket
使用以下內容,建立client.html檔案。
請將YAML中入口網關地址修改為實際的入口網關IP。
<!DOCTYPE html> <html> <head> <title>WebSocket example</title> </head> <body> <script> var ws = new WebSocket('ws://{替換為實際的入口網關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>使用Chrome瀏覽器開啟client.html檔案,在鍵盤上按F12鍵,開啟Developer Tools。
重新整理頁面,在Console頁簽下,查看返回碼。
如下圖所示,返回碼始終為1006,而不是自訂的返回碼4321。

方式二:通過EnvoyFilter訪問WebSocket
使用以下內容,建立EnvoyFilter。
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將EnvoyFilter綁定至指定的工作負載或命名空間。具體操作,請參見步驟二:將Envoy過濾器模板綁定至工作負載或命名空間。
使用Chrome瀏覽器開啟client.html檔案,在鍵盤上按F12鍵,開啟Developer Tools。
重新整理頁面,在Console頁簽下,查看返回碼。
如下圖所示,返回碼變為4321,符合預期。因此,您可以通過EnvoyFilter解決WebSocket關閉時返回碼不一致的問題。
