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",....}