websocket server
1 2 3 4 5 6
| FROM maven:3.8.5-openjdk-17 as build WORKDIR /user/src/app COPY . . RUN ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && mvn clean package -DskipTests CMD mvn exec:java -f pom.xml
|
部署(保证pod可以被独立访问)
statefulset方式
使用statefulset+headless service的方式对pod进行独立访问
DNS: pod名称-编号.headless名称.namespace.svc.cluster.local
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| apiVersion: v1 kind: Service metadata: name: netty-socketio-demo-headless namespace: socket-test spec: ports: - port: 9092 name: web targetPort: 9092 clusterIP: None selector: app: netty-socketio-demo --- apiVersion: apps/v1 kind: StatefulSet metadata: name: netty-socketio-demo namespace: socket-test spec: serviceName: netty-socketio-demo-headless replicas: 2 selector: matchLabels: app: netty-socketio-demo template: metadata: labels: app: netty-socketio-demo spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - topologyKey: "kubernetes.io/hostname" labelSelector: matchExpressions: - key: app operator: In values: - netty-socketio-demo containers: - name: netty-socketio-demo image: $REGISTRY_ADDRESS/${NODE_ENV}/${CI_PROJECT_NAME}:v${CI_PIPELINE_ID} imagePullPolicy: IfNotPresent resources: limits: memory: 512Mi requests: memory: 512Mi livenessProbe: tcpSocket: port: 9092 readinessProbe: tcpSocket: port: 9092 ports: - containerPort: 9092 name: web protocol: TCP imagePullSecrets: - name: nexus3secret dnsPolicy: ClusterFirst dnsConfig: options: - name: ndots value: "2" restartPolicy: Always
|
statefulset在istio中存在的问题
现象: client 通过 headless service 访问 server,当 server 的 pod 发生重建后,client 访问 server 失败,网关返回502
Istio 运维实战系列(3):让人头大的『无头服务』-下
https://github.com/imroc/istio-guide/issues/8
使用k8s service的externalTrafficPolicy: Cluster + NodePort方式进行部署
externalTrafficPolicy是k8s中默认提供的一种实现session粘性的方法,当配置externalTrafficPolicy: Cluster,代表请求打到当前节点kube-proxy后不再进行二次转发,但前提是当前的Service必须是Nodeport/Loadbalence类型
默认session粘性持续的有效期是3个小时,最大可以配置一天,但是我们的服务需要保持一个长链接,所以此方案不行
使用deployment+多实例/多端口方式进行部署【✓】
如下测试案例为一个server,两个deployment,两个service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| apiVersion: v1 kind: Service metadata: name: netty-socketio-demo1 namespace: socket-test spec: selector: app: netty-socketio-demo1 type: ClusterIP ports: - port: 9092 targetPort: 9092 protocol: TCP --- apiVersion: apps/v1 kind: Deployment metadata: name: netty-socketio-demo1 namespace: socket-test labels: app: netty-socketio-demo1 spec: replicas: 1 selector: matchLabels: app: netty-socketio-demo1 template: metadata: labels: app: netty-socketio-demo1 spec: containers: - image: $REGISTRY_ADDRESS/${NODE_ENV}/${CI_PROJECT_NAME}:v${CI_PIPELINE_ID} imagePullPolicy: IfNotPresent resources: limits: memory: 128Mi requests: memory: 128Mi livenessProbe: tcpSocket: port: 9092 readinessProbe: tcpSocket: port: 9092 name: netty-socketio-demo1 ports: - containerPort: 9092 protocol: TCP imagePullSecrets: - name: nexus3secret dnsPolicy: ClusterFirst restartPolicy: Always --- apiVersion: v1 kind: Service metadata: name: netty-socketio-demo2 namespace: socket-test spec: selector: app: netty-socketio-demo2 type: ClusterIP ports: - port: 9093 targetPort: 9092 protocol: TCP --- apiVersion: apps/v1 kind: Deployment metadata: name: netty-socketio-demo2 namespace: socket-test labels: app: netty-socketio-demo2 spec: replicas: 1 selector: matchLabels: app: netty-socketio-demo2 template: metadata: labels: app: netty-socketio-demo2 spec: containers: - image: $REGISTRY_ADDRESS/${NODE_ENV}/${CI_PROJECT_NAME}:v${CI_PIPELINE_ID} imagePullPolicy: IfNotPresent resources: limits: memory: 128Mi requests: memory: 128Mi livenessProbe: tcpSocket: port: 9092 readinessProbe: tcpSocket: port: 9092 name: netty-socketio-demo2 ports: - containerPort: 9092 protocol: TCP imagePullSecrets: - name: nexus3secret dnsPolicy: ClusterFirst restartPolicy: Always
|
网关(nginx)
nginx实现负载方式(保证session粘性)
ip_hash
Nginx的ip_hash策略,可以在客户端IP不变的情况下,将其发出的请求路由到同一台目标服务器, 实现会话粘滞,避免处理session共享问题。
普通Hash算法存在⼀个问题,以ip_hash为例,集群中有tomcat1,tomcat2,tomcat3 三台Tomcat服务器,
假定客户端ip固定没有发⽣改变,现在tomcat3出现问题宕机了,服务器数量由3个变为了2个,之前所有的求模都需要重新计算。

