MoreRSS

site iconLixueduan | 李学端修改

博客名:指月小筑。专注云原生,Go,坚持分享最佳实践、经验干货。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Lixueduan | 李学端的 RSS 预览

Claude Code 也能跑本地模型?CCR 多模型 智能路由,成本直降 90%

2026-03-19 04:00:00

claude-code-router.jpeg

Claude Code 是 Anthropic 推出的强大 AI 编程助手,但每月的订阅费用让很多开发者望而却步。

通过 Claude Code Router (CCR),我们可以:

  • 对接本地模型:部署 GLM5 等开源模型,实现零成本使用
  • 多模型智能路由:根据任务类型自动选择最合适的模型
  • 灵活组合:本地 + 云端混合部署,兼顾隐私、成本和质量

本文将手把手教你搭建这套方案,让你的 AI 编程助手成本降低 90% 以上。

1. 环境准备和工具安装

1.1 安装 Claude Code

首先需要在你的开发机器上安装 Claude Code:

1
2
# 官方安装脚本(需要科学上网)
curl -fsSL https://claude.ai/install.sh | bash

1.2 安装 Claude Code Router

Claude Code Router 是一个 Node.js 包,用于桥接 Claude Code 和本地模型:

1
2
# 需要 Node.js 20+ 版本
npm install -g @musistudio/claude-code-router

1.3 验证安装

安装完成后,验证两个工具是否正常工作:

1
2
3
4
5
6
7
# 检查 Claude Code 版本
❯ claude --version
2.1.51 (Claude Code)

# 检查 CCR 版本
❯ ccr version
claude-code-router version: 2.0.0

2. 模型部署

2.1 模型下载

在服务器上下载 GLM5 模型:

根据硬件配置选择合适的版本

1
2
3
4
5
# 使用 huggingface-cli 下载模型
# FP8 版本 推荐配置:H200*8
hf download zai-org/GLM-5-FP8 --local-dir /raid/lixd/models/glm5-fp8
# INT4 版本
hf download Intel/GLM-5-int4-mixed-AutoRound --local-dir /raid/lixd/models/glm5-int4-mixed-autoround

2.2 vLLM 服务启动

使用 vLLM 框架部署模型服务,确保已安装 GPU 驱动和 NVIDIA Container Toolkit。

由于 GLM5 需要最新版的 transformers 支持,官方镜像暂未包含,需要先构建自定义镜像:

已经推送到 lixd96/vllm-openai:v0.17.1-cu130,直接拉取即可 docker pull lixd96/vllm-openai:v0.17.1-cu130

构建自定义镜像:

创建 Dockerfile

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
FROM vllm/vllm-openai:v0.17.1-cu130

