Search code examples
node.jsreactjskubernetessocket.iokubernetes-ingress

Kubernetes Nginx Ingress and Socket.io Connection Issues


I'm currently having issues with my react app chatting with a nodejs socket.io app.

However, I have narrowed it down and believe it is an ingress misconfiguration. Port-forwarding the socket.io nodejs pod and connecting with react via 127.0.0.1:3020 works fine.

Socket.io Deployment File

apiVersion: apps/v1
kind: Deployment
metadata:
  name: websockettest-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      component: websockettest
  template:
    metadata:
      labels:
        component: websockettest
    spec:
      containers:
        - name: websockettest
          image: websockettest
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3020

Socket IO Service Config

apiVersion: v1
kind: Service
metadata:
  name: websockettest-cluster-ip-service
spec:
  type: ClusterIP
  selector:
    component: websockettest
  ports:
    - port: 3020
      targetPort: 3020

Ingress Configuration

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-service
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.org/websocket-services: websockettest-cluster-ip-service
spec:
  rules:
    - http:
        paths:
          - path: /websockettest/?(.*)
            backend:
              serviceName: websockettest-cluster-ip-service
              servicePort: 3020

Nodejs Socket.io

const http = require('http');
const express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io')(server);
io.set("transports", ["websocket"]);

io.on('connection', function (socket) {
  console.log('connected socket!');

  socket.on('greet', function (data) {
    console.log(data);
    socket.emit('respond', { hello: 'Hello' });
  });
  socket.on('disconnect', function () {
    console.log('Socket disconnected');
  });
});

const port = process.env.PORT || 3020;
server.listen(port, () => {
  console.log(`Server is up on port ${port}`);
});

React Socket.io-client

// Various Attempts:
// websockettest
// websockettest-cluster-ip-service
// http://192.168.64.11:3020
// :3020/websockettest
// 127.0.0.1:3020 - port forwarding works

const socket = io('192.168.64.11/websockettest', {
  'reconnection': false, 
  transports: ['websocket']
});

Solution

  • Socket.io has a specific path it uses for websocket communication. Due to this, the kubernetes ingress needs to be configured to support "/socket.io". Additionally, the node.js middleware also needs to have custom namespace for the socket endpoint.

    ingress-ws-service.yaml

    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress-ws-service
      annotations:
        nginx.ingress.kubernetes.io/use-regex: "true"
        nginx.org/websocket-services: "websockettest-cluster-ip-service"
    
    spec:
      rules:
        - http:
            paths:
              - path: /websockettest/.*
                backend:
                  serviceName: websockettest-cluster-ip-service
                  servicePort: 3020
              - path: /socket.io/.*
                backend:
                  serviceName: websockettest-cluster-ip-service
                  servicePort: 3020
    

    Nodejs Socket.io

    const http = require('http');
    const express = require('express');
    var app = express();
    var server = http.createServer(app);
    var io = require('socket.io')(server);
    
    const nsp = io.of("/websockettest");
    
    nsp.on('connection', function (socket) {
      console.log('connected socket!');
    
      socket.on('greet', function (data) {
        console.log(data);
        nsp.emit('respond', { hello: 'Hello' });
      });
      socket.on('disconnect', function () {
        console.log('Socket disconnected');
      });
    });
    
    const port = process.env.PORT || 3020;
    server.listen(port, () => {
      console.log(`Server is up on port ${port}`);
    });
    

    React Client

    const socket = io('/websockettest', {
      'reconnection': true, 
      transports: ['websocket']
    });