Job 做不到的事,催生了 Workflows
一句话总结
Argo Workflows 是让多个 container 按顺序和依赖关系运行的 engine。只有从 Kubernetes Job 出发,理解它为什么不够用,才能真正理解 Workflow 的各个字段。Events 是负责决定由什么触发 workflow 的独立项目。
为什么需要它
Job 表达“运行这个 container,直到成功”。到这里它非常完美,问题出现在第二项任务加入之后。如果 build 完成后才能 test,test 完成后才能 deploy,deploy 失败又要发送 notification,该如何连接两三个 Job?只有三种方法:把全部步骤写进一个 container 的 shell script;让外部 orchestrator(如 Jenkins)按顺序创建 Job;或者引入可以表达依赖关系的上层概念。
第一种方法会丢失失败位置。script 在中途终止时,只能翻查日志确认在哪里失败,retry 也总要从头开始。第二种方法把状态放在集群外部,orchestrator 一旦故障,pipeline 就会成为孤儿。Workflows 选择第三种:把任务间依赖与数据传递,以声明形式写入 Kubernetes object。
工作原理
Workflow spec 包含 templates 数组和一个 entrypoint。entrypoint 是指向首先执行哪个 template 的名称。template 有多种类型,典型类型包括真正运行 container 的 container template、按顺序调用其他 template 的 steps template、按依赖图调用的 dag template,以及等待人工批准的 suspend template。
steps 与 dag 的差异在于表达能力。steps 是列表的列表,同一层中的任务并行执行,下一层随后执行,是一种简单模型。dag 则由每个 task 通过 dependencies 直接声明自己的前置条件,可以画出 A 与 B 都结束后运行 C、只有 B 结束就运行 D 这样的 graph。pipeline 超过五个步骤后,steps 往往很难说明“该步骤为什么位于这里”,通常会转向 dag。
数据沿两种路径传递:parameters 是字符串,artifacts 是文件。在一个 template 的 outputs.artifacts 中写入路径后,该路径的内容会上传到 artifact repository;下一个 template 的 inputs.artifacts 会将其下载并解压到指定路径。需要这个中间存储,是它与 Job 的决定性差异。Job 没有在 Pod 间传递文件的方法,只能共享 PVC,或自行上传到其他位置。
WorkflowTemplate 负责复用。可以把常用 template 发布到 namespace,再让各 Workflow task 通过 templateRef 指向其名称与 template。这样,组织只需在一个位置修改标准 build 流程,所有 pipeline 都能使用。CronWorkflow 会把整个 Workflow spec 包含在 workflowSpec 下,再添加 schedule 与 concurrencyPolicy。将 concurrencyPolicy 设为 Forbid 后,如果上一次执行尚未结束,就会跳过新的执行。
Events 由 EventSource → Sensor → Trigger 三类 object 组成。EventSource 订阅 webhook、S3、Kafka、calendar 等外部世界,把事件送入内部 event bus;Sensor 订阅该 bus,条件满足时由 Trigger 创建 Workflow 或其他 Kubernetes object。仅看这份列表,就能理解它为何是独立项目。如果开始把这些 adapter 放入 deployment controller,Argo CD 就不再是部署工具,而会变成 integration middleware。
现场会遇到的情况
作者的家庭实验室中,KubeVirt 曾真实暴露这种差异。component 状态全部显示 AllComponentsReady,但 VM 无法启动。拆开 virt-launcher Pod spec 后,发现缺少包含 init container 待执行 binary 的 volume mount。这是在该集群中第三次确认同一教训:“状态为 Ready”与“真实可用”是不同命题。
workflow 中也有完全相同的陷阱。dag task 以 Succeeded 结束,只表示 container 以 0 退出,并不表示它真的留下了下一个 task 期待的 artifact。如果 outputs.artifacts 指定路径中没有文件,该 task 可能显示成功,下一 task 却会因找不到输入而失败。因此,传递 artifact 的 task 最后应自行确认文件是否存在于指定路径。同样,在 workflow 中运行 GPU workload 时,如果只请求 nvidia.com/gpu: 1,需要 32GB 显存的训练任务也可能被调度到 8GB laptop GPU。因为从 Kubernetes 视角看,二者都是一个 GPU。应为节点添加性能等级 label,并使用 nodeSelector 选择。
下一项实验要做什么
在 /root/capa-wf/ 中编写 Workflow(dag 依赖、parameter、artifact)、CronWorkflow 与 WorkflowTemplate reference,然后把完成同类工作的 Kubernetes Job 和 CronJob 部署到真实集群中,并排比较。最后,从文件与集群两侧提取数值,创建 summary ConfigMap。