正确版本控制 Docker 镜像

xpt*_*xpt 20 versioning docker

在 4 年前的问题Docker 镜像版本控制和生命周期管理之后,因为恕我直言,它没有正确解决Docker 镜像的版本控制问题 :

我认为这个答案不够充分,因为同一个标签可能有连续的版本。我们需要一种能够将依赖关系锁定到特定标签版本的方法。

并且,

答案是不要使用latest.

我在网上找到的“解决方案”也令人困惑。例如,

  • 这里微调后不能使用latest,而“解决方案”暗示要标注的两倍。我强调“暗示”,因为没有可靠的建议(对我来说)。
  • 而且在这里它甚至表明,我们需要做docker push 两次在同一图像上。

那么,如何正确地对 Docker 镜像进行版本控制(在本地以及在推送/发布到 docker hub 时)?

修正:

到目前为止,有两个答案。感谢那。

  • 两者都使用 git 的短版本 ID。
  • 并且都错过了答案中的推送/发布部分。

因为我确实需要将我的 docker 镜像推送/发布到 Docker 存储库,并且从这里开始暗示latest如果您使用特定的 ID 标记,在拉取最新版本时不使用会给您带来麻烦。此外,使用git的短版本ID可能是内部使用的一个很好的解决方案,但是当发布docker镜像供公众使用时,它可能不是最好的解决方案。

Dav*_*aze 22

Docker 根本没有给标签值赋予任何语义意义。标签可以是任何字符串值,并且标签可以重复使用。唯一的特殊标记值是,如果您只imagename在 adocker pulldocker run命令中说,它会自动解释为imagename:latest

从机制上讲,您可以为同一个图像提供多个标签,但您需要为docker push所有这些标签。推送的昂贵部分是图层内容,因此这主要只是推送现有图像上的备用标签的事实。类似地,提取图像标签(如果它是您已有图像的副本)几乎是免费的,但没有简单的方法可以找出给定图像的所有标签。

我会推荐:

  1. 每个构建提供一个唯一标识符,例如源代码控制提交 ID 或时间戳。
  2. 如果您发布正式版本,还要使用版本号标记该版本的版本。(更一般地说,如果当前的源代码控制提交被标记,则使用源代码控制标记标记 Docker 镜像。)
  3. 如果它对您的开发工作流有用,还可以用分支名称标记作为分支提示的构建。
  4. 鉴于它的突出地位,将某些内容标记为latest(可能是最新版本)可能很有用。
  5. latest在引用构建的镜像(在docker run命令、DockerfileFROM行、Kubernetes pod 规范等中)时,避免使用您希望更改的标签和其他标签。

事情这样的组合可能意味着相同的图像进行标记imagename:g1234567:1.2.3:master,和:latest,你的CI系统需要做四点docker push上课。您可能会期望前两个图像相当稳定,但后两个图像会定期更改。然后你可以imagename:1.2.3有信心地运行类似的东西。

(想到的一个特殊情况是很少更改的软件包,因此如果有上游修复或安全更新,则可能需要重新构建。为此重用相同的标签似乎很典型:例如,ubuntu:18.04每周更新一次或两个。)

  • 这可能不是最全面的答案,但我接受它是因为它回答了我在 OP 中的_特定_困惑/问题。即,这里的所有答案都解决了我想问的问题。因此,对于将来来这里寻找一般答案的人,也请查看其他答案。 (3认同)

BMi*_*tch 14

docker 中的图像通过引用引用,最常见的是图像存储库和标签。该标签是指向特定图像的相对自由格式的字符串。标签最好被认为是一个可变指针,它可以改变,你可以有多个指针指向同一个图像,并且可以在底层图像保持不变的情况下删除它。

由于 docker 不会在标签上强制执行太多结构(除了验证它包含有效字符并且不超过长度限制),强制执行这是每个存储库维护者的工作,并且产生了许多不同的解决方案。


对于存储库维护者,这里有一些常见的实现:

选项 A:理想情况下,存储库维护者遵循某种形式的semver。此版本号应映射到打包软件的版本,通常带有用于映像修订的附加补丁号。重要的是,以这种方式标记的图像不仅应包括 1.2.3-1 版的标签,还应包括 1.2.3、1.2 和 1 版的标签,每个版本都更新到各自层次结构中的最新版本。这允许下游用户依赖 1.2 并在出现错误修复和安全更新时自动获取 1.2.4、1.2.5 等的更新。

