ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Buildah ONBUILD 实战指南:让基础镜像的触发指令自动注入派生镜像

Buildah ONBUILD 实战指南:让基础镜像的触发指令自动注入派生镜像 云原生【免费下载链接】buildahA tool that facilitates building OCI images.项目地址https://gitcode.com/gh_mirrors/bu/buildah点击查看免费下载本文以 Buildah 官方教程 docs/tutorials/03-on-build.md 为主体系统讲解 ONBUILD 指令在 Buildah 中的完整用法既可以在 Dockerfile 中通过ONBUILD指令声明也可以通过buildah config --onbuild直接配置镜像元数据。读完本文你将掌握 ONBUILD 的格式限制仅 Docker 格式镜像支持、三种实战构建路径纯 Dockerfile、纯 Buildah 命令、两者混用以及 ONBUILD 在 Buildah 源码中的底层执行机制能够针对基础镜像共享 75% 配置、派生镜像做少量定制的典型场景如 Maven/Java 开发容器快速落地。理解 ONBUILD触发式指令与其格式限制ONBUILD 指令的本质是把一条或多条命令**存储进容器镜像的元数据meta data**中当该镜像日后被用作其他镜像的基础镜像base image时这些命令才会被触发执行。用一句话概括就是延迟执行——它不改变承载 ONBUILD 指令的那个镜像本身的内容只影响那些基于它、通过FROM指令派生出来的镜像。primary image含 ONBUILD 元数据 │ │ 被 FROM 引用时触发 ▼ derived image在 FROM 之后自动执行 ONBUILD 命令再执行自身指令ONBUILD 有几个关键行为特征一个镜像可以携带多条ONBUILD 指令它们按声明顺序依次触发ONBUILD 指令不修改包含它们的镜像内容——原始镜像的文件系统与配置保持不变只有基于该镜像派生的新镜像才会因为FROM指令而执行这些触发命令。格式限制OCI 与 Docker 镜像格式的差异ONBUILD 并非所有镜像格式都支持。遵循 Open Container InitiativeOCIimage specification 的容器镜像不支持ONBUILD 指令。Buildah 默认创建的镜像正是 OCI 格式因此默认情况下通过 Buildah 创建的镜像无法携带 ONBUILD 指令只有 Buildah 以Docker 格式创建的镜像才能使用 ONBUILD。在 Buildah 中可以通过两种方式覆盖默认格式改用 Docker 格式命令行选项--formatdocker环境变量export BUILDAH_FORMATdockerFedora 上的写法不论选择哪种格式Buildah 都能与 OCI 或 Docker 格式的镜像、容器无缝协作只是 ONBUILD 元数据仅在 Docker 格式下被保留与触发。这个限制在源码中有明确的印证。在 config.go 中Builder.SetOnBuild()的实现如下// SetOnBuild sets a trigger instruction to be executed when the image is used // as the base of another image. // Note: this setting is not present in the OCIv1 image format, so it is // discarded when writing images using OCIv1 formats. func (b *Builder) SetOnBuild(onBuild string) { if onBuild ! b.Format ! define.Dockerv2ImageManifest { b.Logger.Warnf(ONBUILD is not supported for OCI image format, %s will be ignored. Must use docker format, onBuild) } b.Docker.Config.OnBuild append(b.Docker.Config.OnBuild, onBuild) }从源码可以看到两点实现事实当 builder 的格式不是Dockerv2ImageManifest即 Docker 格式时SetOnBuild会输出告警日志提示 ONBUILD is not supported for OCI image format... Must usedockerformatONBUILD 被存放在b.Docker.Config.OnBuild字段中——这正是 Docker 镜像配置docker.Config的一部分OCIv1 格式配置中不存在对应字段因此写为 OCI 格式时该设置会被丢弃。对应的读取与清空方法也在同一文件config.goOnBuild()返回Docker.Config.OnBuild的副本ClearOnBuild()将Docker.Config.OnBuild置为空切片。环境准备安装 Buildah本教程的安装步骤假设运行环境为 Fedora。由于安装 Buildah 软件包需要 root 权限先切换到 root$ sudo -s然后安装 Buildah# dnf -y install buildah安装完成后确认环境中没有任何残留镜像与容器。列出所有镜像# buildah images同时查看当前容器列表# buildah containers正常情况下两者都应为空列表说明环境干净可用。背景说明buildah run 与 podman run 的分工在开始示例之前先厘清两个命令的定位。buildah run模拟的是 Dockerfile 中的RUN指令——它主要用于构建过程中的调试与执行也是本教程反复使用的命令而podman run模拟的是docker run侧重于容器的运行管理。两者属于不同的项目Podman 专注于管理容器、镜像与 Pod而 Buildah 聚焦于容器镜像的构建。本教程只使用 Buildah 的命令完成全部构建流程。示例一在 Dockerfile 中声明 ONBUILD第一个示例由 Chris CollinsGitHub clcollins提供演示的目标是/bar文件只出现在派生镜像中而不出现在原始镜像中。首先创建两个 Dockerfile。第一个定义基础镜像包含普通指令RUN touch /foo和触发指令ONBUILD RUN touch /bar$ cat EOF Dockerfile FROM registry.fedoraproject.org/fedora:latest RUN touch /foo ONBUILD RUN touch /bar EOF第二个 Dockerfile 以第一个镜像为基础额外执行RUN touch /baz$ cat EOF Dockerfile-2 FROM onbuild-image RUN touch /baz EOF接下来构建第一个镜像并验证 ONBUILD 是否已写入镜像元数据# buildah build --formatdocker -f Dockerfile -t onbuild-image . # buildah inspect --format {{.Docker.Config.OnBuild}} onbuild-image [RUN touch /bar]关键点说明--formatdocker必不可少——它确保镜像以 Docker 格式保存ONBUILD 元数据才得以保留buildah inspect --format {{.Docker.Config.OnBuild}}直接读取 Docker 配置中的OnBuild字段输出[RUN touch /bar]即证明触发指令已存储进镜像元数据此刻可以验证onbuild-image自身的文件系统中并没有/bar文件——ONBUILD 指令没有改变包含它的镜像内容。现在构建第二个镜像。从下面的 STEP 输出可以清楚地看到 ONBUILD 的触发时机——RUN touch /bar在FROM之后、镜像自身的RUN touch /baz之前自动执行# buildah build --formatdocker -f Dockerfile-2 -t result-image . STEP 1: FROM onbuild-image STEP 2: RUN touch /bar # Note /bar is created here based on the ONBUILD in the base image STEP 3: RUN touch /baz COMMIT result-image {output edited for brevity} $ container$(sudo buildah from result-image:latest) # buildah run $container ls /bar /foo /baz /bar /baz /foo最终验证结果一目了然/bar、/baz、/foo三个文件同时存在于result-image的容器中。其中/foo来自基础镜像构建阶段/bar来自 ONBUILD 触发指令/baz来自第二个 Dockerfile 自身的指令——三者合并完整展示了 ONBUILD 的工作链路。示例二通过buildah config --onbuild配置触发指令Buildah 的灵活之处在于构建镜像未必需要 Dockerfile。你可以用buildah from、buildah run、buildah config、buildah commit等命令把 Dockerfile 里的每一条指令拆解为一次 CLI 操作实现随时随地对镜像进行现场调整。下面把示例一改造为纯命令方式。流程是先用buildah from创建 Fedora 容器 → 用buildah run添加/foo→ 用buildah config --onbuild配置触发指令 → 用buildah commit保存为镜像# buildah from --formatdocker --name onbuild-container registry.fedoraproject.org/fedora:latest # buildah run onbuild-container touch /foo # buildah config --onbuildRUN touch /bar onbuild-container # buildah commit --formatdocker onbuild-container onbuild-image {output edited for brevity} # buildah inspect --format {{.Docker.Config.OnBuild}} onbuild-image [RUN touch /bar]buildah config --onbuild的参数值就是完整的指令文本如RUN touch /bar与 Dockerfile 中ONBUILD后跟的内容一致。buildah inspect再次确认触发指令已写入镜像元数据。接下来用示例一的第二个 Dockerfile 构建派生镜像ONBUILD 依旧如期触发# buildah build --formatdocker -f Dockerfile-2 -t result-image . STEP 1: FROM onbuild-image STEP 2: RUN touch /bar # Note /bar is created here based on the ONBUILD in the base image STEP 3: RUN touch /baz COMMIT result-image {output edited for brevity} $ container$(buildah from result-image) # buildah run $container ls /bar /foo /baz /bar /baz /foo加分项完全用 Buildah 命令拼装派生镜像如果你想彻底摆脱 Dockerfile派生镜像同样可以用 Buildah 命令直接拼装——从onbuild-image创建容器时ONBUILD 触发指令会在buildah from阶段自动执行无需额外输入# buildah from --formatdocker --name result-container onbuild-image result-container # buildah run result-container touch /baz # buildah run result-container ls /bar /foo /baz /bar /baz /foo这个细节值得特别注意buildah from输出result-container后/bar已经存在了因为 ONBUILD 指令在容器创建from时就被执行这正是后续只需要touch /baz一条命令的原因。源码印证buildah from中的 ONBUILD 执行器为什么buildah from就能触发 ONBUILD答案在 cmd/buildah/from.go 的onBuild()函数中。其核心逻辑是遍历builder.OnBuild()返回的每条指令解析出命令名与参数后分派执行func onBuild(ctx context.Context, builder *buildah.Builder, quiet bool) error { ctr : 0 for _, onBuildSpec : range builder.OnBuild() { ctr ctr 1 commands : strings.Split(onBuildSpec, ) command : strings.ToUpper(commands[0]) args : commands[1:] if !quiet { fmt.Fprintf(os.Stderr, STEP %d: %s\n, ctr, onBuildSpec) } switch command { case ADD: case COPY: ... if err : builder.AddContext(ctx, dest, command ADD, buildah.AddAndCopyOptions{}, args...); err ! nil { return err } case ONBUILD: builder.SetOnBuild(strings.Join(args, )) case RUN: ... if err : builder.RunContext(ctx, args, buildah.RunOptions{Stdout: stdout}); err ! nil { return err } ... default: logrus.Errorf(unknown OnBuild command %q; ignored, onBuildSpec) } } builder.ClearOnBuild() return nil }从源码结构可以归纳出以下实现事实每条 ONBUILD 指令都会以STEP N: 指令的形式打印到 stderr——这就是前面示例中STEP 2: RUN touch /bar输出的来源指令支持的分派范围很广ADD、COPY、ANNOTATION、CMD、ENV、ENTRYPOINT、EXPOSE、HOSTNAME、LABEL、MAINTAINER、ONBUILD、RUN、SHELL、STOPSIGNAL、USER、VOLUME、WORKINGDIR均被映射到对应的builder.SetXxx()调用遇到无法识别的指令会输出unknown OnBuild command %q; ignored告警并跳过所有 ONBUILD 指令执行完毕后调用builder.ClearOnBuild()防止触发指令在派生容器上被重复累积执行。示例三多条 ONBUILD 指令组合COPY RUN第二个buildah config示例展示多条 ONBUILD 的协作先把一个 shell 脚本复制进派生镜像再在派生镜像中执行它。这里使用 Introduction Tutorial 中的脚本。首先在本地目录创建脚本文件runecho.sh#!/usr/bin/env bash for i in seq 0 9; do echo This is a new container from ipbabble [ $i ] done赋予执行权限$ chmod x runecho.sh然后创建第二个主镜像。这一次配置两条ONBUILD 指令——第一条用COPY把脚本放进镜像的/usr/bin第二条用RUN执行它。本示例只用 Buildah 命令完成同样的指令完全可以翻译成 Dockerfile或保存为脚本复用# buildah from --formatdocker --name onbuild-container-2 fedora:latest onbuild-container-2 # buildah config --onbuildCOPY ./runecho.sh /usr/bin/runecho.sh onbuild-container-2 # buildah config --onbuildRUN /usr/bin/runecho.sh onbuild-container-2 # buildah commit --formatdocker onbuild-container-2 onbuild-image-2 {output edited for brevity} # buildah inspect --format {{.Docker.Config.OnBuild}} onbuild-image-2 [COPY ./runecho.sh /usr/bin/runecho.sh RUN /usr/bin/runecho.sh]注意buildah inspect的输出两条 ONBUILD 指令按声明顺序存储在同一列表中中间没有分隔符——这就是builder.SetOnBuild()使用append累积的结果。现在从onbuild-image-2创建派生容器。buildah from会依次触发两条指令先把脚本复制到容器的/usr/bin目录然后就地运行# buildah from --formatdocker --name result-container-2 onbuild-image-2 STEP 1: COPY ./runecho.sh /usr/bin/runecho.sh STEP 2: RUN /usr/bin/runecho.sh This is a new container pull ipbabble [ 1 ] This is a new container pull ipbabble [ 2 ] This is a new container pull ipbabble [ 3 ] This is a new container pull ipbabble [ 4 ] This is a new container pull ipbabble [ 5 ] This is a new container pull ipbabble [ 6 ] This is a new container pull ipbabble [ 7 ] This is a new container pull ipbabble [ 8 ] This is a new container pull ipbabble [ 9 ] result-container-2由于result-container-2的/usr/bin中已经保存了脚本副本之后可以随时独立运行它而无需再次触发 ONBUILD# buildah run result-container-2 /usr/bin/runecho.sh This is a new container pull ipbabble [ 1 ] This is a new container pull ipbabble [ 2 ] This is a new container pull ipbabble [ 3 ] This is a new container pull ipbabble [ 4 ] This is a new container pull ipbabble [ 5 ] This is a new container pull ipbabble [ 6 ] This is a new container pull ipbabble [ 7 ] This is a new container pull ipbabble [ 8 ] This is a new container pull ipbabble [ 9 ]这个示例展示了 ONBUILD 组合不同指令的威力COPY负责注入资产RUN负责执行动作两者叠加即可在每次派生镜像创建时自动完成装脚本 跑脚本的完整初始化流程。纵深解析构建引擎与命令行中的 ONBUILD 全链路三个示例覆盖了 ONBUILD 的完整生命周期声明Dockerfile /buildah config→ 存储镜像元数据→ 触发buildah from/buildah build→ 执行STEP N输出。下面从源码层面对这条链路做最后补充。buildah config --onbuild的参数入口在 cmd/buildah/config.go 中--onbuild被定义为可多次指定的字符串切片参数其官方帮助文本明确写明了格式限制flags.StringSliceVar(opts.onbuild, onbuild, []string{}, add onbuild command to be run on images based on this image. Only supported on docker formatted images)在 cmd/buildah/config.go 中每次指定--onbuild都会先调用builder.SetOnBuild(onbuild)写入配置再通过conditionallyAddHistory()在启用--add-history时记录一条形如ONBUILD 指令的构建历史/bin/sh -c #(nop) ONBUILD %s保证镜像历史与 Dockerfile 语义一致。构建引擎中的 ONBUILD 传递在buildah buildbud流程中imagebuildah/stage_executor.go 会把 builder 当前持有的 ONBUILD 指令复制进 Docker 配置结构dConfig : docker.Config{ ... OnBuild: builder.OnBuild(), ... }而在阶段配置应用到 builder 时imagebuildah/stage_executor.go则先ClearOnBuild()清空再从config.OnBuild逐条SetOnBuild()恢复确保 FROM 基础镜像携带的触发指令被完整继承s.builder.ClearOnBuild() for _, onBuildSpec : range config.OnBuild { s.builder.SetOnBuild(onBuildSpec) }buildah commit的格式参数最后保存镜像时务必保持 Docker 格式。在 cmd/buildah/commit.go 中--format的默认值来自defaultFormat()帮助文本为 formatof the image manifest and metadata。示例中显式使用--formatdocker或在环境中设置BUILDAH_FORMATdocker正是为了保证 ONBUILD 元数据在 commit 阶段不被 OCI 格式丢弃。总结与后续学习路径回顾三个示例的共同模式先构建一个携带 ONBUILD 指令的主镜像再用极少的步骤创建次级容器镜像。ONBUILD 的价值在于把公共初始化步骤安装依赖、注入脚本、设置环境、暴露端口等沉淀到主镜像中此后每个派生镜像的构建都不必重复这些步骤——正如两个示例所演示的派生镜像的构建往往只需要FROM加一两条自身指令即可完成。这在需要为多个项目准备共性 75%、个性 25%的开发环境如 Maven、Java 开发容器时尤其有用。本文内容基于当前仓库 docs/tutorials/03-on-build.md 整理源码佐证可继续查看ONBUILD 的存储、告警与读取config.gobuildah from阶段的 ONBUILD 执行器cmd/buildah/from.go--onbuild参数解析与历史记录cmd/buildah/config.go构建引擎中的 ONBUILD 继承与恢复imagebuildah/stage_executor.go前置入门内容可参考 Introduction Tutorial镜像仓库与标签管理可参考 02-registries-repositories.md若想进一步了解 Buildah 的其他命令可以查阅仓库 docs/ 下的各命令 man page如 buildah-config.1.md、buildah-from.1.md、buildah-commit.1.md。如有问题或改进建议欢迎到 Buildah 的 Issues 页面提交反馈。赞分享云原生【免费下载链接】buildahA tool that facilitates building OCI images.项目地址https://gitcode.com/gh_mirrors/bu/buildah点击查看免费下载相关推荐searx Docker 部署实战指南镜像运行、配置注入与自建镜像searx Docker 部署实战指南镜像运行、配置注入与自建镜像 本篇指南围绕 searx 官方文档《Docker installation》展开系统讲解后端搜索引擎30分钟把智能门锁接入Home Assistant远程控制与访问管理实战30分钟把智能门锁接入Home Assistant远程控制与访问管理实战 凌晨一点手机弹出一条推送「前门仍处于解锁状态且家中无人。」你点开 Home A文档教程智能家居物联网Buildah文档生成自动化创建镜像说明Buildah文档生成自动化创建镜像说明 你是否还在为手动编写容器镜像文档而烦恼是否希望有一种方式能自动生成清晰、专业的镜像说明本文将带你探索如何利用Bu云原生上一篇终极免费打字练习软件Qwerty Learner 完整使用指南下一篇Dozer路线图规划如何制定项目发展计划创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表