正确控制 Docker 个图像
Properly Versioning Docker Images
关注 4 年前的问题
,因为恕我直言,它没有解决版本控制 Docker 图片 正确 :
I don't find this answer to be adequate, as there can be successive versions of the same tag. We need a way to be able to lock down dependencies onto a particular version of a tag.
还有,
the answer is to not use latest
.
我在网上找到的"solution"也很混乱。例如,
- Here它hinted不使用
latest
,而"solution"hinted是标记两次。我强调“hinted”,因为没有可靠的建议(对我来说)。
- 和here它甚至表明我们需要对同一张图像做
docker push
两次。
那么,如何正确地对 Docker 图像进行版本控制(在本地以及 pushing/publishing 到 docker 中心时)?
修改:
到目前为止有两个答案。感谢那。
- 两者都使用 git 的短版本 ID。
- 并且都错过了答案中的 pushing/publishing 部分。
因为我做需要push/publish我的docker图像到Docker存储库, here 暗示 如果你使用特定的 ID 标记,不使用 latest
会给你带来麻烦。
此外,使用 git 的短版本 ID 可能是内部使用的一个很好的解决方案,但是当发布 docker 图像供 public 消费时,它可能不是最好的解决方案。
对于 docker-based 应用程序,我用 git 提交的短哈希标记它们。这样,您可以立即识别容器中的代码。我不确定如何处理创建用作基础图像的 docker 图像。
对我来说,这就是能够分辨出 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>
Docker 对标记值没有任何语义意义。标签可以是任何字符串值,标签可以重复使用。唯一的特殊标记值是,如果您在 docker pull
或 docker run
命令中只说 imagename
,它会自动解释为 imagename:latest
.
从机制上讲,您可以为同一张图片指定多个标签,但您需要 docker push
所有标签。推送的昂贵部分是图层内容,因此这主要只是将替代标签推送到现有图像上。同样,提取图像标签(如果它是您已有图像的副本)几乎是免费的,但是没有简单的方法可以找出给定图像的所有标签。
我会推荐:
- 为每个构建一个唯一标识符,类似于源代码控制提交 ID 或时间戳。
- 如果以及当您发布正式版本时,还请使用版本号标记该版本的构建。 (更一般地说,如果标记了当前源代码管理提交,请使用源代码管理标记标记 Docker 图像。)
- 如果它对您的开发工作流程有用,还可以使用分支名称标记作为分支提示的构建。
- 鉴于其突出性,将某些内容标记为
latest
(可能是最新版本)可能很有用。
- 避免使用
latest
和您希望在引用构建镜像时更改的其他标签(在 docker run
命令、Docker 文件 FROM
行、Kubernetes pod 规范中, ...).
这种组合可能意味着同一张图片被标记为 imagename:g1234567
、:1.2.3
、:master
和 :latest
,并且您的 CI 系统会需要做四个 docker push
es。您可能希望前两个图像相当稳定,但后两个会定期更改。然后,您可以自信地 运行 类似 imagename:1.2.3
的内容。
(想到的一个特殊情况是很少更改的软件包,因此如果有上游修复程序或安全更新,则可能需要重建。为此重复使用相同的标签似乎很典型:例如, ubuntu:18.04
每两周更新一次。)
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
和 alpine
标签之外,您可能还会看到 alpine-1.5
等标签。
选项 C: 一些项目遵循更多的滚动发布,不提供向后兼容性承诺。这通常是通过内部版本号或日期字符串来完成的,实际上 Docker 本身也使用它,尽管有一个过程来弃用功能并避免破坏性更改。我已经看到很多公司的内部项目使用这种策略来版本化他们的图像,依赖于 CI 服务器的内部版本号。
选项 D: 我不太喜欢将 Git 修订散列作为图像标签,因为这些不传达任何细节而无需返回 [=61] =] 存储库。并非每个用户都可能具有此访问权限或技能来理解此参考。通过查看两个不同的哈希值,如果不进行外部检查,我不知道哪个更新或与我的应用程序兼容。他们还假设唯一重要的版本号来自 Git,并忽略了相同的 Git 修订可能用于创建多个图像,来自不同的父图像、不同的体系结构,或者只是多个 Dockerfiles/multistage 目标在同一个 Git 仓库中。相反,我喜欢在我们获得围绕图像注释的工具后使用 label schema, and eventually the 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 并发生 backwards-incompatible 更改时,您可能会遇到中断。您的下一次部署将无意中包含这些更改,除非您明确依赖于承诺在不破坏更改的情况下修复错误和安全补丁的标签。
我使用 git 提交哈希和构建时间戳(串联)进行标记
这仅仅是因为我想认识到有时构建服务器上的情况会发生变化,这意味着相同的代码可能会以不同的方式编译。例如。将构建服务器切换为使用 Java 13 而不是 Java 11.
进行编译
关注 4 年前的问题
I don't find this answer to be adequate, as there can be successive versions of the same tag. We need a way to be able to lock down dependencies onto a particular version of a tag.
还有,
the answer is to not use
latest
.
我在网上找到的"solution"也很混乱。例如,
- Here它hinted不使用
latest
,而"solution"hinted是标记两次。我强调“hinted”,因为没有可靠的建议(对我来说)。 - 和here它甚至表明我们需要对同一张图像做
docker push
两次。
那么,如何正确地对 Docker 图像进行版本控制(在本地以及 pushing/publishing 到 docker 中心时)?
修改:
到目前为止有两个答案。感谢那。
- 两者都使用 git 的短版本 ID。
- 并且都错过了答案中的 pushing/publishing 部分。
因为我做需要push/publish我的docker图像到Docker存储库, here 暗示 如果你使用特定的 ID 标记,不使用 latest
会给你带来麻烦。
此外,使用 git 的短版本 ID 可能是内部使用的一个很好的解决方案,但是当发布 docker 图像供 public 消费时,它可能不是最好的解决方案。
对于 docker-based 应用程序,我用 git 提交的短哈希标记它们。这样,您可以立即识别容器中的代码。我不确定如何处理创建用作基础图像的 docker 图像。
对我来说,这就是能够分辨出 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>
Docker 对标记值没有任何语义意义。标签可以是任何字符串值,标签可以重复使用。唯一的特殊标记值是,如果您在 docker pull
或 docker run
命令中只说 imagename
,它会自动解释为 imagename:latest
.
从机制上讲,您可以为同一张图片指定多个标签,但您需要 docker push
所有标签。推送的昂贵部分是图层内容,因此这主要只是将替代标签推送到现有图像上。同样,提取图像标签(如果它是您已有图像的副本)几乎是免费的,但是没有简单的方法可以找出给定图像的所有标签。
我会推荐:
- 为每个构建一个唯一标识符,类似于源代码控制提交 ID 或时间戳。
- 如果以及当您发布正式版本时,还请使用版本号标记该版本的构建。 (更一般地说,如果标记了当前源代码管理提交,请使用源代码管理标记标记 Docker 图像。)
- 如果它对您的开发工作流程有用,还可以使用分支名称标记作为分支提示的构建。
- 鉴于其突出性,将某些内容标记为
latest
(可能是最新版本)可能很有用。 - 避免使用
latest
和您希望在引用构建镜像时更改的其他标签(在docker run
命令、Docker 文件FROM
行、Kubernetes pod 规范中, ...).
这种组合可能意味着同一张图片被标记为 imagename:g1234567
、:1.2.3
、:master
和 :latest
,并且您的 CI 系统会需要做四个 docker push
es。您可能希望前两个图像相当稳定,但后两个会定期更改。然后,您可以自信地 运行 类似 imagename:1.2.3
的内容。
(想到的一个特殊情况是很少更改的软件包,因此如果有上游修复程序或安全更新,则可能需要重建。为此重复使用相同的标签似乎很典型:例如, ubuntu:18.04
每两周更新一次。)
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
和 alpine
标签之外,您可能还会看到 alpine-1.5
等标签。
选项 C: 一些项目遵循更多的滚动发布,不提供向后兼容性承诺。这通常是通过内部版本号或日期字符串来完成的,实际上 Docker 本身也使用它,尽管有一个过程来弃用功能并避免破坏性更改。我已经看到很多公司的内部项目使用这种策略来版本化他们的图像,依赖于 CI 服务器的内部版本号。
选项 D: 我不太喜欢将 Git 修订散列作为图像标签,因为这些不传达任何细节而无需返回 [=61] =] 存储库。并非每个用户都可能具有此访问权限或技能来理解此参考。通过查看两个不同的哈希值,如果不进行外部检查,我不知道哪个更新或与我的应用程序兼容。他们还假设唯一重要的版本号来自 Git,并忽略了相同的 Git 修订可能用于创建多个图像,来自不同的父图像、不同的体系结构,或者只是多个 Dockerfiles/multistage 目标在同一个 Git 仓库中。相反,我喜欢在我们获得围绕图像注释的工具后使用 label schema, and eventually the 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 并发生 backwards-incompatible 更改时,您可能会遇到中断。您的下一次部署将无意中包含这些更改,除非您明确依赖于承诺在不破坏更改的情况下修复错误和安全补丁的标签。
我使用 git 提交哈希和构建时间戳(串联)进行标记
这仅仅是因为我想认识到有时构建服务器上的情况会发生变化,这意味着相同的代码可能会以不同的方式编译。例如。将构建服务器切换为使用 Java 13 而不是 Java 11.
进行编译