资源配置与容量管理
本章详细说明 K3s 集群中 SessionWorker 容器的资源限制机制——CPU、内存、磁盘如何被分配和隔离,以及 Backend 如何配置和监控这些资源。
核心概念:K8s(Kubernetes,容器编排系统)通过 Linux cgroups(控制组)技术实现资源隔离。每个 Pod(容器组)运行在独立的 cgroup 中,操作系统内核负责强制执行资源上限,确保一个智能体容器不会影响宿主机或其他容器的正常运行。
1. 资源限制的底层原理:cgroups
1.1 什么是 cgroups
cgroups(Control Groups,控制组)是 Linux 内核提供的资源隔离机制。当 K3s 创建一个 SessionWorker Pod 时,containerd(容器运行时)会为该容器创建一个 cgroup,内核通过该 cgroup 控制容器可使用的资源上限。
graph TB
subgraph HOST["用户本地机器"]
KERNEL["Linux 内核<br/>cgroups 子系统"]
subgraph CGROUP["cgroup: agent-worker-xxx"]
CPU_CG["CPU 控制<br/>cpu.cfs_quota_us / cpu.cfs_period_us"]
MEM_CG["内存控制<br/>memory.limit_in_bytes"]
IO_CG["磁盘控制<br/>ephemeral-storage 监控"]
end
subgraph POD["SessionWorker Pod"]
AC["agentcore 进程"]
end
end
KERNEL --> CGROUP
CGROUP --> POD
AC -.->|"受 cgroup 限制"| CPU_CG
AC -.->|"受 cgroup 限制"| MEM_CG
AC -.->|"受 cgroup 限制"| IO_CG
style HOST fill:#f5f5f5,stroke:#333
style CGROUP fill:#fff3e0,stroke:#e65100
style POD fill:#e8f4fd,stroke:#0969da
1.2 cgroups 如何控制 CPU
K8s 将 CPU requests/limits 转换为 cgroups v2 的以下参数:
| K8s 配置 | cgroups 参数 | 作用 |
|---|---|---|
requests.cpu: 500m | cpu.weight(按比例分配) | 保证容器在 CPU 竞争时至少获得 0.5 核的计算时间 |
limits.cpu: 500m | cpu.max = 50000 100000(50ms/100ms 周期) | 硬限制:每 100ms 周期内最多使用 50ms CPU 时间,即 0.5 核 |
500m表示 500 毫核(millicores),即 0.5 个 CPU 核心1000m=1= 1 个完整核心- 超过 limits 时:进程不会被杀死,而是被节流(throttled)——内核暂停进程直到下一个周期
1.3 cgroups 如何控制内存
| K8s 配置 | cgroups 参数 | 作用 |
|---|---|---|
requests.memory: 1Gi | 调度参考(保证节点有足够内存) | K8s 调度器只把 Pod 放到有 >= 1Gi 可分配内存的节点上 |
limits.memory: 1Gi | memory.max = 1073741824 | 硬限制:容器总内存不超过 1Gi |
- 超过 limits 时:内核触发 OOM Killer(Out-Of-Memory 杀手),直接终止容器进程
- 这比 CPU 严格得多——CPU 超限只是变慢,内存超限直接被杀
- 因此 agentcore 需要控制自身内存使用,特别是处理大型上下文时
1.4 cgroups 如何控制磁盘
ephemeral-storage(临时存储)的限制方式与 CPU/内存不同:
| K8s 配置 | 机制 | 作用 |
|---|---|---|
limits.ephemeral-storage: 4Gi | kubelet 周期性扫描容器写入量 | 超过限额时驱逐(evict)Pod |
- K8s 不使用 cgroups 限制磁盘,而是通过 kubelet(节点代理程序)定期检查
- 检查间隔默认约 10 秒
- 超限后 Pod 被驱逐(不是进程被杀,而是整个 Pod 被删除重建)
2. Requests 与 Limits 的区别
这两个概念是理解 K8s 资源管理的关键:
| 概念 | 含义 | 类比 |
|---|---|---|
| Requests(请求) | 容器运行的最低保障 | 酒店预订:保证你有房间 |
| Limits(限额) | 容器运行的最高上限 | 自助餐上限:最多吃这么多 |
graph LR
subgraph RANGE["资源使用范围"]
R["Requests<br/>最低保障<br/>500m CPU"]
L["Limits<br/>最高上限<br/>500m CPU"]
end
R -->|"实际使用在此范围内"| L
当前配置中 requests = limits(均为 500m CPU / 1Gi 内存),这意味着:
- 容器获得固定资源配额,不多不少
- 这种配置称为 Guaranteed QoS(保证型服务质量),是最稳定的模式
- 适合本地部署(只有一台机器,资源管理要简单可控)
3. 当前资源配置
3.1 RuntimeProfile 配置文件
Backend 通过 runtime_profiles.yaml(运行时配置文件)定义不同的资源档位。每个智能体包可以指定使用哪个 profile:
| Profile 名称 | CPU(requests/limits) | 内存(requests/limits) | 临时存储(requests/limits) |
|---|---|---|---|
codex-standard | 500m / 500m | 1Gi / 1Gi | 4Gi / 4Gi |
codex-large | 500m / 500m | 1Gi / 1Gi | 4Gi / 4Gi |
claude-code-standard | 500m / 500m | 1Gi / 1Gi | 4Gi / 4Gi |
claude-code-large | 500m / 500m | 1Gi / 1Gi | 4Gi / 4Gi |
当前状态:四个 profile 的资源配置完全相同。这是初始阶段的简化设计,未来可根据不同智能体的实际资源消耗进行差异化配置。
3.2 资源单位说明
| 单位 | 含义 | 换算 |
|---|---|---|
m(毫核) | CPU 时间片,1000m = 1 个核心 | 500m = 0.5 核 |
Mi / Gi | 内存,Mi = 1024*1024 字节 | 1Gi = 1024Mi = 约 1.07 GB |
Ki / Mi / Gi | 存储同上 | 4Gi = 约 4.29 GB |
3.3 资源在代码中的流转
graph TB
YAML["runtime_profiles.yaml<br/>资源档位配置"] --> RS["ResourceSettings<br/>cpu/memory/ephemeral_storage"]
RS --> RM["resource_map()<br/>转换为 K8s Quantity"]
RM --> SPEC["Container.resources<br/>requests + limits"]
SPEC --> K8S["K8s API Server"]
K8S --> KUBELET["kubelet 节点代理"]
KUBELET --> CG["创建 cgroup<br/>设置资源限制"]
style YAML fill:#e8f4fd,stroke:#0969da
style CG fill:#fff3e0,stroke:#e65100
ResourceSettings(资源设置)的 Rust 类型定义:
#![allow(unused)]
fn main() {
pub struct ResourceSettings {
pub cpu: String, // 如 "500m"
pub memory: String, // 如 "1Gi"
pub ephemeral_storage: String, // 如 "4Gi"
}
}
resource_map() 方法将其转换为 K8s API 所需的 BTreeMap<String, Quantity>,requests 和 limits 设置为相同值。
4. 容器安全上下文
除了资源限制,K8s 还通过 SecurityContext(安全上下文)限制容器的系统权限:
4.1 Pod 级安全设置
securityContext:
runAsNonRoot: true # 禁止以 root 身份运行
runAsUser: 1000 # 以 UID 1000 运行所有进程
runAsGroup: 1000 # 以 GID 1000 运行
fsGroup: 1000 # 挂载卷的文件组设为 1000
seccompProfile:
type: RuntimeDefault # 启用 seccomp 系统调用过滤
| 设置 | 作用 | 安全意义 |
|---|---|---|
runAsNonRoot | 强制非 root 运行 | 即使镜像 Dockerfile 指定 root,K8s 也会拒绝启动 |
runAsUser: 1000 | 固定 UID | agentcore 以普通用户身份运行,无法访问系统文件 |
fsGroup: 1000 | 挂载卷文件组 | 确保 agentcore 可以读写挂载的 workspace 目录 |
seccompProfile | 系统调用白名单 | 阻止容器执行危险的系统调用(如 mount、reboot) |
4.2 容器级安全设置
securityContext:
allowPrivilegeEscalation: false # 禁止提权
capabilities:
drop: ["ALL"] # 丢弃所有 Linux capabilities
readOnlyRootFilesystem: false # 允许写入(agentcore 需要临时文件)
| 设置 | 作用 |
|---|---|
allowPrivilegeEscalation: false | 阻止通过 setuid/setgid 提升权限 |
capabilities.drop: ALL | 移除所有特殊权限(网络配置、进程管理等) |
readOnlyRootFilesystem: false | agentcore 需要写入临时文件和日志 |
4.3 其他安全设置
automountServiceAccountToken: false # 不挂载 K8s 服务账号令牌
terminationGracePeriodSeconds: 30 # 优雅关闭等待 30 秒
automountServiceAccountToken: false:防止容器内进程访问 K8s API,消除容器逃逸风险terminationGracePeriodSeconds: 30:给 agentcore 30 秒时间保存状态后退出
5. 空闲回收策略(WorkerIdlePolicy)
SessionWorker 采用 Deployment 长驻模式,需要主动回收空闲资源:
5.1 策略参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
idle_ttl_seconds | u64(必填) | 300(5 分钟) | Worker 空闲多久后自动关闭 |
max_lifetime_seconds | Option(可选) | 3600(1 小时) | Worker 最长存活时间,无论是否空闲 |
5.2 回收流程
graph TD
IDLE["Worker 进入 IDLE 状态"] --> TIMER["启动空闲计时器"]
TIMER --> CHECK{"idle_ttl_seconds 到期?"}
CHECK -->|"否, 收到新消息"| RESET["重置计时器"]
RESET --> IDLE
CHECK -->|"是"| DRAIN["进入 DRAINING 状态"]
DRAIN --> SAVE["通过 WS 通知 Backend"]
SAVE --> CLOSE["发送 WS Close 帧"]
CLOSE --> EXIT["进程退出"]
EXIT --> K8S["K8s 检测到 Pod 退出"]
K8S --> DELETE["Deployment 缩容 / 删除"]
IDLE --> MAX{"max_lifetime_seconds 到期?"}
MAX -->|"是"| DRAIN
MAX -->|"否"| IDLE
6. 节点健康监控
Backend 通过 K8s API 实时监控本地 K3s 节点的健康状态。
6.1 监控的指标
| 指标名 | 含义 | 健康值 |
|---|---|---|
| Ready | 节点是否正常运行 | True |
| MemoryPressure | 节点内存是否紧张 | False |
| DiskPressure | 节点磁盘是否紧张 | False |
| PIDPressure | 节点进程数是否过多 | False |
| NetworkUnavailable | 节点网络是否不可用 | False |
6.2 ClusterNodeStatus
Backend 将 K8s 节点状态转换为 ClusterNodeStatus(集群节点状态)供 Helper 展示:
#![allow(unused)]
fn main() {
pub struct ClusterNodeStatus {
pub name: String, // 节点名称
pub conditions: Vec<NodeCondition>, // 上述 5 项指标
pub allocatable: ResourceSummary, // 可分配资源总量
pub capacity: ResourceSummary, // 资源总容量
}
}
Helper 通过 GET /api/v1/cluster/status 获取该信息,显示在右侧状态面板。
7. 容量规划指南
7.1 单个 Worker 的资源消耗
| 资源 | 配额 | 用途说明 |
|---|---|---|
| CPU | 500m(0.5 核) | 主要消耗在 agentcore 推理框架、工具执行、文本处理 |
| 内存 | 1Gi | 上下文缓存、推理中间状态、checkpoint 数据库缓存 |
| 磁盘 | 4Gi | workspace 文件、checkpoint 数据库、临时文件、日志 |
7.2 宿主机资源建议
| 场景 | 最小配置 | 推荐配置 |
|---|---|---|
| 1 个智能体同时运行 | 2 核 / 4Gi 内存 | 4 核 / 8Gi 内存 |
| 2-3 个智能体同时运行 | 4 核 / 8Gi 内存 | 8 核 / 16Gi 内存 |
| 4-6 个智能体同时运行 | 8 核 / 16Gi 内存 | 16 核 / 32Gi 内存 |
注意:上述配置需要为宿主机系统、K3s 组件、Backend 服务预留约 1-2 核 CPU 和 2-4Gi 内存。
7.3 磁盘空间规划
| 组件 | 典型大小 | 说明 |
|---|---|---|
| K3s 系统 | 约 500Mi | K3s 二进制 + 系统容器 |
| 容器镜像缓存 | 约 1-5Gi | 取决于使用的智能体镜像数量 |
| 每个 Worker workspace | 最大 4Gi | ephemeral-storage limits |
| checkpoint 数据库 | 通常 < 100Mi / 会话 | 取决于对话轮次和工具调用复杂度 |
8. 资源超限行为汇总
当容器超过资源限制时,K8s 的处理方式因资源类型而异:
| 资源 | 超限行为 | 严重程度 | 恢复方式 |
|---|---|---|---|
| CPU | 进程被节流(throttled),变慢但不被杀 | 低 | 等待下一个 CPU 周期自动恢复 |
| 内存 | OOM Killer 终止进程,Pod 可能重启 | 高 | K8s 自动重启 Pod(受 restartPolicy 控制) |
| 磁盘 | Pod 被驱逐(evicted),需要重新调度 | 高 | Backend 检测到 Pod 丢失,可能需要重建 Worker |
9. 资源配置调优建议
9.1 CPU 调优
- 当前
500m适合大多数场景(agentcore 主要等待大模型 API 响应,CPU 实际使用率较低) - 如果智能体需要执行 CPU 密集型工具(如代码编译、数据处理),可提高到
1000m或2000m - 通过
kubectl top pod观察实际 CPU 使用率来决定
9.2 内存调优
1Gi适合一般对话场景- 处理长上下文(> 100k tokens)或大文件分析时,可能需要
2Gi或更多 - 内存超限后果严重(OOM Kill),建议 limits 设为实际峰值使用量的 1.5 倍
9.3 磁盘调优
4Gi足够大多数场景- 如果智能体需要下载大型数据集或生成大量文件,可提高到
8Gi或16Gi - 注意:ephemeral-storage 是容器可写层 + emptyDir 的总和
10. 诊断命令
当需要排查资源问题时,可在本地终端执行以下命令:
| 命令 | 用途 |
|---|---|
kubectl top nodes | 查看节点 CPU / 内存使用率 |
kubectl top pods -n agent-sandbox | 查看各 Pod 的 CPU / 内存实时使用 |
kubectl describe pod <pod-name> -n agent-sandbox | 查看 Pod 的资源请求、限制、事件(含 OOMKilled 记录) |
kubectl get events -n agent-sandbox --sort-by=.metadata.creationTimestamp | 查看最近事件(驱逐、重启等) |
kubectl describe node | 查看节点可分配资源和已分配量 |
下一步
了解了资源管理后,请继续阅读:
- AgentCore 通信机制 — 了解 WebSocket 双向通信协议
- 生命周期管理 — 了解 Worker 从创建到销毁的完整流程
- 外部数据库访问 — 了解如何连接宿主机数据库