# 安装 git 用于从源码安装 transformers
RUN apt-get update && apt-get install -y git && \
 rm -rf /var/lib/apt/lists/*

# 安装最新版 transformers(GLM5 需要最新版支持)
RUN pip install --no-cache-dir git+https://github.com/huggingface/transformers.git

# 设置工作目录
WORKDIR /app

# 默认启动命令
ENTRYPOINT ["vllm"]

构建并推送镜像:

1
2
# 构建镜像
docker build -t lixd96/vllm-openai:v0.17.1-cu130-ex .

启动服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
docker run -d \
 --name vllm-glm5 \
 --runtime nvidia \
 --gpus '"device=0,1,2,3"' \
 -p 8000:8000 \
 -v /raid/lixd/models/glm5-int4-mixed-autoround:/glm5-int4-mixed-autoround \
 --shm-size 16g \
 lixd96/vllm-openai:v0.17.1-cu130 \
 serve /glm5-int4-mixed-autoround \
 --tensor-parallel-size 4 \
 --tool-call-parser glm47 \
 --reasoning-parser glm45 \
 --enable-auto-tool-choice \
 --served-model-name glm5 \
 --trust-remote-code \
 --gpu-memory-utilization 0.85 \
 --api-key "your-api-key" \
 --host 0.0.0.0 \
 --port 8000

关键参数说明:

  • GPU 配置:根据实际 GPU 数量调整 --gpus--tensor-parallel-size 参数
  • 内存共享--shm-size 16g 确保容器有足够共享内存
  • 工具调用--enable-auto-tool-choice 启用工具调用功能,--tool-call-parser glm47 使用 GLM 系列的工具调用解析器
  • 推理解析--reasoning-parser glm45 启用 GLM 的推理能力
  • 信任远程代码--trust-remote-code 允许加载模型的自定义代码
  • 张量并行--tensor-parallel-size 4 在多 GPU 上并行处理

2.3 服务验证

部署完成后,验证服务是否正常运行:

 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
# 查看可用模型列表
curl http://localhost:8000/v1/models

# 基础对话测试
curl http://localhost:8000/v1/chat/completions \
 -H "Content-Type: application/json" \
 -d '{
 "model": "glm5",
 "messages": [
 {"role": "user", "content": "你好,请介绍一下你自己"}
 ],
 "max_tokens": 100,
 "temperature": 0.7
 }'

# 流式对话测试
curl http://localhost:8000/v1/chat/completions \
 -H "Content-Type: application/json" \
 -d '{
 "model": "glm5",
 "messages": [
 {"role": "user", "content": "用一句话解释什么是 Kubernetes"}
 ],
 "max_tokens": 200,
 "stream": true
 }'

如果一切正常,你应该能看到模型的响应输出。

2.4 配置 Claude Code Router

配置说明

核心配置文件如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
 // LLM 提供商
 "Providers": [
 {
 "name": "openai",
 "baseUrl": "https://api.openai.com/v1",
 "apiKey": "$OPENAI_API_KEY",
 "models": ["gpt-4", "gpt-3.5-turbo"]
 }
 ],

// 路由
{
 "Router": {
 "default": "openai,gpt-4",
 "background": "openai,gpt-3.5-turbo",
 "think": "openai,gpt-4",
 "longContext": "anthropic,claude-3-opus"
 }
}}
  • Providers:对接不同供应商提供的不同模型
  • Router:则是将模型分类 https://musistudio.github.io/claude-code-router/zh-CN/docs/server/config/routing
    • default:默认路由
    • background:后台任务,推荐用轻量级模型
    • think:思考密集型任务,推荐用能力强的模型
    • longContext:长上下文时会使用该模型
    • webSearch:网络搜索任务
    • image:图像任务

完整配置

创建或编辑配置文件 ~/.claude-code-router/config.json

 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
{
 "LOG": true,
 "LOG_LEVEL": "debug",
 "CLAUDE_PATH": "",
 "HOST": "127.0.0.1",
 "PORT": 3456,
 "APIKEY": "",
 "API_TIMEOUT_MS": "600000",
 "PROXY_URL": "",
 "transformers": [],
 "Providers": [
 {
 "name": "vllm",
 "api_base_url": "http://your-server-ip:8000/v1/chat/completions",
 "api_key": "your-api-key",
 "models": [
 "glm5"
 ],
 "transformer": {
 "use": [
 "enhancetool",
 [
 "sampling",
 {
 "temperature": "0.7",
 "top_p": "0.8",
 "top_k": "20",
 "repetition_penalty": "1.05"
 }
 ]
 ]
 }
 }
 ],
 "StatusLine": {
 "enabled": true,
 "currentStyle": "default",
 "default": {
 "modules": []
 },
 "powerline": {
 "modules": [
 {
 "type": "workDir",
 "icon": "󰉋",
 "text": "{{workDirName}}",
 "color": "bright_blue"
 },
 {
 "type": "gitBranch",
 "icon": "🌿",
 "text": "{{gitBranch}}",
 "color": "bright_green"
 },
 {
 "type": "model",
 "icon": "🤖",
 "text": "{{model}}",
 "color": "bright_yellow"
 },
 {
 "type": "usage",
 "icon": "📊",
 "text": "{{inputTokens}} → {{outputTokens}}",
 "color": "bright_magenta"
 },
 {
 "type": "script",
 "icon": "📜",
 "text": "Script Module",
 "color": "bright_cyan",
 "scriptPath": ""
 }
 ]
 }
 },
 "Router": {
 "default": "vllm,glm5",
 "background": "vllm,glm5",
 "think": "vllm,glm5",
 "longContext": "vllm,glm5",
 "longContextThreshold": 102400,
 "webSearch": "vllm,glm5",
 "image": ""
 },
 "CUSTOM_ROUTER_PATH": ""
}

配置说明:

  • API 地址:将 your-server-ip 替换为实际的服务器 IP 地址
  • API 密钥:设置与 vLLM 启动时相同的 API 密钥
  • 路由配置:将所有任务类型都指向本地模型
  • 状态栏:启用自定义状态栏显示工作目录、Git 分支、模型信息等
  • 日志级别:调试阶段可设为 debug,生产环境建议改为 info

3. 开始使用

3.1 启动 Claude Code Router

使用 ccr code 替代原来的 claude 命令:

效果如下: ccr-demo.png

启动后,在 claude 界面输入以下命令切换模型

1
/model vllm,glm5

之后就可以正常使用了。

ps: 不切换模型也可以,只是终端会一直显示用的 Claude 的模型。

4. 进阶:多模型智能路由

前面我们介绍了单一模型的配置方式,但在实际使用中,不同类型的任务对模型能力的要求各不相同。
Claude Code Router 的强大之处在于支持对接多个模型,并根据任务类型自动路由到最合适的模型

4.1 为什么需要多模型路由?

不同任务对模型的需求差异很大:

任务类型 特点 推荐模型策略
日常对话 频率高、响应快 轻量级模型,节省资源
代码生成 需要准确性和逻辑性 中等能力模型
复杂推理 需要深度思考 顶级能力模型
长文本处理 需要大上下文窗口 支持长上下文的模型
图像理解 需要多模态能力 视觉语言模型

通过合理配置,可以实现:

  • 成本优化:简单任务用便宜模型,复杂任务才用昂贵模型
  • 性能平衡:快速响应与高质量输出的权衡
  • 资源利用:充分利用不同模型的优势

4.2 多模型配置示例

以下是一个对接多个模型服务商的完整配置:

 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
{
 "LOG": true,
 "LOG_LEVEL": "info",
 "HOST": "127.0.0.1",
 "PORT": 3456,
 "API_TIMEOUT_MS": "600000",
 "Providers": [
 {
 "name": "local-glm5",
 "api_base_url": "http://192.168.1.100:8000/v1/chat/completions",
 "api_key": "your-local-key",
 "models": ["glm5"],
 "transformer": {
 "use": ["enhancetool"]
 }
 },
 {
 "name": "deepseek",
 "api_base_url": "https://api.deepseek.com/v1/chat/completions",
 "api_key": "$DEEPSEEK_API_KEY",
 "models": ["deepseek-chat", "deepseek-reasoner"]
 },
 {
 "name": "openai",
 "api_base_url": "https://api.openai.com/v1/chat/completions",
 "api_key": "$OPENAI_API_KEY",
 "models": ["gpt-4o", "gpt-4o-mini"]
 },
 {
 "name": "zhipu",
 "api_base_url": "https://open.bigmodel.cn/api/paas/v4/chat/completions",
 "api_key": "$ZHIPU_API_KEY",
 "models": ["glm-4-plus", "glm-4-flash"]
 }
 ],
 "Router": {
 "default": "local-glm5,glm5",
 "background": "zhipu,glm-4-flash",
 "think": "deepseek,deepseek-reasoner",
 "longContext": "local-glm5,glm5",
 "longContextThreshold": 64000,
 "webSearch": "openai,gpt-4o-mini",
 "image": "openai,gpt-4o"
 }
}

4.3 路由策略详解

default(默认路由)

  • 用途:常规对话和代码编写
  • 建议:选择性价比高的主力模型
  • 示例:本地 GLM5 或 DeepSeek Chat

background(后台任务)

  • 用途:自动触发的辅助任务,如代码审查、状态检查
  • 特点:频率高、对质量要求相对较低
  • 建议:使用轻量快速模型,如 GLM-4-Flash、GPT-4o-mini
  • 成本优势:可节省 80% 以上的 API 费用

think(思考密集型)

  • 用途:复杂算法设计、架构决策、疑难问题排查
  • 特点:需要深度推理能力
  • 建议:使用最强推理模型,如 DeepSeek Reasoner、Claude Opus
  • 注意:这类模型响应较慢,但输出质量最高

longContext(长上下文)

  • 用途:分析大型代码库、处理长文档
  • 触发条件:当上下文超过 longContextThreshold 时自动切换
  • 建议:选择支持 64K+ 上下文的模型
  • 示例:GLM5(200K)、Claude(200K)

webSearch(网络搜索)

  • 用途:联网查询资料
  • 建议:选择支持工具调用的模型
  • 示例:GPT-4o、DeepSeek Chat

image(图像任务)

  • 用途:截图分析、图表解读
  • 建议:使用多模态模型
  • 示例:GPT-4o、Claude 3.5 Sonnet

4.4 最佳实践建议

工作原理

Claude Code Router 的核心原理是通过启动一个 HTTP 中转服务,在不修改 Claude Code 源码的情况下完成请求的转发和格式转换。

Claude Code 使用 Anthropic API 规范进行通信,而大多数模型服务商(如 OpenAI、DeepSeek 等)使用 OpenAI API 规范。CCR 的作用就是:

  1. 接收请求:CCR 启动一个 HTTP 服务(默认端口 3456),监听 Claude Code 的请求
  2. 格式转换:将 Anthropic API 格式的请求转换为 OpenAI API 格式
  3. 智能路由:根据任务类型将请求分发到不同的模型服务商
  4. 响应转换:将模型的响应转换回 Anthropic API 格式返回给 Claude Code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌─────────────────────────────────────────────────────────────┐
│ Claude Code │
│ (使用 Anthropic API 规范通信) │
└─────────────────────────┬───────────────────────────────────┘
 │ Anthropic API 格式
 ┌─────────────────────┐
 │ CCR HTTP 服务 │
 │ (Express.js) │
 │ │
 │ • 格式转换 │
 │ • 智能路由 │
 │ • 请求重写 │
 └──────────┬──────────┘
 │ OpenAI API 格式
 ┌─────────────────┼─────────────────┐
 │ │ │
 ┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐
 │ 本地 │ │ 云端 │ │ 云端 │
 │ GLM5 │ │ DeepSeek │ │ OpenAI │
 │ (default)│ │ (think) │ │ (image) │
 └─────────┘ └───────────┘ └───────────┘

按需分配,降低成本

CCR 的核心优势在于可以根据任务特点选择最合适的模型,实现成本与质量的平衡:

任务类型 特点 推荐策略
日常开发 频率高、响应要求快 性价比高的主力模型
复杂推理 需要深度思考 推理能力强的模型
图像理解 需要多模态能力 支持视觉的模型
后台任务 自动触发、频率高 轻量快速模型
长文本 需要大上下文 支持长上下文的模型

成本对比:

1
2
3
4
5
6
7
8
┌────────────────────────────────────────────────┐
│ Claude Max Plan │
│ $200 / 月 │
├────────────────────────────────────────────────┤
│ CCR 多模型路由 │
│ 日常任务用便宜模型 + 复杂任务用强模型 │
│ ~$20 / 月 │
└────────────────────────────────────────────────┘

通过合理配置路由策略,将高频低难度任务分配给低成本模型,仅在必要时调用高端模型,月成本可降低 90% 以上。

5. 常见问题与解决方案

5.1 “Unable to connect to Anthropic services” 错误

如果运行 ccr code 时报错:“Unable to connect to Anthropic services”,可以修改 ~/.claude.json 文件,在最外层添加以下配置:

1
2
3
{
 "hasCompletedOnboarding": true
}

保存后重新运行即可。

6. 小结

本文介绍了如何通过 Claude Code Router 打破 Claude Code 的使用限制:

  • 本地部署:通过 vLLM 部署 GLM5 模型,实现零成本使用
  • 多模型路由:根据任务类型智能分发,高频任务用便宜模型,复杂任务用强模型
  • 成本优化:相比 Claude Max Plan,合理配置后成本可降低 90% 以上

CCR 还支持对接更多 Provider,如 DeepSeek、智谱 AI、OpenAI 等,可根据实际需求灵活组合。如果你也在寻找高性价比的 AI 编程方案,不妨试试这套配置。

Kubernetes教程(五十二)---Velero快速入门:开源备份恢复工具实战

2026-03-11 04:00:00

velero-quickstart.png

在之前的文章《Kubernetes PVC Clone & Snapshot 实战:基于 Csi-Driver-Nfs 的完整示例》中,我们探讨了如何使用 Kubernetes 内置的 PVC 克隆和快照功能进行数据保护。然而,当我们需要对整个 Kubernetes 集群进行全面的备份恢复时,就需要更专业的工具。

Velero(前身 Heptio Ark)正是这样一个专业的 Kubernetes 备份恢复工具,已成为 CNCF 毕业项目。它不仅能够备份持久卷数据,还能备份整个集群的应用配置、服务和资源状态,提供企业级的灾难恢复和集群迁移能力。

你将学到:

  • Velero 架构原理和核心特性
  • 单集群备份恢复完整操作流程
  • 跨集群应用迁移实战演示
  • 生产环境最佳实践和故障排查

1. Velero 简介

1.1 什么是 Velero

在前一篇文章中我们介绍了 PVC 级别的数据保护方案,但当我们需要对整个 Kubernetes 应用栈进行备份恢复时,就需要更全面的解决方案。

Velero(前身 Heptio Ark)正是一个用于备份与恢复 Kubernetes 集群资源和持久卷(PV/PVC)的专业开源工具,提供安全可靠的数据保护解决方案。

Velero 在 Kubernetes 生态中已被广泛视为开源领域事实标准 / 最主流备份恢复工具(CNCF 毕业项目,社区采用率最高)。

1.2 核心特性

  • 集群资源备份恢复:支持 Deployment、Service、ConfigMap、Secret 等 Kubernetes 资源的备份与恢复
  • 持久卷数据保护:通过云快照或文件系统备份工具(Kopia)保护 PVC 数据
  • 定时备份调度:支持自动化定时备份策略
  • 跨集群迁移:实现集群间应用和数据的安全迁移
  • 多存储后端:支持 S3、GCS、Azure Blob 等多种对象存储

1.3 适用场景

  • 灾难恢复:应对集群故障、误删除等意外情况

  • 集群升级迁移:安全地将应用迁移到新版本集群

  • 开发测试:为开发测试环境提供快速数据恢复能力

  • 合规要求:满足数据保护和审计合规需求

  • 开源地址https://github.com/vmware-tanzu/velero

  • 官方文档https://velero.io/docs/v1.17/

2. 架构和工作原理

2.1 组件构成

Velero 采用客户端-服务器架构:

  • 服务端:运行在 Kubernetes 集群中的控制器组件
  • 客户端:本地命令行工具,需要配置 kubectl 和集群 kubeconfig

2.2 备份流程

Velero 采用 Operator 模式,通过创建 CRD 对象触发备份操作:

velero-workflow.png

  1. 创建备份任务:Velero 客户端调用 Kubernetes API 创建 Backup CRD
  2. 监听任务:Backup Controller 通过 watch 机制获取备份任务
  3. 收集数据:Controller 通过 kube-apiserver 获取需要备份的资源数据
  4. 存储备份:将数据上传到指定的对象存储后端

2.3 存储后端配置

Velero 通过两种 CRD 管理存储后端:

BackupStorageLocation

定义 Kubernetes 集群资源的存储位置,主要用于存储 YAML 清单文件等元数据。支持 S3 兼容存储(如 MinIO、AWS S3、阿里云 OSS 等)。

VolumeSnapshotLocation

定义持久卷快照的存储位置,需要云提供商插件支持。对于不支持快照的环境,可以使用文件系统备份工具 Restic/Kopia。

Restic 是一款用 Go 语言开发的数据加密备份工具,支持多种存储后端:Local、SFTP、AWS S3、MinIO、Azure Blob Storage、Google Cloud Storage 等。
Kopia 是 Velero 从 v1.10 版本开始引入的新一代 文件级备份工具,用于替代传统的 Restic,相比 Restic 具有更好的性能和可靠性。

3. 安装部署

3.1 准备工作

存储插件选择

Velero 支持多种存储插件,可通过 官方文档 查看完整列表。本教程使用 MinIO 作为 S3 兼容的对象存储。

MinIO 部署

可以参考 MinIO 官方文档部署服务,或使用 Velero 提供的快速部署脚本:

1
kubectl apply -f https://raw.githubusercontent.com/vmware-tanzu/velero/main/examples/minio/00-minio-deployment.yaml

3.2 安装 CLI 工具

GitHub release 页面下载最新压缩包:

1
2
3
4
5
6
7
8
wget https://github.com/vmware-tanzu/velero/releases/download/v1.17.2/velero-v1.17.2-linux-amd64.tar.gz

tar -zxvf velero-v1.17.2-linux-amd64.tar.gz

mv velero-v1.17.2-linux-amd64/velero /usr/local/bin/

# 查看版本
velero version

3.3 安装服务端组件

创建认证文件

先创建 S3 配置文件,提供 MinIO 的访问凭证:

1
2
3
4
5
cat > credentials-s3 <<EOF
[default]
aws_access_key_id = your-access-key-id
aws_secret_access_key = your-secret-access-key
EOF

部署 Velero 服务端

开始部署 Velero 服务端组件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
REGISTRY=docker.io
S3URL=http://your-minio-endpoint:port
BUCKET=velero

velero install \
 --namespace velero \
 --provider aws \
 --image $REGISTRY/velero/velero:v1.17.2 \
 --plugins $REGISTRY/velero/velero-plugin-for-aws:v1.13.2 \
 --secret-file ./credentials-s3 \
 --bucket $BUCKET \
 --use-node-agent \
 --use-volume-snapshots=false \
 --backup-location-config region=minio,s3ForcePathStyle="true",s3Url=$S3URL

参数说明

  • --namespace:指定部署的 namespace 名称,默认为 velero
  • --provider:定义插件提供方,aws 表示 AWS S3 兼容
  • --image:定义运行 velero 的镜像
  • --plugins:指定使用 aws s3 兼容的插件镜像
  • --secret-file:指定对象存储认证文件
  • --bucket:指定对象存储 Bucket 桶名称
  • --use-node-agent:创建 Velero Node Agent 守护进程,用于通过 Kopia 备份 PVC 数据
  • --use-volume-snapshots:是否启用快照功能

注意:从 Velero 1.7 开始,Kopia 成为默认的文件系统备份工具,取代了之前的 Restic。Kopia 提供了更好的性能和可靠性。

  • --backup-location-config:指定对象存储地址信息
    • region:MinIO 本身没有区域概念,但 AWS S3 插件需要 region 参数,因此使用 minio 作为占位符
    • s3ForcePathStyle:路径风格访问,强制使用路径风格的 URL(http://endpoint/bucket/key),这是 MinIO 的推荐访问方式
    • s3Url:MinIO 服务地址

验证安装状态

1
2
3
4
5
6
7
8
9
# 检查 Pod 状态
kubectl -n velero get pod

# 查看 BackupStorageLocation
kubectl -n velero get BackupStorageLocation

# 示例输出:
# NAME PHASE LAST VALIDATED AGE DEFAULT
# default Available 43s 5m true

3.4 卸载 Velero

如果需要从集群中完全卸载 Velero:

1
2
3
4
5
6
# 使用 velero 命令卸载
velero uninstall

# 或者手动删除相关资源
kubectl delete namespace/velero clusterrolebinding/velero
kubectl delete crds -l component=velero

4. 基础操作

4.1 备份命令详解

备份命令:velero create backup NAME [flags]

4.2 定时备份配置

除了手动备份外,Velero 还支持定时备份,可以通过 Schedule CRD 实现自动化备份:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 创建每天凌晨2点的定时备份
velero create schedule daily-backup \
 --schedule="0 2 * * *" \
 --include-namespaces nginx-example \
 --default-volumes-to-fs-backup \
 --ttl 72h

# 查看定时备份列表
velero get schedules

# 查看定时备份的执行历史
velero get backups --selector velero.io/schedule-name=daily-backup

定时表达式说明

  • 0 2 * * *:每天凌晨2点执行
  • 0 */6 * * *:每6小时执行一次
  • 0 0 * * 0:每周日午夜执行

常用选项

  • --exclude-namespaces stringArray:从备份中排除的命名空间
  • --exclude-resources stringArray:从备份中排除的资源类型
  • --include-cluster-resources optionalBool[=true]:是否包含集群级资源
  • --include-namespaces stringArray:要包含的命名空间(默认 ‘*’)
  • --include-resources stringArray:要备份的资源类型
  • --labels mapStringString:为备份添加标签
  • -o, --output string:输出格式(table/json/yaml)
  • -l, --selector labelSelector:按标签筛选资源
  • --snapshot-volumes optionalBool[=true]:是否为 PV 创建快照
  • --storage-location string:指定备份位置
  • --ttl duration:备份数据的过期时间
  • --volume-snapshot-locations strings:指定快照位置

5. 实战演示:单集群备份恢复

5.1 创建测试应用

创建一个 Nginx 应用并挂载 PVC,用于验证备份恢复功能:

 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
