Tekton Pipeline `nop` 镜像深度解析:优雅终止 Sidecar 与 Affinity Assistant 常驻容器的内部实现
发布时间:2026/9/25 15:23:40 锦皓数字建站

云原生CI/CDDevOps后端【免费下载链接】pipelineA cloud-native Pipeline resource.项目地址https://gitcode.com/gh_mirrors/pipelin/pipeline点击查看免费下载nop是 Tekton Pipeline 内部使用的一个“最小化空操作”镜像承担两项关键职能在 TaskRun 全部 step 执行完毕后优雅终止 Sidecar 容器以及为 Affinity Assistant 提供“只存在、不做事”的常驻容器以支撑 Workspace 亲和性调度。本文将结合仓库源码与控制器配置从镜像设计动机、Sidecar 停止的 Patch 机制、tekton_run_indefinitely特殊参数到镜像的注入与校验方式完整还原这一“小而关键”组件的底层原理。一、nop镜像是什么一个服务于两个内部职能的最小镜像按照 cmd/nop/README.md 的定义nop镜像只负责 Tekton 的两项内部功能停止 Sidecar 容器Stopping sidecar containers支撑 Affinity Assistant StatefulSetAffinity Assistant StatefulSet。设计上刻意保持镜像极小原因有二一是优化镜像拉取延迟image pull latency二是为潜在攻击者呈现最小攻击面minimal surface。由于该镜像会被批量注入到每个带有 Sidecar 的 TaskRun Pod 以及每个启用亲和性助手的 PipelineRun 中镜像体积直接关系到集群调度与拉取开销同时因为它可能被调度到任意命名空间运行最小化攻击面同样是安全考量的一部分。从构建配置可以看到它的真实身份config/controller.yaml 中控制器通过-nop-image参数声明ko://github.com/tektoncd/pipeline/cmd/nop并在ko resolve阶段替换为按 digest 引用的镜像地址。它和entrypoint、sidecarlogresults、workingdirinit一起构成了 Tekton 在 Pod 内执行任务所需的全部内部镜像集合。二、职能一优雅终止 Sidecar 容器2.1 为什么需要“终止 Sidecar”在 Kubernetes 中Sidecar 容器与主容器共享生命周期——只要 Sidecar 还在运行Pod 就不会进入Completed状态。Tekton 的任务执行模型是“步骤串行、Sidecar 并行”因此当 TaskRun 中所有 step 都执行完毕时必须主动把仍在运行的 Sidecar 停下来否则 TaskRun 永远不会结束。Tekton 采用的做法见 cmd/nop/README.md把 Sidecar 容器的image替换成一个“无论传入什么参数都会立即以 0 退出”的镜像从而实现优雅停止。2.2 底层的 JSON Patch 实现停止动作的源码实现在 pkg/pod/entrypoint.gobuildSidecarStopPatchpkg/pod/entrypoint.go#L274-L309遍历 Pod 的Status.ContainerStatuses凡是“不是 step 容器”且“状态为 Running”的容器就在 spec 中找到对应索引生成一条replace操作把/spec/containers/{index}/image替换为nopImageStopSidecarspkg/pod/entrypoint.go#L333-L367负责拉取 Pod、仅在 Pod 处于Running阶段时才构造 Patch 并通过 K8s client 以JSONPatchType提交无待停止 Sidecar 时直接返回。值得注意的是 Patch 构建中的两个防御性细节不能简单通过容器名是否带sidecar-前缀来判断注入的 Sidecar 容器可能没有该前缀因此代码用!IsContainerStep(s.Name)判断当 feature flagResultExtractionMethod sidecar logs时由控制器注入的sidecar-log-results容器会被跳过让它自然优雅退出而不是被强杀pkg/pod/entrypoint.go#L283-L285。2.3 调用链从 TaskRun 完成到 Sidecar 停止TaskRun 控制器在任务进入完成阶段时调用stopSidecarspkg/reconciler/taskrun/taskrun.go#L466-L494若 TaskRun 尚无关联 Pod或已处于Cancelled/TimedOut状态此时 Pod 已在failTaskRun中被删除则跳过调用podconvert.StopSidecars(ctx, c.Images.NopImage, c.KubeClientSet, tr.Namespace, tr.Status.PodName)即上一节描述的 Patch 流程若 Patch 后仍有 SidecarStatus 显示为 Running则依据 Pod 的最新ContainerStatuses更新 TaskRun 的 Sidecar 状态。单元测试 pkg/pod/entrypoint_test.go 中的TestStopSidecars对这一行为做了完整验证构造包含普通 Sidecar 与注入 Sidecar 的 Pod断言 Patch 之后两个容器的镜像均被替换为nop-image。2.4 重要注意事项指定了command的 Sidecar原文档特别标注了一条NB警告如果 Sidecar 容器自己指定了command那么替换镜像后**nop二进制并不会被调用**K8s 会执行容器的自定义 command容器可能以非零退出码退出。Tekton 不会把这种情况判定为 TaskRun 失败但可能产生多余的日志或指标噪音。这是用户在自定义 Sidecar 时应当规避的用法。2.5nop二进制的退出行为nop/main.go 的实现非常直白func main() { if len(os.Args) 2 os.Args[1] tekton_run_indefinitely { log.Println(Waiting indefinitely...) ch : make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) log.Println(received signal:, -ch) } log.Println(Exiting...) os.Exit(0) }除tekton_run_indefinitely之外的任何参数甚至无参数都会走完main直接os.Exit(0)立即退出——这正是 Sidecar 停止场景需要的“即插即止”语义。三、职能二Affinity Assistant StatefulSet 的常驻容器3.1 Affinity Assistant 为什么需要“一直存在”的容器Affinity Assistant 是 Tekton 实现 workspaces 的关键机制它为 PipelineRun 创建一个 KubernetesStatefulSet让 PV 被调度到同一个可用区从而避免多节点间共享 PVC 的调度冲突。这个 StatefulSet 中的容器“不需要做任何事只需要存在”——它的意义是占位并保持运行。3.2 特殊参数tekton_run_indefinitely为了让容器无限期运行Affinity Assistant 向nop镜像传入唯一识别串tekton_run_indefinitelycmd/nop/README.md。nop主程序识别到该参数后进入“Waiting indefinitely...”状态注册SIGINT/SIGTERM信号监听收到信号后打印并退出。这意味着它的生命周期完全由 Kubernetes 或运维人员通过信号控制。该参数的注入位置在 pkg/reconciler/pipelinerun/affinity_assistant.go#L375-L395containers : []corev1.Container{{ Name: affinity-assistant, Image: containerConfig.Image, Args: []string{tekton_run_indefinitely}, // Set requests limits to get QoS class _Guaranteed_. Resources: corev1.ResourceRequirements{ Limits: corev1.ResourceList{ cpu: resource.MustParse(50m), memory: resource.MustParse(100Mi), }, Requests: corev1.ResourceList{ cpu: resource.MustParse(50m), memory: resource.MustParse(100Mi), }, }, ... }}容器名为affinity-assistantreplica 数固定为 1单例并把各 Workspace 的 PVC 以 VolumeMount 挂载进去资源上刻意让requests limits50m CPU / 100Mi 内存从而获得 Kubernetes 的GuaranteedQoS 等级确保这个占位容器不会被轻易驱逐——从源码结构看这是对“只存在”这一需求在调度层面的保障。3.3 两种职能如何在一个镜像中共存两个职能看似矛盾一个要求“立即退出”一个要求“永远运行”nop通过参数判别将它们统一默认路径立即以 0 退出命中tekton_run_indefinitely则常驻等待信号。整份 main 函数不足 20 行正是“最小化、单一职责”设计哲学的体现。四、镜像的配置、注入与校验nop镜像不是硬编码在业务代码里的字符串而是作为控制器启动参数注入并纳入校验体系启动参数控制器 Deployment 通过-nop-image指定config/controller.yaml#L80使用ko://引用以便ko resolve按 digest 替换统一管理Images结构体把NopImage与EntrypointImage、ShellImage等一起管理pkg/apis/pipeline/images.go#L26-L41注释明确其语义为“用于终止 Sidecar 的容器镜像”启动校验Images.Validate()会在任一镜像为空时返回错误pkg/apis/pipeline/images.go#L44-L64防止控制器在镜像缺失的情况下带病启动。由于镜像引用最终以 digest 形式固化在控制器镜像清单中部署时无需额外拉取Sidecar 停止阶段只需将 Pod spec 中的 image 字符串替换为已缓存的nop镜像即可这进一步解释了为什么“极小体积”对整体性能如此重要。五、小结nop镜像虽然只承担“立即退出”与“无限期等待”两个简单行为却是 Tekton 任务生命周期收尾Sidecar 优雅停止与 Workspace 亲和性调度Affinity Assistant得以成立的基础设施。理解它的关键在于三件事Sidecar 停止本质是用 JSON Patch 替换镜像让容器以 0 码即刻退出tekton_run_indefinitely是一个信号驱动的常驻模式服务于“只存在不做事”的占位容器镜像通过控制器-nop-image参数注入、统一校验并保持最小体积以兼顾拉取延迟与攻击面。相关核心源码与测试均可在仓库中继续深入cmd/nop/main.go、pkg/pod/entrypoint.go、pkg/reconciler/taskrun/taskrun.go、pkg/reconciler/pipelinerun/affinity_assistant.go 与 pkg/pod/entrypoint_test.go。赞分享云原生CI/CDDevOps后端【免费下载链接】pipelineA cloud-native Pipeline resource.项目地址https://gitcode.com/gh_mirrors/pipelin/pipeline点击查看免费下载相关推荐Tekton Pipeline 的 Tekton Bundle Contract 详解OCI 镜像中 Task/Pipeline 的分发契约与实现验证Tekton Pipeline 的 Tekton Bundle Contract 详解OCI 镜像中 Task/Pipeline 的分发契约与实现验证 本指南云原生CI/CDDevOps后端Tekton Pipeline 依赖库 go-containerregistry authn 包深度解析容器镜像仓库认证的 Keychain 机制与 Docker 配置兼容Tekton Pipeline 依赖库 go containerregistry authn 包深度解析容器镜像仓库认证的 Keychain 机制与 Dock云原生CI/CDDevOps后端Tekton Pipeline Bundles Resolver从 OCI 镜像包中解析 Task 与 Pipeline 的配置与实践Tekton Pipeline Bundles Resolver从 OCI 镜像包中解析 Task 与 Pipeline 的配置与实践 本文围绕 Tekton云原生CI/CDDevOps后端上一篇QuickLook 插件终极指南macOS 空格键快速预览文件的完整配置攻略下一篇抖音批量下载工具从单条视频到整站主页的完整使用指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。