LabHub
学习 学习路径 课程

CAPA — Argo 项目认证助理

追踪绿色流水线背后消失的文件

在 LabHub 中继续学习

目标

区分业务执行成功与 artifact 保留、清理成功,并通过无冲突的 key 和最小权限进行验证。

为什么重要

即使业务成功,最终文件也可能消失,中间文件也可能持续堆积。本实验使用 VM 中真实运行的 Argo Workflows 4.1.3 和 SeaweedFS 4.46。我们不会只看界面上的成功标记,而会同时确认执行 UID、真实文件字节以及 GC 错误。基础 cluster 与 storage 会自动准备,学生需要修改 policy 或亲自运行对照 scenario。

步骤

  1. 读取 argo namespace 中的 artifact-repositories 和 Service seaweed。在 /root/capa-artifacts/inventory.json 中记录 namespace=argo、endpoint=seaweed:8333、writer_bucket=artifacts、anonymous_allowed=false。评分还会对照真实授权账户能否读取文件,以及匿名请求和访问其他 team bucket 是否被拒绝。
  2. 在准备好的 /root/capa-artifacts/isolated.yaml 中,只修改 produce 的两个 output 的 s3.key。temporary 为 runs/{{workflow.uid}}/temp.txt,final 为 runs/{{workflow.uid}}/final.json。保留其余 pipeline 和 final 的 Never,使用实验 helper 运行 isolated。比较两个不同 Workflow 的 UID 与最终文件内容。
  3. isolated 的两次执行结束后检查 storage。在 /root/capa-artifacts/retention.json 中记录 business=Succeeded、temporary=deleted、final=retained、final_policy=Never。还要检查实验记录,确认两个最终文件的生产 UID 不同,并确认 A 清理时 B 的 input 仍然存在。
  4. 准备好的 missing-retain.yaml 故意省略 final artifact 的 Never。使用 helper 运行 missing-retain 后,在 /root/capa-artifacts/missing.json 中记录 business=Succeeded、final_present=false、fix=Never。不要把这个对照执行的 YAML 改成正确 policy;本步骤要解释错误原因和应修复的 field。
  5. 准备好的 gc-denied.yaml 使用没有 Role 的 GC 账户 gc-denied。使用 helper 运行 gc-denied,并在 /root/capa-artifacts/gc-error.json 中写入 business=Succeeded、condition=ArtifactGCError、cause=RBAC、force_finalizer=false。读取真实 forbidden log 和剩下的两个文件,不要删除 finalizer。
  6. 在 /root/capa-artifacts/gc-binding.yaml 中编写并应用 RoleBinding capa-gc-repair。namespace 为 argo,roleRef 为 apiGroup=rbac.authorization.k8s.io、kind=Role、name=artifact-gc;subjects 只包含 argo 的一个 ServiceAccount gc-denied。不要扩大现有 Role,使用 gc-repaired.yaml 验证新执行。保留原失败 Workflow 的记录与 finalizer。
  7. 保持 collision.yaml 中的 shared/{{workflow.parameters.batch}} key 不变,使用 helper 运行 collision。确认 A 的 input 被 B 的 UID 覆盖,A 失败后 A 的 GC 又删除 B 的 input,导致 B 也失败。两个 Workflow 的 Failed/Error 是该对照实验的预期结果。不要仅通过删除文件来伪造失败。
  8. on-deletion.yaml 同时使用 OnWorkflowDeletion 和 final 的 Never。使用 helper 运行 on-deletion。helper 会在成功后立即观测文件,确认准确的 Workflow UID,只删除该 Workflow,再次观测。业务完成时应存在两个文件,删除后只保留 final 文件。
  9. 在 /root/capa-artifacts/report.json 中,把 lost_result_uid 写为 missing-retain 的真实 UID,把 gc_failure_uid 写为 gc-denied 的真实 UID。记录 same_business_status=true、same_storage_result=false、status_is_retention_proof=false。根据真实文件状态与错误层级,说明两个 Succeeded 为什么需要不同处置。

参考

区分允许的读取与应拒绝的读取

读取 argo namespace 中的 artifact-repositories 和 Service seaweed。在 /root/capa-artifacts/inventory.json 中记录 namespace=argo、endpoint=seaweed:8333、writer_bucket=artifacts、anonymous_allowed=false。评分还会对照真实授权账户能否读取文件,以及匿名请求和访问其他 team bucket 是否被拒绝。