cat > nginx-demo.yaml <<EOF
apiVersion: v1
kind: Namespace
metadata:
 name: nginx-example
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: nginx-html-pvc
 namespace: nginx-example
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 1Gi
 storageClassName: nfs  # 请修改为你的集群中可用的StorageClass
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 namespace: nginx-example
spec:
 replicas: 1
 selector:
 matchLabels:
 app: nginx
 template:
 metadata:
 labels:
 app: nginx
 spec:
 containers:
 - name: nginx
 image: registry.cn-shanghai.aliyuncs.com/99cloud-sh/nginx:1.20
 ports:
 - containerPort: 80
 volumeMounts:
 - name: html-volume
 mountPath: /usr/share/nginx/html  # Nginx默认的网页根目录
 volumes:
 - name: html-volume
 persistentVolumeClaim:
 claimName: nginx-html-pvc
---
apiVersion: v1
kind: Service
metadata:
 name: nginx-service
 namespace: nginx-example
spec:
 type: NodePort  # 为了方便从集群外部访问,使用NodePort类型
 selector:
 app: nginx
 ports:
 - protocol: TCP
 port: 80
 targetPort: 80
EOF
1
kubectl apply -f nginx-demo.yaml

5.2 写入测试数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 获取 Nginx Pod 名称
NGINX_POD=$(kubectl get pods -n nginx-example -l app=nginx -o jsonpath='{.items[0].metadata.name}')
echo $NGINX_POD

# 创建测试文件并复制到 Pod
echo '<h1>Hello from Velero Backup Demo!</h1><p>This file was created on '"$(date)"'.</p>' > test-index.html
kubectl cp test-index.html nginx-example/$NGINX_POD:/usr/share/nginx/html/index.html

# 验证数据写入
kubectl get svc nginx-service -n nginx-example
curl http://<cluster-ip>:80

5.3 执行备份

1
velero backup create nginx-backup-pvc --include-namespaces nginx-example --default-volumes-to-fs-backup

参数说明

  • --include-namespaces:指定要备份的命名空间
  • --default-volumes-to-fs-backup:使用文件系统备份工具备份 PVC 数据

5.4 监控备份状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 查看备份列表
velero backup get

# 示例输出:
# NAME STATUS ERRORS WARNINGS CREATED EXPIRES STORAGE LOCATION SELECTOR
# nginx-backup-pvc Completed 0 0 2026-01-27 08:36:54 +0000 UTC 29d default <none>

# 查看详细备份信息
velero backup describe nginx-backup-pvc

# 查看备份日志
velero backup logs nginx-backup-pvc

5.5 验证备份结果

备份完成后,可以在 MinIO 存储中查看备份内容:

Kubernetes 资源配置备份velero-save-in-minio-yaml.png

PVC 数据文件备份velero-save-in-minio-pvc.png

5.5 模拟灾难恢复

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 删除应用模拟灾难
kubectl delete namespace nginx-example

# 执行恢复
velero restore create --from-backup nginx-backup-pvc

# 监控恢复进度
velero restore get

# 示例输出:
# NAME BACKUP STATUS STARTED COMPLETED ERRORS WARNINGS CREATED SELECTOR
# nginx-backup-pvc-20260127084915 nginx-backup-pvc Completed 2026-01-27 08:49:15 +0000 UTC 2026-01-27 08:49:23 +0000 UTC 0 1 2026-01-27 08:49:15 +0000 UTC <none>

# 验证恢复结果
kubectl -n nginx-example get pod,svc,pvc
curl <nginx-service-cluster-ip>

6. 高级功能:跨集群迁移

只要我们将每个 velero 实例指向相同的对象存储,velero 就能将资源从一个群集迁移到另一个群集。

velero-demo-cross-cluster.png

6.1 环境要求

  • 集群版本一致:建议两个 Kubernetes 集群版本保持一致
  • 存储类匹配:确保目标集群有相同名称的 StorageClass
  • 共享存储后端:两个集群的 Velero 需连接同一对象存储

以下是集群信息:

角色 集群IP 集群版本 部署软件
K8S 集群A 172.16.131.134 v1.34.2 velero、demo-app
K8S 集群B 172.16.131.171 v1.34.2 velero、minio

6.2 源集群操作

在集群 A 部署上应用。

部署测试应用

创建一个 nginx app,并挂载 pvc 用于验证备份恢复功能。

 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
cat > nginx-demo.yaml <<EOF
apiVersion: v1
kind: Namespace
metadata:
 name: nginx-example
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: nginx-html-pvc
 namespace: nginx-example
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 1Gi
 storageClassName: nfs # 请修改为你的集群中可用的StorageClass
---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 namespace: nginx-example
spec:
 replicas: 1
 selector:
 matchLabels:
 app: nginx
 template:
 metadata:
 labels:
 app: nginx
 spec:
 containers:
 - name: nginx
 image: registry.cn-shanghai.aliyuncs.com/99cloud-sh/nginx:1.20
 ports:
 - containerPort: 80
 volumeMounts:
 - name: html-volume
 mountPath: /usr/share/nginx/html # Nginx默认的网页根目录
 volumes:
 - name: html-volume
 persistentVolumeClaim:
 claimName: nginx-html-pvc
---
apiVersion: v1
kind: Service
metadata:
 name: nginx-service
 namespace: nginx-example
spec:
 type: NodePort # 为了方便从集群外部访问,使用NodePort类型
 selector:
 app: nginx
 ports:
 - protocol: TCP
 port: 80
 targetPort: 80
EOF
1
kubectl apply -f nginx-demo.yaml

写入测试

1
2
NGINX_POD=$(kubectl get pods -n nginx-example -l app=nginx -o jsonpath='{.items[0].metadata.name}')
echo $NGINX_POD

将一个简单的 html 复制到 nginx 数据目录中

1
2
3
4
# 首先在本机创建一个测试文件
echo '<h1>Hello from Velero Backup Demo!</h1><p>This file was created on '"$(date)"'.</p>' > test-index.html
# 将文件拷贝到Pod内的PVC目录
kubectl cp test-index.html nginx-example/$NGINX_POD:/usr/share/nginx/html/index.html

访问 nginx 进行验证

1
2
3
4
5
6
kubectl get svc nginx-service -n nginx-example
# 使用输出的NodePort端口访问,例如节点IP为192.168.1.100,端口为32000
curl http://<cluster-ip>:80

root@lixd-dev-2:~# curl 10.111.66.141
<h1>Hello from Velero Backup Demo!</h1><p>This file was created on Tue Jan 27 07:49:17 UTC 2026.</p>

Velero 备份

1
2
3
root@lixd-dev-1:~/velero# velero backup create nginx-backup-pvc --include-namespaces nginx-example --default-volumes-to-fs-backup
Backup request "nginx-backup-pvc" submitted successfully.
Run `velero backup describe nginx-backup-pvc` or `velero backup logs nginx-backup-pvc` for more details.

参数:

  • --include-namespaces:指定命名空间,这里测试仅备份 nginx-example 下的内容。
  • --default-volumes-to-fs-backup:使用文件系统备份工具(Kopia)来备份数据。这种方式更通用。

查看状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
root@lixd-dev-1:~/velero# velero backup get
NAME STATUS ERRORS WARNINGS CREATED EXPIRES STORAGE LOCATION SELECTOR
nginx-backup-pvc InProgress 0 0 2026-01-28 07:41:25 +0000 UTC 29d default <none>
root@lixd-dev-1:~/velero# velero backup get
NAME STATUS ERRORS WARNINGS CREATED EXPIRES STORAGE LOCATION SELECTOR
nginx-backup-pvc Completed 0 0 2026-01-28 07:41:25 +0000 UTC 29d default <none>

# 查看备份详细信息
$ velero backup describe nginx-backup
 
# 查看备份日志
$ velero backup logs nginx-backup

这样就备份好了。

6.4 集群 B 恢复

恢复

在目标集群查看备份对象(如果连接到同一个 S3 存储,Velero 会自动同步):

1
2
3
root@lixd-dev-2:~# velero get backup
NAME STATUS ERRORS WARNINGS CREATED EXPIRES STORAGE LOCATION SELECTOR
nginx-backup-pvc Completed 0 0 2026-01-28 07:41:25 +0000 UTC 29d default <none>

恢复数据

1
2
3
root@lixd-dev-2:~# velero restore create --from-backup nginx-backup-pvc
Restore request "nginx-backup-20260127075810" submitted successfully.
Run `velero restore describe nginx-backup-20260127075810` or `velero restore logs nginx-backup-20260127075810` for more details.

查看恢复进度

1
2
3
4
5
6
root@lixd-dev-2:~# velero restore get
NAME BACKUP STATUS STARTED COMPLETED ERRORS WARNINGS CREATED SELECTOR
nginx-backup-pvc-20260128080008 nginx-backup-pvc InProgress 2026-01-28 08:00:08 +0000 UTC <nil> 0 0 2026-01-28 08:00:08 +0000 UTC <none>
root@lixd-dev-2:~# velero restore get
NAME BACKUP STATUS STARTED COMPLETED ERRORS WARNINGS CREATED SELECTOR
nginx-backup-pvc-20260128080008 nginx-backup-pvc Completed 2026-01-28 08:00:08 +0000 UTC 2026-01-28 08:00:17 +0000 UTC 0 1 2026-01-28 08:00:08 +0000 UTC <none>

验证

验证 Nginx 是否完全恢复

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
root@lixd-dev-2:~/velero# kubectl -n nginx-example get po
NAME READY STATUS RESTARTS AGE
nginx-deployment-7cf7667b79-cmjfm 1/1 Running 0 41s
root@lixd-dev-2:~/velero# kubectl -n nginx-example get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service NodePort 10.103.125.238 <none> 80:32285/TCP 42s
root@lixd-dev-2:~/velero# kubectl -n nginx-example get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
nginx-html-pvc Bound pvc-99f0060d-fd8b-4887-a5e5-cdc6858d3590 1Gi RWO nfs <unset> 45s
root@lixd-dev-2:~# curl 10.111.49.149
<h1>Hello from Velero Backup Demo!</h1><p>This file was created on Wed Jan 28 07:48:15 UTC 2026.</p>

一切正常。

跨集群迁移完成。

小结

Velero 作为一个成熟的 Kubernetes 备份恢复工具,提供了完整的灾难恢复和集群迁移解决方案。通过本文的实战演示,您可以掌握:

  1. Velero 的基本安装和配置
  2. 单集群内应用的备份恢复操作
  3. 跨集群的应用和数据迁移流程
  4. 生产环境的最佳实践建议

KubeClipper 1.5.0 发布:全新工作负载界面与 Kubernetes 1.35 支持

2026-03-04 04:00:00

kubeclipper-release-1.5.0.jpeg

最近,KubeClipper 正式发布了 1.5.0 版本。这次更新带来了多项重要改进,其中最引人注目的是新增的工作负载管理界面,用户现在可以直接在 Web UI 中管理 Deployment、StatefulSet 等 Kubernetes 工作负载。同时,该版本还升级了对 Kubernetes 及其组件的支持,并修复了大量 bug,提升了平台的稳定性和用户体验。

KubeClipper 是一个轻量便捷的 Kubernetes 多集群全生命周期管理工具,旨在提供易使用、易运维、极轻量、生产级的 Kubernetes 多集群管理服务,让运维工程师从繁复的配置和晦涩的命令行中解放出来,实现一站式管理跨区域、跨基础设施的多 K8S 集群。

🚀 5分钟快速体验

如果你是第一次接触 KubeClipper,可以通过以下步骤快速上手:

  1. 一键安装工具curl -sfL https://oss.kubeclipper.io/get-kubeclipper.sh | KC_REGION=cn bash -
  2. 部署服务kcctl deploy
  3. 创建集群kcctl create cluster --name demo --master YOUR_IP --untaint-master
  4. 访问界面:浏览器访问 http://YOUR_IP:8080,账号 admin/Thinkbig1

全程只需5-10分钟,就能拥有一个功能完整的 Kubernetes 环境!

1. KubeClipper 1.5.0 新特性详解

相比于之前的版本,1.5.0 在易用性和功能性方面都有显著提升。最大的变化是新版增加了对 k8s 1.35.0 的支持,另外新增的工作负载管理功能让用户可以完全摆脱命令行操作。

KubeClipper 的核心优势:

  • 易使用:提供 Web 控制台,通过点点鼠标即可创建和管理 k8s 集群
  • 极轻量:架构简单,不依赖 Ansible,部署方便快捷
  • 生产级:支持在线、离线、代理方式部署,提供多版本 K8S、CRI、CNI 选择
  • 离线友好:提供离线部署方式,对国内用户极为友好