在真实的生产环境下,若后台服务器有许多台,客户端数量也非常庞大的情况下,影响是非常大的;
服务器缩容/扩容都会发生这样的问题,大量的客户端请求被路由到其他的服务器,客户端在原来服务器的会话都会丢失。
那么如何解决这样的问题呢?
答案就是一致性Hash算法。
在一致性Hash算法实现过程中,每⼀台服务器负责⼀段,⼀致性哈希算法对于节点的增减都只需重定位环空间中的⼀⼩部分数据,具有较好的容错性和可扩展性。
数据(请求)倾斜问题
⼀致性哈希算法在服务节点太少时,容易因为节点分布不均匀⽽造成数据倾斜问题。
例如系统中只有两台服务器,其环分布如下,节点2只能负责⾮常⼩的⼀段,⼤量的客户端请求就落在了节点1上

sticky【✓】
Sticky是nginx的一个模块,它是基于cookie的一种nginx的负载均衡解决方案,通过分发和识别cookie,来使同一个客户端的请求落在同一台服务器上,默认标识名为route
- 客户端首次发起访问请求,nginx接收后,发现请求头没有cookie,则以轮询方式将请求分发给后端服务器。
- 后端服务器处理完请求,将响应数据返回给nginx。
- 此时nginx生成带route的cookie,返回给客户端。route的值与后端服务器对应,可能是明文,也可能是md5、sha1等Hash值
- 客户端接收请求,并保存带route的cookie。
- 当客户端下一次发送请求时,会带上route,nginx根据接收到的cookie中的route值,转发给对应的后端服务器。
- 较ip_hash负载方式来说,stick可以保障负载方式更为均匀,而且不需要考虑同域的情况
nginx代理方式
仅为 ws 协议代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| upstream server { sticky; server netty-socketio-demo1.socket-test:9092 weight=1 max_fails=3 fail_timeout=20; server netty-socketio-demo2.socket-test:9093 weight=1 max_fails=3 fail_timeout=20; } location /websocket/ { proxy_pass http://myserver; proxy_http_version 1.1; proxy_read_timeout 360s; proxy_redirect off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header REMOTE-HOST $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Host $server_name; }
|
既支持http又支持 ws 的配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| http { map $http_upgrade $connection_upgrade { default keep-alive; 'websocket' upgrade; }
server { ... location /websocket/ { proxy_pass http://myserver; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } }
|
测试
未带route-cookie访问会进行轮询,然后返回cookie到client,下次访问就会带着cookie找到上一次访问的server实例
