k8s-agent架构思考(二)

k8s-agent架构思考(二)

背景思考

前面完成了一个 agent基础功能,这个agent是可以做为一个整体部署到任何一台机器的,

下一个问题是:如何将Agent部署到Kubernetes集群中,并支持多Agent的管理和使用?

用户分配agent方案选择

如果要考虑云agent的话, 就需要思考一些实现思路了:

  1. **固定机器模式: **直接把agent部署到线上,一个用户指定一个,需要的时候开启? 类似家里的电脑一样,需要就开,不用就关

  2. 固定分配池模式:维护一个 Agent 云池,用户首次接入时被分配到某个agent,后续请求始终路由到同一 agent,并动态加载该用户的会话、MCP 等信息(类似网吧固定机位)。此方案在资源利用率与性能之间取得平衡。

  3. 完全无状态池模式:用户每次请求可能落到不同 agent(类似 HTTP 服务器集群)。虽然弹性好,但若用户数据量较大,每次从外部存储加载会影响延迟。

1的话,类似系统镜像那样,成本太高了; 3的话,用户的资料可能比较多,每次都从外部加载性能不太好.

这边采用了2

2的话,又涉及到了用户资料怎么存的问题,

一, 用k8s的pvc,每个用户一个, 用文件来加载. 这个好处就是很多agent都是用文件来存用户资料的,如user.md, session, message也是用文件的,无缝接入。 但这种办法依赖了k8s体系,部署到其它地方的agent要各想各的办法.

二,就是用外部数据库,比如session, messages 需要的时候加载一下, 这里每个数据可能不同的表,有些agent的逻辑要重构一下.

考虑到扩展性,和采用数据库更灵活的查询等. 这里采用二

存储用数据库,但不应让agent直接访问数据库,应该建个服务封装好用户请求,做好用户鉴权)

云agent模块划分

业务中心

业务中心就是用户注册登陆相关的业务内容, 常规业务内容,会提供用户去选择某个agent, 查下面注册中心的可用agent,然后分配,如果注册中心没有可用agent了,这里要加一个排队模块.

这里选择agent,还是分配agent看各自业务,也可能是同时支持

agent注册中心

注册中心负责Agent的"登记"与"发现", AgentRegistry,

流程上

1,当agent启动时,会调用注册中心,注册保留, 状态为availible

2,当用户需要用到agent时,从注册中心获取可用agent, 提交占用,将此agent的状态改为 used

3.当用户退出或是相关连接断了时, 调用agent的exit 接口, agent退出,并且k8s会重新拉起来.

交互参考:

注册接口关键参数解析

name # 当前pod惟一标识

http_url # 让其它服务端请求过来的链接 (这里服务端请求的密钥就各自直接配置了)

ws_url # 客户端连接agent的链接,

Agent 云

这里采用k8s来部署agent

上面说到,采用了"固定分配池模式 ",

即用户一但定了机器,则加载用户的tools, memory,messages到内存, 这次会话(session)的每次请求都会路由到此agent。即同一个客户端发10次请求都会路由到同一个pod

这个平时用的k8s不太一样,一般的业务服务中是做集群比较多,每个pod都是平等的, 进行负载均衡, 特点就是无状态,简单点说,同一个客户端发10条请求,这10条请求可以到10个不同的pod,

这里涉及到一个清理的问题,即一个Pod既然有了用户的信息在内存,那下一个用户来不是会串掉?这里做了简单方式,即用完直接销毁,也不做业务上清理动作了,销毁完后k8s会自动创建一个新的pod.

如果是在物理/虚拟机上的话,则走清理内存动作

部署说明

k8s中常用deployment部署业务系统,这里用到statefulset, 因为每个pod都是要指定的,所以这里想固定一下pod名称,方便路由使用

参考 deployment.yaml

YAML 复制代码
apiVersion: v1
kind: Service
metadata:
  name: ag-unit-headless
spec:
  selector:
    app: ag-unit
  ports:
    - port: 9201
      targetPort: 9201
  clusterIP: None # Headless Service,不分配 ClusterIP
  type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: ag-unit
  labels:
    app: ag-unit
spec:
  serviceName: ag-unit-headless # 关联上面的 Headless Service
  replicas: 3
  selector:
    matchLabels:
      app: ag-unit
  template:
    metadata:
      labels:
        app: ag-unit
    spec:
      imagePullSecrets:
        - name: aliyun-registry
      containers:
        - name: ag-unit
          image: <镜像名>
          imagePullPolicy: Always
          ports:
            - containerPort: 9201
          envFrom:
            - configMapRef:
                name: ag-unit-config
          resources:
            requests:
              memory: "256Mi"
              cpu: "100m"
            limits:
              memory: "2Gi"
              cpu: "1000m"
agent访问中心gateway

这里没有写中间件做转发了,直接用ingress 来给每个节点直接路由,这块实际