本次更新的主要亮点:

  • 🆕 工作负载 UI 管理:直接在 Web 界面管理 K8s 工作负载
  • 🔄 版本升级:Kubernetes 版本更新至 1.35
  • 🛠️ 技术改进:Go 版本升级、大量依赖库更新、CI/CD 优化
  • 🔧 稳定性提升:节点健康检查、系统服务管理、竞态条件修复
  • 🔒 安全性增强:镜像仓库认证、TLS 配置优化、权限管理改进
  • 🐛 Bug 修复:大量稳定性改进和问题修复

1.1 工作负载管理界面

这是 1.5.0 版本最重要的新功能。之前用户需要通过命令行或第三方工具管理 Kubernetes 工作负载,现在可以直接在 KubeClipper 的 Web UI 中进行可视化操作:

  • Workload 管理:创建、编辑、扩缩容、滚动更新
  • 实时状态查看:Pod 状态、事件日志查看
  • 一键操作:重启、回滚、删除等常用操作

1.2 组件版本升级

1.5.0版本全面升级了支持的组件版本:

  • Kubernetes: 支持最新 v1.35 版本
  • CNI 插件: Calico v1.29.6
  • 容器运行时: containerd 1.7.29
  • 开发工具: Go 语言版本从 1.19 升级到 1.23
  • 其他组件: 同步更新到最新兼容版本

1.3 技术改进和 Bug 修复

1.5.0版本包含了大量的技术改进和稳定性提升:

核心功能改进

  • Kubernetes: 支持最新 v1.35 版本,Calico、Containerd 同步更新
  • 容器运行时优化:修复 SystemdCgroup 配置,改进 containerd 兼容性
  • 证书管理:延长证书有效期至 10 年,避免重复处理证书到期问题

稳定性提升

  • 节点健康检查:使用 kube clientset 检查节点和集群健康状况
  • 错误处理优化:改进命令执行失败时的标准错误输出
  • 系统服务管理:新增 systemctl 工具类,应用于容器运行时和 kubelet
  • 竞态条件修复:解决集群部署和节点添加过程中的并发问题

安全性增强

  • 镜像仓库认证:支持 CRI registry 认证功能
  • TLS 配置:改进证书生成和验证机制
  • 权限管理:优化 kubeconfig 文件生成逻辑

开发工具链升级

  • Go 版本升级:从 1.19 升级到 1.23
  • 依赖库更新:大量第三方库版本升级,包括安全补丁
  • CI/CD 优化:新增代码格式化检查和构建检查

2. 安装部署 KubeClipper 1.5.0

2.1 环境要求

在开始安装之前,确保满足以下要求:

  • 操作系统: CentOS 7+/Ubuntu 18.04+
  • 内核版本: 4.4+
  • 内存: 至少4GB RAM
  • 存储: 至少 20GB 可用空间
  • 网络: 节点间网络互通

2.2 安装 kcctl 工具

1
2
3
4
5
# 下载最新版本的 kcctl
curl -sfL https://oss.kubeclipper.io/get-kubeclipper.sh | KC_REGION=cn bash -

# 验证安装
kcctl version

正常输出如下:

1
2
root@lixd-dev-1:~# kcctl version
kcctl version: version.Info{Major:"1", Minor:"5", GitVersion:"v1.5.0", GitCommit:"0930af19ab9bddd4883f7a46ac91e67335fc1978", GitTreeState:"clean", BuildDate:"2026-02-26T05:25:31Z", GoVersion:"go1.23.0", Compiler:"gc", Platform:"linux/amd64"}

2.3 部署 KubeClipper

1
2
3
4
5
# 对于 aio 环境使用 kcctl deploy 即可完成部署
kcctl deploy

# 更多参数参考 kcctl deploy -h
# kcctl deploy --server $IPADDR_SERVER --agent $IPADDR_AGENT --pk-file /root/.ssh/id_rsa --pkg $PKG --ip-detect=interface=ens3 --v 5 

在打印出如下的 KubeClipper banner 后即表示安装完成。

1
2
3
4
5
6
7
8
 _ __ _ _____ _ _
| | / / | | / __ \ (_)
| |/ / _ _| |__ ___| / \/ |_ _ __ _ __ ___ _ __
| \| | | | '_ \ / _ \ | | | | '_ \| '_ \ / _ \ '__|
| |\ \ |_| | |_) | __/ \__/\ | | |_) | |_) | __/ |
\_| \_/\__,_|_.__/ \___|\____/_|_| .__/| .__/ \___|_|
 | | | |
 |_| |_|

安装过程中需要去阿里云下载离线安装包,大概 1 分钟即可下载完成。

2.4 访问 Web UI

安装完成后,打开浏览器,访问 http://$IP 即可进入 KubeClipper 控制台。

kc-console-login.jpg

您可以使用默认帐号密码 admin / Thinkbig1 进行登录。

3. 快速上手体验

3.1 创建 K8s 集群

部署成功后可以使用 kcctl 工具或者通过控制台创建 k8s 集群, 这里咱们使用 kcctl 工具进行创建。

查看当前 agent 节点

1
2
3
4
5
6
root@lixd-dev-1:~# kcctl get node
+--------------------------------------+------------------------+---------+----------------+-------------+-----+--------+
| ID | HOSTNAME | REGION | IP | OS/ARCH | CPU | MEM |
+--------------------------------------+------------------------+---------+----------------+-------------+-----+--------+
| bf989745-73c7-4582-ab3a-f8f6c96b9ae7 | lixd-dev-1.taiko.local | default | 172.16.131.134 | linux/amd64 | 4 | 7937Mi |
+--------------------------------------+------------------------+---------+----------------+-------------+-----+--------+

然后使用以下命令创建 k8s 集群:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 其中 IP 为上一步中的 server1 节点 IP
root@lixd-dev-1:~# kcctl create cluster --name demo --master 172.16.131.134 --untaint-master
[2026-03-02T06:33:45Z][INFO] use default containerd version 1.7.29
[2026-03-02T06:33:45Z][INFO] use default calico version v3.29.6
[2026-03-02T06:33:45Z][INFO] use default k8s version v1.35.0
+------+---------+--------------+--------------+------------+----------------------------+-------------------------------+
| NAME | REGION | MASTER COUNT | WORKER COUNT | STATUS | APISERVER CERTS EXPIRATION | CREATE TIMESTAMP |
+------+---------+--------------+--------------+------------+----------------------------+-------------------------------+
| demo | default | 1 | 0 | Installing | | 2026-03-02 06:33:45 +0000 UTC |
+------+---------+--------------+--------------+------------+----------------------------+-------------------------------+

大概两分钟左右即可完成集群创建,可以使用以下命令查看实时日志:

 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
root@lixd-dev-1:~# kcctl get operation --selector kubeclipper.io/cluster=demo
+--------------------------------------+---------+---------------+------------+---------------------+-------+
| ID | CLUSTER | NAME | STATUS | CREATED AT | STEPS |
+--------------------------------------+---------+---------------+------------+---------------------+-------+
| 118e57ec-c467-4e11-8436-2813ec9e3b51 | demo | CreateCluster | successful | 2026-03-02 06:33:45 | 14 |
+--------------------------------------+---------+---------------+------------+---------------------+-------+

root@lixd-dev-1:~# kcctl logs 118e57ec-c467-4e11-8436-2813ec9e3b51 --summary --follow

===== Step: [2026-03-02 06:33:45] installRuntime (fd8ff5bb-dae1-481f-93a4-bfaeea44b402) [successful]
 Node: lixd-dev-1.taiko.local (172.16.131.134) bf989745-73c7-4582-ab3a-f8f6c96b9ae7 [successful] [4s]

===== Step: [2026-03-02 06:33:49] nodeEnvSetup (355a75de-6e39-44f0-b33b-05067c42e479) [successful]
 Node: lixd-dev-1.taiko.local (172.16.131.134) bf989745-73c7-4582-ab3a-f8f6c96b9ae7 [successful] [1s]

===== Step: [2026-03-02 06:33:49] installExtension (33bba175-5589-4ef6-889a-b99df53c4a6b) [successful]
 Node: lixd-dev-1.taiko.local (172.16.131.134) bf989745-73c7-4582-ab3a-f8f6c96b9ae7 [successful] [34s]

===== Step: [2026-03-02 06:34:23] installPackages (444027ce-66b3-4df6-8435-816bb8985bc7) [successful]
 Node: lixd-dev-1.taiko.local (172.16.131.134) bf989745-73c7-4582-ab3a-f8f6c96b9ae7 [successful] [41s]

===== Step: [2026-03-02 06:35:04] renderKubeadmConfig (d59b27e1-fdd6-4e02-8c63-4e21ee2c2d3d) [successful]
 Node: lixd-dev-1.taiko.local (172.16.131.134) bf989745-73c7-4582-ab3a-f8f6c96b9ae7 [successful] [1s]

===== Step: [2026-03-02 06:35:04] initControlPlane (d7b8db58-9169-420f-b1da-7a4cb80c1dc9) [successful]
 Node: lixd-dev-1.taiko.local (172.16.131.134) bf989745-73c7-4582-ab3a-f8f6c96b9ae7 [successful] [14s]

===== Step: [2026-03-02 06:35:18] cniImageLoader (0b3d3c34-80df-4b36-ac2c-6f007d11d681) [successful]
 Node: lixd-dev-1.taiko.local (172.16.131.134) bf989745-73c7-4582-ab3a-f8f6c96b9ae7 [successful] [44s]

....

或者使用以下命令查看集群状态

1
kcctl get cluster -o yaml|grep status -A5

也可以进入控制台查看实时日志

进入 Running 状态即表示集群安装完成,您可以使用 kubectl 命令来查看集群健康状况。

1
2
3
4
5
6
7
8
root@lixd-dev-1:~# kubectl get cs
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd-0 Healthy ok
root@lixd-dev-1:~# kubectl get node
NAME STATUS ROLES AGE VERSION
lixd-dev-1.taiko.local Ready control-plane 2m55s v1.35.0

至此,我们的单节点版本就算是结束了,通过几条命令就快速创建出来一个 K8s 集群。

3.2 工作负载体验

进入工作负载界面

登录 Web UI 后,在集群管理界面,点击展开集群后的更多按钮,然后点击 工作负载 按钮跳转到工作负载界面: kc-console-workload1.png

工作负载界面展示

集群节点信息 kc-console-workload2.png

Deployment 管理: kc-console-workload3.png

更多功能就留给大家自行探索了~

4. 小结

KubeClipper 1.5.0 版本的发布标志着该项目在易用性、功能性和技术成熟度方面都达到了新的高度。作为一次重要的里程碑更新,1.5.0 不仅在用户体验上实现了质的飞跃,更在技术架构上为未来的发展奠定了坚实基础。

技术深度升级:

  • 现代化技术栈: Go 1.23 升级,依赖库全面更新,确保长期维护性
  • 广泛版本支持: 支持最新的 Kubernetes v1.35,满足不同环境需求
  • 生产级稳定性: 完善的健康检查、错误处理和系统服务管理
  • 安全性增强: 镜像仓库认证、TLS 优化等多层次安全防护

用户体验提升:

  • 简化操作: 工作负载 UI 管理,图形化界面降低学习成本
  • 全面兼容: 支持最新的 Kubernetes 生态系统和组件
  • 稳定可靠: 经过充分测试的生产级稳定性
  • 开源免费: 完全开源,社区活跃

对于正在寻找简单易用的 Kubernetes 管理平台的团队来说,KubeClipper 1.5.0 无疑是一个值得尝试的选择。无论是初学者还是经验丰富的运维人员,都能从这个版本中找到适合自己的价值点。

相关链接:

Kubernetes PVC Clone &amp; Snapshot 实战:基于 csi-driver-nfs 的完整示例

2026-02-10 08:00:00

k8s-pvc-clone-snapshot.jpeg 在 Kubernetes 里做“数据复制”通常有两条路:

  • PVC Clone:从已有 PVC 快速克隆出一个新 PVC(开发/测试回放、批量创建环境很常用)
  • VolumeSnapshot:先对 PVC 打快照,再从快照创建新 PVC(更贴近生产最佳实践,避免“边写边克隆”的不确定性)

本文以 csi-driver-nfs 为例,从 0 跑通 Clone 与 Snapshot,并给出跨命名空间场景需要的关键配置与排错点。

版本与前置条件

本文默认你已经有一个可用的 StorageClass(下文以 nfs 为例),并已安装 csi-driver-nfs

版本要求(供参考):

示例环境:Kubernetes v1.34.2,csi-driver-nfs v4.12.1,满足上述要求。

1. PVC Clone 是什么?

PVC Clone:创建 PVC 时通过 dataSourceRef 引用一个已存在的 PVC(或 VolumeSnapshot),由 CSI 侧完成数据复制,得到一个内容相同的新 PVC。

