- 云原生
- 容器编排
- 工作流自动化
- 任务调度
- 后端
【免费下载链接】argo-workflows
Workflow Engine for Kubernetes
导读
在 Argo Workflows 中,并非所有 workflow executors 都能从容器的镜像基础层(base layer,例如/tmp)直接收集输出 artifacts 与 parameters;一旦为 Workflow Pod 配置了 security context,这一限制会更加明显。本指南将说明这一约束的成因,并给出通过挂载emptyDir卷绕开限制的完整可运行配置,同时结合源码解释输入/输出 artifacts 在底层是如何与 emptyDir 交互的,帮助你在实战中正确设计 Workflow 的卷与输出路径。
emptyDir 为什么是输出 artifacts 的"必需品"
Argo Workflows 的 executor(如 docker、kubelet、emissary 等)负责在任务容器完成后收集输出 artifacts 和 parameters。问题的关键在于:
- 输出 artifacts/parameters 的读取位置:executor 需要读取容器内用户指定的路径(例如
/tmp/result.txt或/mnt/out/hello_world.txt)。当该路径位于容器的可写层(基础层)时,不同 executor 的收集能力并不一致——部分 executor 无法从基础层提取文件。 - 安全上下文的叠加影响:当你按照 workflow-pod-security-context.md 为 Pod 配置
runAsNonRoot、非 root 用户或受限的 Pod Security Standards 时,executor 对基础层文件的访问权限进一步受限,"无法从基础层获取输出"的可能性会明显升高。这也是该文档特别提醒"如果你用 security context 运行 workflow pods,很可能无法从基础层获取输出 artifacts/parameters"的原因。
绕开这一约束最直接、最轻量的办法,就是把输出写入一个显式挂载的卷,而不是镜像的基础层。emptyDir卷随 Pod 生命周期创建、无需预先申请 PVC、不依赖任何存储驱动,因此是首选方案。
注意:这一约束仅针对输出 artifacts/parameters。输入 artifacts/parameters 如需中转,系统会自动为其挂载一个 emptyDir(详见下文源码解析),无需用户手动处理。
实战:挂载 emptyDir 输出卷的完整配置
原文档给出的示例展示了一个典型的输出场景:任务把cowsay的输出同时写到标准输出和一个位于 emptyDir 卷中的文件,再从该文件生成输出 parameter:
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: empty-dir- spec: entrypoint: main templates: - name: main container: image: argoproj/argosay:v2 command: [sh, -c] args: ["cowsay hello world | tee /mnt/out/hello_world.txt"] volumeMounts: - name: out mountPath: /mnt/out volumes: - name: out emptyDir: { } outputs: parameters: - name: message valueFrom: path: /mnt/out/hello_world.txt拆解这份配置的三个关键点:
- 声明卷:在模板(或 Workflow 顶层)的
volumes中声明名为out的卷,emptyDir: {}即表示使用默认介质(通常是节点的本地磁盘)。Kubernetes 还支持通过emptyDir.medium: Memory使用内存文件系统、通过sizeLimit限制容量,Argo Workflows 完全透传这些字段。 - 挂载卷:在容器的
volumeMounts中将out挂载到/mnt/out。务必保证输出路径(/mnt/out/hello_world.txt)位于该挂载点之下,而不是默认的/tmp。 - 声明输出:在
outputs.parameters中通过valueFrom.path指向卷内的文件,executor 即可从卷中读取内容并写入输出 parameter。
你还可以参考仓库中的真实示例 examples/volumes-emptydir.yaml,它使用alpine:3.23镜像验证 emptyDir 是否真正挂载成功。该示例特意检查/proc/mounts而不是mount命令,因为 busybox 的mount读取/etc/mtab,在存在镜像卷(例如 init-less pod)时会漏掉 kubelet 创建的挂载点,而/proc/mounts是内核提供的挂载真相来源:
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: volumes-emptydir- spec: entrypoint: volumes-emptydir-example volumes: - name: workdir emptyDir: {} templates: - name: volumes-emptydir-example container: image: alpine:3.23 command: ["/bin/sh", "-c"] args: [" if grep -q /mnt/vol /proc/mounts; then echo \"Volume mounted and found\"; else echo \"Not found\"; exit 1; fi "] volumeMounts: - name: workdir mountPath: /mnt/vol这种"写文件 → 检查挂载 → 声明输出"的组合,是排查 emptyDir 输出问题时的有效手段:如果挂载失败,容器会以非零退出码结束,Workflow 会立即报错,而不是等到收集阶段才发现路径不可读。
输入 Artifacts 的自动 emptyDir 挂载(源码级解析)
原文档强调"输入 artifacts/parameters 如果需要会自动挂载到 empty-dir"。这一行为在 workflow/controller/workflowpod.go 中有完整实现:
- 常量
inputArtifactsVolumeName = "input-artifacts"(workflowpod.go)定义了系统内部使用的卷名; - 函数
addInputArtifactsVolumes(workflowpod.go)在模板存在输入 artifacts 时,会构造一个EmptyDir卷追加到 Pod 的volumes,并把它挂载到 init 容器、wait/executor 容器以及主容器上。
从源码结构看,有两种挂载策略:
- 传统(legacy)模式:对每个输入 artifact,以
SubPath: art.Name的方式把共享 emptyDir 挂载到art.Path指定的位置,相当于为每个 artifact 建立独立的 bind mount; - init-less 模式:由于主容器与 supervisor 并发启动,kubelet 会在 supervisor 写入文件前把每个 SubPath 预创建为空目录,导致 SubPath 方案失效,因此改为把整个
input-artifacts卷挂载到common.ExecutorArtifactBaseDir(即/argo/inputs/artifacts,见 workflow/common/common.go),再由 emissary executor 在容器就绪后通过符号链接把每个 artifact 放入用户指定路径。
另外,workflow/executor/executor.go 中 executor 还会检查 artifact 路径是否与共享的 input-artifact emptyDir 重叠,避免把主容器内写回的数据与 emptyDir 中的原始输入混淆——这也说明输入/输出与 emptyDir 的交互在控制器与 executor 两侧都有对应的防御逻辑。
输出 Artifacts 如何从 emptyDir 卷中收集(源码级解析)
对于输出侧,控制器同样有专门的处理逻辑:
- 函数
addOutputArtifactsVolumes(workflowpod.go)会把主容器的全部volumeMounts镜像到 wait sidecar 与 artifact-plugin sidecar,镜像挂载点统一放在common.ExecutorMainFilesystemDir(即/mainctrfs,见 workflow/common/common.go)之下,并强制ReadOnly: false以兼容重叠挂载; - 这样,对于产生在 PVC、emptyDir 等挂载卷中的输出 artifacts,wait 容器可以直接从
volumeMount读取文件并上传,而不必依赖docker cp之类从基础层提取文件的手段——这正是"把输出放进卷里就能可靠收集"的底层原理。
换句话说,你手动挂载 emptyDir 并声明输出路径,实际上是复用了控制器为 wait 容器准备的这一套"卷镜像"机制:主容器写入卷的数据,wait 容器在/mainctrfs下能看到完全一致的视图,从而绕开了 executor 无法读取基础层的限制。
使用 emptyDir 的注意事项
- 输出路径必须落在挂载点之下:
valueFrom.path指向的文件必须位于volumeMounts.mountPath之内,否则文件写入了基础层,问题依旧存在。 - 数据生命周期与 Pod 绑定:
emptyDir卷随 Pod 删除而清空,只适合 Pod 内部的数据中转(如任务输出、临时文件、容器间共享),不适合跨 Pod 或跨 Workflow 持久化——需要持久化时应改用 PVC 或对象存储 artifact。 - 路径重叠问题:如果某个输入 artifact 的
path恰好是某个已挂载卷路径的祖先或重叠,控制器会检测到重叠并跳过 emptyDir 挂载(见 workflowpod.go 的FindOverlappingVolume检查);init-less 模式下若art.Path是某卷挂载点的祖先,配置会直接报CodeBadRequest拒绝(workflowpod.go)。设计路径时应避免 artifact 路径与卷挂载路径互相包含。 - 安全性不受影响:
emptyDir只是绕开"从基础层读取"的限制,并不降低 Pod 的隔离性;在配置了 security context 的非 root 场景下,需要确保容器用户对挂载点有写权限(例如通过securityContext.fsGroup或卷的权限设置)。
总结与延伸阅读
emptyDir是 Argo Workflows 中解决"输出 artifacts/parameters 无法从基础层收集"这一约束的轻量级标准方案:在volumes中声明emptyDir,在容器中挂载它,并把outputs的valueFrom.path指向卷内路径即可。配合 workflowpod.go 中addInputArtifactsVolumes与addOutputArtifactsVolumes的实现,你可以确认输入侧已由系统自动处理 emptyDir,而输出侧则依赖 wait 容器的卷镜像机制完成可靠收集。
若需要进一步了解相关主题,可继续阅读仓库中的以下资料:
- examples/volumes-emptydir.yaml:emptyDir 挂载与挂载验证的真实示例;
- docs/workflow-executors.md:不同 executor 的能力差异;
- docs/workflow-pod-security-context.md:Pod 安全上下文配置及其影响;
- workflow/controller/workflowpod.go:Workflow Pod 构建与卷挂载的核心实现;
- workflow/executor/executor.go:executor 侧的 artifact 路径与 emptyDir 重叠检查。
- 云原生
- 容器编排
- 工作流自动化
- 任务调度
- 后端
【免费下载链接】argo-workflows
Workflow Engine for Kubernetes
相关推荐
Argo Workflows 常见问题解决方案
Argo Workflows 常见问题解决方案 1. 项目基础介绍与主要编程语言 Argo Workflows 是一个开源的容器原生工作流引擎,用于在 Kube
云原生容器编排工作流自动化任务调度后端FairSeq序列建模框架:多模态Transformer架构与分布式训练技术深度解析
FairSeq序列建模框架:多模态Transformer架构与分布式训练技术深度解析 Facebook AI Research开发的FairSeq序列建模工具包
云原生容器编排工作流自动化任务调度后端Zero to Emacs and Org-roam:用Org-roam-ui把笔记变成知识图谱,一眼看穿你的笔记关联
Zero to Emacs and Org roam:用Org roam ui把笔记变成知识图谱,一眼看穿你的笔记关联 Zero to Emacs and Or
云原生容器编排工作流自动化任务调度后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考