如何在不更新镜像的情况下更新 Kubernetes 部署

4

背景

我们正在使用k8s 1.7。我们使用deployment.yml来维护/更新k8s集群状态。在deployment.yml中,pod的image被设置为${some_image}:latest。一旦创建了deployment,当有代码合并到master时,pod的image将更新为${some_image}:${build_num}

现在的情况是,假设我们需要修改deployment.yml中的资源限制并重新应用它。那么deployment的image也会更新为${some_image}:latest。我们想要保持镜像在集群状态中的原样,而不必在deployment.yml中维护实际的标签。我们知道可以在文件中省略replicas,它默认从集群状态中取值。

问题

在1.7版本中,spec.template.spec.containers[0].image是必需的。

  1. 是否可能应用deployment.yml而不将image更新为${some_image}:latest(类似于--ignore-image-change参数或deployment.yml中的特定字段)?如果可以,如何实现?

此外,我在1.10文档中发现image是可选的。

  1. 这是真的吗?如果是,从哪个版本开始?

--- 更新 ---

CI在每次合并到主干时构建和部署新的镜像。在部署时,CI运行命令kubectl set image deployment/app container=${some_image}:${build_num},其中${build_num}是pipeline的构建号。

要应用deployment.yml,我们运行kubectl apply -f deployment.yml


你能解释一下“由CI指定”的意思吗?如果我理解正确,你正在使用“latest”镜像标签来保持最新状态,但有时你不想让“latest”拉取最新的镜像?为什么不坚持使用适合你的应用程序的标签,而不必担心镜像的最新标签呢? - erstaples
你能提供链接到文档的吗?这些文档说明.spec.containers[]。image字段是可选的吗? - erstaples
我们使用CI来构建和部署镜像。在部署时,它运行kubectl set image deployment/app container=image:tag命令,其中tag是构建号。无论在deployment.yml中设置为什么,一旦再次运行,它都会更新部署的镜像。在https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10/#container-v1-core中提到:此字段是可选的,以允许更高级别的配置管理默认值或...这让我想知道1.10是否像副本一样将其设置为可选。 - benjah1
4个回答

0

像Const一样,我强烈建议不要在任何docker镜像中使用:latest,而是使用CI/CD来解决版本问题。

我们在Jenkins X项目中也有同样的问题,我们有许多git仓库,当我们更改诸如库或基础docker镜像之类的内容时,我们需要在pom.xml、package.json、Dockerfiles、helm charts等中更改许多版本。

我们使用一个简单的CLI工具叫做UpdateBot,它自动化生成所有下游仓库的Pull Requests。我们倾向于将其视为库和基础镜像的持续交付;)。例如,这里是UpdateBot在Jenkins X组织仓库上生成的当前Pull Requests

那么,当我们发布新的基础镜像时,这里是如何更新Dockerfiles / helm charts的方法: https://github.com/jenkins-x/builder-base/blob/master/jx/scripts/release.sh#L28-L29


