20 KiB
20 KiB
code_ai_agent CI/CD 工作流方案设计
计划文件: .omc/plans/code_ai_agent_cicd.md
创建日期: 2026-03-27
状态: 待用户确认
1. 方案概述
将 code_ai_agent 从单纯的代码生成服务升级为具备完整 DevOps 工作流能力的「代码员工 Agent」。新增 Git 操作、SSH 远程执行、K8s 部署触发能力,全部通过 HTTP API 暴露。
完整工作流
外部调用方 (agent-manager / 人工)
│
▼
code_ai_agent Pod
┌──────────────────────────────────────────┐
│ api_server.py (HTTP 路由层) │
│ ┌──────────┬──────────┬──────────────┐ │
│ │ /git/* │ /ssh/* │ /deploy/k8s │ │
│ └────┬─────┴────┬─────┴──────┬───────┘ │
│ │ │ │ │
│ src/server/tools/ (工具实现层) │
│ ┌────▼─────┐ ┌──▼──────┐ ┌──▼────────┐ │
│ │git_tools │ │ssh_tools│ │deploy_tools│ │
│ └────┬─────┘ └──┬──────┘ └──┬────────┘ │
│ │ │ │ │
│ /workspace/{task_id}/ (隔离工作空间) │
└───┬───┴──────────┴────────────┴───────────┘
│
├─► Gitee (http://gitee.ath.cx:3000)
├─► Azure VM (SSH 22)
└─► K8s API Server
典型工作流序列
1. POST /api/v1/git/clone → 克隆仓库到 /workspace/{task_id}
2. POST /api/v1/git/branch → 创建 feature/xxx 分支
3. POST /api/v1/code/generate → 使用现有能力生成/修改代码
4. POST /api/v1/git/status → 确认变更
5. POST /api/v1/git/commit-push → 提交并推送
6. POST /api/v1/ssh/exec → SSH 到 Azure VM 执行测试
7. POST /api/v1/deploy/k8s → 测试通过后触发 K8s 部署
2. 新增 API 端点设计(api_server.py)
2.1 Git 操作端点
POST /api/v1/git/clone
// 请求
{
"repo_url": "http://gitee.ath.cx:3000/zhanggangyong/agent_management.git",
"task_id": "task-20260327-001",
"branch": "main",
"depth": 1
}
// 响应
{
"success": true,
"task_id": "task-20260327-001",
"workspace": "/workspace/task-20260327-001",
"branch": "main",
"commit": "abc1234"
}
POST /api/v1/git/branch
// 请求
{
"task_id": "task-20260327-001",
"branch_name": "feature/auto-fix-bug-123",
"from_branch": "main"
}
// 响应
{ "success": true, "branch": "feature/auto-fix-bug-123", "base_commit": "abc1234" }
POST /api/v1/git/status
// 请求
{ "task_id": "task-20260327-001" }
// 响应
{
"success": true,
"branch": "feature/auto-fix-bug-123",
"staged": ["src/main.py"],
"unstaged": ["README.md"],
"untracked": ["new_file.py"],
"raw_output": "M src/main.py\n?? new_file.py"
}
POST /api/v1/git/commit-push
// 请求
{
"task_id": "task-20260327-001",
"message": "fix: resolve null pointer in agent executor",
"files": ["src/agent.py"],
"push": true
}
// 响应
{ "success": true, "commit": "def5678", "pushed": true, "branch": "feature/auto-fix-bug-123" }
POST /api/v1/git/diff
// 请求
{ "task_id": "task-20260327-001", "staged": false }
// 响应
{ "success": true, "diff": "--- a/src/main.py\n+++ b/src/main.py\n..." }
2.2 SSH 操作端点
POST /api/v1/ssh/exec
// 请求
{
"host": "<azure-vm-ip>",
"user": "azureuser",
"command": "cd /app && pytest tests/ -v --tb=short",
"timeout": 300,
"task_id": "task-20260327-001"
}
// 响应
{
"success": true,
"exit_code": 0,
"stdout": "collected 42 items ... 42 passed",
"stderr": "",
"duration_seconds": 45.2
}
说明: host 若不传,从环境变量 SSH_TEST_HOST 读取;user 从 SSH_USER 读取,默认 azureuser。
2.3 部署端点
POST /api/v1/deploy/k8s
// 请求
{
"namespace": "agent-manager",
"deployment": "agent-manager",
"image": "agnettaiji.azurecr.io/ai-agents/agent-manager:v1.2.3",
"strategy": "set-image",
"wait": true,
"timeout": 300
}
// strategy: "rollout-restart" | "set-image"
// 响应
{ "success": true, "deployment": "agent-manager", "status": "rolled out", "duration_seconds": 62 }
3. 新增工具函数设计(mcp_server.py + tools/ 模块)
3.1 文件结构变化
agent_templates/agents/code_ai_agent/
├── Dockerfile # 修改:增加 git/ssh/kubectl
├── requirements.txt # 修改:增加 paramiko, gitpython
├── src/server/
│ ├── api_server.py # 修改:新增 /git /ssh /deploy 路由
│ ├── mcp_server.py # 修改:新增工具注册
│ ├── mcp_http_server.py # 不变
│ └── tools/ # 新增目录
│ ├── __init__.py
│ ├── git_tools.py # Git 操作实现
│ ├── ssh_tools.py # SSH 操作实现
│ ├── deploy_tools.py # K8s 部署实现
│ └── workspace.py # 工作空间管理
└── k8s/ # 新增:agent 专属 K8s 配置
├── code-ai-agent-deployment.yaml
└── code-ai-agent-secret.yaml
3.2 git_tools.py 核心接口
class GitTools:
def __init__(self):
self.workspace_root = "/workspace"
self._gitee_user = os.getenv("GITEE_USERNAME")
self._gitee_token = os.getenv("GITEE_TOKEN")
def clone(self, repo_url, task_id, branch="main", depth=1) -> dict
def create_branch(self, task_id, branch_name, from_branch=None) -> dict
def get_status(self, task_id) -> dict
def stage_files(self, task_id, files=None) -> dict # None = git add -A
def commit(self, task_id, message) -> dict
def push(self, task_id, branch=None) -> dict
def get_diff(self, task_id, staged=False) -> dict
def cleanup(self, task_id) -> dict # 删除工作空间
def _inject_credentials(self, repo_url) -> str:
# http://user:token@gitee.ath.cx:3000/...
parsed = urlparse(repo_url)
return parsed._replace(
netloc=f"{self._gitee_user}:{self._gitee_token}@{parsed.hostname}:{parsed.port}"
).geturl()
def _run(self, cmd, cwd) -> tuple[int, str, str]
# subprocess.run,捕获 stdout/stderr,设置超时
3.3 ssh_tools.py 核心接口
class SSHTools:
def __init__(self):
self._key_path = "/root/.ssh/id_rsa" # 从 Secret 挂载
self._default_host = os.getenv("SSH_TEST_HOST")
self._default_user = os.getenv("SSH_USER", "azureuser")
def exec(self, command, host=None, user=None, timeout=120, task_id=None) -> dict:
# 使用 paramiko 连接,执行命令,返回 stdout/stderr/exit_code
# 每次调用建立新连接,操作完毕后关闭
def _get_client(self, host, user) -> paramiko.SSHClient
3.4 deploy_tools.py 核心接口
class DeployTools:
def __init__(self):
# 优先使用挂载的 kubeconfig,其次 in-cluster config
self._kubeconfig = "/root/.kube/config"
def rollout_restart(self, namespace, deployment, wait=True, timeout=300) -> dict
def set_image(self, namespace, deployment, image, wait=True, timeout=300) -> dict
def get_status(self, namespace, deployment) -> dict
def _run_kubectl(self, args) -> tuple[int, str, str]
3.5 workspace.py — 工作空间管理
class WorkspaceManager:
ROOT = "/workspace"
@staticmethod
def get_path(task_id: str) -> str:
# 返回 /workspace/{task_id}
# task_id 只允许 [a-zA-Z0-9_-],防止路径注入
@staticmethod
def create(task_id: str) -> str
@staticmethod
def cleanup(task_id: str) -> None
@staticmethod
def list_tasks() -> list[str]
@staticmethod
def disk_usage() -> dict # 返回各 task_id 占用磁盘大小
4. 安全设计
4.1 SSH 私钥注入
方案:K8s Secret → Volume Mount(只读)
# 新建 Secret(在 code-ai-agent 命名空间下)
apiVersion: v1
kind: Secret
metadata:
name: code-ai-agent-ssh-secret
namespace: agent-manager
type: Opaque
data:
id_rsa: <base64-encoded-private-key>
id_rsa.pub: <base64-encoded-public-key>
known_hosts: <base64-encoded-known_hosts> # 预置 Azure VM
# Deployment volumeMounts
volumeMounts:
- name: ssh-secret
mountPath: /root/.ssh
readOnly: true
volumes:
- name: ssh-secret
secret:
secretName: code-ai-agent-ssh-secret
defaultMode: 0400 # 私钥必须 0400,否则 SSH 拒绝
初始化:容器 entrypoint 或 initContainer 执行 chmod 700 /root/.ssh && chmod 600 /root/.ssh/id_rsa。
4.2 Git 凭证安全传递
| 方案 | 说明 | 推荐度 |
|---|---|---|
| Token 嵌入 URL | http://user:token@host/repo 内存拼接,不落盘 |
P0 首选 |
| git credential store | 写入 ~/.git-credentials 文件权限 600 |
备选 |
| SSH key for git | gitee 配置 deploy key,统一 SSH | P2 升级 |
实现要点:_inject_credentials() 在内存拼接带 token 的 URL;clone 完成后用 git remote set-url origin <无密码URL> 替换;日志中对 URL 做 token 脱敏。
4.3 权限隔离
- code_ai_agent 使用独立 ServiceAccount
code-ai-agent - RBAC 只授予
agent-manager命名空间下 Deployment 的get/patch/update - SSH 连接只允许白名单 host(
SSH_ALLOWED_HOSTS环境变量,ssh_tools.py 校验) /workspace挂载独立 emptyDir,不与其他 agent 共享- API 通过现有
X-API-Keyheader 鉴权
5. 工作空间设计
5.1 目录结构
/workspace/
├── task-20260327-001/
│ ├── agent_management/ # 克隆的仓库
│ └── .meta.json # 任务元数据(时间、branch、状态)
├── task-20260327-002/
│ └── agent_management/
└── .workspace_index.json
5.2 并发隔离策略
task_id由调用方传入或服务端uuid4()自动生成- 每个 task_id 对应独立目录,无共享文件
- 任务完成后调用清理接口或设置 TTL 自动清理
- 磁盘告警:workspace 总占用超过 10GB 返回 503
task_id只允许[a-zA-Z0-9_-],防止路径穿越注入
5.3 新增管理端点
GET /api/v1/workspace/list → 列出所有 task_id 和磁盘占用
DELETE /api/v1/workspace/{task_id} → 清理指定工作空间
6. Dockerfile 修改
当前状态: 只安装 gcc,无 git/ssh/kubectl。
修改后关键变更:
FROM python:3.12-slim
WORKDIR /app
ENV PYTHONUNBUFFERED=1 PYTHONDONTWRITEBYTECODE=1
# 新增:git + openssh-client + curl(kubectl 安装需要)
RUN apt-get update && apt-get install -y \
gcc git openssh-client curl ca-certificates gnupg \
&& rm -rf /var/lib/apt/lists/*
# 新增:安装 kubectl
RUN curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" \
&& chmod +x kubectl && mv kubectl /usr/local/bin/
# 新增:paramiko(SSH)、gitpython(可选,subprocess git 为主)
RUN pip install --no-cache-dir -r requirements.txt requests paramiko gitpython
# 新增:工作空间目录(PVC 挂载时会覆盖)
RUN mkdir -p /workspace /tmp/projects
EXPOSE 8000 8001
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["python", "run_api_server.py"]
镜像大小预估影响: git + openssh ≈ +30MB,kubectl ≈ +50MB,paramiko ≈ +5MB。总增量约 85MB,可接受。
7. K8s 部署配置修改
7.1 新增文件:code-ai-agent-deployment.yaml
code_ai_agent 需要独立 Deployment(与 agent-manager 主服务分离),关键新增配置段:
spec:
template:
spec:
serviceAccountName: code-ai-agent
containers:
- name: code-ai-agent
env:
- name: GITEE_USERNAME
valueFrom:
secretKeyRef:
name: agent-manager-secret
key: GITEE_USERNAME
- name: GITEE_TOKEN
valueFrom:
secretKeyRef:
name: agent-manager-secret
key: GITEE_TOKEN
- name: SSH_TEST_HOST
valueFrom:
secretKeyRef:
name: code-ai-agent-ssh-secret
key: SSH_TEST_HOST
- name: SSH_USER
value: "azureuser"
volumeMounts:
- name: ssh-secret
mountPath: /root/.ssh
readOnly: true
- name: kubeconfig
mountPath: /root/.kube
readOnly: true
- name: workspace
mountPath: /workspace
resources:
requests:
memory: "512Mi"
cpu: "300m"
limits:
memory: "1Gi"
cpu: "1000m"
volumes:
- name: ssh-secret
secret:
secretName: code-ai-agent-ssh-secret
defaultMode: 0400
- name: kubeconfig
secret:
secretName: kubeconfig-secret
optional: true
- name: workspace
emptyDir:
sizeLimit: 20Gi
7.2 agent-manager-secret 新增 key
在现有 k8s/agent-manager-secret.yaml 补充:
GITEE_USERNAME: "zhanggangyong"
7.3 新建 code-ai-agent-ssh-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: code-ai-agent-ssh-secret
namespace: agent-manager
type: Opaque
data:
id_rsa: <base64-encoded-private-key>
known_hosts: <base64-encoded-known_hosts>
SSH_TEST_HOST: <base64-encoded-azure-vm-ip>
7.4 RBAC 新增 Role + RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: code-ai-agent-role
namespace: agent-manager
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "patch", "update"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
8. 实现优先级
P0 — 核心能力(第一阶段,必须先完成)
| 编号 | 内容 | 验收标准 |
|---|---|---|
| P0-1 | Dockerfile 安装 git + openssh-client + kubectl | docker run ... git --version 输出正常 |
| P0-2 | workspace.py 工作空间管理 | 单元测试覆盖路径注入防护(task_id 含 ../ 时拒绝) |
| P0-3 | git_tools.py:clone + branch + status + commit + push | 成功 clone gitee 仓库,创建分支并推送 |
| P0-4 | ssh_tools.py:exec | SSH 到 Azure VM 执行 echo ok,返回 exit_code=0 |
| P0-5 | api_server.py 新增 /git/* 和 /ssh/exec 路由 | HTTP 调用返回正确 JSON,异常时返回 4xx/5xx |
| P0-6 | SSH Secret + Volume Mount K8s 配置 | Pod 启动后 /root/.ssh/id_rsa 权限为 0400 |
P1 — 完整工作流(第二阶段)
| 编号 | 内容 | 验收标准 |
|---|---|---|
| P1-1 | deploy_tools.py:rollout-restart + set-image | 成功触发 K8s 滚动更新,等待就绪返回 |
| P1-2 | api_server.py 新增 /deploy/k8s 路由 | 调用后 deployment 完成更新,status 字段正确 |
| P1-3 | RBAC:code-ai-agent ServiceAccount + Role | kubectl auth can-i patch deployment 返回 yes |
| P1-4 | git diff 接口 | 返回正确 unified diff 格式 |
| P1-5 | workspace list/cleanup 管理端点 | GET /workspace/list 返回含磁盘占用的列表 |
| P1-6 | mcp_server.py 注册新工具 | MCP 工具列表中出现 git_clone、ssh_exec、k8s_deploy |
P2 — 增强与优化(第三阶段)
| 编号 | 内容 | 说明 |
|---|---|---|
| P2-1 | 替换 HTTP token 为 SSH key 方式访问 git | 更安全,需 gitee 配置 deploy key |
| P2-2 | workspace 磁盘告警 + TTL 自动清理 | 防止 emptyDir 耗尽,定时任务每小时扫描 |
| P2-3 | SSH 连接池(paramiko Transport 复用) | 减少高频调用连接建立开销 |
| P2-4 | /api/v1/pipeline/run 编排端点 | 单次调用完成 clone→修改→测试→部署全流程 |
| P2-5 | 操作审计日志(structured log) | 所有 git/ssh/deploy 操作可追溯,含 task_id |
9. 潜在风险与注意事项
风险 1:Git Token 泄露
- 场景: token 嵌入 URL 后被
git remote -v、进程环境变量或日志打印 - 缓解: clone 后立即
git remote set-url origin <无密码URL>;日志中 URL 做正则脱敏;不将 token 写入任何文件
风险 2:workspace 磁盘耗尽
- 场景: 大量任务未清理,emptyDir 超限导致 Pod 被驱逐
- 缓解: emptyDir 设
sizeLimit: 20Gi;API 层磁盘检查(超 10GB 返回 503);P2 阶段加 TTL 自动清理
风险 3:SSH 私钥被容器内进程读取
- 场景: 容器内其他进程或代码执行漏洞读取
/root/.ssh/id_rsa - 缓解: Volume
defaultMode: 0400;容器以非 root 用户运行(P2 阶段);考虑使用 Vault Agent Injector 替代 Secret Volume
风险 4:K8s 部署权限过宽
- 场景: code_ai_agent 被攻击后可滥用 kubectl 权限影响其他服务
- 缓解: RBAC 严格限制到
agent-manager命名空间,只允许 get/patch/update Deployment;禁止 delete、exec、secret 等危险操作
风险 5:并发 git 操作冲突
- 场景: 两个任务使用相同 task_id 或同一仓库并发操作
- 缓解: task_id 全局唯一(UUID);每个 task_id 独立目录;api_server.py 对同一 task_id 的写操作加文件锁
风险 6:Azure VM SSH 连接超时或不可达
- 场景: 网络抖动或 VM 重启导致 SSH 命令挂起
- 缓解: paramiko 设置
banner_timeout、auth_timeout、timeout;所有 ssh.exec 调用强制设置timeout参数(默认 120s);超时后返回明确错误而非挂起
风险 7:CI/CD 循环触发
- 场景: code_ai_agent 推送代码触发 CI,CI 再触发 code_ai_agent,形成死循环
- 缓解: commit message 加
[skip-ci]标记;部署端点需要明确的 image tag 参数,不自动推断
10. 工作计划(Task Flow)
Step 1:基础设施准备(P0-1, P0-6)
- 修改
Dockerfile,安装 git/openssh/kubectl - 创建
code-ai-agent-ssh-secret.yaml - 更新
agent-manager-secret.yaml补充GITEE_USERNAME - 验收: Pod 启动正常,
/root/.ssh/id_rsa权限 0400
Step 2:工作空间与 Git 工具(P0-2, P0-3)
- 实现
src/server/tools/workspace.py - 实现
src/server/tools/git_tools.py - 编写单元测试
- 验收: 能 clone gitee 仓库,创建分支,commit+push
Step 3:SSH 工具与 API 路由(P0-4, P0-5)
- 实现
src/server/tools/ssh_tools.py - 在
api_server.py注册/git/*和/ssh/exec路由 - 验收: HTTP 调用 clone + ssh exec 全流程通
Step 4:部署工具与完整流程(P1-1 ~ P1-3)
- 实现
src/server/tools/deploy_tools.py - 注册
/deploy/k8s路由 - 配置 RBAC
- 验收: 调用
/deploy/k8s触发滚动更新成功
Step 5:MCP 工具注册与增强(P1-4 ~ P1-6, P2)
- 在
mcp_server.py注册新工具 - workspace 管理端点
- 按需推进 P2 优化项
成功标准
- 完整工作流(clone → branch → 代码修改 → commit/push → SSH 测试 → K8s 部署)可通过 HTTP API 驱动,无人工干预
- 所有凭证(git token、SSH 私钥)通过 K8s Secret 注入,不硬编码
- 并发多任务互不干扰(task_id 隔离)
- 单个操作失败有明确错误信息,不影响其他任务
- Pod 重启后工作空间可按需重建(无状态设计)
Does this plan capture your intent?
proceed— 开始实现,移交 executoradjust [X]— 返回调整某个模块设计restart— 废弃重新开始