选项 B:与上面的 semver 选项类似,许多项目都包含其他重要的元数据及其标签,例如用于该构建的架构或基础映像。这在 alpine 与 debian/slim 图像或 arm 与 amd 编译代码中很常见。这些通常会与 semver 结合使用,因此alpine-1.5除了alpine-1alpine标签之外,您还可能会看到诸如、 之类的标签。

选项 C:一些项目更多地遵循滚动发布,不提供向后兼容性承诺。这通常使用内部版本号或日期字符串来完成,实际上 Docker 本身也使用它,尽管有一个过程来弃用功能并避免破坏性更改。我已经看到很多公司的内部项目使用这种策略来版本他们的图像,依赖于来自 CI 服务器的内部版本号。

选项 D:我不太喜欢将 Git 修订哈希作为图像标签,因为如果不参考 Git 存储库,这些不会传达任何细节。并非每个用户都具有理解此参考的权限或技能。通过查看两个不同的哈希,我不知道哪个更新或与我的应用程序兼容,而无需进行外部检查。他们还假设唯一重要的版本号来自 Git,并忽略了相同的 Git 修订版可能用于创建多个镜像,来自不同的父镜像、不同的架构,或者只是在同一个 Git 存储库中的多个 Dockerfiles/multistage 目标。相反,我喜欢使用标签模式,并最终使用图像规范注释一旦我们获得了围绕图像注释的工具,就可以跟踪 Git 修订等细节。这会将 Git 修订版放入您可以查询以验证图像的元数据中,同时仍将标签本身留给用户提供信息。


对于图像用户,如果您需要避免来自上游的意外更改,我知道有两种选择。

第一个是运行您自己的注册服务器,并将您的外部依赖项拉到本地服务器。Docker 包含一个可供您安装的独立注册表映像,并且该 API 是开放的,这允许许多工件存储库供应商支持Docker注册表。请注意定期更新此注册表,并在更新破坏您的环境时提供返回到以前版本的方法。

第二种选择是根据可变标签停止。相反,您可以使用图像固定,它引用注册表对无法更改的清单的 sha256 唯一引用。当您检查从注册服务器拉取的图像时,您可以在 RepoDigests 中找到此值:

$ docker inspect -f '{{json .RepoDigests}}' debian:latest
["debian@sha256:de3eac83cd481c04c5d6c7344cd7327625a1d8b2540e82a8231b5675cef0ae5f"]

$ docker run -it --rm debian@sha256:de3eac83cd481c04c5d6c7344cd7327625a1d8b2540e82a8231b5675cef0ae5f /bin/bash
root@ac9db398dc03:/#
Run Code Online (Sandbox Code Playgroud)

像这样绑定到特定图像的最大风险是缺少安全更新和重要的错误修复。如果您选择此选项,请确保有一个程序来定期更新这些图像。

无论您采用哪种解决方案来拉取映像,使用 latest 仅适用于快速开发人员测试,不适用于任何生产用例。latest 的行为完全取决于存储库维护者,有些总是将其更新到最后一个版本,有些使它成为最后一个稳定版本,有些则根本忘记更新它。如果您依赖于最新版本,当上游镜像从 1.5 版本更改为 2.0 版本时,您可能会遇到中断,并且发生向后不兼容的更改。您的下一次部署将无意中包含这些更改,除非您明确依赖一个标签,该标签承诺在不破坏更改的情况下提供错误修复和安全补丁。

  • 非常感谢最全面的回答。如果它回答了我在 OP 中的具体困惑/问题,我会接受它。但无论如何还是要感谢并点赞! (2认同)

小智 7

我用 git commit hash 和构建时间戳标记(连接)

这仅仅是因为我想认识到有时构建服务器上的事情会发生变化,这意味着相同的代码可能以不同的方式编译。例如,将构建服务器切换为使用 Java 13 而不是 Java 11 进行编译。


Sla*_*mir 6

对我来说,这一切都是为了能够分辨出(我的)软件的哪个版本进入了 Docker 镜像。我的建议是使用类似 git 的短版本 ID 的东西。我不使用,latest因为它没有有用的上下文。

以 Git 版本为标签构建 Docker 镜像。在stable-package-name下面仅仅是一个应用程序,如“HelloWorld”的或任何你喜欢的名字:

REV_TAG=$(git log -1 --pretty=format:%h)
docker build -t <stable-package-name>:$REV_TAG .
Run Code Online (Sandbox Code Playgroud)

稍后我将我标记的内容推送到远程存储库:

# nominate the tagged image for deployment
docker tag <stable-package-name>:$REV_TAG <repository-name>:$REV_TAG

# push docker image to remote repository
docker push <repository-name>
Run Code Online (Sandbox Code Playgroud)