注意:必须是 CSI 驱动支持 Clone,否则新 PVC 可能会被创建出来但数据为空/不符合预期。

2. 准备源 PVC

1
2
kubectl create ns source
kubectl create ns storage-source

创建源 PVC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
cat << 'EOF' > source-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: source-test-pvc
 namespace: source
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 10Gi
 storageClassName: nfs  # 使用您的StorageClass
EOF
1
2
kubectl apply -f source-pvc.yaml
kubectl -n source get pvc source-test-pvc

创建写入数据的 Pod

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# source-data-pod.yaml
cat << 'EOF' > source-data-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: source-data-pod
 namespace: source
spec:
 containers:
 - name: busybox
 image: registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0
 command: ["sleep", "3600"]
 volumeMounts:
 - name: data
 mountPath: /data
 volumes:
 - name: data
 persistentVolumeClaim:
 claimName: source-test-pvc
EOF
1
2
kubectl apply -f source-data-pod.yaml
kubectl -n source wait --for=condition=Ready pod/source-data-pod --timeout=300s

写入文件到源 PVC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 进入Pod
kubectl -n source exec -it source-data-pod -- sh

# 在Pod内部执行
echo "This is a test file created at $(date)" > /data/test-file.txt
echo "Additional test data" >> /data/test-file.txt
mkdir /data/test-directory
echo "File in subdirectory" > /data/test-directory/sub-file.txt
ls -la /data/
cat /data/test-file.txt

# 退出Pod
exit

验证数据写入成功

1
kubectl -n source exec source-data-pod -- cat /data/test-file.txt

3. PVC Clone 实战(同命名空间)

创建克隆 PVC

核心就是在新 PVC 的 dataSourceRef 里引用源 PVC:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# pvc-clone-test.yaml
cat << 'EOF' > pv-clone-test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: test-clone-pvc
 namespace: source
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 10Gi  # 与源PVC相同的大小
 storageClassName: nfs  # 与源PVC相同的StorageClass
 dataSourceRef:
 kind: PersistentVolumeClaim
 name: source-test-pvc
EOF
1
2
kubectl apply -f pv-clone-test.yaml
kubectl -n source get pvc test-clone-pvc
1
2
3
4
5
# 监控PVC状态变化
kubectl -n source describe pvc test-clone-pvc 

# 查看相关事件
kubectl -n source get events --field-selector involvedObject.name=test-clone-pvc

创建验证 Pod 挂载克隆 PVC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# clone-verification-pod.yaml
cat << 'EOF' > clone-verification-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: clone-verification-pod
 namespace: source
spec:
 containers:
 - name: busybox
 image: registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0
 command: ["sleep", "3600"]
 volumeMounts:
 - name: data
 mountPath: /data
 volumes:
 - name: data
 persistentVolumeClaim:
 claimName: test-clone-pvc
EOF
1
2
kubectl apply -f clone-verification-pod.yaml
kubectl -n source wait --for=condition=Ready pod/clone-verification-pod --timeout=300s

检查克隆数据

1
2
3
4
5
# 检查文件结构
kubectl exec clone-verification-pod -n source -- ls -la /data/

# 验证文件内容
kubectl exec clone-verification-pod -n source -- cat /data/test-file.txt

4. 跨命名空间 Clone(进阶)

跨 namespace clone 需要额外配置:

  • kube-apiserver & kube-controller-manager 开启 CrossNamespaceVolumeDataSource 特性

  • csi provisioner 开启 CrossNamespaceVolumeDataSource 特性

  • 安装 Gateway API CRD

  • 创建 ReferenceGrant 配置权限

如果你只需要“简单跑通”,可以先跳过本节,直接看后面的 Snapshot 实战;跨命名空间的关键点是:apiserver/controller-manager + csi-provisioner 开特性,以及 ReferenceGrant 授权

K8s 开启 FeatureGate

kube-apiserver & kube-controller-manager 需要开启以下特性:

  • AnyVolumeDataSource: 1.24及其以上版本,默认开启,之前的版本需要手动配置

  • CrossNamespaceVolumeDataSource:所有版本都默认为 false,需要手动开启

多个 master 节点都需要配置。

直接编辑 manifest 下的 yaml 即可:

1
2
vi /etc/kubernetes/manifests/kube-apiserver.yaml
vi /etc/kubernetes/manifests/kube-controller-manager.yaml

都添加下面这个配置:

1
--feature-gates=CrossNamespaceVolumeDataSource=true

保存退出后,kubelet 会自动重启 Pod。

验证

1
2
ps -ef|grep kube-apiserver|grep CrossNamespaceVolumeDataSource
ps -ef|grep kube-controller-manager|grep CrossNamespaceVolumeDataSource

(可选)此时可以检查 apiserver / controller-manager 是否已带上对应 feature gate 参数。

CSI Provisioner 开启 FeatureGate

同时还需要为 CSI Provisioner 开启 CrossNamespaceVolumeDataSource 特性。

ps:不同的 CSI 安装方式不同,以 csi-driver-nfs 为例。

1
kubectl -n kube-system edit deploy csi-nfs-controller

里面有多个 container,搜索 csi-provisioner 找到对应的 container。在 command 中增加配置,开启特性:

1
--feature-gates=CrossNamespaceVolumeDataSource=true

就像这样:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
 spec:
 containers:
 - args:
 - -v=2
 - --csi-address=$(ADDRESS)
 - --leader-election
 - --leader-election-namespace=kube-system
 - --extra-create-metadata=true
 - --feature-gates=HonorPVReclaimPolicy=true
 - --feature-gates=CrossNamespaceVolumeDataSource=true
 - --timeout=1200s
 - --retry-interval-max=30m
 env:
 - name: ADDRESS
 value: /csi/csi.sock
 image: 172.16.131.171:5000/sig-storage/csi-provisioner:v5.3.0
 imagePullPolicy: IfNotPresent
 name: csi-provisioner

安装 CRD

安装 Gateway API CRD,核心是 referencegrants CRD。

1
2
# 安装 Gateway API v1.0.0(稳定版,包含 ReferenceGrant CRD)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml

然后创建 ClusterRoleBinding 配置权限,让 csi 能够访问 referencegrants 对象。

 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
cat << 'EOF' > rbac.yaml
# 创建 ClusterRole(包含 referencegrants 的权限)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
 name: csi-nfs-referencegrant-role
rules:
- apiGroups: ["gateway.networking.k8s.io"]
 resources: ["referencegrants"]
 verbs: ["get", "list", "watch"] # 对应错误中的 list 权限,补充 get/watch 避免后续报错

---
# 绑定 ClusterRole 到 csi-nfs-controller-sa
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
 name: csi-nfs-referencegrant-binding
subjects:
- kind: ServiceAccount
 name: csi-nfs-controller-sa # 出错的服务账号名称
 namespace: kube-system # 服务账号所在命名空间
roleRef:
 kind: ClusterRole
 name: csi-nfs-referencegrant-role
 apiGroup: rbac.authorization.k8s.io
EOF
1
kubectl apply -f rbac.yaml

创建 ReferenceGrant 配置权限

由于需要在新 namespace 创建 pvc 引用源 namespace 下的 pvc,跨 namespace 访问 pvc 需要授权。

在源 namespace 创建 进行授权

可以授权访问所有 pvc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: allow-cross-namespace-pvc-clone
 namespace: source  # 重要:必须在源 namespace 中
spec:
 from:
 - group: ""
 kind: PersistentVolumeClaim
 namespace: storage-source  # 目标 namespace
 to:
 - group: ""
 kind: PersistentVolumeClaim

storage-source ns 可以访问 source ns 下的所有 pvc

或者指定 name 仅授权访问某一个 pvc:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: allow-pvc-cloning
 namespace: source  # 必须在源 PVC 所在的命名空间
spec:
 from:
 - group: ""
 kind: PersistentVolumeClaim
 namespace: storage-source # 允许哪个命名空间来访问
 to:
 - group: ""
 kind: PersistentVolumeClaim
 name: source-test-pvc  # 允许访问哪个具体的 PVC(也可以不写 name 以允许全部)

storage-source ns 可以访问 source ns 下的名为 source-test-pvc 的 pvc

如果没有权限,新 pvc 会一直 pending:

1
2
3
4
kubectl -n storage-source describe pvc test-clone-pvc

# 典型报错(未授权):
# failed to provision volume with StorageClass "nfs": accessing source/source-test-pvc of PersistentVolumeClaim dataSource from storage-source/test-clone-pvc isn't allowed

创建克隆 PVC(跨命名空间)

在另一个 namespace 下创建 pvc,引用 source namespace 下的源 pvc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# ex-pv-clone-test.yaml
cat << 'EOF' > ex-pv-clone-test.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: test-clone-pvc
 namespace: storage-source
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 10Gi # 与源PVC相同的大小
 storageClassName: nfs # 与源PVC相同的StorageClass
 dataSourceRef:
 kind: PersistentVolumeClaim
 name: source-test-pvc
 namespace: source
EOF
1
2
kubectl apply -f ex-pv-clone-test.yaml
kubectl -n storage-source get pvc

创建验证 Pod 挂载克隆 PVC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# clone-verification-pod.yaml
cat << 'EOF' > ex-clone-verification-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: clone-verification-pod
 namespace: storage-source
spec:
 containers:
 - name: busybox
 image: registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0
 command: ["sleep", "3600"]
 volumeMounts:
 - name: data
 mountPath: /data
 volumes:
 - name: data
 persistentVolumeClaim:
 claimName: test-clone-pvc
EOF
1
kubectl apply -f ex-clone-verification-pod.yaml

检查克隆数据

1
kubectl -n storage-source exec clone-verification-pod -- cat /data/test-file.txt

5. 推荐流程:Snapshot -> PVC

直接 PVC Clone 在生产里不一定推荐,因为源 PVC 可能正在写入,Clone 的一致性/时点语义取决于底层实现与业务写入方式。

最佳实践是: 源 pvc -> snapshot -> 新 pvc。

[可选]部署 kubernetes-csi snapshotter

开源组件,用于提供快照功能,如果部署 csi 时部署过则跳过。

拉取代码

1
git clone https://github.com/kubernetes-csi/external-snapshotter 

部署

1
2
3
kubectl -n kube-system kustomize external-snapshotter/deploy/kubernetes/snapshot-controller | kubectl create -f -

kubectl kustomize external-snapshotter/client/config/crd | kubectl create -f -

创建 snapshot class

创建针对 nfs-csi 的快照类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# nfs-snapshot-class.yaml
cat << 'EOF' > nfs-snapshot-class.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
 name: nfs-snapshot-class
 annotations:
 snapshot.storage.kubernetes.io/is-default-class: "true"
driver: nfs.csi.k8s.io
deletionPolicy: Delete
EOF
1
2
3
kubectl apply -f nfs-snapshot-class.yaml

kubectl get VolumeSnapshotClass

创建快照

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# source-snapshot.yaml
cat << 'EOF' > source-snapshot.yaml
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
 name: source-test-snapshot
 namespace: source  # 快照与源PVC在同一命名空间
spec:
 volumeSnapshotClassName: nfs-snapshot-class
 source:
 persistentVolumeClaimName: source-test-pvc  # 源PVC名称
EOF
1
2
3
kubectl apply -f source-snapshot.yaml

kubectl -n source get volumesnapshot

等待快照 Ready:

1
2
3
4
5
# 等待快照就绪(重要!)
kubectl -n source wait volumesnapshot/source-test-snapshot --for=jsonpath='{.status.readyToUse}'=true --timeout=300s

# 确认快照状态
kubectl -n source get volumesnapshot source-test-snapshot -o yaml

(可选)你也可以到后端存储侧检查快照是否生成,以及从快照恢复出来的数据目录是否符合预期。

创建 Grant 配置权限

跨 namespace 使用 snapshot 同样需要创建 Grant 授权

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# cross-ns-snapshot-grant.yaml
cat << 'EOF' > cross-ns-snapshot-grant.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
 name: allow-all-cross-namespace-snapshots
 namespace: source
spec:
 from:
 - group: ""
 kind: PersistentVolumeClaim
 namespace: storage-source
 to:
 - group: snapshot.storage.k8s.io
 kind: VolumeSnapshot
EOF
1
kubectl apply -f cross-ns-snapshot-grant.yaml

从快照创建 pvc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# pvc-from-snapshot.yaml
cat << 'EOF' > pvc-from-snapshot.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
 name: test-pvc-from-snapshot
 namespace: storage-source  # 目标命名空间
spec:
 accessModes:
 - ReadWriteOnce
 resources:
 requests:
 storage: 10Gi
 storageClassName: nfs
 dataSourceRef:
 apiGroup: snapshot.storage.k8s.io
 kind: VolumeSnapshot
 name: source-test-snapshot
 namespace: source  # 快照所在的命名空间
