Claude Code 生产事故指南:检测、回滚、RCA 与预防
Claude Code 生产事故实战指南:密钥泄露、误删、数据库、成本、回滚、RCA 与预防。
Claude Code 能读取文件、修改代码并执行命令,所以在生产仓库里非常快。速度本身不是问题,问题是一次含糊的批准也可能泄露密钥、删除文件、覆盖 main、执行危险迁移,或让 API 调用失控。
本文不是某家公司的真实事故报告,而是 ClaudeCodeLab 在演练、仓库审查和内容运营中整理出的合成案例。时间和金额只是示例,但事故形态适合在真正出事前练习。
事故指影响用户、数据、安全、成本或可用性的事件。封锁是先阻止损害继续扩大。RCA 是根因分析。回滚是回到最后一个安全版本。
具体配置请以官方 Claude Code settings 和 hooks guide 为准。本文把它们组织成一条流程:检测、封锁、诊断、回滚、沟通、复盘、预防复发。
响应流程
| 阶段 | 目标 | 可以让 Claude Code 做什么 |
|---|---|---|
| 检测 | 确认变更和影响范围 | 汇总告警、日志、diff、部署和最近命令 |
| 封锁 | 阻止损害扩大 | 提出吊销密钥、暂停任务、关闭 feature flag 或禁用接口 |
| 诊断 | 缩小直接原因 | 对比最后的正常部署和可疑变更 |
| 回滚 | 回到安全状态 | 列出目标版本、数据风险和验证命令 |
| 沟通 | 降低不确定性 | 起草状态、影响、下次更新时间和负责人 |
| 复盘 | 把事故变成学习 | 填写 RCA、时间线、检测遗漏和行动项 |
| 预防 | 降低复发概率 | 增加权限、hooks、CI、告警和审查关卡 |
涉及密钥、账单、个人数据和数据库写入时,先封锁,再调查。
七种具体事故模式
| 模式 | 会发生什么 | 第一动作 | 常见失败 |
|---|---|---|---|
| 密钥泄露 | .env、日志或截图暴露 API key | 吊销、轮换、检查日志 | 清理 git,却忘记 CI 日志 |
| 危险删除 | rm -rf 删除必要文件 | 停止操作、查备份、列出未跟踪文件 | git checkout . 无法恢复未跟踪文件 |
| force push | main 覆盖团队提交 | 停止 push、查 reflog、建恢复分支 | 混淆 --force-with-lease 和 --force |
| 数据库迁移 | drop、全表更新或锁表导致故障 | 暂停写入、保存状态、规划恢复 | 未测试 SQL 直接进生产 |
| API 无限重试 | 失败越多调用越多,成本上升 | 杀进程、暂停队列、检查限额 | “重试”变成无限循环 |
| 依赖部署失败 | 本地通过,生产启动 503 | 激活上一版部署、检查 lockfile | npm update 意外升级 major |
| 缺少认证 | 管理接口公开 | 禁用接口、查访问日志、必要时通知 | 只写“admin”,没有写认证要求 |
三个演练案例
密钥泄露通常来自 GitHub secret scanning、云告警或账单页面。第一步是吊销密钥,不是继续查原因。之后再检查公开仓库、PR、CI 日志、聊天记录和监控。
git status --short
git diff --cached --name-only
git log --all -- .env .env.local
git grep -n "sk-" -- ':!node_modules' ':!dist'
数据库迁移失败时,先暂停写入。代码可以快速回滚,但删除的数据需要备份、WAL、审计日志或外部系统重新同步。
psql "$DATABASE_URL" -c "select now();"
psql "$DATABASE_URL" -c "\d users"
pg_dump "$DATABASE_URL" --schema-only > schema_before_repair.sql
API 重试必须有硬上限。把下面保存为 incident-budget-runner.mjs,用它包住批处理。
#!/usr/bin/env node
import { spawn } from "node:child_process";
const command = process.argv.slice(2);
const maxAttempts = Number(process.env.MAX_ATTEMPTS || 3);
const maxCostCents = Number(process.env.MAX_COST_CENTS || 200);
const costPerAttempt = Number(process.env.COST_PER_ATTEMPT_CENTS || 0);
if (command.length === 0) {
console.error("用法: node incident-budget-runner.mjs <命令> [...参数]");
process.exit(2);
}
let estimatedCost = 0;
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
const child = spawn(command[0], command.slice(1), {
stdio: "inherit",
shell: process.platform === "win32"
});
const exitCode = await new Promise((resolve) => {
child.on("exit", (code) => resolve(code ?? 1));
});
estimatedCost += costPerAttempt;
if (exitCode === 0) process.exit(0);
if (estimatedCost >= maxCostCents) {
console.error(`已停止: 预估成本达到 ${estimatedCost} 美分`);
process.exit(1);
}
const delayMs = Math.min(1000 * 2 ** (attempt - 1), 10_000);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
console.error(`失败: 已尝试 ${maxAttempts} 次`);
process.exit(1);
Claude Code 防护栏
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Bash(git push --force *main*)",
"Bash(git push -f *main*)",
"Bash(rm -rf /*)",
"Bash(rm -rf ~*)"
],
"ask": [
"Bash(git push*)",
"Bash(rm*)",
"Bash(npm install*)",
"Bash(*migrate*)",
"Bash(*deploy*)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-danger.sh"
}
]
}
]
}
}
#!/usr/bin/env bash
set -euo pipefail
payload="$(cat)"
command="$(node -e 'const fs = require("fs"); const raw = fs.readFileSync(0, "utf8") || "{}"; const json = JSON.parse(raw); console.log(json.tool_input?.command || "");' <<< "$payload")"
blocked='(rm[[:space:]]+-rf[[:space:]]+(/|~)|git[[:space:]]+push[[:space:]].*(-f|--force)([[:space:]]|$)|DROP[[:space:]]+TABLE|TRUNCATE[[:space:]])'
if [[ "$command" =~ $blocked ]]; then
echo "已阻止危险命令: $command" >&2
exit 2
fi
exit 0
沟通和复盘模板
## 事故更新
- 状态: 调查中 / 已封锁 / 正在验证恢复
- 影响: 功能、用户、开始时间
- 当前动作: 已停止任务、已回滚部署、正在查看日志
- 下次更新: YYYY-MM-DD HH:mm
- 负责人:
# 复盘: [标题]
## 摘要
- 开始:
- 检测:
- 恢复:
- 影响:
- 严重级别: P0/P1/P2/P3
## 时间线
| 时间 | 事件 |
| --- | --- |
| HH:mm | |
## 原因
- 直接原因:
- 根因:
- 为什么检测晚了:
## 预防
| 行动 | 负责人 | 截止日期 |
| --- | --- | --- |
| | | |
外部参考可以阅读 Google SRE 的 Postmortem Culture。
封锁完成后,不要急着让 Claude Code “顺手修好全部”。先把证据冻结下来:部署版本、相关 PR、告警截图、日志查询、执行过的命令、受影响用户范围和已经采取的封锁动作。然后把修复拆成三类:立即恢复服务的动作、当天必须补上的检测动作、下一个迭代再做的结构性预防。这样做的好处是,Claude Code 可以分别生成回滚 PR、告警规则和复盘行动项,而不是把临时补丁、长期重构和沟通文案混在一次巨大修改里。团队复盘时也能看清楚,事故是权限太宽、验证不足、自动化缺少预算,还是人工批准点放错了位置。
相关阅读和 CTA
继续阅读 Claude Code 安全最佳实践、权限设置指南、API 成本指南 和 验证记录流程。
个人使用可先下载免费速查表。需要可复用模板时,查看 ClaudeCodeLab 产品。团队要整理 CLAUDE.md、权限、hooks、审查和事故演练,可以看 Claude Code 培训与咨询。
在 ClaudeCodeLab 的演练中,最有效的改变是先写封锁步骤,再写诊断步骤。发布前验证 JSON、Bash 和 Node 代码的语法,也减少了简单错误。一次 20 分钟演练通常就能暴露缺少告警、备份未验证、Claude Code 权限过宽这些问题。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code权限安全阶梯:逐步放开访问而不失控
从只读到有限编辑、验证命令和部署检查的 Claude Code 权限升级流程。
Claude Code 小PR证据包:让小改动真正可审查
用差异、验证命令、公开URL、CTA路径和回滚说明,把Claude Code的小PR变得可审查。
Claude Code 提交前 Review Gate:同时检查差异、测试、公开 URL 和 CTA
提交前用 Claude Code 审查差异范围、build、公开 URL、Gumroad 链接、咨询 CTA、缺少测试和无关文件。