I am trying to get websockets to work on GKE. Seems very trivial, but I am failing to get this to work, I just continuously keep getting 400 at Nginx Ingress.
The manifest is like this:
apiVersion: v1
kind: Namespace
metadata:
name: my-test
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-ws-backend
namespace: my-test
labels:
app: my-ws-backend
spec:
replicas: 1
selector:
matchLabels:
app: my-ws-backend
template:
metadata:
labels:
app: my-ws-backend
spec:
containers:
- name: backend
image: ksdn117/web-socket-test
imagePullPolicy: Always
ports:
- containerPort: 8010
env:
- name: NODE_ENV
value: production
- name: DEBUG
value: socket*
---
apiVersion: v1
kind: Service
metadata:
name: my-ws-backend
namespace: my-test
spec:
selector:
app: my-ws-backend
ports:
- protocol: TCP
port: 80
targetPort: 8010
sessionAffinity: ClientIP
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ws-ingress
namespace: my-test
annotations:
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/upgrade-insecure-requests: "true"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
nginx.ingress.kubernetes.io/server-snippet: |
error_log /var/log/nginx/error.log debug;
cert-manager.io/cluster-issuer: letsencrypt-prod-nginx
spec:
ingressClassName: nginx
rules:
- host: ws-my-test.myhost.com
http:
paths:
- path: /socket.io
pathType: Prefix
backend:
service:
name: my-ws-backend
port:
number: 80
tls:
- hosts:
- ws-my-test.myhost.com
secretName: ws-my-test-cert
I tried hitting the endpoint with wscat and a simplistic Node.js script shown below to test. What am I missing?
const { io } = require('socket.io-client');
const socket = io('wss://ws-my-test.myhost.com', {
transports: ['websocket'],
reconnection: false,
});
socket.on('connect', () => {
console.log('Connected!');
socket.disconnect();
});
socket.on('connect_error', (err) => {
console.error('Connection error:', err);
});
Got this working in the end, these are the annotations in my Ingress
cert-manager.io/cluster-issuer: letsencrypt-prod-nginx
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/proxy-buffering: "off"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
I think the problem was including duplicates as my annotations below:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
which were not required, and caused the header to have duplicate values set in the header, that caused rejection of the request with status 400.
Ingress-NGINX controller already comes preconfigured with the required Upgrade/Connection headers set, so not needed to set them again.