EOF
1
2
3
kubectl apply -f pvc-from-snapshot.yaml

kubectl -n storage-source get pvc test-pvc-from-snapshot

创建验证 Pod 挂载新 PVC

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# snapshot-verification-pod.yaml
cat << 'EOF' > snapshot-verification-pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: snapshot-verification-pod
 namespace: storage-source
spec:
 containers:
 - name: busybox
 image: registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0
 command: ["sleep", "3600"]
 volumeMounts:
 - name: data
 mountPath: /data
 volumes:
 - name: data
 persistentVolumeClaim:
 claimName: test-pvc-from-snapshot
EOF
1
kubectl apply -f snapshot-verification-pod.yaml

检查数据

1
2
3
4
5
# 检查文件结构
kubectl -n storage-source exec snapshot-verification-pod -- ls -la /data/

# 验证文件内容
kubectl -n storage-source exec snapshot-verification-pod -- cat /data/test-file.txt

6. 参考

Kubernetes v1.26: Alpha support for cross-namespace storage data sources

CSI Volume Cloning

Volume Snapshots

Kubernetes教程(五十)---K8s 1.35:In-Place Pod Resize 正式 GA,实现 Pod 资源零中断调整

2026-01-20 08:00:00

in-place-pod-resize-ga.jpeg 想象一下这样的场景:你的生产系统突然流量激增,某个 Pod 的 CPU 使用率已经飙升到 90%,传统做法是重建整个 Pod,导致服务中断 30 秒以上。而现在,只需一行命令,CPU 资源瞬间调整完毕,服务零中断!

这就是 Kubernetes 1.35 带来的重磅功能:原地 Pod 资源调整(In-Place Pod Resize)正式 GA!🎉

1. 什么是 In-Place Pod Resize?

原地 Pod 资源调整(In-Place Pod Resize) 是 Kubernetes 的一项革命性特性,允许直接修改正在运行的 Pod 的 CPU 和内存资源配置,而无需重建 Pod。

1.1 核心意义

In-Place Pod Resize 是一个基础构建块,可解锁无缝、垂直的自动缩放功能,并提高工作负载效率。

🎯 核心优势

  • ⚡ 零中断调整:CPU 调整无需重启,内存调整仅在必要时重启

    • 对延迟或重启敏感的工作负载可以在不停机或状态丢失的情况下就地修改其资源
  • ⚙️ 即时生效:调整立即见效,无需等待 Pod 重建

  • 🛡️ 高稳定性:告别重建导致的网络抖动和服务中断

  • 🎛️ 简化运维:资源调优从"重型手术"变为"日常微调"

🚀 解锁新能力

更强大的自动缩放功能:自动缩放器现在可以调整资源,影响更小。例如,垂直Pod自动缩放器(VPA)的 InPlaceOrRecreate 更新模式利用了这一特性,现已升级到 beta 版。这使得资源可以根据使用情况自动无缝地调整,并将中断降至最低。

解决临时资源需求:可以快速调整临时需要更多资源的工作负载。这将启用 CPU 启动加速(AEP-7862)等功能,应用程序可以在启动期间请求更多 CPU,然后自动缩减。

1.2 发展历程

这个功能可不是一蹴而就的!从 Kubernetes v1.27 开始作为 Alpha 版本引入,经过社区反复打磨:

  • v1.27: 🧪 Alpha 版本,基础功能验证
  • v1.33: 🏗️ Beta 版本,稳定性大幅提升
  • v1.35: 🎉 正式 GA,生产环境完全就绪!

经过多次迭代,现在你可以放心大胆地在生产环境中使用这个强大的功能了!

2. ⚠️ 重要限制和注意事项

2.1 💚 支持的资源类型

资源类型 支持程度 重启要求 说明
CPU ✅ 完全支持 🚫 无需重启 最常用,零中断调整
内存 ✅ 支持 ⚠️ 通常需要重启 大多数应用不支持热更新内存
存储 ❌ 不支持 - 技术限制,未来可能支持
GPU 等扩展资源 ❌ 不支持 - 仍在开发中

2.2 🏷️ QoS 类限制

Pod 的 QoS(服务质量)类调整有严格要求,调整后必须保持原有 QoS 类不变:

  • 🛡️ Guaranteedrequests = limits,最高优先级
  • ⚖️ Burstablerequests < limits,中等优先级
  • 🎲 BestEffort:无资源限制,最低优先级

⚠️ 重要提醒:无法通过资源调整改变 Pod 的 QoS 类!

2.3 🐳 容器类型支持情况

容器类型 支持程度 说明
普通容器 ✅ 完全支持 最常见的应用场景
Sidecar 容器 ✅ 支持调整 适用于服务网格等场景
Init 容器 ⚠️ 条件支持 仅支持调整尚未执行的Init容器
Ephemeral 容器 ❌ 不支持 临时调试容器不支持

2.4 🖥️ 节点和运行时限制

环境/配置 支持程度 影响范围
Windows Pod ❌ 不支持 整个 Windows 环境
静态 CPU 管理器 ❌ 不支持 CPU 亲和性场景
内存管理器 ❌ 部分不支持 特定内存策略
Swap 内存 ⚠️ 条件支持 仅特定配置下可用

2.5 🛡️ 安全和稳定性保证

🚨 重要风险提醒

  • 💥 内存减少风险:降低内存限制时,如果当前使用量超过新限制,可能会触发 OOM!
  • ✅ 调整验证:Kubelet 会严格验证调整请求的可行性,确保安全
  • 🔄 回滚保护:调整失败时自动保持原有配置,不会破坏运行状态

💡 最佳实践:调整内存前务必检查当前使用量,避免意外 OOM

3. 🔧 核心修改,一探究竟

3.1 📊 资源字段重构

In-Place Pod Resize 对 Kubernetes API 做了精妙的设计:

🎯 资源分为两层

  • 期望资源spec.containers[*].resources - 你想要的资源配置
  • 实际资源status.containerStatuses[*].resources - 容器真正获得的资源

✨ 关键变化spec.containers[*].resources 现在支持运行时修改!requestslimits 都可以动态调整。

3.2 📋 新增 Pod 状态

为了让调整过程透明可观测,Kubernetes 引入了三个新的 Pod 状态:

状态 含义 常见原因
PodResizePending ⏳ 调整请求待处理 资源不足、节点压力大等
PodResizeInProgress 🔄 正在执行调整 Kubelet 正在处理调整请求
PodResizeStatusInfeasible ❌ 无法调整(不符合策略) 违反QoS类限制、容器类型不支持等

通过 kubectl get podkubectl describe pod,你可以实时监控调整进度!

3.3 ⚙️ resizePolicy 配置

新增 resizePolicy 字段,让你精确控制每种资源的调整行为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: Pod
spec:
 containers:
 - name: app
 image: nginx
 resizePolicy: # 🎛️ 新增配置
 - resourceName: cpu  # 支持 cpu、memory
 restartPolicy: NotRequired  # CPU:无需重启(默认)
 - resourceName: memory
 restartPolicy: RestartContainer # 内存:需要重启容器
 resources:
 requests:
 cpu: 100m
 memory: 128Mi
 limits:
 cpu: 100m
 memory: 128Mi

🔧 restartPolicy 选项

  • NotRequired:调整无需重启(CPU 的最佳选择)
  • RestartContainer:调整需要重启容器(内存的默认选择)

💡 高级用法:如果你的应用支持热更新内存,可以将内存的 restartPolicy 设为 NotRequired,实现真正的零中断调整!

4. 🎮 实战演示,亲手试试!

4.1 🏗️ 创建测试 Pod

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cat << 'EOF' > pod-resize.yaml
apiVersion: v1
kind: Pod
metadata:
 name: resize-demo
spec:
 containers:
 - name: pause
 image: registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0
 command: ["sleep", "3600"]
 resizePolicy:
 - resourceName: cpu
 restartPolicy: NotRequired # Default, but explicit here
 - resourceName: memory
 restartPolicy: RestartContainer
 resources:
 limits:
 memory: "200Mi"
 cpu: "700m"
 requests:
 memory: "200Mi"
 cpu: "700m"
EOF

