当使用Firefox和Safari时,关闭WebSocket可以正常工作,但Istio和Chrome的组合会导致WebSocket关闭时返回"wasclean": false
和返回码1006
。本文介绍如何通过EnvoyFilter解决Websocket关闭时返回码不一致的问题。
前提条件
已创建Kubernetes托管版集群。具体操作,请参见创建Kubernetes托管版集群。
已创建一个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关闭时返回码不一致的问题。