🌐 Languages: 中文(当前)· English
Related: aws-devops-agent-cn-bridge — 让 Global 分区的 DevOps Agent 能访问 aws-cn 资源的跨分区桥接(CFN + IAM Roles Anywhere)。
一个轻量级 bridge 层,把 AWS DevOps Agent(预览期)与 Slack 双向打通:
- 告警 → 调查 → 推 Slack 的全自动 SRE 主线(CloudWatch Alarm → Lambda → DevOps Agent → Slack Block Kit 摘要)
- Slack 内多轮对话:
@devops_agent <问题>调起调查,同 thread 内复用同一 chat session,后续追问免 @
原生集成只能「完了推一下」是单向的,本仓补齐其余纬度(详见 §0.0 为什么要做这个 bridge)。
- 🔌 告警入口可控 — Lambda-A 把任意 namespace 的 CloudWatch alarm(EKS / RDS / Lambda / 自定义 metric / ContainerInsights…)结构化拼进 investigation description,不假设只是 EC2
- 🔄 EventBridge 事件幂等 — 同一 alarm 不会被重复立案调查(DDB 原子 claim by
alarmName + state.timestamp) - 💬 Slack 主动调起 · thread 多轮 — Lambda-C 接 Slack Events API,fast-path 3s ack + Lambda 自异步调 worker;DDB 记录 thread_ts → chat_id 映射,同 thread 内免 @续问
- 🛡️ Slack 签名验证 + 5min replay 防御 + event_id 幂等 — 双订阅(
app_mention+message.channels)不会双发 prompt - 🌍 跨分区可扩展 — 事件管道全走 EventBridge + Lambda,可以叠加 cn-bridge 让 Global 区 Agent 调查 aws-cn 资源
- 📦 一键脚本部署 — IAM / Lambda / EventBridge / API Gateway / DDB / DLQ / CloudWatch alarm 全脚本化,幂等可重复执行;Slack App 创建这一步手动一次在
docs/slack-setup.md - 🔁 可快速适配飞书 — chat 层抽象都在 Lambda-C 里(签名验证、Block Kit 渲染、
@mention剖析、chat.update占位子),换成飞书只需重写slack_verify.py、Block Kit → 飞书 card / interactive message、webhook URL 格式。Lambda-A/B/EventBridge 主线零修改。
git clone <repo-url> && cd aws-devops-agent-slack-bridge
cp .env.example .env
# edit .env with your account/region/Slack/Agent Space values
bash scripts/deploy.sh # main alarm → investigation → Slack pipeline
bash scripts/deploy-chatbot.sh # optional: Slack chatbot for interactive chat最低需要填的 ·env·:AWS_ACCOUNT_ID / AWS_REGION / DEVOPS_AGENT_SPACE_ID / SLACK_WEBHOOK_URL / SLACK_TEST_CHANNEL。详见 §2.5 Configuration。
状态: ✅ 已端到端验证通过(FIS 删 2 个 EKS node → DevOps Agent 自动调查 → Slack 推摘要 + thread 多轮对话)。
本仓里所有
<UPPERCASE_PLACEHOLDER>都靠.env注入,文件不入库。
AWS DevOps Agent 自带原生 Slack 集成(官方文档),但只覆盖最基本的「调查完成 → 把结论推到一个 Slack 频道」这一种场景。落到生产 SRE 工作流里,原生集成有几个绕不过去的限制:
1. 只是单向通知,不支持 Slack 内对话
AWS 官方原文:「configure AWS DevOps Agent to update a Slack channel you select with incident response investigation key findings, root cause analyses, and generated mitigation plans」。换句话说,原生集成是 agent → Slack 的单向通道,没有 Slack 内 @mention 追问、同 thread 多轮上下文、按需发起调查这些能力。值班人员在 Slack 里看到摘要后想追问「为什么不是 X 原因」「再查一下 Y 资源」,必须切到 AWS Console 的 chat 界面,节奏被打断。
→ 本仓 Lambda-C + DynamoDB thread 表 解决:@devops_agent <问题> 在 Slack 直接调 send_message API,同 thread 内复用同一 chat_id,把 DevOps Agent 的对话能力暴露到 Slack 里。
2. 触发逻辑是黑盒,无法塞业务上下文 原生集成只能把 CloudWatch alarm(或 webhook)作为触发源,Agent Space 自己决定如何把 alarm 翻译成 investigation backlog task。当 alarm 来自非 EC2 namespace(EKS、自定义 metric、ContainerInsights),原生流程拿到的 description 经常缺关键 dimensions,导致 Agent 调查方向偏。
→ 本仓 Lambda-A 解决:在我们自己的 Lambda 里把 namespace + dimensions + alarm reason 结构化拼进 create_backlog_task 的 description(顺手修了一个 EventBridge event 里 accountId / region 在 top-level 而不是 detail 里的取值 bug),保证调查从一开始就拿到完整上下文。
3. 输出格式不可控
原生 Slack 通知是固定模板,没有 Block Kit 自定义、没有不同事件类型分别走不同 channel 的能力、不能把 executionId / journal 链接 / 关键 metric 抽成可点击元素。
→ 本仓 Lambda-B 解决:监听 aws.aidevops 的 Investigation Completed 事件,自己拉 list_journal_records → investigation_summary_md,用 Slack Block Kit 自定义渲染(标题、严重度色块、journal 链接、相关资源 dimensions),可以按 alarm 类型路由到不同频道。
4. 区域 / 跨分区限制 DevOps Agent 服务本身只在少数 region 可用,且 AWS 中国区不支持。如果应用部署在 aws-cn 或者 Agent Space 所在 region 与告警源 region 不同,原生集成无能为力。
→ 本仓事件管道全部走 EventBridge + Lambda,可以无侵入地接入 aws-devops-agent-cn-bridge(IAM Roles Anywhere 跨分区 assume),让 Global 分区的 Agent Space 调查 aws-cn 资源。
5. 部署不可代码化 原生 Slack 集成必须 手动 走「Console → Register → Slack OAuth → 选 workspace → Allow → 回 Console 关联 Agent Space」流程,每个频道都要手动加 bot 用户。无法纯 IaC 管理,多账号 / 多环境复制成本高。
→ 本仓 scripts/deploy.sh + scripts/deploy-chatbot.sh 把除「创建 Slack App」之外的所有部署步骤脚本化(IAM、Lambda、EventBridge rule、API Gateway、DDB 表),.env 注入参数;Slack App 那一次性手动配置在 docs/slack-setup.md 里有详细 checklist。
一句话:原生集成是 demo 级 的「调查完了通知一下」,本仓是 生产级 的「告警入口可控、调查上下文完整、Slack 内可对话、跨区可扩展」的 bridge 层。
CloudWatch Alarm (任意 namespace)
│ state = ALARM
▼
EventBridge Rule-1 ── aws.cloudwatch / CloudWatch Alarm State Change
│
▼
Lambda-A (devops-agent-trigger-investigation)
│ create_backlog_task(taskType=INVESTIGATION)
│ 把 namespace + dimensions + alarm reason 结构化进 description
▼
DevOps Agent ── 自主调查 5–15 min
│ - 调 use_aws describe_xxx 收集资源数据
│ - 拉 CloudWatch metrics / CloudTrail events
│ 发布 aws.aidevops / Investigation Completed
▼
EventBridge Rule-2 ── aws.aidevops / Investigation Completed
│
▼
Lambda-B (devops-agent-notify-slack)
│ list_journal_records → investigation_summary_md
│ Slack Incoming Webhook → Block Kit
▼
Slack #<SLACK_CHANNEL_ID>
- AWS CLI v2 已配置
<ACCOUNT_ID>凭证 - AWS DevOps Agent 在
ap-northeast-1已可用(preview 阶段) - 已确认:你已经在 console 创建好 Agent Space(如果没有,aidevops console → Create Agent Space),把 ID 填进
.env的DEVOPS_AGENT_SPACE_ID
控制台拿 ID:
aidevops→Agent Spaces→ 点详情 → 右上角 Copy ARN → 取agentspace/后的 UUID。
DevOps Agent 客户端在 boto3 1.43.0 才发布到公开 PyPI。Lambda Python 3.12 内置的 boto3 是更老的版本,不带 devops-agent 客户端,所以必须打 layer。部署脚本已自动处理(pip install boto3>=1.43.0 -t layer/python)。
服务名映射(要全部记住):
- boto3 client 名:
devops-agent- IAM action 前缀:
aidevops:- EventBridge source:
aws.aidevops- 服务 endpoint:
aidevops.<region>.amazonaws.com
频道 <SLACK_CHANNEL_ID> 已有 Incoming Webhook(被 petsite-ops-slack-notifier 用着,本方案直接复用——不用新建 Slack App,不用 Bot Token)。
Webhook URL 已挪到 Secrets Manager(2026-05-20,P0-6):
arn:aws:secretsmanager:<region>:<ACCOUNT_ID>:secret:devops-agent/slack-webhook-url-<RANDOM_SUFFIX>
Lambda-B 通过 SLACK_WEBHOOK_SECRET_ARN env var 拿到 ARN,cold-start 时 GetSecretValue 一次后缓存到 module global。
Webhook 轮换流程:aws secretsmanager put-secret-value --secret-id devops-agent/slack-webhook-url --secret-string <new-url> → 等下一次 cold start(≤15 分钟空闲后)生效。
awsv2.x、python3.12、pip、zip、unzip、jq、envsubst(gettext包带)kubectl(验证 EKS node 时用)
仓库里没有任何账号 / 资源 ID 真值;全部走 .env:
cp .env.example .env
$EDITOR .env # 填进真值.env 已在 .gitignore,真值永远不进 git。.env.example 全是占位符 + 注释说明,可以放心 push。
| 变量 | 怎么拿 |
|---|---|
AWS_ACCOUNT_ID |
aws sts get-caller-identity --query Account --output text |
AWS_REGION |
你计划部署的 region(本项目所有资源同区) |
DEVOPS_AGENT_SPACE_ID |
Console → aidevops → Agent Spaces → 详情 → Copy ARN → 取 agentspace/ 后的 UUID |
DEVOPS_AGENT_SPACE_ARN |
同上,整个 ARN |
SLACK_CHANNEL / SLACK_TEST_CHANNEL |
Slack 客户端:右键频道 → View channel details → 底部 Channel ID (Cxxxxxx) |
SLACK_WEBHOOK_URL |
Slack App → Incoming Webhooks → Add New Webhook(deploy.sh 写进 Secrets Manager 后 Lambda 从那读) |
SLACK_BOT_TOKEN_SECRET_ID / SLACK_SIGNING_SECRET_ID |
chatbot 才用,建 Slack App 流程见 docs/slack-setup.md |
API_GATEWAY_INVOKE_URL |
第一次跑 deploy-chatbot.sh 后自动打印,复制回 .env |
安全提示:千万不要
git add .env。脚本和 docs 都从.env读,<...>占位符只用于文档展示,不会走进 AWS API。
cd /home/ubuntu/tech/devops-agent
cp .env.example .env
# 编辑 .env 填真值后再继续。详见 §2.5 Configuration。./scripts/deploy.sh脚本步骤:
- 校验当前 caller 是
<ACCOUNT_ID> - 创建 IAM Role
DevOpsAgentDemoLambdaRole(含aidevops:*投放任务 / 读 chat / 拉 journal 权限) - 构建并发布
boto3-latestlayer(boto3 ≥ 1.43.0,含 devops-agent 客户端) - 部署 Lambda-A
devops-agent-trigger-investigation - 部署 Lambda-B
devops-agent-notify-slack - 创建 EventBridge Rule-1(任意 CloudWatch Alarm → Lambda-A)
- 创建 EventBridge Rule-2(Investigation Completed → Lambda-B)
- 打印资源 ARN
幂等:可重复执行,已存在资源走 update 路径。
PetSite 账号默认的 ContainerInsights node 告警已经全废(NodeName 写死了旧节点 IP)。要让 删 node 类故障真的能触发告警,跑:
./scripts/add-node-loss-alarms.sh会创建 4 个告警(period 60s, eval 1):
| 告警 | 触发条件 | 真实命中率(FIS 实测) |
|---|---|---|
petsite-asg-instances-below-desired-workers1a60 |
GroupInServiceInstances / GroupDesiredCapacity < 1.0(metric math) |
❌ 故障窗口 < 60s 时不触发 |
petsite-asg-instances-below-desired-workers1cFE |
同上 | 同上 |
petsite-eks-pods-unschedulable |
AWS/EKS scheduler_pending_pods_UNSCHEDULABLE > 0 |
✅ 命中(实测 60s 故障窗口能捕到) |
petsite-eks-apiserver-5xx |
AWS/EKS apiserver_request_total_5XX > 5/min |
关键经验:本账号
ContainerInsightsnamespace 完全没 metric(CW agent / fluent-bit 没安装或停了),所以最初尝试用cluster_node_count/cluster_failed_node_count都失败。最后落到AWS/EKS scheduler_pending_pods_UNSCHEDULABLE,这是 EKS 内置的 control-plane metric,不依赖任何 add-on,并且能真实反映"node 不够导致 pod 调度失败"。
devops-agent/
├── README.md ← 本文件
├── .env.example ← 配置模板(占位符,可入库)
├── .env ← 真实配置(.gitignore 排除,永远不入库)
├── .gitignore
├── iam/
│ ├── lambda-role-trust.json Lambda 信任策略
│ ├── devops-agent-policy.json aidevops:* 权限(含 chat)
│ ├── chatbot-policy.json.template chatbot 用 IAM policy 模板(账号 ID 用 ${AWS_ACCOUNT_ID} 占位,deploy 时 envsubst 渲染到 .build/iam/)
│ └── backups/
│ └── RestrictToTokyoRegion-*.json 旧 region 限制 policy 备份(已删除)
├── lib/
│ └── agent_chat.py DevOps Agent EventStream 解析与多轮对话(CLI + Lambda-C 共用)
├── lambda/
│ ├── cli/chat.py Chat CLI 入口
│ ├── lambda_a/lambda_function.py 触发调查(通用化 description)
│ ├── lambda_b/lambda_function.py Slack webhook 通知
│ └── lambda_c/ Slack Chatbot(交互式多轮)
│ ├── lambda_function.py entry + worker dispatcher
│ └── slack_verify.py Slack 签名验证
├── tests/ 71 测试覆盖 Lambda-A/B/C + 签名验证
├── eventbridge/
│ ├── rule-1-alarm-to-lambda-pattern.json
│ └── rule-2-investigation-completed-pattern.json
└── scripts/
├── deploy.sh 一键部署主链路(读 .env)
├── deploy-chatbot.sh 部署 Lambda-C + API GW + DLQ + alarms
├── setup-dashboard.sh provision CloudWatch Dashboard(deploy.sh 默认会调)
├── cleanup.sh 一键清理主链路
├── cleanup-chatbot.sh 清理 chatbot 资源
├── add-node-loss-alarms.sh PetSite 专用:加 EKS/ASG node-loss 告警
├── chat.sh Chat CLI wrapper
├── run-fis-2node-termination.sh PetSite 专用:FIS 删 2 个 node 验证脚本
└── test-trigger.sh (旧)stress-ng EC2 触发测试
除了异步的 Backlog Task 调查,本项目还提供一个 CLI 工具,调 create_chat + send_message 同步跟 Agent 聊天。
# 单轮问答
./scripts/chat.sh "List running EC2 instances"
# 交互式 REPL(多轮上下文)
./scripts/chat.sh -i
# 恢复之前的会话(多轮追问)
./scripts/chat.sh --resume <executionId> "follow-up question"
# 看到 executionId / token usage / tool calls / context utilization
./scripts/chat.sh --show-ids "..."
# 看到 Agent 的 thinking 过程
./scripts/chat.sh --show-thinking "..."实现要点(lambda/cli/chat.py):
-
send_message返回一个 EventStream,需要按 内容块类型 分流:block type 含义 默认是否展示 final_response最终回答 ✅ stdout textAgent 思考过程 ❌( --show-thinking才展)chat_title自动生成的会话标题 ❌( --show-ids才展)context_usage上下文窗口利用率 ❌ metadata tool_use/tool_summaryAgent 调用的 AWS API 详情 ❌( --show-ids才展) -
多轮对话靠复用同一个
executionId。create_chat只需一次。 -
usage.inputTokens/outputTokens可以跟踪成本。 -
context_usage.context_window.utilization超过 70% 考虑开新会话。
# 把所有 dimensions 都摊出来,不假设是 EC2
metric = metrics[0].get("metricStat", {}).get("metric", {})
namespace = metric.get("namespace", "")
metric_name = metric.get("name", "")
dimensions = metric.get("dimensions", {}) or {}
# ⚠️ accountId / region 在 EventBridge event 的 TOP LEVEL,不在 detail 里!
# 之前曾从 detail.get("accountId") 读,永远是空。现在从 event.get("account") 读。
account = event.get("account", "")
region = event.get("region", REGION)
description = (
# ===== Investigation starting point(与控制台字段对齐)=====
f"Investigation starting point:\n"
f" Source: CloudWatch Alarm '{alarm_name}'\n"
f" Account: {account} Region: {region}\n"
f" Namespace: {namespace}\n"
f" Metric: {metric_name}\n"
f" Dimensions:\n" + "\n".join(f" - {k}: {v}" for k, v in dimensions.items()) + "\n"
f" Alarm reason: {reason}\n\n"
# ===== Investigation details =====
f"Investigation details:\n"
f" Identify the root cause of this alarm. ..."
)| 告警类型 | Namespace | 关键 dimensions | 是否需改代码 |
|---|---|---|---|
| EC2 CPU 飙升 | AWS/EC2 |
InstanceId |
❌ 自适配 |
| RDS 连接耗尽 | AWS/RDS |
DBInstanceIdentifier |
❌ 自适配 |
| EKS pods unschedulable | AWS/EKS |
ClusterName |
❌ 自适配 |
| Lambda 错误率 | AWS/Lambda |
FunctionName |
❌ 自适配 |
| ALB 5xx | AWS/ApplicationELB |
LoadBalancer, TargetGroup |
❌ 自适配 |
- 复用
petsite-ops-slack-notifier用的 webhook - 标准库
urllib.request,无第三方依赖 - Block Kit 三段式:header / metadata fields / divider / summary chunks
- summary > 2900 字符自动按行分块(Slack mrkdwn block 单块 3000 字符上限)
- 仅在
data.status == "COMPLETED"才发,避免中间状态噪音
iam/devops-agent-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DevOpsAgentBacklog",
"Effect": "Allow",
"Action": [
"aidevops:CreateBacklogTask",
"aidevops:GetBacklogTask",
"aidevops:ListJournalRecords",
"aidevops:ListBacklogTasks"
],
"Resource": "*"
},
{
"Sid": "DevOpsAgentChat",
"Effect": "Allow",
"Action": [
"aidevops:CreateChat",
"aidevops:SendMessage",
"aidevops:ListChats",
"aidevops:ListPendingMessages"
],
"Resource": "*"
}
]
}
⚠️ IAM action 前缀是aidevops:(不是devops-agent:)。
Agent Space 服务角色另外说一下:
DevOpsAgentRole-AgentSpace-<RANDOM_SUFFIX>是 Agent 自己用来调 AWS API(EC2/RDS/EKS Describe)的角色,不归本项目管。它原本挂了一个RestrictToTokyoRegioninline policy 限制只能查 Tokyo,但副作用是 Agent 第一步 DescribeRegions 在 us-east-1 endpoint 就被 deny,导致整个调查链路失效。该 policy 已经在调试时删除(备份在iam/backups/),允许 Agent 跨 region 扫描(每次问答 ~30 个 tool call 是这个原因)。如需恢复 region 限制,参考官方文档 用 tag-based scoping 替代纯 region deny。
Rule-1(任意 ALARM → Lambda-A):
{
"source": ["aws.cloudwatch"],
"detail-type": ["CloudWatch Alarm State Change"],
"detail": { "state": { "value": ["ALARM"] } }
}Rule-2(Investigation Completed → Lambda-B):
{
"source": ["aws.aidevops"],
"detail-type": ["Investigation Completed"]
}除了 告警 → 调查 → 推送 Slack 这条异步主线,还部署了 Slack Chatbot:用户在 #<SLACK_CHANNEL_ID> 里 @devops_agent <问题> 即可调 DevOps Agent chat,结果推回同一 thread,同 thread 内多轮追问会复用同一个 chat session。
架构:
Slack @mention → Slack Events API → API Gateway HTTP API → Lambda-C
│ (fast path: ack 200 in <3s)
├→ lambda.invoke(self, _internal=chat) 异步自调用
│
└→ (worker path)
1. DDB get_item(thread_ts) 查/建 chat
2. devops-agent.send_message 调用
3. Slack chat.postMessage 占位占 + chat.update 替换
环境变量(都在 .env):
SLACK_BOT_TOKEN_SECRET_ID=devops-agent/slack-chatbot-token
SLACK_SIGNING_SECRET_ID=devops-agent/slack-signing-secret
SLACK_TEST_CHANNEL=<SLACK_CHANNEL_ID>
THREAD_TABLE_NAME=devops-agent-slack-threads
LAMBDA_C_NAME=devops-agent-slack-chatbot部署:
# 1. 创建 Slack App(手动一次)、拿 Bot Token + Signing Secret、存进 Secrets Manager(详见 docs/slack-setup.md)
# 2. 部署(脚本会自动建 DDB 表 + 启用 TTL,幂等可重复执行)
export ENV_FILE=.env
./scripts/deploy-chatbot.sh
# 输出里会包含 API GW Invoke URL,用于下一步
# 3. 在 Slack App 控制台配 Event Subscriptions(手动一次):
# Request URL = 上一步 Invoke URL
# Subscribe to bot events: app_mention
# Save → Reinstall App验证:
# 在 #<SLACK_CHANNEL_ID> 里:
@devops_agent ping # 得到 "Hey there!" 之类
@devops_agent list EC2 instances in <region> # 得到 EC2 列表
# 同 thread 追问(点上一条的 “Reply in thread”):
which one is biggest? # thread 内免 @(见§5.6 Slack 使用说明)详细使用说明、Slack App 配置、多 topic 场景、设计要点都集中在 §5.6 Slack 使用说明 里。
Slack App 一次性配置(详细步骤在 docs/slack-setup.md):
| 步骤 | 在哪做 | 做什么 |
|---|---|---|
| 1 | https://api.slack.com/apps → Create | 新建 App,名字随意(如 PetSite DevOps Agent) |
| 2 | OAuth & Permissions → Bot Token Scopes | 加 app_mentions:read、chat:write、channels:history、groups:history(私有频道时) |
| 3 | Event Subscriptions → Enable | Request URL 填 deploy-chatbot.sh 输出的 API GW URL;订阅 bot events: app_mention + message.channels |
| 4 | Install to Workspace | 拿到 Bot Token (xoxb-...) + Signing Secret,存进 Secrets Manager(脚本用) |
| 5 | 频道里 /invite @devops_agent |
把 bot 加进所有需要的频道(私有频道必须,公共频道也建议) |
正常使用方式:
| 场景 | 怎么发 | bot 行为 |
|---|---|---|
| 新问题(顶层) | @devops_agent <问题> 在频道顶层发 |
bot 创建新 chat session,回复以 thread 形式开启 |
| 同 thread 追问 | 直接在 thread 里发消息(不用 @) | bot 复用这个 thread 已有的 chat session(DDB hit),保留多轮上下文 |
| 老 thread 重新激活 | 7 天内同 thread 继续发 | bot 仍复用原 chat(DDB TTL 7 天) |
| 老 thread > 7 天 | 同 thread 发消息 | DDB 已过期 → 当作新问题处理(不可避免 |
| 新 topic(新 thread) | 必须 @devops_agent <问题> 重新顶层发 |
bot 开新 chat session,与之前 thread 隔离 |
| 老 channel 内不相关闲聊 | 不要 @ bot | bot 不响应(message.channels 命中后查 DDB,没记录就丢;EMF metric UnhandledMessageEvents 计数) |
- 不要在私有频道直接 mention,必须先
/invite @devops_agent加进去 - 不要把 bot 加进 #general 这种高噪声频道 —— 即使 message.channels 会 DDB 过滤掉 noise,也会消耗 Lambda invocation
- thread 内多轮对话期间,每条新消息消耗 1 次 Lambda 调用(fast-path ack + worker 异步),reserved concurrency 上限 5(脚本默认)
- bot 只看 thread_ts 不看用户身份:所以同 thread 内任何人发消息 bot 都会响应(设计上是为了协作场景;如果不想这样要在 worker 里加 user 白名单)
架构:
Slack @mention → Slack Events API → API Gateway HTTP API → Lambda-C
│ (fast path: ack 200 in <3s)
├→ event_id 幂等 check(DDB evt: 前缀)
├→ lambda.invoke(self, _internal=chat) 异步自调用
│
└→ (worker path)
1. DDB get_item(thread_ts) 查/建 chat
2. devops-agent.send_message
3. Slack chat.postMessage 占位 + chat.update 替换
关键设计点:
- Slack 3 秒响应规则 — fast path 必须在 3s 内 200 ack。同 Lambda 自异步调用(
InvocationType=Event)走 worker;fast path 实测 热启动 ~258ms / 冷启动 ~1019ms - 多轮对话 —
thread_ts作为 DDB PK,同 thread 复用同一executionId(chat session) - 双订阅幂等 —
app_mention+message.channels同一 user message 会触发两次 event,DDBevt:前缀的 ConditionalCheck 原子去重 - 安全 — Slack signing 验签(v0:timestamp:body HMAC-SHA256)+ 5min replay protection +
hmac.compare_digest时序安全比较 - TTL — thread 表 7 天后自动过期;超过一周没人提的 thread 下次会重启会话
为什么没用 Incoming Webhook:webhook 只能单向发,不能接 Slack events,双向必须 Bot Token。Lambda-B(推 Slack 摘要)走 webhook 即可;Lambda-C(多轮交互)必须走 Bot Token + chat.postMessage / chat.update。
单元测试覆盖:tests/test_slack_verify.py 12 个场景(正常签名 / 大写 header / 过期时间戳 / 未来时间戳 / 篡改 body / 错 secret / 缺 header / 空 secret / 非 bytes body / timing-safe 比对 等);tests/test_lambda_c.py 25 个场景(fast/worker path、event 幂等、thread reply 自动响应、race-loser LWW 等)。
./scripts/run-fis-2node-termination.sh会克隆账号里现成的 EKS node termination 模板(EXT25AmEAp21foyA),改 instanceTerminationPercentage=100,删 PetSite/workers1a60 nodegroup 的全部 2 个 node。
复用账号里现有的 openclaw-health-canary Synthetic(rate 30 min,频率较低)。如需更密集流量验证 ALB 5xx 告警:
# 临时压一会儿
hey -z 5m -c 5 http://<your-alb-dns-name>/| 时间点 | 事件 | 备注 |
|---|---|---|
| T+0s | FIS start-experiment |
状态 initiating |
| T+13s | FIS completed |
2 个 1a node 已被 terminate |
| T+1min | ASG 补上 1 个新 node | minSize=2 强制 |
| T+2min | petsite-eks-pods-unschedulable OK → ALARM(3 pods unschedulable) |
✅ 告警捕到 |
| T+2min | EventBridge → Lambda-A → create_backlog_task |
DevOps Agent 接到 |
| T+3min | 告警 ALARM → OK(pods 已重新调度) | 故障窗口约 60s |
| T+5–15min | DevOps Agent 调查完成,发 Investigation Completed 事件 |
|
| T+5–15min | Lambda-B 拉 journal → Slack <SLACK_CHANNEL_ID> |
收到 markdown 摘要 |
source .env # so AWS_REGION / DEVOPS_AGENT_SPACE_ID are loaded into shell
# Lambda-A 实时日志
aws logs tail /aws/lambda/devops-agent-trigger-investigation --follow --region "${AWS_REGION}"
# Lambda-B 实时日志
aws logs tail /aws/lambda/devops-agent-notify-slack --follow --region "${AWS_REGION}"
# 当前 Investigation 任务
python3 -c "
import os, boto3, json
c = boto3.client('devops-agent', region_name=os.environ['AWS_REGION'])
r = c.list_backlog_tasks(agentSpaceId=os.environ['DEVOPS_AGENT_SPACE_ID'], filter='taskType=INVESTIGATION')
print(json.dumps(r['tasks'][:5], default=str, indent=2))
"- ASG
GroupInServiceInstances < Desired告警没触发 — 故障窗口 < 60s,1-min period 的告警没采到 < 1.0 的 datapoint。删 node 类故障 ASG 告警不可靠 AWS/EKS scheduler_pending_pods_UNSCHEDULABLE是关键检测点 — 这是 EKS 内置 metric,反映 实际症状(pod 找不到 node)。建议作为 EKS 集群的 baseline alarm- ContainerInsights metric 在本账号不存在 — 任何依赖
cluster_node_count/cluster_failed_node_count的告警都会 INSUFFICIENT_DATA - Bug found & fixed:原 Lambda-A 从
event['detail'].get('accountId')读 account ID,但 EventBridge 事件结构里account和region是 top-level 字段。已改成event.get('account')/event.get('region')
./scripts/cleanup.sh # 主链路(IAM role / Lambda-A&B / 两个 EventBridge 规则)
./scripts/cleanup-chatbot.sh # chatbot(Lambda-C / API Gateway / SlackChatbotAccess inline policy)会删:IAM role、3 个 Lambda、2 个 EventBridge 规则、API Gateway HTTP API。 不会删(需手动):
- boto3 layer 版本(layer 是按账号共享,cleanup 留下避免误删别的项目)
- Agent Space
- 3 个 SQS DLQ(
devops-agent-trigger-dlq/devops-agent-notify-dlq/devops-agent-slack-chatbot-dlq) - CloudWatch Alarms(
devops-agent-slack-chatbot-{errors,apigw-5xx,ddb-throttle}+ 4 个 node-loss 告警) - CloudWatch Dashboard
DevOpsAgent-PetSite - DynamoDB
devops-agent-slack-threads - Secrets Manager 三个 secret(webhook URL / bot token / signing secret)
- Slack webhook + Slack App
手动清理示例:
# DLQ
for q in devops-agent-trigger-dlq devops-agent-notify-dlq devops-agent-slack-chatbot-dlq; do
url=$(aws sqs get-queue-url --region "${AWS_REGION}" --queue-name "$q" --query QueueUrl --output text 2>/dev/null) \
&& aws sqs delete-queue --region "${AWS_REGION}" --queue-url "$url"
done
# Dashboard
aws cloudwatch delete-dashboards --region "${AWS_REGION}" --dashboard-names DevOpsAgent-PetSite
# Chatbot alarms
aws cloudwatch delete-alarms --region "${AWS_REGION}" --alarm-names \
devops-agent-slack-chatbot-errors \
devops-agent-slack-chatbot-apigw-5xx \
devops-agent-slack-chatbot-ddb-throttle
# Node-loss alarms (PetSite-specific)
aws cloudwatch delete-alarms --region "${AWS_REGION}" --alarm-names \
petsite-asg-instances-below-desired-workers1a60 \
petsite-asg-instances-below-desired-workers1cFE \
petsite-eks-pods-unschedulable \
petsite-eks-apiserver-5xx{
"source": "aws.aidevops",
"detail-type": "Investigation Completed",
"detail": {
"version": "1.0.0",
"metadata": {
"agent_space_id": "<AGENT_SPACE_UUID>",
"task_id": "<UUID>",
"execution_id": "exe-ops1-<UUID>"
},
"data": {
"task_type": "INVESTIGATION",
"priority": "HIGH",
"status": "COMPLETED",
"created_at": "...",
"updated_at": "...",
"summary_record_id": "<UUID>"
}
}
}| recordType | 说明 |
|---|---|
investigation_summary_md |
完整调查摘要(Markdown)—— Lambda-B 用这个发 Slack |
investigation_summary |
结构化摘要(JSON) |
symptom |
发现的症状 |
finding |
调查发现(含根因) |
observation |
观测数据 |
investigation_gap |
调查信息缺口 |
message |
Agent 对话消息 |
全部用占位符;运行
bash scripts/print-config.sh或自己看 .env /aws sts get-caller-identity拿到真值。
Account: <ACCOUNT_ID>
Region: <region>
Agent Space: <agent-space-name>
Agent Space ID: <AGENT_SPACE_UUID>
Agent Space ARN: arn:aws:aidevops:<region>:<ACCOUNT_ID>:agentspace/<AGENT_SPACE_UUID>
Slack Channel: <SLACK_CHANNEL_ID>
Slack Webhook: <secret> arn:aws:secretsmanager:<region>:<ACCOUNT_ID>:secret:devops-agent/slack-webhook-url-<RANDOM_SUFFIX>
Slack App (bot): <slack-app-id> (PetSite DevOps Agent)
Slack Bot User: <slack-bot-user-id> (devops_agent)
Slack Bot ID: <slack-bot-id>
Bot Token Secret: arn:aws:secretsmanager:<region>:<ACCOUNT_ID>:secret:devops-agent/slack-chatbot-token-<RANDOM_SUFFIX>
Sign Secret: arn:aws:secretsmanager:<region>:<ACCOUNT_ID>:secret:devops-agent/slack-signing-secret-<RANDOM_SUFFIX>
IAM Role (Lambda): arn:aws:iam::<ACCOUNT_ID>:role/DevOpsAgentDemoLambdaRole
IAM Role (Agent): arn:aws:iam::<ACCOUNT_ID>:role/service-role/DevOpsAgentRole-AgentSpace-<RANDOM_SUFFIX>
Lambda-A: arn:aws:lambda:<region>:<ACCOUNT_ID>:function:devops-agent-trigger-investigation
Lambda-B: arn:aws:lambda:<region>:<ACCOUNT_ID>:function:devops-agent-notify-slack
Lambda-C: arn:aws:lambda:<region>:<ACCOUNT_ID>:function:devops-agent-slack-chatbot
API Gateway: <API_ID>
API Invoke URL: https://<API_ID>.execute-api.<region>.amazonaws.com/slack/events
DDB Thread Table: arn:aws:dynamodb:<region>:<ACCOUNT_ID>:table/devops-agent-slack-threads
Layer (boto3): arn:aws:lambda:<region>:<ACCOUNT_ID>:layer:boto3-latest:1
Rule-1: DevOps-Agent-Demo-Alarm-To-Lambda
Rule-2: DevOps-Agent-Investigation-Completed
EKS Cluster: <eks-cluster-name>
PetSite ALB: <alb-dns-name>