Deploy to Kubernetes
Three objects: a Deployment running your websocketd image, a Service in front of it, and an Ingress that keeps long-lived upgrades open. Build the image first, following run in a container .
apiVersion: apps/v1
kind: Deployment
metadata:
name: websocketd
spec:
replicas: 2
selector:
matchLabels:
app: websocketd
template:
metadata:
labels:
app: websocketd
spec:
containers:
- name: websocketd
image: myregistry/myapp-ws:0.5.0
args:
- "--port=8080"
- "--address=0.0.0.0"
- "--origin=https://ws.example.com"
- "/app/myscript.sh"
ports:
- name: ws
containerPort: 8080
readinessProbe:
tcpSocket:
port: ws
initialDelaySeconds: 2
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: websocketd
spec:
selector:
app: websocketd
ports:
- port: 80
targetPort: ws
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: websocketd
annotations:
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
ingressClassName: nginx
rules:
- host: ws.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: websocketd
port:
number: 80
Apply it, and wss://ws.example.com/ reaches your script.
The Ingress annotations
The ingress-nginx controller proxies WebSocket upgrades without any
opt-in annotation. What it does not do is guess how long you want the
connection held open: its proxy_read_timeout and proxy_send_timeout
default to 60 seconds, and they are idle timers, so an open but quiet
WebSocket connection is cut at the minute mark. The two annotations above
raise them.
You can instead keep the connection from ever going idle, by adding
--pingms=30000 to the container args. websocketd then sends a ping frame
every 30 seconds, which resets the controller's read timer. Doing both is
reasonable.
The annotation keys above are specific to ingress-nginx. Traefik, HAProxy
Ingress and the cloud controllers all proxy upgrades too, but each spells its
timeouts differently. Check the controller you run.
What a readiness probe can and cannot tell you
Use a tcpSocket probe, as above. It answers exactly one question: is
websocketd listening? That is the right question, because websocketd
validates its whole configuration before it binds. A pod that accepts a TCP
connection has a websocketd whose flags were accepted.
An httpGet probe does not work. websocketd answers a plain, non-upgrade
GET on an endpoint path with 404 Not Found, and Kubernetes counts any
status outside 200 to 399 as a failure, so the pod never becomes ready.
Neither probe tells you your script works. websocketd does not run your
program until a client completes a WebSocket upgrade, so nothing has executed
it at probe time. A broken interpreter, a missing data file or a script that
exits immediately all pass readiness and fail on first connection. If you
need that covered, add a --staticdir health file and probe it, or run a
client-side check outside the cluster.
Replicas and shared state
Scaling out is safe as far as websocketd is concerned. It keeps nothing between connections, and each connection gets its own process wherever it lands. See the process model .
It is your wrapped program that decides whether replicas are safe. If two connections must see each other's state, they now have to do so across pods. See sharing state across connections .
Two connections from the same browser can land on different pods. Nothing in the configuration above pins them together, and websocketd offers no session affinity of its own. If you need it, set it on the Service or on the Ingress controller.
Keeping the ports in step
Three numbers have to agree: --port=8080 in the container args, the
containerPort, and the Service's targetPort. Naming the port ws and
referring to it by name, as above, removes two of the three chances to get
that wrong.
Next
- Run in a container for the image these objects run.
- The exposure checklist for what to settle before the Ingress is public.
- The security model
for what
--origindoes and does not cover.