正确版本化Docker映像

时间:2019-05-19 22:26:52

标签: docker versioning

关注4岁的问题 Docker image versioning and lifecycle management,因为恕我直言,它无法正确处理Docker映像的版本

  

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

还有

  

答案是不使用latest

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

  • Here使其提示不使用latest,而“解决方案” hint 则要标记两次。我强调“ 提示”,因为(对我)没有可靠的建议。
  • here甚至表明我们需要对同一张图片进行docker push 两次

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

AMEND:

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

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

要做 需要将我的Docker映像推送/发布到Docker存储库,并且从here暗示 如果您使用特定的ID标记,则使用latest会给您带来最新版本的麻烦。 此外,使用git的简短版本ID可能是内部使用的不错解决方案,但是当发布docker映像以供公众使用时,它可能不是最佳解决方案。

5 个答案:

答案 0 :(得分:1)

对我来说,这就是能够判断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 .

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

# 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>

答案 1 :(得分:1)

Docker根本没有标记值的语义。标签完全可以是任何字符串值,并且标签可以重复使用。唯一的特殊标记值是,如果您仅在imagenamedocker pull命令中说docker run,它将自动解释为imagename:latest

从机械上讲,您可以为同一张图片赋予多个标签,但是您需要docker push全部使用它们。推送的最昂贵部分是图层内容,因此这通常只会在现有图像上推送替代标签的事实。同样,拉取图像标签(如果它是您已经拥有的图像的副本)几乎是免费的,但是没有一种简单的方法可以找出给定图像的所有标签。

我建议:

  1. 赋予每个一个唯一的标识符,例如源控件提交ID或时间戳。
  2. 如果并且当您进行正式发行时,还请用发行号标记该发行版本的内部版本。 (更一般而言,如果标记了当前源代码控制提交,请使用源代码控制标记Docker镜像。)
  3. 如果对您的开发工作流程有用,还请使用分支名称来标记作为分支提示的构建。
  4. 鉴于其突出的地位,将某物标记为latest(也许是最新版本)可能很有用。
  5. 在引用内置映像时(在latest命令,Dockerfile docker run行,Kubernetes pod规范等中,请避免使用FROM和其他希望更改的标记)。 / li>

这种组合可能意味着同一张图像被标记为imagename:g1234567:1.2.3:master:latest,并且您的CI系统需要做四个{{ 1}}。您可能希望前两个图像相当恒定,但后两个图像会定期更改。然后,您可以放心运行docker push之类的东西。

(想到的一种特殊情况是,软件包很少更改,因此,如果有上游修复程序或安全更新,可能需要重新构建。通常,为此重复使用相同的标签:例如,{ {1}}每两周更新一次。)

答案 2 :(得分:1)

泊坞窗中的图像由参考引用,最常见的是图像存储库和标签。该标签是指向特定图像的相对自由形式的字符串。最好将标记视为可变的指针,可以对其进行更改,可以使多个指针指向同一图像,并且可以在基础图像可能保持不变的情况下将其删除。

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


对于存储库维护者,以下是一些常见的实现:

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

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

选项C::某些项目遵循的是滚动版本,不提供向后兼容性保证。通常,这是通过内部版本号或日期字符串来完成的,并且确实Docker本身使用了此方法,尽管该过程具有弃用功能并避免破坏更改的过程。我已经看到很多公司的内部项目使用此策略来版本化其图像,这取决于CI服务器的内部版本号。

选项D:我不太喜欢将Git修订哈希作为图像标签,因为这些图像在不返回Git存储库的情况下不会传递任何细节。并非每个用户都有这种访问权限或技能来理解此参考。通过查看两个不同的哈希值,如果没有外部检查,我不知道哪个更新或与我的应用程序兼容。他们还假设唯一重要的版本号来自Git,而忽略了相同的Git修订版可用于从同一Git存储库中的不同父映像,不同体系结构或仅多个Dockerfile /多阶段目标创建多个映像。相反,我喜欢使用label schema,最终喜欢使用image spec annotations,一旦我们获得了围绕图像注释的工具,即可跟踪Git修订之类的细节。这样会将Git修订版放入元数据中,您可以查询该元数据以验证图像,同时仍将标签本身留给用户参考。


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

首先是运行您自己的注册表服务器,并将外部依赖项拉到本地服务器。 Docker包含您可以安装的image for a standalone registry,并且该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:/#

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

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

答案 3 :(得分:0)

对于基于docker的应用程序,我用git commit的短哈希标记它们。这样,您可以立即识别容器中的代码。我不确定如何处理创建为基础映像的Docker映像。

答案 4 :(得分:0)

我用git commit hash和构建时间戳记(串联)标记

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