部署说明

这里三个pod时分别对应于三个service, 和三个ingress, 访问时 是 <域名>/ag-unit-0/xxx 访问0这个agent, 同理,1,2也是一样,

要自己重复写0,1,2是比较麻烦的, 实际使用中是用helm来写template,不过helm文件不太好读,这里就用原始yaml来.

YAML 复制代码
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: strip-unit-prefix # 中间件的名称
  namespace: ag-unit # 确保和你的 Ingress 在同一个命名空间
spec:
  stripPrefix:
    prefixes:
      - /ag-unit-0
      - /ag-unit-1
      - /ag-unit-2
    # 强制在重写后的路径前加上斜杠,防止路径为空
    forceSlash: true
---
apiVersion: v1
kind: Service
metadata:
  name: ag-unit-0
  namespace: ag-unit
spec:
  selector:
    app: ag-unit
    statefulset.kubernetes.io/pod-name: ag-unit-0 # StatefulSet 自动打上的标签
  ports:
    - port: 9201
      targetPort: 9201
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: ag-unit-1
  namespace: ag-unit
spec:
  selector:
    app: ag-unit
    statefulset.kubernetes.io/pod-name: ag-unit-1
  ports:
    - port: 9201
      targetPort: 9201
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: ag-unit-2
  namespace: ag-unit
spec:
  selector:
    app: ag-unit
    statefulset.kubernetes.io/pod-name: ag-unit-2
  ports:
    - port: 9201
      targetPort: 9201
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ag-unit-ingress
  namespace: ag-unit
  annotations:
    #Traefik 常见注解,具体取决于你的 Traefik 配置,通常不需要特殊注解
    # traefik.ingress.kubernetes.io/router.entrypoints: web
    traefik.ingress.kubernetes.io/router.middlewares: ag-unit-strip-unit-prefix@kubernetescrd
spec:
  ingressClassName: traefik # 指定使用 traefik
  rules:
    # - host: webx.example.com
    - http:
        paths:
          - pathType: Prefix
            path: /ag-unit-0
            backend:
              service:
                name: ag-unit-0 # 对应下面为 Pod 0 创建的 Service
                port:
                  number: 9201

          - pathType: Prefix
            path: /ag-unit-1
            backend:
              service:
                name: ag-unit-1 # 对应下面为 Pod 1 创建的 Service
                port:
                  number: 9201

          - pathType: Prefix
            path: /ag-unit-2
            backend:
              service:
                name: ag-unit-2 # 对应下面为 Pod 2创建的 Service
                port:
                  number: 9201

我这边agent是用websocket来发信息了,所以这里agent0,1,2,都是有ws链接的,自己注册的时候把ws链接配置好, 然后发给注册中心注册

k8s架构

模块架构与说明

1.1 在agent启动时,会请求agent注册中心注册, 把自己的url存起来, 这里请求注册中心应该用公钥请求,agent注册中心用私钥解参数,

3.1 客户端登陆后会要连上agent, 业务中心会提供use_agent接口,此接口会查一下注册中心可用的agent, 然后检查后返回给客户端(连接agent的链接和凭证token),并把agent标记为使用中, (如果没有可用aghent,则要让用户排队)

3.4 客户端use_agent返回后,连接上agent链接,建立好连接,然后进行agent的通信

4.1 退出则会通知注册中心和agent进行退出.

退出应该是多种检查的,如ws连接断掉了,发现用户已经没在线了,也会要退出的,

总结

相关推荐
绿智校园1 小时前
一套基座替代五类系统:DeepBasic Folar与传统BA/SCADA/IoT平台的架构对比
物联网·架构
吃饱了得干活1 小时前
为什么你的Service越写越臃肿?三层架构的“业务逻辑层”是个黑盒
java·后端·架构
show4332 小时前
2026微信小程序批量处理视频文件架构方案:免费批量实测
微信小程序·小程序·架构
一拳不是超人2 小时前
Godot 信号不是线程安全的:我是怎么在后台线程里翻车的
前端·架构
小林ixn2 小时前
Docker + Nginx 反向代理:从“我电脑能跑”到“哪里都能跑”
nginx·docker·容器
三言老师2 小时前
K8s集群运行时异常趋势分析预警实操
java·开发语言·kubernetes
深念Y2 小时前
Wine 运行 HiTool 踩坑记录
linux·windows·容器·桌面·wine·虚拟器
吃饱了得干活2 小时前
从经典的三层架构到DDD:一次对“业务逻辑层”的解剖与重构
java·后端·架构
coder_lorraine2 小时前
手把手教你用 Docker Compose 部署 Twikoo 评论系统
mongodb·docker·容器
一拳不是超人2 小时前
被 Tauri「体积小」种草后,我拿它做了个本地 AI 桌面工具,然后踩了这些坑
前端·架构