You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Microk8s下带暂停功能的独占式Pod会话亲和性调度问题

解决方案:基于状态跟踪的动态Pod路由方案

针对你这种有状态独占型服务的调度需求,核心要解决两个问题:用户会话与Pod的绑定关系管理、Pod占用状态的动态感知与路由调整,不能依赖单纯的Cookie亲和,以下是适合Microk8s环境的落地方案:

核心架构思路

引入轻量状态存储组件(比如Redis),维护两个核心数据集合:

  • Pod状态表:记录每个Pod的ID、当前状态(空闲/已占用)、占用用户ID
  • 用户绑定表:记录每个用户ID对应的历史绑定PodID(保留优先级)

当用户发起请求时,先通过状态存储查询应路由的目标Pod,再通过Ingress/服务网格实现动态路由,完全规避静态Cookie亲和的局限性。

具体实现步骤

1. 部署状态存储组件(Microk8s环境)

直接启用Microk8s内置的Redis插件,快速搭建状态存储:

microk8s enable redis

通过microk8s kubectl get pods -n kube-system确认Redis Pod运行状态。

2. 改造应用服务,添加状态交互逻辑

在卡尔曼滤波器服务中嵌入以下逻辑:

  • Pod启动注册:Pod启动后,向Redis写入自身状态为空闲,注册ID用Pod的hostname(K8s中Pod hostname默认等于Pod名称):
    # 伪代码示例(Python)
    import redis
    import os
    
    r = redis.Redis(host="redis-kube-system.svc.cluster.local", port=6379, db=0)
    pod_id = os.environ["HOSTNAME"]
    # 初始化Pod状态为空闲
    r.hset("pod_status", pod_id, "idle")
    
  • 用户请求处理:
    1. 用户首次请求:查询所有空闲状态的Pod,选择一个后更新其状态为occupied,同时记录用户与Pod的绑定关系:
      # 从请求头提取用户标识
      user_id = request.headers.get("X-User-ID")
      # 查询空闲Pod列表
      idle_pods = [pod for pod, status in r.hgetall("pod_status").items() if status == b"idle"]
      if idle_pods:
          assigned_pod = idle_pods[0]
          # 更新Pod状态、用户绑定关系、Pod占用者信息
          r.hset("pod_status", assigned_pod, "occupied")
          r.hset("user_binding", user_id, assigned_pod)
          r.hset("pod_owner", assigned_pod, user_id)
      
    2. 用户后续请求:先查询历史绑定Pod,根据Pod状态决定路由目标:
      user_id = request.headers.get("X-User-ID")
      bound_pod = r.hget("user_binding", user_id)
      target_pod = None
      
      if bound_pod:
          pod_status = r.hget("pod_status", bound_pod)
          pod_owner = r.hget("pod_owner", bound_pod)
          # 绑定Pod仍被当前用户占用,继续使用
          if pod_status == b"occupied" and pod_owner == user_id.encode():
              target_pod = bound_pod
          # 绑定Pod已空闲,重新占用
          elif pod_status == b"idle":
              r.hset("pod_status", bound_pod, "occupied")
              r.hset("pod_owner", bound_pod, user_id)
              target_pod = bound_pod
          # 绑定Pod被其他用户占用,重新分配空闲Pod
          else:
              idle_pods = [pod for pod, status in r.hgetall("pod_status").items() if status == b"idle"]
              if idle_pods:
                  target_pod = idle_pods[0]
                  r.hset("pod_status", target_pod, "occupied")
                  r.hset("user_binding", user_id, target_pod)
                  r.hset("pod_owner", target_pod, user_id)
      
  • 会话结束/释放Pod:用户主动结束会话(或暂停超过自定义最长保留时间)时,将对应Pod状态改回空闲:
    user_id = request.headers.get("X-User-ID")
    bound_pod = r.hget("user_binding", user_id)
    if bound_pod:
        r.hset("pod_status", bound_pod, "idle")
        r.delete("pod_owner", bound_pod)
        # 可选:保留用户绑定关系,下次优先分配该Pod(若空闲)
        # r.delete("user_binding", user_id)
    

3. 配置Ingress实现动态路由

启用Microk8s Ingress插件后,修改Nginx Ingress的ConfigMap,添加Lua脚本实现请求转发前的状态查询:

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
data:
  enable-underscores-in-headers: "true"
  lua_shared_dict: "redis_cache 10m;"
  server-snippet: |
    location / {
        access_by_lua_block {
            local redis = require "resty.redis"
            local red = redis:new()
            red:set_timeout(1000)
            local ok, err = red:connect("redis-kube-system.kube-system.svc.cluster.local", 6379)
            if not ok then
                ngx.log(ngx.ERR, "failed to connect to redis: ", err)
                return ngx.exit(500)
            end
            local user_id = ngx.req.get_headers()["X-User-ID"]
            local target_pod = nil
            if user_id then
                target_pod, err = red:hget("user_binding", user_id)
                if err then
                    ngx.log(ngx.ERR, "failed to get user binding: ", err)
                end
            end
            if target_pod then
                # 转发到对应Pod的Headless Service地址
                ngx.var.upstream = target_pod .. ".your-service-headless.default.svc.cluster.local"
            else
                # 无绑定则转发到默认服务,由应用层处理分配
                ngx.var.upstream = "your-service.default.svc.cluster.local"
            end
            red:close()
        }
        proxy_pass http://$upstream;
    }

同时配置Headless Service,让每个Pod拥有独立DNS记录:

apiVersion: v1
kind: Service
metadata:
  name: your-service-headless
spec:
  clusterIP: None
  selector:
    app: your-app
  ports:
  - port: 80
    targetPort: 8080

关键优势

  • 完美支持暂停功能:用户暂停后返回,若绑定Pod未被占用则继续使用,被占用则自动切换到空闲Pod
  • 资源利用率高:用户释放后Pod立即回到空闲状态,可被其他用户复用
  • 故障自愈:Pod意外故障时,可通过Redis过期键或Pod存活探针清理状态,用户下次请求自动分配新Pod

内容的提问来源于stack exchange,提问作者Leo Chen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 22:13:19