0
然而,在deployment.yml文件中,我们指定了镜像的最新标签,因为不可能保持此字段最新。
使用“:latest”标签违反Kubernetes部署的最佳实践,原因有很多,回滚和版本控制就是其中之一。要正确解决这个问题,您可能需要重新考虑CI/CD流程的方法。例如,我们使用ci-pipeline或ci-job版本来标记镜像。
是否可以更新部署而不更新指定的文件中的镜像?如果可以,如何操作?
  • 要更新pod而不更改镜像,您有一些选项,每个选项都有一些限制,并且它们都需要一些操作技巧,并引入了额外的故障点,因为它违反了推荐的方法。
  • k8s可以从您的远程注册表中拉取镜像(您必须跟踪哈希值,因为最新版本不在您的直接控制范围内-可能存在问题)。您可以在运行pod的节点的本地docker注册表上检查使用的哈希值。
  • k8s可以从本地节点注册表中拉取镜像(您必须确保在所有潜在的用于运行pod的节点上,“:latest”在本地注册表中处于同一页上,以使此工作-可能存在问题)。一旦到达那里,您可以玩弄容器的imagePullPolicy,使得当CI工具部署时-它使用yaml的apply(与create相对)并将图像策略设置为Always,紧随其后的是Never的图像策略的应用(也可能存在问题),将拉取策略限制为已经拉取到本地存储库的图像(如前所述,可能存在问题)。
  • 以下是有关此方法的文档摘录:默认情况下,kubelet将尝试从指定的注册表中拉取每个图像。但是,如果容器的imagePullPolicy属性设置为IfNotPresent或Never,则使用本地图像(分别优先或排他性)。 如果您想依赖预拉取的图像作为注册表身份验证的替代品,则必须确保集群中的所有节点都具有相同的预拉取图像。
  • 关于k8s如何处理图像以及为什么最新标记可能会反弹的更多信息,请参见此处:https://kubernetes.io/docs/concepts/containers/images/

0

如果您不想在CI中处理复杂的deployment.yaml语法,可以使用模板处理器。例如mustache。这会稍微改变CI流程:

  1. 在模板配置(env1.yaml)中更新镜像版本
  2. 从模板deployment.mustache和env1.yaml生成deployment.yaml
    $ mustache env1.yml deployment.mustache > deployment.yaml
  3. 将配置应用于集群。
    $ kubectl apply -f deployment.yaml

主要优点:

  1. env1.yaml始终包含最新的主构建镜像,因此您正在使用正确的镜像创建部署对象。
  2. env1.yaml易于在CI步骤中更新或生成。
  3. deployment.mustache保持不变,您可以确保最终的deployment.yaml中可能发生的所有更改都是镜像版本。

如果mustache不适合您的CI,还有许多其他模板渲染解决方案。


-1
你是否了解从Docker Registry拉取镜像的repo.example.com/some-tag@sha256:... 语法?它几乎完美地解决了你所描述的问题。

从评论中更新:

你正在解决错误的问题;该文件仅用于将内容加载到集群中 - 从那一刻起,元数据的权威副本就在集群中。 kubectl patch 命令可以是一种手术方式来更改某些内容,而不必诉诸于 sed(或更糟),但不应尝试在集群外部维护集群状态。


你能更具体一些吗?我不知道如何使用摘要来引用图像以避免在使用deployment.yml更新部署时更新图像。我们的CI会在每次合并到主分支时构建和部署新的镜像。摘要始终不同。此外,为了使这更有趣,由于某种原因,我们必须使用Packer... - benjah1
1
你的问题仍然非常令人困惑,但是如果你的目标是更新Deployment,但要确保当前的镜像仍然在使用中,即使是对于新创建的Pod,那么指定@sha256将会固定该镜像位置,因为不会再有更多的镜像被推送到:latest并且具有相同的哈希值。我非常、非常、非常同意其他答案提到的:latest是个坏消息,但是如果你的组织不能开始使用带编号的镜像,那么使用sha256是最好的折衷方案。 - mdaniel
我们必须使用Packer。"必须"是一个非常强烈的措辞,几乎肯定会使你遇到的任何问题变得更糟。 - mdaniel
正确。我知道在 file 中可以省略副本,并且默认情况下它会从 集群状态 中获取值。想知道是否有类似于图像的东西。或者可能是一个像 --ignore-image-change 这样的参数,或者文件中的一个特定字段,它将忽略图像的差异。 - benjah1
1
我知道这是一个老答案,但你能解释一下“但是不应该尝试在集群外部维护集群状态”吗?我不会在没有某些更改的迹象的情况下向集群推送更改,通过版本化清单,我可以非常轻松地管理这些更改的迹象。这似乎更符合不可变基础设施的理念。您是否存储了 kubectl patch 命令?您如何知道运行了哪些命令?您完全依赖于集群提供面包屑吗? - Damien Roche
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接