在Windows Docker Desktop的K8s中,如何通过Service访问指定Pod?
你的核心问题是会话粘性:客户端需要和处理初始请求的同一个Pod建立WebSocket连接,同时还要支持Pod的水平扩缩容(HPA)。下面是几种适配你场景的方案,按实现复杂度和实用性排序:
方案1:Service会话亲和性(最简单的快速方案)
Kubernetes的Service原生支持会话亲和性,能让来自同一个客户端IP的请求始终路由到同一个Pod。这个方案不需要修改业务代码,配置简单,完全适配HPA需求。
修改你的Service配置:
apiVersion: v1 kind: Service metadata: name: my-service1 labels: app: stream spec: ports: - port: 8090 targetPort: 8090 name: port8090 selector: app: stream type: LoadBalancer # 添加会话亲和性配置 sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 3600 # 会话保持时长,可根据业务调整,比如设置为WebSocket连接的超时时间
优点:
- 零业务代码修改,直接用K8s原生能力解决问题
- 完美兼容HPA:扩缩容时K8s会自动调整会话路由,新Pod会接收新的客户端请求,不影响现有会话
- 配置简单,几分钟就能生效
注意事项:
- 如果多个客户端共享同一个公网IP(比如通过NAT网关访问),会导致所有这些客户端都被路由到同一个Pod,可能造成负载不均
- WebSocket是长连接,只要连接保持,会话亲和性就会一直生效;如果连接断开重连,只要客户端IP不变,还是会回到同一个Pod
方案2:StatefulSet+Ingress精准路由(适配复杂场景)
如果会话亲和性不满足你的需求(比如客户端IP经常变化),可以改用StatefulSet部署Stream-Server,利用其稳定的网络标识,再通过Ingress实现路径到特定Pod的路由。
步骤1:将Deployment改为StatefulSet
StatefulSet会给每个Pod分配稳定的DNS标识(比如stream-deployment-0.stream-service.default.svc.cluster.local),即使Pod重启或扩缩容,这个标识也不会改变。
apiVersion: v1 kind: Service metadata: name: stream-service labels: app: stream spec: ports: - port: 8090 targetPort: 8090 name: port8090 selector: app: stream clusterIP: None # 配置为Headless Service,用于StatefulSet的DNS解析 --- apiVersion: apps/v1 kind: StatefulSet metadata: name: stream-deployment labels: app: stream spec: replicas: 3 serviceName: stream-service # 关联上面的Headless Service selector: matchLabels: app: stream template: metadata: labels: app: stream spec: containers: - image: stream-server-mock:latest name: stream-server-mock imagePullPolicy: Never env: - name: STREAMER_IP valueFrom: fieldRef: fieldPath: status.podIP - name: STREAMER_ADDRESS value: "$(HOSTNAME).stream-service.default.svc.cluster.local:8090" # 使用稳定DNS标识 ports: - containerPort: 8090
步骤2:配置Ingress路径路由
以Nginx Ingress为例,配置路径转发规则,让客户端可以通过http://<loadbalancer-ip>/pod0访问stream-deployment-0,/pod1访问stream-deployment-1:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: stream-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 spec: rules: - http: paths: - path: /pod0/(.*) pathType: Prefix backend: service: name: stream-service port: number: 8090 - path: /pod1/(.*) pathType: Prefix backend: service: name: stream-service port: number: 8090 # 后续扩缩容时,只需添加对应Pod的路径规则即可
步骤3:调整业务逻辑
让Stream-Server返回对应的Ingress路径(比如http://<loadbalancer-ip>/pod0),客户端通过该路径建立WebSocket连接,Ingress会直接转发到对应的Pod。
优点:
- 完全不受客户端IP变化的影响,精准路由到指定Pod
- StatefulSet的稳定标识适配HPA:新Pod会自动获得新的DNS标识,只需同步更新Ingress配置(可以用自动化脚本或Argo CD等工具实现批量更新)
缺点:
- 需要将Deployment改为StatefulSet,同时调整业务代码返回的地址格式
- Ingress路径需要和Pod数量同步,扩缩容时需要额外的配置维护
方案3:自定义会话跟踪(灵活性最高)
如果上面的方案都不满足你的特殊需求,你可以在REST-Server中实现自定义会话跟踪逻辑:
- 当Stream-Server处理请求时,把自身的Pod名称/ID返回给REST-Server
- REST-Server将该Pod标识与客户端会话绑定(比如存储在Redis中)
- 客户端建立WebSocket连接时携带会话ID,REST-Server根据会话ID找到对应的Pod,再通过代理服务(比如Spring Cloud Gateway)将请求转发到该Pod
这种方式复杂度较高,但灵活性最强,适合有特殊业务规则的场景。
推荐方案
如果你的客户端IP相对稳定(比如企业内网客户端),**方案1(Service会话亲和性)**是最优选择,配置简单且完全兼容HPA。如果客户端IP经常变化或需要精准路由,**方案2(StatefulSet+Ingress)**是更好的选择,虽然需要一些配置调整,但能满足复杂场景的需求。
内容的提问来源于stack exchange,提问作者Anthony

