使用 Socket.io、ExpressJS 和 Nginx Ingress 的 Kubernetes Websockets
Kubernetes Websockets using Socket.io, ExpressJS and Nginx Ingress
我想使用 Socket.io 将 React Native 应用程序连接到托管在 Google 云平台 (GKE) 上的 Kubernetes 集群内的服务器。
Nginx Ingress Controller 声明似乎有问题,但我找不到它。
我试过添加nginx.org/websocket-services
;重写我的后端代码,以便它在端口 3004 上使用一个单独的 NodeJS 服务器(一个简单的 HTTP 服务器),然后通过 Ingress Controller 在与端口 3003 上不同的路径下公开它;以及来自其他 SO 问题和 Github 问题的多项其他建议。
可能有用的信息:
- 集群主版本:
1.15.11-gke.15
- 我使用启用了 RBAC 的 Helm (
stable/nginx-ingress
) 管理的负载均衡器
- 所有部署和服务都在命名空间内
gitlab-managed-apps
- 我在尝试连接到 socket.io 时收到的错误是:
Error: websocket error
前端部分,代码如下:
App.js
const socket = io('https://example.com/app-sockets/socketns', {
reconnect: true,
secure: true,
transports: ['websocket', 'polling']
});
我希望上面的内容将我连接到名为 socketdns
的 socket.io 命名空间。
后端代码为:
app.js
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const redis = require('socket.io-redis');
io.set('transports', ['websocket', 'polling']);
io.adapter(redis({
host: process.env.NODE_ENV === 'development' ? 'localhost' : 'redis-cluster-ip-service.gitlab-managed-apps.svc.cluster.local',
port: 6379
}));
io.of('/').adapter.on('error', function(err) { console.log('Redis Adapter error! ', err); });
const nsp = io.of('/socketns');
nsp.on('connection', function(socket) {
console.log('connected!');
});
server.listen(3003, () => {
console.log('App listening to 3003');
});
入口服务是:
ingress-service.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-connect-timeout: "7200"
nginx.ingress.kubernetes.io/proxy-read-timeout: "7200"
nginx.ingress.kubernetes.io/proxy-send-timeout: "7200"
nginx.org/websocket-services: "app-sockets-cluster-ip-service"
name: ingress-service
namespace: gitlab-managed-apps
spec:
tls:
- hosts:
- example.com
secretName: letsencrypt-prod
rules:
- host: example.com
http:
paths:
- backend:
serviceName: app-cms-cluster-ip-service
servicePort: 3000
path: /?(.*)
- backend:
serviceName: app-users-cluster-ip-service
servicePort: 3001
path: /app-users/?(.*)
- backend:
serviceName: app-sockets-cluster-ip-service
servicePort: 3003
path: /app-sockets/?(.*)
- backend:
serviceName: app-sockets-cluster-ip-service
servicePort: 3003
path: /app-sockets/socketns/?(.*)
只需将注释更改为
nginx.ingress.kubernetes.io/websocket-services: "app-sockets-cluster-ip-service"
而不是
nginx.org/websocket-services: "app-sockets-cluster-ip-service"
大多数情况下它会解决您的问题。
解决方案是删除 nginx.ingress.kubernetes.io/rewrite-target: /
注释。
这是一个有效的配置:(请注意 apiVersion 自问题被提出后已更改)
入口配置
ingress-service.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "64m"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
name: ingress-service
namespace: default
spec:
tls:
- hosts:
- example.com
secretName: letsencrypt-prod
rules:
- host: example.com
http:
paths:
- backend:
service:
name: app-sockets-cluster-ip-service
port:
number: 3003
path: /app-sockets/?(.*)
pathType: Prefix
服务上(Express.js):
app.js
const redisAdapter = require('socket.io-redis');
const io = require('socket.io')(server, {
path: `${ global.NODE_ENV === 'development' ? '' : '/app-sockets' }/sockets/`,
cors: {
origin: '*',
methods: ['GET', 'POST'],
},
});
io.adapter(redisAdapter({
host: global.REDIS_HOST,
port: 6379,
}));
io.of('/').adapter.on('error', err => console.log('Redis Adapter error! ', err));
io.on('connection', () => {
//...
});
global.NODE_ENV === 'development' ? '' : '/app-sockets'
位与开发中的一个问题有关。如果您在此处更改它,您还必须在下面的代码片段中进行更改。
正在开发中,服务在 http://localhost:3003
下(套接字端点是 http://localhost:3003/sockets
)。
在生产中,服务在 https://example.com/app-sockets
下(套接字端点是 https://example.com/app-sockets/sockets
)。
在前端
connectToWebsocketsService.js
/**
* Connect to a websockets service
* @param tokens {Object}
* @param successCallback {Function}
* @param failureCallback {Function}
*/
export const connectToWebsocketsService = (tokens, successCallback, failureCallback) => {
//SOCKETS_URL = NODE_ENV === 'development' ? 'http://localhost:3003' : 'https://example.com/app-sockets'
const socket = io(`${ SOCKETS_URL.replace('/app-sockets', '') }`, {
path: `${ NODE_ENV === 'development' ? '' : '/app-sockets' }/sockets/`,
reconnect: true,
secure: true,
transports: ['polling', 'websocket'], //required
query: {
// optional
},
auth: {
...generateAuthorizationHeaders(tokens), //optional
},
});
socket.on('connect', successCallback(socket));
socket.on('reconnect', successCallback(socket));
socket.on('connect_error', failureCallback);
};
注意:我无法在问题中提到的项目上执行此操作,但我在另一个托管在 EKS 而非 GKE[=51 上的项目上执行此操作=].请随时确认这是否也适用于 GKE。
我想使用 Socket.io 将 React Native 应用程序连接到托管在 Google 云平台 (GKE) 上的 Kubernetes 集群内的服务器。
Nginx Ingress Controller 声明似乎有问题,但我找不到它。
我试过添加nginx.org/websocket-services
;重写我的后端代码,以便它在端口 3004 上使用一个单独的 NodeJS 服务器(一个简单的 HTTP 服务器),然后通过 Ingress Controller 在与端口 3003 上不同的路径下公开它;以及来自其他 SO 问题和 Github 问题的多项其他建议。
可能有用的信息:
- 集群主版本:
1.15.11-gke.15
- 我使用启用了 RBAC 的 Helm (
stable/nginx-ingress
) 管理的负载均衡器 - 所有部署和服务都在命名空间内
gitlab-managed-apps
- 我在尝试连接到 socket.io 时收到的错误是:
Error: websocket error
前端部分,代码如下:
App.js
const socket = io('https://example.com/app-sockets/socketns', {
reconnect: true,
secure: true,
transports: ['websocket', 'polling']
});
我希望上面的内容将我连接到名为 socketdns
的 socket.io 命名空间。
后端代码为:
app.js
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const redis = require('socket.io-redis');
io.set('transports', ['websocket', 'polling']);
io.adapter(redis({
host: process.env.NODE_ENV === 'development' ? 'localhost' : 'redis-cluster-ip-service.gitlab-managed-apps.svc.cluster.local',
port: 6379
}));
io.of('/').adapter.on('error', function(err) { console.log('Redis Adapter error! ', err); });
const nsp = io.of('/socketns');
nsp.on('connection', function(socket) {
console.log('connected!');
});
server.listen(3003, () => {
console.log('App listening to 3003');
});
入口服务是:
ingress-service.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-connect-timeout: "7200"
nginx.ingress.kubernetes.io/proxy-read-timeout: "7200"
nginx.ingress.kubernetes.io/proxy-send-timeout: "7200"
nginx.org/websocket-services: "app-sockets-cluster-ip-service"
name: ingress-service
namespace: gitlab-managed-apps
spec:
tls:
- hosts:
- example.com
secretName: letsencrypt-prod
rules:
- host: example.com
http:
paths:
- backend:
serviceName: app-cms-cluster-ip-service
servicePort: 3000
path: /?(.*)
- backend:
serviceName: app-users-cluster-ip-service
servicePort: 3001
path: /app-users/?(.*)
- backend:
serviceName: app-sockets-cluster-ip-service
servicePort: 3003
path: /app-sockets/?(.*)
- backend:
serviceName: app-sockets-cluster-ip-service
servicePort: 3003
path: /app-sockets/socketns/?(.*)
只需将注释更改为
nginx.ingress.kubernetes.io/websocket-services: "app-sockets-cluster-ip-service"
而不是
nginx.org/websocket-services: "app-sockets-cluster-ip-service"
大多数情况下它会解决您的问题。
解决方案是删除 nginx.ingress.kubernetes.io/rewrite-target: /
注释。
这是一个有效的配置:(请注意 apiVersion 自问题被提出后已更改)
入口配置
ingress-service.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/use-regex: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "64m"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
name: ingress-service
namespace: default
spec:
tls:
- hosts:
- example.com
secretName: letsencrypt-prod
rules:
- host: example.com
http:
paths:
- backend:
service:
name: app-sockets-cluster-ip-service
port:
number: 3003
path: /app-sockets/?(.*)
pathType: Prefix
服务上(Express.js):
app.js
const redisAdapter = require('socket.io-redis');
const io = require('socket.io')(server, {
path: `${ global.NODE_ENV === 'development' ? '' : '/app-sockets' }/sockets/`,
cors: {
origin: '*',
methods: ['GET', 'POST'],
},
});
io.adapter(redisAdapter({
host: global.REDIS_HOST,
port: 6379,
}));
io.of('/').adapter.on('error', err => console.log('Redis Adapter error! ', err));
io.on('connection', () => {
//...
});
global.NODE_ENV === 'development' ? '' : '/app-sockets'
位与开发中的一个问题有关。如果您在此处更改它,您还必须在下面的代码片段中进行更改。
正在开发中,服务在 http://localhost:3003
下(套接字端点是 http://localhost:3003/sockets
)。
在生产中,服务在 https://example.com/app-sockets
下(套接字端点是 https://example.com/app-sockets/sockets
)。
在前端
connectToWebsocketsService.js
/**
* Connect to a websockets service
* @param tokens {Object}
* @param successCallback {Function}
* @param failureCallback {Function}
*/
export const connectToWebsocketsService = (tokens, successCallback, failureCallback) => {
//SOCKETS_URL = NODE_ENV === 'development' ? 'http://localhost:3003' : 'https://example.com/app-sockets'
const socket = io(`${ SOCKETS_URL.replace('/app-sockets', '') }`, {
path: `${ NODE_ENV === 'development' ? '' : '/app-sockets' }/sockets/`,
reconnect: true,
secure: true,
transports: ['polling', 'websocket'], //required
query: {
// optional
},
auth: {
...generateAuthorizationHeaders(tokens), //optional
},
});
socket.on('connect', successCallback(socket));
socket.on('reconnect', successCallback(socket));
socket.on('connect_error', failureCallback);
};
注意:我无法在问题中提到的项目上执行此操作,但我在另一个托管在 EKS 而非 GKE[=51 上的项目上执行此操作=].请随时确认这是否也适用于 GKE。