lenaxia/k8s-mechanic
GitHub: lenaxia/k8s-mechanic
一个运行在集群内的 Kubernetes 智能故障修复控制器,通过 LLM agent 自动调查故障根因并在 GitOps 仓库中提交带修复建议的 Pull Request。
Stars: 36 | Forks: 1
# k8s-mechanic
k8s-mechanic 是一个 Kubernetes 控制器,用于监控集群中的故障,
自动进行调查,并在您的 GitOps 仓库中创建带有修复建议的 Pull Request ——
这一切都无需离开您的集群。
当 Pod 发生 CrashLoopBackOff、Deployment 性能下降或节点进入 NotReady 状态时,
mechanic 会生成一个集群内的 [OpenCode](https://opencode.ai) agent,
它会检查运行中的集群,在您的 GitOps 仓库中定位相关的 manifest,
确定根本原因,并开启一个 PR。您只需审查并合并即可。无需外部 operator,
无需外部数据库,也无需在集群外运行持久服务。
## 功能概述
1. **检测故障** — 通过 Kubernetes API 原生监控 Pod、Deployment、StatefulSet、PVC、Node 和 Job
2. **按父资源去重** — 同一个 Deployment 的重复 Pod 重启只会触发一次调查,而不是每次重启触发一次
3. **稳定后再行动** — 可配置的时间窗口(默认:120秒)在分发任务前过滤掉瞬态故障
4. **集群内调查** — 一个具有只读 RBAC 权限的 agent Job 会克隆您的 GitOps 仓库,并检查运行中的集群
5. **开启 PR** — 包含结构化的 PR 正文:摘要、证据、根本原因、修复建议和置信度
**每次调用的三种可能结果:**
| 结果 | 何时发生 | 动作 |
|---|---|---|
| 修复 PR | 确定了根本原因,置信度为中或高 | 开启一个带有针对性 manifest 更改的 PR |
| 调查 PR | 根本原因不明或置信度低 | 开启一个带有调查报告并标记为 `needs-human-review` 的 PR |
| 评论 | 该指纹已存在开启的 PR | 评论最新的发现;不创建新 PR |
Agent prompt 中强制执行的硬性约束:永远不直接提交到 `main` 分支;
永远不触碰 GitOps 仓库中的 Kubernetes Secrets;每次调用只产生一个结果。
## 功能特性
**[OpenCode Agentic 工作流](docs/WORKLOGS/0071_2026-02-23_epic08-pluggable-agent-complete.md)** — 调查由运行在您集群内的 [OpenCode](https://opencode.ai) 驱动。
支持任何 OpenAI 兼容的 LLM endpoint。计划推出更多 agent 后端。
**[检测](docs/WORKLOGS/0033_2026-02-22_epic09-native-provider-complete.md)** — 原生监控 Pod、Deployment、StatefulSet、PVC、Node 和 Job。
覆盖 `CrashLoopBackOff`、`ImagePullBackOff`、`OOMKilled`、降级的 Deployment、不可调度的 Pod、失败的 Job、PVC 供应失败以及不健康的 Node。
**[去重](docs/WORKLOGS/0011_2026-02-20_epic01-controller-core-logic.md)** — 发现的问题会根据父资源指纹
(`sha256(namespace + kind + parentObject + sorted errors)`) 进行去重。
同一个 Deployment 的重复 Pod 重启只会产生一次调查。
状态存储在 `RemediationJob` CRD 对象中 —— 可在 watcher 重启后保留,无需外部存储。
**[严重性分级](docs/WORKLOGS/0075_2026-02-24_epic24-severity-tiers-complete.md)** — 每个发现的问题都会根据检测到的状况被分类为 `critical`、`high`、`medium` 或 `low`
(例如:CrashLoopBackOff 重启 >5 次 → critical;OOMKilled → high;
降级但可用的 Deployment → medium)。watcher Deployment 上的 `MIN_SEVERITY` 环境变量
将抑制低于配置阈值的发现。Agent 在运行时接收严重性
并相应地调整其调查深度 —— 对于 critical 级别达到最大的彻底性,对于 low 级别采取保守的
最小变更建议。
**[稳定窗口](docs/WORKLOGS/0030_2026-02-22_epic09-story12-stabilisation-window.md)** — 一个可配置的等待期(默认:120秒)在分发调查之前
抑制瞬态故障。
**[并发节流](docs/WORKLOGS/0011_2026-02-20_epic01-controller-core-logic.md)** — `maxConcurrentJobs`(默认:3)限制了同时运行的 agent
Job。超出的发现会作为 `Pending` 排队,并在有可用槽位时分发。
**[可自定义的 Agent Prompt](docs/WORKLOGS/0071_2026-02-23_epic08-pluggable-agent-complete.md)** — 调查 prompt 从 ConfigMap 挂载,
并可以通过 `values.yaml` 中的 `prompt.coreOverride` / `prompt.agentOverride`
完全覆盖。
**[Prometheus 指标](docs/WORKLOGS/0038_2026-02-23_epic10-helm-chart-implementation.md)** — 可选的指标 Service 和 Prometheus Operator
`ServiceMonitor`,用于 watcher 健康状况的可观测性。
**[自动关闭已解决的发现](docs/WORKLOGS/0091_2026-02-26_epic26-auto-close-resolved.md)** — 当 Kubernetes 发现的问题被清除时(Deployment 恢复、
PVC 供应完成、Node 恢复 Ready),watcher 会自动关闭 agent 开启的 GitHub PR。
适用于进行中的 Job(Pending/Dispatched/Running)以及集群自我修复后 PR 已过期的已成功 Job。
直接通过 REST API 使用 GitHub App 安装令牌 —— watcher 中不需要 `gh` CLI。
可通过 `watcher.prAutoClose: false` 选择退出。
**[感知 PR 合并的去重](https://github.com/lenaxia/k8s-mechanic/pull/23)** — 当 mechanic 开启的 PR 被合并时,
`RemediationJob` 会以较短的 TTL(1小时)被标记为逻辑删除,而不是默认的 7 天 TTL。
这可以防止在 GitOps 协调器应用修复且集群稳定期间,相同的发现被立即重新调查。
**[Dry-run 模式](docs/WORKLOGS/0089_2026-02-25_epic20-dry-run-mode.md)** — 设置 `watcher.dryRun: true` 以运行完整的调查
管道而不开启任何 PR。Agent 会生成一份调查报告,写入到名为
`mechanic-dryrun-` 的 ConfigMap 中。适用于在 staging 环境中验证 mechanic 行为,
或在生产环境中启用新 LLM 模型之前进行验证。
**[GitHub App 令牌过期保护](docs/WORKLOGS/0088_2026-02-25_epic22-token-expiry-guard.md)** — 主 agent 容器在继续之前会检查
安装令牌的过期时间。如果令牌已过期或将在 60 秒内过期,Job 会快速失败并显示明确的错误,
而不是在调查深入时静默失败。
**[强制 Manifest 验证](docs/WORKLOGS/0087_2026-02-25_epic18-manifest-validation.md)** — 在向 GitOps 仓库提交任何更改之前,
agent 会对每个修改过的 manifest 运行 `kubeconform`(对于 overlay 更改还会运行
`kustomize build`)。如果验证失败,agent 会开启一个标记为 `validation-failed` 的占位 PR,
其中包含完整的错误输出,而不是提交一个不符合 schema 的 manifest。
### 安全性
**[Secret 脱敏](docs/WORKLOGS/0054_2026-02-23_story01-secret-redaction.md)** — 从集群状态(pod `Waiting.Message`、
节点状况消息等)提取的错误文本,在存储到 `RemediationJob` 或注入到 agent 之前,
会经过脱敏过滤器。模式包括 URL 凭证、
长度 ≥ 40 个字符的 base64 编码值,以及常见的 secret 密钥前缀(`password=`、
`token=`、`api-key=` 等)。
**[Prompt 注入检测](docs/WORKLOGS/0055_2026-02-23_story05-prompt-injection-defence.md)** — `Finding.Errors` 的每个字段限制为 500 个字符,
并在 prompt 中包装在显式的不可信数据信封中。注入
启发式规则(`ignore.*previous.*instructions`)会被检测并记录;可配置为
完全抑制该发现(`INJECTION_DETECTION_ACTION=suppress`)。
**[Agent 网络策略](docs/WORKLOGS/0057_2026-02-23_story02-network-policy.md)** — 可选的 `NetworkPolicy` 将 agent Job 的出站流量限制为
集群 API server、GitHub 和 LLM endpoint。通过 `values.yaml` 中的
`networkPolicy.enabled: true` 启用。需要支持 `NetworkPolicy` 的 CNI(Cilium、Calico 等)。
**[只读 Agent RBAC](docs/WORKLOGS/0016_2026-02-20_epic04-deploy-manifests.md)** — agent 仅在集群范围内持有 `get/list/watch` 权限。
它不能创建、修改或删除任何 Kubernetes 资源。所有集群更改都通过
Git 和您的 GitOps 协调器进行。
**[Namespace 作用域的 Agent RBAC](docs/WORKLOGS/0058_2026-02-23_story04-agent-rbac-scoping.md)** — `AGENT_RBAC_SCOPE=namespace` 将 agent 从
集群范围的 `ClusterRole` 切换为 namespace 作用域的 `Role`,从而限制 agent 只能读取您指定的 namespace。
**[结构化审计日志](docs/WORKLOGS/0056_2026-02-23_story03-audit-log.md)** — 所有抑制和分发决策都会发出带有 `audit: true` 的结构化日志行,
可从任何日志聚合系统(Loki、Elasticsearch、Datadog)查询,用于事后取证。
**[Trivy CVE 扫描](https://github.com/lenaxia/k8s-mechanic/actions)** — 每次发布时都会使用 [Trivy](https://trivy.dev) 扫描 `mechanic-watcher` 和 `mechanic-agent` 镜像
(`CRITICAL` 和 `HIGH`,忽略不可修复的)。如果检测到任何可修复的漏洞,构建将失败。上游预构建二进制文件中不可修复的 CVE(尚未使用所需 Go 版本发布的工具)
记录在 [`.trivyignore`](.trivyignore) 中,并带有用于重新评估的强制性到期日期。
**[短期 GitHub 凭证](docs/WORKLOGS/0014_2026-02-20_epic03-agent-image-complete.md)** — agent 永远不会持有长期有效的 PAT。在 init 容器中交换 GitHub
App 安装令牌(1小时 TTL),并且永远不会暴露给主 agent 容器。
#### 强化模式
强化模式(`values.yaml` 中的 `agent.hardenKubectl: true`,默认关闭)在以下
始终开启的默认设置之上增加了额外的读取限制。
**始终开启(无论是否启用强化模式)**
| 控制项 | 作用 |
|---|---|
| **kubectl 写入阻止** | `apply`、`create`、`delete`、`edit`、`patch`、`replace`、`scale`、`label`、`annotate`、`taint`、`drain`、`cordon`、`uncordon`、`rollout restart/undo` —— 全部以退出码 1 终止。所有集群更改都通过 Git 和您的 GitOps 协调器进行。 |
| **kubectl 输出脱敏** | 所有 `kubectl` 输出都通过管道传递给 `redact` 二进制文件。任何匹配已知 secret 模式(`base64 ≥ 40 个字符`、`password=…`、`token=…` 等)的值都将被替换为 `[REDACTED]`。如果缺少 `redact`,wrapper 将硬性失败。 |
| **工具输出脱敏** | `helm`、`flux`、`sops`、`talosctl`、`yq`、`stern`、`kubeconform`、`kustomize`、`age`、`age-keygen` 和 `gh` 都具有 PATH 覆盖包装器,通过管道将输出传递给 `redact`。包装器闭包失败 —— 如果 `redact` 二进制文件不存在,工具将以退出码 1 终止。 |
| **环境变量中无 secret** | agent 进程环境中不存在任何凭证或 API 密钥。所有秘密材料在 agent 启动前写入文件,并从环境中完全移除。 |
**仅在强化模式下**
| 控制项 | 增加的功能 |
|---|---|
| **kubectl secret 阻止** | `get secret(s)`、`describe secret(s)` 和 `get all` 被阻止。Kubernetes Secrets 永远不会通过 `kubectl` 到达 LLM 上下文。 |
| **kubectl exec / port-forward 阻止** | `exec` 和 `port-forward` 被阻止。agent 无法开启交互式会话或将端口转发到集群工作负载。 |
**数据渗出测试**
安全控制措施在常规的红蓝对抗中得到了验证。结果和完整的渗出泄漏记录位于
[`docs/SECURITY/EXFIL_LEAK_REGISTRY.md`](docs/SECURITY/EXFIL_LEAK_REGISTRY.md)。
## 快速开始
### 前置条件
- Kubernetes >= 1.28
- Helm >= 3.14
- 一个安装在 GitOps 仓库上的 GitHub App,具有以下权限:Contents (write)、Pull Requests (write)、Issues (write)
- 兼容 OpenAI 的 LLM API 密钥
#### GitHub App 权限
| 权限 | 级别 | 目的 |
|---|---|---|
| Contents | Write | 克隆仓库、创建分支、推送更改 |
| Pull requests | Write | 创建和评论 Pull Request |
| Issues | Write | 在 PR 描述中引用 issue |
### 1. 创建所需的 Secret
```
kubectl create namespace mechanic
```
#### github-app
`github-app` Secret 必须包含三个键:
```
apiVersion: v1
kind: Secret
metadata:
name: github-app
namespace: mechanic
stringData:
app-id: "" # numeric ID from https://github.com/settings/apps/
installation-id: "12345678" # numeric ID from the installation URL (see below)
private-key: |
```
**App ID** 显示在您的 GitHub App 设置页面上,地址为 `https://github.com/settings/apps/`。
每个用户创建自己的 GitHub App;项目作者无法看到您的凭证、令牌或仓库。
**Installation ID** 是您查看应用安装情况时 URL 中的数字后缀:
`https://github.com/organizations//settings/installations/`
(个人账户:`https://github.com/settings/installations/`)。
它也可以通过 App JWT 认证的 `GET https://api.github.com/app/installations` 返回。
私钥仅在 agent Job 的 init 容器中用于交换短期安装令牌(1小时 TTL)。
它永远不会被注入到主 agent 容器中。
#### llm-credentials-opencode
`llm-credentials-opencode` secret 将完整的
[OpenCode 配置](https://opencode.ai/docs) 作为其 `provider-config` 键保存。
正确的 schema 应将 `model` 作为**顶层**键(格式:`"/"`);
`options` 属于 `provider.` **内部**,而不是在根级别。
OpenCode 是目前唯一可用的 agentic provider,更多选项将在稍后推出。
**原生 OpenAI (`api.openai.com`)**
```
apiVersion: v1
kind: Secret
metadata:
name: llm-credentials-opencode
namespace: mechanic
stringData:
provider-config: |
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"openai": {
"apiKey": "sk-"
}
},
"model": "openai/gpt-4o"
}
```
**自定义 OpenAI 兼容端点(自托管、Ollama、Azure 等)**
对于任何不是 `api.openai.com` 的端点,或者使用未在内置 OpenAI provider 中注册的模型名称,
您必须使用 `"npm": "@ai-sdk/openai-compatible"` 定义一个自定义 provider。
您不能将内置的 `openai` provider 用于不同的 base URL。
```
apiVersion: v1
kind: Secret
metadata:
name: llm-credentials-opencode
namespace: mechanic
stringData:
provider-config: |
{
"$schema": "https://opencode.ai/config.json",
"provider": {
"myprovider": {
"npm": "@ai-sdk/openai-compatible",
"name": "My Provider",
"options": {
"baseURL": "https://my-llm-endpoint/v1",
"apiKey": "sk-"
},
"models": {
"my-model-id": {
"name": "My Model Name"
}
}
}
},
"model": "myprovider/my-model-id"
}
```
**其他 provider**
OpenCode 支持 75 个以上的 provider。任何具有 OpenAI 兼容 API 的 provider
(Ollama、LM Studio、llama.cpp、Azure OpenAI、Groq、Together AI、OpenRouter、
DeepSeek 等等)都可以使用上述自定义 provider 模式运行。
对于内置 provider(Anthropic、Amazon Bedrock、Google Vertex AI、GitHub
Copilot 等),配置结构略有不同 —— 请查阅 OpenCode 文档中的完整 provider 目录:
- **Provider 目录** — [opencode.ai/docs/providers](https://opencode.ai/docs/providers/)
- **内置 provider**(Anthropic、Bedrock、Vertex、Groq 等)— 每个的配置示例
- **自定义 provider 模式** — [opencode.ai/docs/providers#custom-provider](https://opencode.ai/docs/providers/#custom-provider)
### 2. 使用 Helm 安装
```
helm install mechanic oci://ghcr.io/lenaxia/charts/mechanic \
--namespace mechanic \
--create-namespace \
--set gitops.repo=myorg/my-gitops-repo \
--set gitops.manifestRoot=kubernetes
```
或从本地克隆安装:
```
helm install mechanic charts/mechanic/ \
--namespace mechanic \
--set gitops.repo=myorg/my-gitops-repo \
--set gitops.manifestRoot=kubernetes
```
### 3. 验证
```
kubectl get deployment -n mechanic
kubectl get rjob -n mechanic
# 显示特定 RemediationJob 的 lifecycle events:
kubectl describe rjob -n mechanic
```
## 配置
### Helm values 参考
所有 `values.yaml` 键及其默认值:
| 键 | 默认值 | 描述 |
|---|---|---|
| `image.repository` | `ghcr.io/lenaxia/mechanic-watcher` | Watcher 镜像仓库 |
| `image.tag` | `""` (使用 `Chart.appVersion`) | Watcher 镜像标签 |
| `image.pullPolicy` | `IfNotPresent` | 镜像拉取策略 |
| `agent.image.repository` | `ghcr.io/lenaxia/mechanic-agent` | Agent 镜像仓库 |
| `agent.image.tag` | `""` (使用 `Chart.appVersion`) | Agent 镜像标签 |
| `gitops.repo` | **必填** | GitOps 仓库,格式为 `org/repo` |
| `gitops.manifestRoot` | **必填** | manifest 根目录在仓库中的路径 |
| `watcher.stabilisationWindowSeconds` | `120` | 发现的问题在分发前必须持续的秒数 |
| `watcher.maxConcurrentJobs` | `3` | 最大同时运行的 agent Job 数 |
| `watcher.minSeverity` | `low` | 分发的最低严重性:`critical`、`high`、`medium` 或 `low` |
| `watcher.remediationJobTTLSeconds` | `604800` | 已完成的 RemediationJob 对象的 TTL(7天) |
| `watcher.sinkType` | `github` | 创建 PR 的接收器类型 |
| `watcher.logLevel` | `info` | 日志级别:debug、info、warn、error |
| `watcher.llmProvider` | `openai` | LLM 就绪门控:`openai` 启用;留空则禁用 |
| `watcher.injectionDetectionAction` | `log` | 当提示注入启发式规则触发时要执行的操作:`log` 或 `suppress` |
| `watcher.maxInvestigationRetries` | `3` | 每个 `RemediationJob` 在永久失败之前的最大 Job 重试次数 |
| `watcher.agentRBACScope` | `cluster` | agent 的 RBAC 作用域:`cluster` 或 `namespace` |
| `watcher.agentWatchNamespaces` | `""` | agent RBAC 作用域的逗号分隔命名空间。当 `agentRBACScope=namespace` 时为必填 |
| `watcher.watchNamespaces` | `""` | watcher 监控故障的逗号分隔命名空间。留空 = 所有命名空间 |
| `watcher.excludeNamespaces` | `""` | watcher 忽略的逗号分隔命名空间。留空 = 不排除 |
| `agentType` | `opencode` | Agent 运行器类型:`opencode`(可用)或 `claude`(存根,尚不可用)。控制使用哪个 `llm-credentials-` Secret。Secret 名称是编译时常量 —— 它们不能通过 Helm values 覆盖。 |
| `prompt.coreOverride` | `""` | 完整核心提示覆盖(替换内置的 `files/prompts/core.txt`) |
| `prompt.agentOverride` | `""` | 完整 agent 提示覆盖(替换内置的 `files/prompts/.txt`) |
| `rbac.create` | `true` | 创建 RBAC 资源 |
| `createNamespace` | `false` | 如果 `Release.Namespace` 不存在则创建它 |
| `metrics.enabled` | `false` | 在端口 8080 上暴露指标 Service |
| `metrics.serviceMonitor.enabled` | `false` | 创建 Prometheus Operator ServiceMonitor |
| `metrics.serviceMonitor.interval` | `30s` | Prometheus 抓取间隔 |
| `metrics.serviceMonitor.scrapeTimeout` | `10s` | Prometheus 抓取超时 |
| `metrics.serviceMonitor.labels` | `{}` | ServiceMonitor 的额外标签 |
| `networkPolicy.enabled` | `false` | 将 agent Job 的出站流量限制为 API server、GitHub 和 LLM endpoint |
| `networkPolicy.apiServerPort` | `6443` | Kubernetes API server 端口(某些发行版使用 `443`) |
| `networkPolicy.additionalEgressRules` | `[]` | 逐字附加的额外出站规则(例如,通过 CIDR 限制 LLM endpoint) |
| `watcher.prAutoClose` | `true` | 当底层发现的问题解决时自动关闭 GitHub PR。设置为 `false` 以保留 PR 等待手动审查 |
| `watcher.dryRun` | `false` | 运行完整的调查管道而不开启 PR。报告将写入到名为 `mechanic-dryrun-` 的 ConfigMap 中 |
| `agent.hardenKubectl` | `false` | 启用强化模式 —— 除了始终开启的写入阻止外,还阻止 `kubectl get/describe secret`、`get all`、`exec` 和 `port-forward` |
### 配置验证
Watcher 在启动时验证配置并显示明确的错误消息。
**数值验证:**
- `MAX_CONCURRENT_JOBS`:必须 > 0
- `REMEDIATION_JOB_TTL_SECONDS`:必须 > 0
- `STABILISATION_WINDOW_SECONDS`:必须 ≥ 0
**枚举验证:**
- `MIN_SEVERITY`:必须是 `critical`、`high`、`medium`、`low` 之一(如果不存在,默认为 `low`)
**格式验证:**
- `GITOPS_REPO`:必须是 `owner/repo` 格式
## 工作原理
```
%%{init: {'flowchart': {'curve': 'linear'}}}%%
flowchart TD
subgraph watcher["mechanic-watcher — Deployment"]
SPR["SourceProviderReconcilers
one per resource type
─────────────────────
watches Pods, Deployments,
StatefulSets, PVCs, Nodes, Jobs
extracts findings
deduplicates by fingerprint"] RJR["RemediationJobReconciler
─────────────────────
watches RemediationJob CRDs
enforces MAX_CONCURRENT_JOBS
syncs Job status back"] end RJ["RemediationJob CRDs
rjob
─────────────────────
durable dedup state
survives restarts"] AJ["mechanic-agent Job
one per finding
─────────────────────
init: git clone repo
main: opencode run
kubectl read-only
gh pr create"] GH["GitOps repository
GitHub"] SPR -->|creates| RJ RJ -->|watched by| RJR RJR -->|creates| AJ AJ -->|opens PR| GH ``` ### Agent 的工作内容 Agent 在集群内以只读 RBAC 权限运行 [OpenCode](https://opencode.ai),并遵循结构化的调查流程: 1. 检查此指纹是否已有开启的 PR —— 如果找到,则在其上评论并退出 2. 对故障资源执行 `kubectl describe` 和 `kubectl get events` 3. 检查相关资源(所属 Deployment、Endpoints、PV 等) 4. 在克隆的 GitOps 仓库中定位相关的 manifest 5. 使用 `flux get all` 和 `helm list` 检查 Flux/Helm 状态 6. 确定根本原因并分配置信度(高 / 中 / 低) 7. 使用 `kubeconform` 和 `kustomize build` 验证建议的更改 8. 开启一个包含结构化正文的 pull request:摘要、证据、根本原因、修复建议、置信度 ### `RemediationJob` CRD 每个唯一的发现都由一个 `RemediationJob` 对象(`rjob`)跟踪。 ``` kubectl get rjob -n mechanic ``` ``` NAME PHASE KIND PARENT JOB AGE mechanic-a3f9c2b14d8e Succeeded Pod Deployment/my-app mechanic-agent-a3f9c2b14d8e 8m mechanic-7bc1d3e90f21 Dispatched Deployment Deployment/api-server mechanic-agent-7bc1d3e90f21 2m mechanic-f4e2a1c85b67 Failed Node Node/worker-03 1h ``` #### RemediationJob 生命周期 ``` stateDiagram-v2 [*] --> Pending : finding detected Pending --> Dispatched : concurrent-job slot available Pending --> Cancelled : source object deleted Dispatched --> Running : Job pod scheduled Running --> Succeeded : agent Job completed Running --> Failed : exit non-zero or deadline exceeded Running --> Cancelled : source object deleted Failed --> Dispatched : retry (RetryCount < MaxRetries) Failed --> PermanentlyFailed : RetryCount >= MaxRetries Succeeded --> [*] Cancelled --> [*] PermanentlyFailed --> [*] ``` - **Pending** — 已检测到发现的问题,正在等待并发 Job 槽位 - **Dispatched** — 已创建 `batch/v1 Job`,等待 Pod 调度 - **Running** — agent Pod 正在执行 - **Succeeded** — agent Job 已完成;如果开启了 PR,`status.prRef` 将包含 PR URL - **Failed** — agent Job 失败(退出码非零或超过截止时间);如果 `RetryCount < MaxRetries` 则重新排队 - **PermanentlyFailed** — `RetryCount` 已达到 `MaxRetries`;不再分发;可通过 `kubectl describe rjob` 查看
- **Cancelled** — 调查进行期间源对象被删除
### 按资源注释控制
三个注释控制着 mechanic 在任何受监控资源(Pod、Deployment、StatefulSet、PVC、Node、Job)
或整个 Namespace 上的行为:
| 注释 | 值 | 效果 |
|---|---|---|
| `mechanic.io/enabled` | `"false"` | 永久抑制来自此资源的所有发现 |
| `mechanic.io/skip-until` | `"YYYY-MM-DD"` | 抑制发现直到此日期的 UTC 当天结束 |
| `mechanic.io/priority` | `"critical"` | 绕过稳定窗口 —— 立即分发 |
**示例:**
```
# 永久禁用对某个 deployment 的 investigations
kubectl annotate deployment my-app mechanic.io/enabled=false
# 将吵闹的 node 静音至维护窗口之后
kubectl annotate node worker-03 mechanic.io/skip-until=2026-03-15
# 在一个关键的 deployment 上立即 dispatch(无 stabilisation window)
kubectl annotate deployment api-server mechanic.io/priority=critical
```
**Namespace 级别控制:** 对 `Namespace` 对象本身进行注释将应用于该命名空间中的所有资源。
这将抑制每一个发现,而不管资源自身的注释如何:
```
# 禁用 kube-system namespace 中的所有 mechanic 活动
kubectl annotate namespace kube-system mechanic.io/enabled=false
# 将 staging 中的所有 findings 抑制至指定日期
kubectl annotate namespace staging mechanic.io/skip-until=2026-04-01
```
`skip-until` 日期是包含在内的:在指定日期*之后*的 UTC 午夜之前,发现都会被抑制。
### 组件
| 组件 | 描述 |
|---|---|
| `mechanic-watcher` | Go 控制器(controller-runtime),监控 Kubernetes 资源、管理 `RemediationJob` CRD 并创建 agent Job |
| `mechanic-agent` | 包含 opencode + kubectl + helm + flux + gh 及支持调查工具的 Docker 镜像 |
### Agent 镜像工具
| 工具 | 版本 | 目的 |
|---|---|---|
| `opencode` | `1.2.10` | AI agent 驱动程序 |
| `kubectl` | `1.35.1` | 集群检查(只读) |
| `helm` | `3.20.0` | Chart 元数据,模板渲染 |
| `flux` | `2.8.0` | Flux 状态、追踪、差异对比 |
| `kustomize` | `5.8.1` | 渲染并验证 Kustomize overlay |
| `gh` | 最新稳定版 | PR 创建、列表、评论 |
| `kubeconform` | `0.7.0` | Kubernetes manifest schema 验证 |
| `yq` | `4.52.4` | YAML 处理 |
| `jq` | apt | JSON 处理 |
| `stern` | `1.33.1` | 多 Pod 日志尾部追踪 |
| `sops` | `3.12.1` | 解密 SOPS 加密的 secret |
| `age` | `1.3.1` | 解密 age 加密的文件 |
| `talosctl` | `1.12.4` | Talos 节点检查(需要 `talosconfig` 挂载) |
所有二进制文件均从官方发布版本获取,并经过 SHA256 校验和验证。
Agent 以非 root 用户运行(`uid=1000`)。
## 路线图
正在积极开发或计划中的功能:
| 领域 | 功能 | 状态 |
|---|---|---|
| 可操作性 | `RemediationJob` 上的 Kubernetes Events(`kubectl describe rjob` 显示生命周期) | 已发布 |
| 可操作性 | Dry-run 模式 —— 在不开启 PR 的情况下进行调查 | 已发布 |
| 可靠性 | `PermanentlyFailed` 阶段 —— 具有死信逻辑删除的重试上限 | 已发布 |
| 可靠性 | GitHub App 令牌过期快速失败保护 | 已 |
| 准确性 | Namespace 作用域的 provider 过滤(`WATCH_NAMESPACES`、`EXCLUDE_NAMESPACES`) | 已发布 |
| 准确性 | 按资源选择性退出注释(`mechanic.io/enabled`、`mechanic.io/skip-until`、`mechanic.io/priority`) | 已发布 |
| 准确性 | 多信号关联(相关发现分组到一个调查中) | 已发布 |
| 准确性 | 强制性 PR 前的 manifest 验证 | 已发布 |
| 影响力 | 发现解决后自动关闭 PR | 已发布 |
| 影响力 | PR 反馈迭代(回复审查者评论) | 已延期 |
| 影响力 | 按需手动触发 | 已延期 |
| 影响力 | GitLab 和 Gitea 接收器支持 | 已评估 |
| 信号源 | Prometheus / Alertmanager 源 provider | 已评估 |
| 信号源 | cert-manager 证书过期 provider | 已评估 |
有关带有价值/复杂性评级和实施说明的完整产品待办事项列表,请参阅 [`docs/BACKLOG/FEATURE_TRACKER.md`](docs/BACKLOG/FEATURE_TRACKER.md)。
## 文档
- [`docs/DESIGN/HLD.md`](docs/DESIGN/HLD.md) — 架构和设计决策
- [`docs/DESIGN/lld/`](docs/DESIGN/lld/) — 组件级低级别设计
- [`docs/BACKLOG/`](docs/BACKLOG/) — 实施待办事项和功能跟踪器
- [`README-LLM.md`](README-LLM.md) — LLM 实施指南
## 开发
### 前置条件
- Go 1.24+
- [`golangci-lint`](https://golangci-lint.run/usage/install/) — 扩展的 linter 套件
- [`gitleaks`](https://github.com/zricethezav/gitleaks) — secret 扫描器
使用 `go install` 安装两者:
```
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go install github.com/zricethezav/gitleaks/v8@latest
```
### Git hooks
克隆后,安装一次 pre-commit hook:
```
make install-hooks
```
该 hook 在每次 `git commit` 时运行并执行两项检查:
| 检查 | 工具 | 捕获内容 |
|---|---|---|
| Secret 扫描 | `gitleaks` | 暂存文件中的 API 密钥、令牌、凭证 |
| Lint | `golangci-lint` | 类型错误、未使用的代码、安全问题、格式问题 |
要在紧急情况下绕过:`git commit --no-verify`
### 常用 make 目标
| 目标 | 描述 |
|---|---|
| `make lint` | 快速 `go vet` 检查 |
| `make lint-full` | 完整的 `golangci-lint` 运行(与 pre-commit 相同) |
| `make lint-secrets` | 使用 `gitleaks` 进行完整仓库 secret 扫描 |
| `make lint-security` | `gosec` HIGH/CRITICAL 安全检查 |
| `make test` | 带有竞态检测器的完整测试套件 |
| `make install-hooks` | 克隆后(重新)安装 git hooks |
## 社区
- **GitHub Discussions** — [github.com/lenaxia/k8s-mechanic/discussions](https://github.com/lenaxia/k8s-mechanic/discussions)
— 提问、想法、架构讨论和展示与分享
- **GitHub Issues** — 错误和功能请求
### 贡献
有关开发环境设置、编码标准、DCO 要求以及如何寻找适合新手的 issue,请参阅 [CONTRIBUTING.md](CONTRIBUTING.md)。
### 治理
有关项目的贡献者阶梯、决策过程以及如何成为维护者的信息,请参阅 [GOVERNANCE.md](GOVERNANCE.md)。
当前维护者列表请见 [MAINTAINERS.md](MAINTAINERS.md)。
### GitHub 仓库主题
此仓库上设置了以下主题以辅助发现。如果您正在维护分支或相关项目,
可能需要应用相同的集合:
`kubernetes` `gitops` `devops` `cloud-native` `operator` `controller`
`remediation` `self-healing` `argocd` `flux` `automation`
主题通过 GitHub Web UI 配置:仓库页面 → "About" 旁边的齿轮图标 → Topics。
## 许可证
Apache 2.0
## 开发实践
本项目始终遵循结构化的 SDLC(软件开发生命周期)实践:
- **待办事项驱动** — 所有功能和史诗都记录在 [`docs/BACKLOG/`](docs/BACKLOG/) 中,
在开始实施之前,具有明确的用户故事拆分、验收标准和价值/复杂性评级。
- **经过安全审查** — [Epic 12](docs/BACKLOG/epic12-security-review/README.md) 针对 mechanic 的完整攻击面运行了
结构化安全审计:secret 脱敏、提示注入检测、网络策略、RBAC 作用域、结构化审计日志,
以及带有文档化发现的正式渗透测试计划。所有 HIGH/CRITICAL 级别的发现都在史诗关闭前
得到了修复。
- **文档齐全** — 每个重要的会话都记录在 [`docs/WORKLOGS/`](docs/WORKLOGS/) 中,
捕获了设计决策、实施说明以及发生更改背后的原因。
开发过程是 AI 加速的(主要是 [OpenCode](https://opencode.ai)),这使得
项目能够快速推进而不损害流程的严谨性。该流程确保了工作内容的可问责性。
one per resource type
─────────────────────
watches Pods, Deployments,
StatefulSets, PVCs, Nodes, Jobs
extracts findings
deduplicates by fingerprint"] RJR["RemediationJobReconciler
─────────────────────
watches RemediationJob CRDs
enforces MAX_CONCURRENT_JOBS
syncs Job status back"] end RJ["RemediationJob CRDs
rjob
─────────────────────
durable dedup state
survives restarts"] AJ["mechanic-agent Job
one per finding
─────────────────────
init: git clone repo
main: opencode run
kubectl read-only
gh pr create"] GH["GitOps repository
GitHub"] SPR -->|creates| RJ RJ -->|watched by| RJR RJR -->|creates| AJ AJ -->|opens PR| GH ``` ### Agent 的工作内容 Agent 在集群内以只读 RBAC 权限运行 [OpenCode](https://opencode.ai),并遵循结构化的调查流程: 1. 检查此指纹是否已有开启的 PR —— 如果找到,则在其上评论并退出 2. 对故障资源执行 `kubectl describe` 和 `kubectl get events` 3. 检查相关资源(所属 Deployment、Endpoints、PV 等) 4. 在克隆的 GitOps 仓库中定位相关的 manifest 5. 使用 `flux get all` 和 `helm list` 检查 Flux/Helm 状态 6. 确定根本原因并分配置信度(高 / 中 / 低) 7. 使用 `kubeconform` 和 `kustomize build` 验证建议的更改 8. 开启一个包含结构化正文的 pull request:摘要、证据、根本原因、修复建议、置信度 ### `RemediationJob` CRD 每个唯一的发现都由一个 `RemediationJob` 对象(`rjob`)跟踪。 ``` kubectl get rjob -n mechanic ``` ``` NAME PHASE KIND PARENT JOB AGE mechanic-a3f9c2b14d8e Succeeded Pod Deployment/my-app mechanic-agent-a3f9c2b14d8e 8m mechanic-7bc1d3e90f21 Dispatched Deployment Deployment/api-server mechanic-agent-7bc1d3e90f21 2m mechanic-f4e2a1c85b67 Failed Node Node/worker-03 1h ``` #### RemediationJob 生命周期 ``` stateDiagram-v2 [*] --> Pending : finding detected Pending --> Dispatched : concurrent-job slot available Pending --> Cancelled : source object deleted Dispatched --> Running : Job pod scheduled Running --> Succeeded : agent Job completed Running --> Failed : exit non-zero or deadline exceeded Running --> Cancelled : source object deleted Failed --> Dispatched : retry (RetryCount < MaxRetries) Failed --> PermanentlyFailed : RetryCount >= MaxRetries Succeeded --> [*] Cancelled --> [*] PermanentlyFailed --> [*] ``` - **Pending** — 已检测到发现的问题,正在等待并发 Job 槽位 - **Dispatched** — 已创建 `batch/v1 Job`,等待 Pod 调度 - **Running** — agent Pod 正在执行 - **Succeeded** — agent Job 已完成;如果开启了 PR,`status.prRef` 将包含 PR URL - **Failed** — agent Job 失败(退出码非零或超过截止时间);如果 `RetryCount < MaxRetries` 则重新排队 - **PermanentlyFailed** — `RetryCount` 已达到 `MaxRetries`;不再分发;可通过 `kubectl describe rjob
标签:AIOps, CISA项目, DNS解析, EVTX分析, GitOps, Human-in-the-loop, K8s控制器, RBAC, StruQ, 安全防护, 容器编排, 开源项目, 持续部署, 故障排查, 故障自愈, 日志审计, 智能运维, 机密信息脱敏, 模块化设计, 自动化修复, 自动提交PR, 请求拦截, 集群监控