Alibaba Cloud Service Mesh (ASM) supports WebSocket (RFC 6455) through Istio sidecar proxies without additional configuration. This guide walks you through deploying a sample WebSocket client and server, then establishing WebSocket connections over HTTP/1.1 and HTTP/2.
How WebSocket works in ASM
A WebSocket connection starts as a standard HTTP request with an Upgrade header. After the client and server negotiate the upgrade, the connection switches to full-duplex, two-way communication over a single persistent connection.
Istio does not recognize the WebSocket protocol directly. Instead, Envoy sidecar proxies treat WebSocket traffic as TCP byte streams after the initial HTTP Upgrade handshake. This means:
Envoy generates a single access log entry per WebSocket connection, written only after the connection closes.
The log entry does not include details of individual messages sent over the connection.
For more information, see HTTP upgrades, HTTP connection manager, and Protocol Selection.
HTTP/1.1 vs. HTTP/2
| Protocol | Behavior |
|---|---|
| HTTP/1.1 | Each connection handles one request. The connection closes after the response. |
| HTTP/2 | Multiple requests run in parallel over a single connection. A slow request does not block others. |
Prerequisites
Before you begin, make sure that you have:
An ASM instance with an ACK cluster added to it. For more information, see Create an ASM instance and Add a cluster to an ASM instance
Sidecar injection enabled for the target namespace. The examples in this guide use the
defaultnamespace. For more information, see Configure sidecar proxy injection policies
Deploy a WebSocket server and client
Step 1: Connect to the ACK cluster
Connect to the ACK cluster by using kubectl. For more information, see Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster.
Step 2: Deploy the WebSocket server
This example uses the Python WebSocket server provided by the WebSocket community. For configuration details, see Deploy to Kubernetes.
Create a file named
websockets-server.yamlwith the following content: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: 80Apply the manifest to the
defaultnamespace:kubectl apply -f websockets-server.yaml -n default
Step 3: Deploy the WebSocket client
The client image is built from a Dockerfile that installs the websockets Python package:
FROM python:3.9-alpine
RUN pip3 install websocketsCreate a file named
websockets-client.yamlwith the following content: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"]Apply the manifest to the
defaultnamespace:kubectl apply -f websockets-client.yaml -n default
Establish WebSocket connections over HTTP/1.1
Because Envoy treats WebSocket connections as TCP byte streams, sidecar proxies record only one access log entry per connection. The entry is written after the connection closes and does not contain per-message details.
Connect to the WebSocket server
Open a shell in the client pod. Use either of the following methods: Option A: ACK console Option B: kubectl
Log on to the ACK console. In the left-side navigation pane, click Clusters.
On the Clusters page, click the name of your cluster. In the left-side pane, choose .
On the Pods page, find
websockets-clientand click Terminal in the Actions column. Then select websockets-client.
kubectl exec -it -n <namespace> <websockets-client-sleep-pod> -c websockets-client -- shConnect to the WebSocket server. Replace
<namespace>with your target namespace (for example,default): Expected output:python3 -m websockets ws://websockets-server.<namespace>.svc.cluster.local:8080Connected to ws://websockets-server.default.svc.cluster.local:8080.Send test messages. Type
helloandworld, then close the connection:> hello < hello > world < world Connection closed: 1000 (OK).
Verify the sidecar proxy logs
Client sidecar log: On the Pods page, click the client pod name. Click the Logs tab and select istio-proxy from the Container drop-down list. The log shows
HTTP/1.1as the protocol:{...."upstream_host":"10.208.0.105:80","bytes_sent":23,"protocol":"HTTP/1.1",....}Server sidecar log: On the Pods page, click the server pod name. Click the Logs tab and select istio-proxy from the Container drop-down list. The log also shows
HTTP/1.1:{...."downstream_local_address":"10.208.0.105:80","upstream_local_address":"127.0.**.**:53983","protocol":"HTTP/1.1",....}
Establish WebSocket connections over HTTP/2
WebSocket does not work natively with HTTP/2. However, Envoy supports tunneling WebSocket over HTTP/2 connections, so all communication within the ASM instance can use HTTP/2.
For Istio versions earlier than 1.12, upgrading HTTP/1.1 connections to HTTP/2 for WebSocket traffic returns HTTP status code 503. In this case, set h2UpgradePolicy to DO_NOT_UPGRADE in the DestinationRule to keep connections on 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_UPGRADEFor Istio 1.12 or later, complete the following steps to enable WebSocket over HTTP/2.
Step 1: Create a DestinationRule
Log on to the ASM console.
In the left-side navigation pane, choose .
On the Mesh Management page, find the target ASM instance. Click the instance name or click Manage in the Actions column.
In the left-side navigation pane, choose . On the page that appears, click Create from YAML.
On the Create page, select default from the Namespace drop-down list. Paste the following YAML into the code editor and click Create: Setting
h2UpgradePolicytoUPGRADEenables HTTP/2 for the WebSocket server.apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: labels: provider: asm name: websockets-server spec: host: websockets-server trafficPolicy: connectionPool: http: h2UpgradePolicy: UPGRADE
Step 2: Create an EnvoyFilter
Enable the HTTP/2 CONNECT method on the server-side sidecar by setting allow_connect to true. This allows Envoy to tunnel WebSocket connections over HTTP/2.
Log on to the ASM console.
In the left-side navigation pane, choose .
On the Mesh Management page, find the target ASM instance. Click the instance name or click Manage in the Actions column.
In the left-side navigation pane, choose .
On the Market Place page, click Template that sets the allow_connect parameter to true to allow updated protocol connections.
On the Plugin Detail page, click the Plugin Config tab. In the Plugin Effective scope section, select Workload Scope and click Add workloads to effective scope.
In the Add workloads to effective scope dialog box, set Namespace to default and Workload Type to Deployment. In the Select workloads section, select websockets-server, click the
icon to move it to the selected section, and click OK.In the YAML code editor of the Plugin Config section, enter
patch_context: SIDECAR_INBOUND. Turn on Plugin Switch and wait for the plug-in to activate.
After the plug-in is enabled, ASM automatically creates the following EnvoyFilter:
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: trueStep 3: Test the connection
Connect to the WebSocket server from the client pod. Replace
<namespace>with your target namespace (for example,default): Expected output:python3 -m websockets ws://websockets-server.<namespace>.svc.cluster.local:8080Connected to ws://websockets-server.default.svc.cluster.local:8080.Send test messages:
> hello < hello > world < world Connection closed: 1000 (OK).
Verify the sidecar proxy logs
Client sidecar log: The client still sends requests over HTTP/1.1:
{...."authority":"websockets-server.default.svc.cluster.local:8080","upstream_service_time":null,"protocol":"HTTP/1.1",....}Server sidecar log: The server receives requests upgraded to HTTP/2: This confirms that the sidecar proxy tunneled the WebSocket connection over HTTP/2.
{...."method":"GET","upstream_local_address":"127.0.**.**:34477","protocol":"HTTP/2",....}