storage reference 的 endpoint 与 bucket 是不同 field,同时也要确认正常请求能够成功。

隔离两次执行的 artifact key

在准备好的 /root/capa-artifacts/isolated.yaml 中,只修改 produce 的两个 output 的 s3.key。temporary 为 runs/{{workflow.uid}}/temp.txt,final 为 runs/{{workflow.uid}}/final.json。保留其余 pipeline 和 final 的 Never,使用实验 helper 运行 isolated。比较两个不同 Workflow 的 UID 与最终文件内容。

即使 Kubernetes name 不同,只要 storage key 相同就会冲突。应把 UID 放入 S3 key,而不是 local filename。

删除中间文件并保留最终结果

isolated 的两次执行结束后检查 storage。在 /root/capa-artifacts/retention.json 中记录 business=Succeeded、temporary=deleted、final=retained、final_policy=Never。还要检查实验记录,确认两个最终文件的生产 UID 不同,并确认 A 清理时 B 的 input 仍然存在。

同时查看 OnWorkflowCompletion default policy 与单个 final 的 Never。可先从 helper status 找到执行名称,再检查观测记录。

分析遗漏最终保留的成功执行

准备好的 missing-retain.yaml 故意省略 final artifact 的 Never。使用 helper 运行 missing-retain 后,在 /root/capa-artifacts/missing.json 中记录 business=Succeeded、final_present=false、fix=Never。不要把这个对照执行的 YAML 改成正确 policy;本步骤要解释错误原因和应修复的 field。

错误配置未必导致业务执行失败。请比较正常 isolated 结果与本次执行的最终文件。

分离业务成功与 GC 权限失败

准备好的 gc-denied.yaml 使用没有 Role 的 GC 账户 gc-denied。使用 helper 运行 gc-denied,并在 /root/capa-artifacts/gc-error.json 中写入 business=Succeeded、condition=ArtifactGCError、cause=RBAC、force_finalizer=false。读取真实 forbidden log 和剩下的两个文件,不要删除 finalizer。

本步骤应该失败的是 GC,而不是业务 container。授权将在下一步骤完成。

用最小 GC 权限验证新执行

在 /root/capa-artifacts/gc-binding.yaml 中编写并应用 RoleBinding capa-gc-repair。namespace 为 argo,roleRef 为 apiGroup=rbac.authorization.k8s.io、kind=Role、name=artifact-gc;subjects 只包含 argo 的一个 ServiceAccount gc-denied。不要扩大现有 Role,使用 gc-repaired.yaml 验证新执行。保留原失败 Workflow 的记录与 finalizer。

GC 只需要 task list/watch 与 task status patch。使用 --subresource=status 检查 status 权限。

观察 shared key 引发的两次失败

保持 collision.yaml 中的 shared/{{workflow.parameters.batch}} key 不变,使用 helper 运行 collision。确认 A 的 input 被 B 的 UID 覆盖,A 失败后 A 的 GC 又删除 B 的 input,导致 B 也失败。两个 Workflow 的 Failed/Error 是该对照实验的预期结果。不要仅通过删除文件来伪造失败。

helper 只为同一次实验 attempt 中的两个执行提供相同 batch。比较 status 中不同的 UID 与 log。

比较完成时刻与删除时刻

on-deletion.yaml 同时使用 OnWorkflowDeletion 和 final 的 Never。使用 helper 运行 on-deletion。helper 会在成功后立即观测文件,确认准确的 Workflow UID,只删除该 Workflow,再次观测。业务完成时应存在两个文件,删除后只保留 final 文件。

删除 Workflow 与删除 Pod 不是同一事件。请读取没有强制移除 finalizer 就完成删除的证据。

报告相同成功状态下不同的 storage 结果

在 /root/capa-artifacts/report.json 中,把 lost_result_uid 写为 missing-retain 的真实 UID,把 gc_failure_uid 写为 gc-denied 的真实 UID。记录 same_business_status=true、same_storage_result=false、status_is_retention_proof=false。根据真实文件状态与错误层级,说明两个 Succeeded 为什么需要不同处置。

从 helper status 读取 UID,不要与 name 混淆。最终结果丢失与临时文件清理失败不是同一种故障。