WebSocket是基于RFC 6455标准,用于客户端和服务器端之间进行双向通信的协议。Istio Sidecar Proxy提供了开箱即用方式使用WebSocket协议,便于您使用WebSocket协议实现服务访问。本文介绍如何在ASM中使用WebSocket over HTTP/1.1和HTTP/2协议访问服务。
背景信息
WebSocket与HTTP协议不同,WebSocket协议使用HTTP Upgrade标头来建立各方之间的连接。Istio无法识别WebSocket协议,但Istio Sidecar Proxy提供了开箱即用方式支持WebSocket协议。关于Istio支持WebSocket协议详细介绍,请参见HTTP upgrades、HTTP connection manager和Protocol Selection。
您可以在ASM中使用WebSocket over HTTP/1.1和HTTP/2协议访问服务,区别如下:
WebSocket over HTTP/1.1协议:一次请求和响应,建立一个连接,用完关闭连接。
WebSocket over HTTP/2协议:多个请求可同时在一个连接上并行执行,某个请求任务耗时严重,不会影响到其它连接的正常执行。
前提条件
已创建一个ASM实例,并已将ACK集群添加到ASM实例中。具体操作,请参见创建ASM实例和添加集群到ASM实例。
已为命名空间注入Sidecar ,具体操作,请参见配置Sidecar注入策略。本文以default命名空间为例。
已通过kubectl工具连接集群。具体操作,请参见通过kubectl工具连接集群。
部署WebSocket客户端和服务端
部署WebSocket服务端。
说明本文使用WebSocket社区提供的Python库中WebSocket服务端为例。如果您想修改WebSocket服务端的部署配置,请参见容器化应用程序。
使用以下内容,创建websockets-server.yaml。
apiVersion: v1 kind: Service metadata: name: websockets-server labels: app: websockets-server spec: type: ClusterIP ports: - port: 8080 targetPort: 80 name: http-websocket selector: app: websockets-server --- apiVersion: apps/v1 kind: Deployment metadata: name: websockets-server labels: app: websockets-server spec: selector: matchLabels: app: websockets-server template: metadata: labels: app: websockets-server spec: containers: - name: websockets-test image: registry.cn-beijing.aliyuncs.com/aliacs-app-catalog/istio-websockets-test:1.0 ports: - containerPort: 80
执行以下命令,在default命名空间下部署WebSocket服务端。
kubectl apply -f websockets-server.yaml -n default
部署WebSocket客户端。
使用以下内容,创建websockets-client.yaml。
apiVersion: v1 kind: Service metadata: name: websockets-client labels: app: websockets-client spec: type: ClusterIP ports: - port: 8080 targetPort: 80 name: http-websockets-client selector: app: websockets-client --- apiVersion: apps/v1 kind: Deployment metadata: name: websockets-client-sleep labels: app: websockets-client spec: selector: matchLabels: app: websockets-client template: metadata: labels: app: websockets-client spec: containers: - name: websockets-client image: registry.cn-beijing.aliyuncs.com/aliacs-app-catalog/istio-websockets-client-test:1.0 command: ["sleep", "14d"]
在default命名空间下部署WebSocket客户端。
kubectl apply -f websockets-client.yaml -n default
使用WebSocket over HTTP/1.1协议
使用WebSocket客户端向服务端发送了多个请求,Sidecar Proxy日志只会包含一个WebSocket连接的访问日志条目。 Envoy将WebSocket连接视为TCP字节流,并且代理只能理解HTTP升级请求或响应,因此Envoy只会在连接关闭后创建该访问日志条目,并且也不会看到每个TCP请求的任何消息。
任选以下方式,打开Shell命令行窗口。
方式一:
登录容器服务管理控制台,在左侧导航栏单击集群。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择
。在容器组页面单击websockets-client右侧操作列下终端,单击websockets-client。
方式二:
执行以下命令,打开Shell命令行窗口。
kubectl exec -it -n <namespace> websockets-client-sleep... -c websockets-client -- sh
执行以下命令,访问WebSocket服务端。
python3 -m websockets ws://websockets-server.default.svc.cluster.local:8080
预期输出:
Connected to ws://websockets-server.default.svc.cluster.local:8080.
输入hello和word,可以看到返回hello和word。
> hello < hello > world < world Connection closed: 1000 (OK).
查看Sidecar Proxy日志。
查看WebSocket客户端的Sidecar Proxy日志。
在容器组页面单击WebSocket客户端容器名称。
单击日志页签,设置容器为istio-proxy。
可以看到日志中包含使用WebSocket over HTTP/1.1协议记录。
{...."upstream_host":"10.208.0.105:80","bytes_sent":23,"protocol":"HTTP/1.1",....}
查看WebSocket服务端的Sidecar Proxy日志。
在容器组页面单击WebSocket服务端容器名称。
单击日志页签,设置容器为istio-proxy。
可以看到日志中包含使用WebSocket over HTTP/1.1协议记录。
{...."downstream_local_address":"10.208.0.105:80","upstream_local_address":"127.0.**.**:53983","protocol":"HTTP/1.1",....}
使用WebSocket over HTTP/2协议
低于1.12版本的Istio使用WebSocket over HTTP/2协议,将不能正常连接到WebSocket服务器端, 并返回503错误。
如果您已经为低于1.12版本的Istio设置HTTP/2升级,并发生报错,您可以通过在目标规则中定义h2UpgradePolicy为DO_NOT_UPGRADE的方式,使低于1.12版本的Istio可以正常使用WebSocket over HTTP/1.1协议。
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
labels:
provider: asm
name: websockets-server
spec:
host: websockets-server
trafficPolicy:
connectionPool:
http:
h2UpgradePolicy: DO_NOT_UPGRADE
Istio 1.12或以上版本使用WebSocket over HTTP/2协议的操作如下:
创建目标规则。
登录ASM控制台。
在左侧导航栏,选择 。
在网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理。
在网格详情页面左侧导航栏,选择 ,然后在右侧页面,单击使用YAML创建。
设置命名空间为default,复制以下内容到文本框中,然后单击创建。
apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: labels: provider: asm name: websockets-server spec: host: websockets-server trafficPolicy: connectionPool: http: h2UpgradePolicy: UPGRADE
h2UpgradePolicy:设置为UPGRADE,表示为websockets-server启用HTTP/2协议。
创建EnvoyFilter。
默认情况下,WebSocket不支持HTTP/2协议,但Envoy却支持WebSocket在HTTP/2流上进行隧道传输,以便在整个部署过程中可以使用统一的HTTP/2网络。您可以通过EnvoyFilter针对目标工作负载设置
allow_connect: true
,从而使WebSocket服务端允许HTTP/2协议连接。登录ASM控制台。
在左侧导航栏,选择 。
在网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理。
在网格详情页面左侧导航栏选择 。
在插件市场页面,单击设置allow_connect为true允许升级的协议连接。
在插件详情页面,单击新建插件实例页签,在插件生效范围区域,选中工作负载生效,然后单击添加工作负载到生效范围。
在添加工作负载到生效范围对话框中,设置命名空间为default,工作负载类型为Deployment,在选择负载区域选中websockets-server,单击图标,将websockets-server添加到已选择区域中,然后单击确定。
在插件配置区域的YAML框中输入
patch_context: SIDECAR_INBOUND
,然后打开生效开关,等待插件启用。在插件启用后,ASM会自动创建Envoy过滤器,Envoy过滤器的YAML示例如下:
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: h2-upgrade-wss labels: asm-system: 'true' provider: asm spec: workloadSelector: labels: app: websockets-server configPatches: - applyTo: NETWORK_FILTER match: context: SIDECAR_INBOUND proxy: proxyVersion: '^1\.*.*' listener: filterChain: filter: name: "envoy.filters.network.http_connection_manager" patch: operation: MERGE value: typed_config: '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager http2_protocol_options: allow_connect: true
执行以下命令,访问WebSocket服务端。
python3 -m websockets ws://websockets-server.<namespace>.svc.cluster.local:8080
预期输出:
Connected to ws://websockets-server.default.svc.cluster.local:8080.
输入hello和word,可以看到返回hello和word。
> hello < hello > world < world Connection closed: 1000 (OK).
查看Sidecar Proxy日志。
查看WebSocket客户端的Sidecar Proxy日志。
在容器组页面单击WebSocket客户端容器名称。
单击日志页签,设置容器为istio-proxy。
可以看到日志中包含使用WebSocket over HTTP/1.1协议记录,说明客户端发起请求使用的是HTTP/1.1协议。
{...."authority":"websockets-server.default.svc.cluster.local:8080","upstream_service_time":null,"protocol":"HTTP/1.1",....}
查看WebSocket服务端的Sidecar Proxy日志。
在容器组页面单击WebSocket服务端容器名称。
单击日志页签,设置容器为istio-proxy。
可以看到日志中包含使用WebSocket over HTTP/2协议记录,说明WebSocket服务端的接收到的协议已经升级为HTTP/2。
{...."method":"GET","upstream_local_address":"127.0.**.**:34477","protocol":"HTTP/2",....}