症状

当我们将基于Socket.io的程序放到生产环境运行时,往往不止部署一个节点,或者是一个节点不止部署一份副本,这个时候,本来正常运行的程序,却在与客户端建立链接时出现了问题:

1
2
Http状态码为400body为:
{"code":1,"message":"Session ID unknown"}

原因

建立链接时,报出了Session ID unknown,联想下socket.io的工作方式,它并不是无状态的Http接口,websocket间的会话是通过session id来标识,当一个请求过来后,原来是与Pod A打交道,这次被路由到了Pod B去,Pod B并没有该id的记录(因为在Pod A中),所以就出现了这样的报错。

解决方案(Traefik)

原因我们已经知道了,就是session会话请求去到了不该去的地方,我们可以让相同的请求去到同样的节点上。
对应traefik,我们可以在service配置annotation:

1
2
3
4
"annotations": {
"traefik.backend.loadbalancer.stickiness": "true",
"traefik.backend.loadbalancer.stickiness.cookieName": "socket"
}

第一个配置traefik.backend.loadbalancer.stickiness是指开启sticky session。
第二个配置traefik.backend.loadbalancer.stickiness.cookieName,可以自定义cookie的字段名。
配置完之后,再查看客户端,问题已经解决了。

Traefik的实现方式

当我们配置好sticky session后,traefik会在连接里面保存一个cookie值,值的内容实际上指向了具体服务的Pod的k8s ip,也就是说,当请求过来后,traefik会直接将请求发送给该ip的Pod,从而实现粘性会话。

参考

using-multiple-nodes
sticky-sessions
socketio-multiple-nodes-in-kubernetes