✨ 注意这里的 resizePolicy 配置:

  • resourceName:指定要控制的资源类型(cpumemory
  • restartPolicy:决定调整时是否需要重启容器

🎯 配置解读

  • CPU 设置为 NotRequired:调整时零中断
  • 内存设置为 RestartContainer:调整时会重启容器(这是默认行为)

💡 为什么内存需要重启? 大多数应用程序和容器运行时不支持动态调整内存分配,所以需要重启来生效。

1
kubectl apply -f pod-resize.yaml

4.2 ⚡ CPU 调整:真正的零中断体验!

1
2
kubectl patch pod resize-demo --subresource resize --patch \
 '{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"800m"}, "limits":{"cpu":"800m"}}}]}}'

🎯 操作目标:将 CPU 从 700m 提升到 800m,看看会发生什么?

验证:查看 Pod 状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
root@lixd-dev-2:~/k8s2# k get po resize-demo -oyaml|grep "resources:" -A 6
 resources:
 limits:
 cpu: 800m
 memory: 200Mi
 requests:
 cpu: 800m
 memory: 200Mi
--
 resources:
 limits:
 cpu: 800m
 memory: 200Mi
 requests:
 cpu: 800m
 memory: 200Mi
root@lixd-dev-2:~/k8s2# k get po
NAME READY STATUS RESTARTS AGE
resize-demo 1/1 Running 0 2m43s

🎉 结果惊艳

  • CPU 成功更新到 800m ✅
  • restart count 依然是 0(没有重启!)✅
  • In-Place Resize 完美生效! 🚀

验证:查看 Pod Event

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
root@lixd-dev-2:~/k8s2# k describe po resize-demo

Events:
 Type Reason Age From Message
 ---- ------ ---- ---- -------
 Normal Scheduled 57s default-scheduler Successfully assigned default/resize-demo to lixd-dev-2.taiko.local
 Normal Pulled 56s kubelet spec.containers{pause}: Container image "registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0" already present on machine and can be accessed by the pod
 Normal Created 56s kubelet spec.containers{pause}: Container created
 Normal Started 56s kubelet spec.containers{pause}: Container started
 Normal ResizeStarted 20s kubelet Pod resize started: {"containers":[{"name":"pause","resources":{"limits":{"cpu":"800m","memory":"200Mi"},"requests":{"cpu":"800m","memory":"200Mi"}}}],"generation":2}
 Normal ResizeCompleted 19s kubelet Pod resize completed: {"containers":[{"name":"pause","resources":{"limits":{"cpu":"800m","memory":"200Mi"},"requests":{"cpu":"800m","memory":"200Mi"}}}],"generation":2}

可以看到 Event 里面可以看到 ResizeStarted 和 ResizeCompleted 事件。

4.3 🧠 内存调整:需要重启但依然高效!

1
2
kubectl patch pod resize-demo --subresource resize --patch \
 '{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"memory":"300Mi"}, "limits":{"memory":"300Mi"}}}]}}'

🎯 操作目标:将内存从 200Mi 提升到 300Mi,观察重启行为。

验证:查看 Pod 状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
root@lixd-dev-2:~/k8s2# k get po resize-demo -oyaml|grep "resources:" -A 6
 resources:
 limits:
 cpu: 700m
 memory: 300Mi
 requests:
 cpu: 700m
 memory: 300Mi
--
 resources:
 limits:
 cpu: 700m
 memory: 300Mi
 requests:
 cpu: 700m
 memory: 300Mi
root@lixd-dev-2:~/k8s2#
root@lixd-dev-2:~/k8s2# k get po
NAME READY STATUS RESTARTS AGE
resize-demo 1/1 Running 1 (83s ago) 118s

📊 结果分析

  • 内存成功更新到 300Mi ✅
  • Restart Count 变为 1(预期的重启)✅
  • 符合 RestartContainer 策略! 🎯

💡 进阶技巧:如果你的应用程序和运行时支持热更新内存,可以将 restartPolicy 设置为 NotRequired,实现内存调整的零中断!

验证:查看 Pod Event

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
root@lixd-dev-2:~/k8s2# k describe po resize-demo
Events:
 Type Reason Age From Message
 ---- ------ ---- ---- -------
 Normal Scheduled 2m53s default-scheduler Successfully assigned default/resize-demo to lixd-dev-2.taiko.local
 Normal ResizeStarted 2m30s kubelet Pod resize started: {"containers":[{"name":"pause","resources":{"limits":{"cpu":"700m","memory":"300Mi"},"requests":{"cpu":"700m","memory":"300Mi"}}}],"generation":2}
 Normal Killing 2m30s kubelet spec.containers{pause}: Container pause resize requires restart
 Normal Pulled 2m (x2 over 2m52s) kubelet spec.containers{pause}: Container image "registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0" already present on machine and can be accessed by the pod
 Normal Created 2m (x2 over 2m52s) kubelet spec.containers{pause}: Container created
 Normal Started 119s (x2 over 2m52s) kubelet spec.containers{pause}: Container started
 Normal ResizeCompleted 119s kubelet Pod resize completed: {"containers":[{"name":"pause","resources":{"limits":{"cpu":"700m","memory":"300Mi"},"requests":{"cpu":"700m","memory":"300Mi"}}}],"generation":2}

可以看到 ResizeStarted 之后有一个 Killing 事件,等重启后才 ResizeCompleted,这也符合内存调整的 RestartContainer 重启策略。

4.4 🚫 资源不足场景:调整请求被拒绝

1
2
kubectl patch pod resize-demo --subresource resize --patch \
 '{"spec":{"containers":[{"name":"pause", "resources":{"requests":{"cpu":"1000"}, "limits":{"cpu":"1000"}}}]}}'

🎯 操作目标:故意申请超出节点容量的 CPU(1000 core),看看系统如何处理?

验证:查看 Pod 状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
root@lixd-dev-2:~/k8s2# k get po resize-demo -oyaml|grep "resources:" -A 6
 resources:
 limits:
 cpu: 1k
 memory: 200Mi
 requests:
 cpu: 1k
 memory: 200Mi
--
 resources:
 limits:
 cpu: 700m
 memory: 200Mi
 requests:
 cpu: 700m
 memory: 200Mi

🔍 现象分析

  • 期望资源(spec)CPU 更新为 1000 core ✅
  • 实际资源(status)仍为 700m(未生效)❌
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
root@lixd-dev-2:~/k8s2# k get po resize-demo -oyaml|grep "conditions:" -A 10
 conditions:
 - lastProbeTime: "2026-01-20T14:03:38Z"
 lastTransitionTime: "2026-01-20T14:03:38Z"
 message: 'Node didn''t have enough capacity: cpu, requested: 1000000, capacity:
 16000'
 observedGeneration: 2
 reason: Infeasible
 status: "True"
 type: PodResizePending

显示 PodResizePending 状态,错误信息清晰明了:“Node didn’t have enough capacity”!

有一个 PodResizePending 状态,因为资源不足,所以一直 pending。

验证:查看 Pod Event

1
2
3
4
5
6
7
8
9
root@lixd-dev-2:~/k8s2# k describe po resize-demo
Events:
 Type Reason Age From Message
 ---- ------ ---- ---- -------
 Normal Scheduled 3m29s default-scheduler Successfully assigned default/resize-demo to lixd-dev-2.taiko.local
 Normal Pulled 3m28s kubelet spec.containers{pause}: Container image "registry.cn-shanghai.aliyuncs.com/kubeclipper/busybox:1.36.0" already present on machine and can be accessed by the pod
 Normal Created 3m28s kubelet spec.containers{pause}: Container created
 Normal Started 3m27s kubelet spec.containers{pause}: Container started
 Warning ResizeInfeasible 3m23s kubelet Pod resize Infeasible: {"containers":[{"name":"pause","resources":{"limits":{"cpu":"1k","memory":"200Mi"},"requests":{"cpu":"1k","memory":"200Mi"}}}],"generation":2,"error":"Node didn't have enough capacity: cpu, requested: 1000000, capacity: 16000"}

Event 里面也可以看到具体问题,ResizeInfeasible 事件。

5. 🎯 总结:资源管理的革命性突破

5.1 💎 功能价值,一目了然

In-Place Pod Resize GA 是 Kubernetes 资源管理领域的一次革命!

🚀 四大核心优势

  • ⚡ 零中断:CPU 调整无需重启,内存调整仅在必要时重启
  • ⚙️ 即时生效:调整立即见效,无需等待 Pod 重建
  • 🛡️ 高稳定性:告别重建导致的网络抖动和服务中断
  • 🎛️ 简化运维:资源调优从"重型手术"变为"日常微调"

🔄 技术革新

  • 破坏性重建非破坏性调整
  • 静态配置动态优化
  • 人工运维智能自动化

5.2 最佳实践建议

生产环境应用建议

  1. CPU 优先使用:优先使用 CPU 的原地调整能力,避免不必要的容器重启
  2. 内存调整策略:根据应用特性选择合适的 restartPolicy
    • 对延迟敏感的应用:评估是否支持热更新内存
    • 普通应用:使用默认的 RestartContainer 策略
  3. QoS 类保持:确保调整后 Pod 的 QoS 类不变,避免调度问题
  4. 资源监控:密切监控调整后的资源使用情况,及时发现和处理异常

配置建议

1
2
3
4
5
6
7
8
9
# 推荐的 resizePolicy 配置
spec:
 containers:
 - name: app
 resizePolicy:
 - resourceName: cpu
 restartPolicy: NotRequired  # CPU 调整无需重启
 - resourceName: memory
 restartPolicy: RestartContainer # 内存调整需要重启(除非应用支持热更新)

集成场景

  • VPA 集成:结合 Vertical Pod Autoscaler 实现智能资源调整
  • 自定义控制器:开发基于业务指标的自动扩缩容控制器
  • 多租户平台:为平台用户提供动态资源调整能力

In-Place Pod Resize 让 Kubernetes 的资源管理变得更加现代化和智能化。现在就开始体验这个强大的功能,让您的应用资源管理更上一层楼吧!

6. 参考

Kubernetes 1.35: In-Place Pod Resize Graduates to Stable

Configure Pod Container Resources In-Place

⚡ AI集群通信革命:GB200 MNNVL通过Kubernetes DRA实现跨节点800Gbps通信

2026-01-07 04:00:00

nvidia-dra-gpu.jpeg

NVIDIA GB200 NVL72 正在将 AI 基础设施推向新的极限,使大规模语言模型训练和低延迟推理工作负载成为可能。随着 Kubernetes 在部署和扩展这些工作负载中的核心作用日益增强,快速演进的 AI 工作负载、基础设施需求和新硬件架构为 Kubernetes 编排和资源管理带来了新的挑战。

在本文中,我们将深入探讨如何通过 Kubernetes DRA (Dynamic Resource Allocation) 和 NVIDIA DRA Driver 在 GB200 平台上启用 Multi-Node NVLink (MNNVL),实现跨节点的 GPU 到 GPU 高带宽通信。

核心概念

Kubernetes DRA (Dynamic Resource Allocation) 简介

DRA (Dynamic Resource Allocation,动态资源分配) 是 Kubernetes v1.30 引入的革命性功能,用于解决传统 Device Plugin 框架在处理复杂异构硬件时的局限性。

DRA 最初始的版本(KEP-3063),于 1.26 版本引入,因可用性问题,在 1.32 版本被撤回 当前的 DRA 是第二个版本(KEP-4381) 于 1.30 引入。

为什么需要 DRA?

传统 Device Plugin 将硬件资源抽象为简单整数计数器,无法表达:

  • 设备特定参数(如 GPU 显存、计算能力、拓扑连接)

    • 无论什么 GPU,都展示为 nvidia.com/gpu
  • 资源共享需求(设备无法在容器间共享)

  • 硬件拓扑关系(NVLink 连接、PCIe 亲和性)

DRA 核心设计

DRA 遵循 Kubernetes 声明式原则,将硬件资源特性作为一等公民纳入 API:

  • ResourceClass:定义硬件资源类型和特性(类似 StorageClass)
  • ResourceClaim:工作负载的声明式资源请求(类似 PVC)
  • ResourceClaimTemplate:创建多个相似 ResourceClaim 的模板
  • DRA Driver:由硬件厂商实现的资源分配逻辑

DRA 的优势

  • 声明式管理:通过标准 API 声明复杂资源需求
  • 参数化配置:支持设备特定参数和资源共享
  • 拓扑感知:原生支持硬件拓扑关系的调度决策
  • 跨节点分配:支持分布式资源分配场景

GB200 MNNVL (Multi-Node NVLink) 的价值

GB200 NVL72 通过引入 Multi-Node NVLink (MNNVL) 技术,将单机 GPU 性能限制扩展到整个机架层面,为分布式 AI 工作负载带来革命性改进。

传统的单节点 DGX 系统受限于单机物理限制,MNNVL 改变了这一局面:

  • 全 NVLink 带宽跨节点通信:通过 NVIDIA NVLink Switch 实现全 NVLink 带宽通信
  • 无缝扩展:将整个机架转换为统一的 GPU 架构
  • 性能倍增:实现超快分布式训练和推理

ComputeDomains:连接底层硬件与 Kubernetes

IMEX (Internode Memory Exchange)

NVIDIA Internode Memory Exchange Service (IMEX) 是 GPU 驱动层面的软件,允许 GPU 跨节点通信。IMEX 对每个单独的 GPU 内存导出/导入操作进行细粒度访问控制,并在称为 IMEX 域的节点组中运行。

ComputeDomains 核心概念

作为 NVIDIA GPUs 的 DRA 驱动程序的一部分提供的 ComputeDomains,将底层 GPU 构造(NVIDIA NVLink 和 NVIDIA IMEX)与现代 Kubernetes 原生调度概念(动态资源分配,简称 DRA)连接起来,为在现代 GPU 硬件上运行分布式多节点工作负载提供所需的基础支持。

如果没有 ComputeDomains,多节点 NVLink 设置将不得不手动定义并固定到位,这限制了 Kubernetes 旨在提供的灵活性,并以牺牲安全隔离、故障隔离和成本效率为代价。

ComputeDomains 通过以下方式工作:

  • 动态创建 IMEX 域:根据工作负载调度自动形成 IMEX 域
  • 安全隔离:每个工作负载获得专用的隔离通信环境
  • 自动清理:工作负载完成后自动释放资源
  • 拓扑感知:理解并优化 GPU 连接关系

通过 ComputeDomains,运行分布式训练或跨复杂 NVLink 连接 GPU 架构的推理变得像部署标准 Kubernetes 工作负载一样简单。

环境部署

版本要求

软件信息:

  • Kubernetes:1.32 及以上,推荐 1.34
  • Containerd:支持 DRA 的版本 1.7.x,推荐 1.7.29
  • GPU Operator:25.3.x 及以上,推荐 25.10.0
  • DRA Driver:推荐部署最新的 25.8.0
  • NVIDIA GPU Driver:需要 565 或更新版本
    • 如果使用 DRA Driver 25.8.0,则需要驱动版本 >= 570.158.1

硬件信息:

  • 系统:GB200 NVL72(一柜系统中的 2 节点子集)
  • 节点数量:2 个节点
  • 每个节点配置
    • GPU:4 个 GB200 GPU(本次测试共 8 个 GPU)
    • GPU 显存:每个 GPU 192GB(189471 MiB)
    • CPU:2 个 Grace CPU

GPU 基本信息:

 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
root@GB200-POD2-F06-Node05:~/lixd/nccl-demo# nvidia-smi
Wed Dec 10 10:24:44 2025
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05 Driver Version: 580.95.05 CUDA Version: 13.0 |
+-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GB200 On | 00000008:01:00.0 Off | 0 |
| N/A 37C P0 169W / 1200W | 0MiB / 189471MiB | 0% Default |
| | | Disabled |
+-----------------------------------------+------------------------+----------------------+
| 1 NVIDIA GB200 On | 00000009:01:00.0 Off | 0 |
| N/A 37C P0 157W / 1200W | 0MiB / 189471MiB | 0% Default |
| | | Disabled |
+-----------------------------------------+------------------------+----------------------+
| 2 NVIDIA GB200 On | 00000018:01:00.0 Off | 0 |
| N/A 38C P0 158W / 1200W | 0MiB / 189471MiB | 0% Default |
| | | Disabled |
+-----------------------------------------+------------------------+----------------------+
| 3 NVIDIA GB200 On | 00000019:01:00.0 Off | 0 |
| N/A 37C P0 166W / 1200W | 0MiB / 189471MiB | 0% Default |
| | | Disabled |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+

GPU 拓扑:

 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
root@GB200-POD2-F06-Node05:~/lixd/nccl-demo# nvidia-smi topo -m
	GPU0	GPU1	GPU2	GPU3	NIC0	NIC1	NIC2	NIC3	NIC4	NIC5	NIC6	NIC7	CPU Affinity	NUMA Affinity	GPU NUMA ID
GPU0	 X 	NV18	NV18	NV18	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	0-71	0,2-17		N/A
GPU1	NV18	 X 	NV18	NV18	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	0-71	0,2-17		N/A
GPU2	NV18	NV18	 X 	NV18	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	72-143	1,18-33		N/A
GPU3	NV18	NV18	NV18	 X 	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	72-143	1,18-33		N/A
NIC0	SYS	SYS	SYS	SYS	 X 	SYS	SYS	SYS	SYS	SYS	SYS	SYS
NIC1	SYS	SYS	SYS	SYS	SYS	 X 	SYS	SYS	SYS	SYS	SYS	SYS
NIC2	SYS	SYS	SYS	SYS	SYS	SYS	 X 	PIX	SYS	SYS	SYS	SYS
NIC3	SYS	SYS	SYS	SYS	SYS	SYS	PIX	 X 	SYS	SYS	SYS	SYS
NIC4	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	 X 	SYS	SYS	SYS
NIC5	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	 X 	SYS	SYS
NIC6	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	 X 	PIX
NIC7	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	SYS	PIX	 X

Legend:

 X = Self
 SYS = Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
 NODE = Connection traversing PCIe as well as the interconnect between PCIe Host Bridges within a NUMA node
 PHB = Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
 PXB = Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
 PIX = Connection traversing at most a single PCIe bridge
 NV# = Connection traversing a bonded set of # NVLinks

NIC Legend:

 NIC0: mlx5_0
 NIC1: mlx5_1
 NIC2: mlx5_2
 NIC3: mlx5_3
 NIC4: mlx5_4
 NIC5: mlx5_5
 NIC6: mlx5_6
 NIC7: mlx5_7

环境准备

GPU Operator

部署

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 添加 NVIDIA Helm 仓库
helm repo add nvidia https://helm.ngc.nvidia.com/nvidia && helm repo update

# 部署 GPU Operator
helm install --wait gpu-operator \
 -n gpu-operator --create-namespace \
 nvidia/gpu-operator \
 --version=v25.10.0 \
 --set driver.enabled=true \
 --set dcgmExporter.serviceMonitor.enabled=true \
 --set dcgm.enabled=true

提示:部署过程可能需要 5-10 分钟,请耐心等待。可以通过 kubectl get pods -n gpu-operator 监控部署进度。

验证

如果部署成功,那么节点上可以看到 nvidia.com/gpu 资源

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
root@GB200-POD2-F06-Node05:~/lixd# kubectl describe node gb200-pod2-f06-node05|grep Capacity -C 7 Addresses:
 InternalIP: 10.0.6.41
 Hostname: gb200-pod2-f06-node05
Capacity:
 cpu: 144
 ephemeral-storage: 1840577300Ki
 hugepages-16Gi: 0
 hugepages-2Mi: 0
 hugepages-512Mi: 0
 memory: 1002717120Ki
 nvidia.com/gpu: 4

NVIDIA DRA Driver

部署

1
2
3
4
5
6
7
# 部署 NVIDIA DRA Driver
helm upgrade --install nvidia-dra-driver-gpu nvidia/nvidia-dra-driver-gpu \
 --version="25.8.0" \
 --create-namespace \
 --namespace nvidia-dra-driver-gpu \
 --set resources.gpus.enabled=false \
 --set nvidiaDriverRoot=/run/nvidia/driver

重要参数说明

  • resources.gpus.enabled=false:禁用默认 GPU 资源管理,由 GPU Operator 处理
  • nvidiaDriverRoot=/run/nvidia/driver:指定 NVIDIA 驱动路径

验证

正常情况下,可以看到节点间的 nvidia.com/gpu.clique label。

1
2
3
4
5
6
root@GB200-POD2-F06-Node05:~/lixd# (echo -e "NODE\tLABEL\tCLIQUE"; kubectl get nodes -o json | \
 /usr/bin/jq -r '.items[] | [.metadata.name, "nvidia.com/gpu.clique", .metadata.labels["nvidia.com/gpu.clique"]] | @tsv') | \
 column -t
NODE LABEL CLIQUE
gb200-pod2-f06-node05 nvidia.com/gpu.clique 69a19a31-f41c-45a5-8245-579b6bce5bdd.32766
gb200-pod2-f06-node06 nvidia.com/gpu.clique 69a19a31-f41c-45a5-8245-579b6bce5bdd.32766

创建 IMEX 负载

 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
cat <<EOF > imex-channel-injection.yaml
---
apiVersion: resource.nvidia.com/v1beta1
kind: ComputeDomain
metadata:
 name: imex-channel-injection
spec:
 numNodes: 1
 channel:
 resourceClaimTemplate:
 name: imex-channel-0
---
apiVersion: v1
kind: Pod
metadata:
 name: imex-channel-injection
spec:
 affinity:
 nodeAffinity:
 requiredDuringSchedulingIgnoredDuringExecution:
 nodeSelectorTerms:
 - matchExpressions:
 - key: nvidia.com/gpu.clique
 operator: Exists
 containers:
 - name: ctr
 image: ubuntu:22.04
 command: ["bash", "-c"]
 args: ["ls -la /dev/nvidia-caps-imex-channels; trap 'exit 0' TERM; sleep 9999 & wait"]
 resources:
 claims:
 - name: imex-channel-0
 resourceClaims:
 - name: imex-channel-0
 resourceClaimTemplateName: imex-channel-0
EOF

查看日志,正常能看到注入的 imex channel

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
root@GB200-POD2-F06-Node05:~/lixd/demo# kubectl apply -f imex-channel-injection.yaml
computedomain.resource.nvidia.com/imex-channel-injection created
pod/imex-channel-injection created
root@GB200-POD2-F06-Node05:~/lixd/demo# kubectl get pods
NAME READY STATUS RESTARTS AGE
imex-channel-injection 1/1 Running 0 5s
root@GB200-POD2-F06-Node05:~/lixd/demo# kubectl logs imex-channel-injection
total 0
drwxr-xr-x 2 root root 60 Jan 5 08:31 .
drwxr-xr-x 6 root root 380 Jan 5 08:31 ..
crw-rw-rw- 1 root root 501, 0 Jan 5 08:31 channel0

至此,说明 nvidia-dra-driver 部署成功。

验证测试

安装 MPI Operator

首先安装 MPI Operator,用于运行多节点 MPI 作业:

1
kubectl create -f https://github.com/kubeflow/mpi-operator/releases/download/v0.6.0/mpi-operator.yaml

nvbandwidth 测试

 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
cat <<EOF > nvbandwidth-test-job.yaml
---
apiVersion: resource.nvidia.com/v1beta1
kind: ComputeDomain
metadata:
 name: nvbandwidth-test-compute-domain
spec:
 numNodes: 2
 channel:
 resourceClaimTemplate:
 name: nvbandwidth-test-compute-domain-channel

---
apiVersion: kubeflow.org/v2beta1
kind: MPIJob
metadata:
 name: nvbandwidth-test
spec:
 slotsPerWorker: 4
 launcherCreationPolicy: WaitForWorkersReady
 runPolicy:
 cleanPodPolicy: Running
 sshAuthMountPath: /home/mpiuser/.ssh
 mpiReplicaSpecs:
 Launcher:
 replicas: 1
 template:
 metadata:
 labels:
 nvbandwidth-test-replica: mpi-launcher
 spec:
 affinity:
 nodeAffinity:
 requiredDuringSchedulingIgnoredDuringExecution:
 nodeSelectorTerms:
 - matchExpressions:
 - key: node-role.kubernetes.io/control-plane
 operator: Exists
 containers:
 - image: ghcr.io/nvidia/k8s-samples:nvbandwidth-v0.7-8d103163
 name: mpi-launcher
 securityContext:
 runAsUser: 1000
 command:
 - mpirun
 args:
 - --bind-to
 - core
 - --map-by
 - ppr:4:node
 - -np
 - "8"
 - --report-bindings
 - -q
 - nvbandwidth
 - -t
 - multinode_device_to_device_memcpy_read_ce
 Worker:
 replicas: 2
 template:
 metadata:
 labels:
 nvbandwidth-test-replica: mpi-worker
 spec:
 affinity:
 podAffinity:
 requiredDuringSchedulingIgnoredDuringExecution:
 - labelSelector:
 matchExpressions:
 - key: nvbandwidth-test-replica
 operator: In
 values:
 - mpi-worker
 topologyKey: nvidia.com/gpu.clique
 containers:
 - image: ghcr.io/nvidia/k8s-samples:nvbandwidth-v0.7-8d103163
 name: mpi-worker
 securityContext:
 runAsUser: 1000
 env:
 command:
 - /usr/sbin/sshd
 args:
 - -De
 - -f
 - /home/mpiuser/.sshd_config
 resources:
 limits:
 nvidia.com/gpu: 4
 claims:
 - name: compute-domain-channel
 resourceClaims:
 - name: compute-domain-channel
 resourceClaimTemplateName: nvbandwidth-test-compute-domain-channel
EOF

Apply

1
2
3
root@GB200-POD2-F06-Node05:~/lixd/demo# kubectl apply -f nvbandwidth-test-job.yaml
computedomain.resource.nvidia.com/nvbandwidth-test-compute-domain created
mpijob.kubeflow.org/nvbandwidth-test created

会自动启动 Pod 进行测试

1
2
3
4
5
root@GB200-POD2-F06-Node05:~/lixd/demo# k get po
NAME READY STATUS RESTARTS AGE
nvbandwidth-test-launcher-xl87m 1/1 Running 0 26s
nvbandwidth-test-worker-0 1/1 Running 0 7m41s
nvbandwidth-test-worker-1 1/1 Running 0 7m41s

查看日志

1
 kubectl logs --tail=-1 -l job-name=nvbandwidth-test-launcher

测试结果如下:

 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
nvbandwidth Version: v0.7
Built from Git version: v0.7

MPI version: Open MPI v4.1.4, package: Debian OpenMPI, ident: 4.1.4, repo rev: v4.1.4, May 26, 2022
CUDA Runtime Version: 13000
CUDA Driver Version: 13000
Driver Version: 580.95.05

Process 0 (nvbandwidth-test-worker-0): device 0: NVIDIA GB200 (00000008:01:00)
Process 1 (nvbandwidth-test-worker-0): device 1: NVIDIA GB200 (00000009:01:00)
Process 2 (nvbandwidth-test-worker-0): device 2: NVIDIA GB200 (00000018:01:00)
Process 3 (nvbandwidth-test-worker-0): device 3: NVIDIA GB200 (00000019:01:00)
Process 4 (nvbandwidth-test-worker-1): device 0: NVIDIA GB200 (00000008:01:00)
Process 5 (nvbandwidth-test-worker-1): device 1: NVIDIA GB200 (00000009:01:00)
Process 6 (nvbandwidth-test-worker-1): device 2: NVIDIA GB200 (00000018:01:00)
Process 7 (nvbandwidth-test-worker-1): device 3: NVIDIA GB200 (00000019:01:00)

Running multinode_device_to_device_memcpy_read_ce.
memcpy CE GPU(row) -> GPU(column) bandwidth (GB/s)
 0 1 2 3 4 5 6 7
 0 N/A 822.16 821.69 821.92 821.45 821.30 821.53 821.69
 1 820.90 N/A 821.92 821.61 821.30 821.06 822.00 821.69
 2 820.59 821.77 N/A 821.69 821.45 821.06 821.37 821.30
 3 820.51 821.77 821.61 N/A 821.37 821.22 821.30 821.92
 4 820.75 821.53 821.45 821.85 N/A 821.37 821.61 821.85
 5 820.51 821.53 821.22 821.69 821.69 N/A 821.61 821.77
 6 820.35 821.30 821.53 821.37 821.30 820.90 N/A 821.14
 7 820.59 821.69 820.98 821.37 821.37 821.14 821.30 N/A

SUM multinode_device_to_device_memcpy_read_ce 45997.93

NOTE: The reported results may not reflect the full capabilities of the platform.
Performance can vary with software drivers, hardware clocks, and system topology.

测试结果显示跨节点 GPU-to-GPU 通信带宽稳定在 820 GB/s 左右,远超传统 InfiniBand 等网络互联方案的性能,为大规模分布式 AI 训练提供了强大的通信基础。

总结

通过本文的实战指南,您已经学会了如何在 GB200 平台上部署和配置 NVIDIA DRA Driver,以启用 Multi-Node NVLink (MNNVL) 功能。主要成就包括:

  • 理解核心概念:掌握了 DRA、IMEX 和 ComputeDomains 的工作原理
  • 完成环境部署:成功部署 GPU Operator 和 DRA Driver
  • 验证功能:通过 nvbandwidth 测试确认跨节点 GPU 通信正常工作

ComputeDomains 技术将复杂的底层 GPU 硬件抽象为 Kubernetes 原生资源,使得多节点分布式 AI 工作负载的管理变得简单而高效。未来,随着更多 NVIDIA 架构的支持,这项技术将在 AI 基础设施领域发挥越来越重要的作用。

参考资料