在Kubernetes中,当ConfigMap更新时如何重新启动Pods?

242

如何在配置映射更改/更新时自动重新启动与部署关联的 Kubernetes Pod 及其相关 Pod?


我知道已经谈论了在配置映射更改时自动重新启动Pod的能力,但据我所知,这在Kubernetes 1.2中尚不可用。

因此,我想要做的(我认为)是对消耗该配置映射的Pod相关的部署资源进行“滚动重启”。是否有可能强制在Kubernetes中滚动重启部署而不更改实际模板中的任何内容?目前这是最好的方法还是存在更好的选择?


5
请为我执行以下命令:$ kubectl set env deployment my deployment --env="LAST_RESTART=$(date)" --namespace ... - maciek
13个回答

198

目前解决此问题最好的方法(在https://github.com/kubernetes/kubernetes/issues/22368 中提到,链接在兄弟回答中)是使用部署(Deployments),并将您的ConfigMaps视为不可变。

当您想要更改配置时,创建一个新的ConfigMap,并在其中包含您想要进行的更改,并将您的部署指向新的ConfigMap。如果新配置无效,则部署将拒绝缩小工作的副本集。如果新配置有效,则旧的ReplicaSet将缩放为0个副本并删除,新的Pod将使用新的配置启动。

虽然不如直接就地编辑ConfigMap快速,但更加安全。


2
这也是我们采取的方法。 - Johan
16
值得一提的是,新的实验性工具“kustomize”支持自动创建确定性配置映射哈希,这意味着您无需手动创建新的配置映射:https://github.com/kubernetes-sigs/kustomize/blob/12d1771bb349e1523bc546e314da63c684a7faf2/examples/configGeneration.md#L5 - Symmetric
1
这就是Spinnaker在幕后所做的事情,因此如果您使用它,就不必担心这个问题。 - Gus
好的方法,但需要处理旧配置映射的删除 :( - Alok Kumar Singh
1
标签选择器是不可变的,最终使用这个方法并按照名称惯例清理配置地图,以此来完成繁重的工作。参考链接:https://dev59.com/AFoU5IYBdhLWcg3wk3p2#51421527 - Alok Kumar Singh
显示剩余4条评论

95

在配置地图更新时发出Pod信号是正在开发的功能(https://github.com/kubernetes/kubernetes/issues/22368)。

您始终可以编写自定义pid1以注意到confimap已更改并重新启动您的应用程序。

您还可以例如:在2个容器中挂载相同的配置地图,在第二个容器中公开HTTP健康检查,如果配置地图内容的哈希值发生更改,则失败,并将其作为第一个容器的活动探针推送(因为Pod中的容器共享同一网络命名空间)。当探测失败时,kubelet会为您重启第一个容器。

当然,如果您不关心Pod在哪些节点上,可以直接删除它们,复制控制器将为您“重新启动”它们。


17
使用一个部署,我会先将其缩小再扩大。不过你仍然会有一小段的停机时间。你可以用一行命令来减少这段时间... kubectl scale deployment/update-demo --replicas=0; kubectl scale deployment/update-demo --replicas=4; - Nick H
1
这是否意味着它所挂载的卷已更新,您只需要在 Pod 上重新读取文件而无需重新启动整个 Pod? - Matt Williamson
@NickH 快速而简单,幸运的是在我的情况下停机时间可以接受,这个方法非常好用,谢谢! - ChocolateAndCheese
1
为了避免停机时间,我们可以将其扩展,比如从一个副本扩展到两个,然后杀死旧实例。这种方法可行吗? - xbmono
3
更容易运行 kubectl rollout restart deployment/my-deploy,k8s 将管理类似于更新部署的滚动重启。这也适用于 DaemonSets 和 StatefulSets。 - nijave
显示剩余4条评论

80

我发现最好的方法是运行Reloader

它允许您定义要监视的configmaps或secrets,当它们被更新时,会执行您的deployment的滚动更新。下面是一个例子:

假设您有一个部署foo和一个名为foo-configmap的ConfigMap。 每次更改configmap时,您希望滚动deployment的pods。 您需要使用以下命令运行Reloader:

kubectl apply -f https://raw.githubusercontent.com/stakater/Reloader/master/deployments/kubernetes/reloader.yaml

然后在您的部署中指定此注释:

kind: Deployment
metadata:
  annotations:
    configmap.reloader.stakater.com/reload: "foo-configmap"
  name: foo
...

1
Reloader 兼容 kubernetes >= 1.9。 - jacktrade
但是我不想每次更改configmap时都要滚动部署的pods,我希望configmap卷文件可以静默更改,而不会重新启动单个pod。 - Steve Wu
2
@SteveWu,这已经发生了 https://kubernetes.io/docs/concepts/configuration/configmap/#mounted-configmaps-are-updated-automatically 但是,如果您运行的应用程序需要被通知或重新启动以获取更新的文件,那么这个问题就是关于这个的。 - jbg

61

Helm 3 文档页面

通常情况下,配置文件以 configmap 或 secret 的形式注入到容器中。根据应用程序的不同,在使用 helm upgrade 更新后,如果这些配置文件被更新,则可能需要重新启动容器,否则应用程序将继续使用旧的配置运行,导致部署不一致。

可以使用 sha256sum 函数与 include 函数配合使用,以确保如果其他规范发生更改,则更新部署模板部分:

kind: Deployment
spec:
  template:
    metadata:
      annotations:
        checksum/config: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
[...]

在我的情况下,由于某些原因,$.Template.BasePath 无法正常工作,但 $.Chart.Name 可以:

spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: admin-app
      annotations:
        checksum/config: {{ include (print $.Chart.Name "/templates/" $.Chart.Name "-configmap.yaml") . | sha256sum }}

23
只适用于Helm,不适用于普通Kubernetes使用。 - Emii Khaos
4
答案有帮助,但可能与此问题无关。 - Anand Singh Kunwar
1
最近发布了helm 3。因此,该链接已过时。它指向master分支。以下URL将引导您进入(目前)最新的helm 2文档:https://github.com/helm/helm/blob/release-2.16/docs/charts_tips_and_tricks.md#automatically-roll-deployments-when-configmaps-or-secrets-change - Marcel Hoyer
很棒的解决方案。我改用sha1sum,因为在我的情况下,sha256sum有65个字符,导致“Deployment.apps“xxx”无效:metadata.labels:无效值:“xxx”:必须不超过63个字符”。另一种选择是“| trunc 63”,但sha1sum应该更加“独特”。 - iptizer
Helm v3的链接:https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments - Radek Liska
除非您使用HELM/kubectl应用更改,否则此操作将无法正常工作。 - Vishrant

23

对于 k8s > 1.15 版本,作为 CI/CD 的一部分,使用配置路径连接到卷挂载的方式进行滚动重启 对我来说效果最好。使用重新加载插件或在部署清单 YML 中设置restartPolicy: Always 并不适用于我。无需更改应用程序代码,对于静态资源和微服务都有效。

kubectl rollout restart deployment/<deploymentName> -n <namespace> 

谢谢!它在我的GKE部署上运行良好。 - Ennio Sousa

16

您可以更新与您的部署不相关的元数据注释。这将触发滚动更新。

例如:

例如:

    spec:
      template:
        metadata:
          annotations:
            configmap-version: 1

14
更改元数据标签不会触发 Pod 的重启。 - dan carter
2
这个回答有点赞,所以我需要问一下。如果我们更新元数据,Kubernetes集群会触发滚动更新吗?@maoz-zadok - titus
3
只要元数据标签在template.spec下面,我相信这个方法是可行的。 - Saikiran Yerram
3
确认使用spec.template.metadata.labels下的标签可以起作用!(已编辑答案,正在审核中)。这是一种非常优雅的方法 👍 - Alok Kumar Singh
2
我建议使用注释而不是标签来实现这个方法,因为您可以自由地更新注释,而标签无法被改变。或者在较新版本的kubectl中,您可以直接调用 kubectl rollout restart deployment/mydeployname 来触发相同配置的新部署。https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands#-em-restart-em- - Symmetric
显示剩余3条评论

12

我曾遇到这样的问题:部署在子图表中,而控制它的值则在父级图表的值文件中。以下是我们用来触发重启的代码:

spec:
  template:
    metadata:
      annotations:
        checksum/config: {{ tpl (toYaml .Values) . | sha256sum }}

显然,这将在任何值更改时触发重新启动,但它适用于我们的情况。最初在子图表中的内容仅在子图表中的config.yaml更改时才起作用:

    checksum/config: {{ include (print $.Template.BasePath "/config.yaml") . | sha256sum }}

1
非常好用。谢谢! - rsmets
真酷!随处可用 :-) - undefined
工作了!谢谢 :D - undefined

4
考虑使用 kustomize(或 kubectl apply -k)并利用其强大的 configMapGenerator 功能。例如,来自:https://kubectl.docs.kubernetes.io/references/kustomize/kustomization/configmapgenerator/
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# Just one example of many...
- name: my-app-config
  literals:
  - JAVA_HOME=/opt/java/jdk
  - JAVA_TOOL_OPTIONS=-agentlib:hprof

  # Explanation below...
  - SECRETS_VERSION=1

然后,在您的部署中简单引用my-app-config。使用kustomize进行构建时,它将自动查找并更新对my-app-config的引用,以及带有更新后缀(例如 my-app-config-f7mm6mhf59 )。

奖励提示:更新密码:我还使用此技术来强制重新加载密码(因为它们受到相同的影响)。虽然我个人完全独立地管理我的密码(使用Mozilla的sops),但您可以在密钥保管库旁边捆绑一个配置映射,例如:部署中:

# ...
spec:
  template:
    spec:
      containers:
        - name: my-app
          image: my-app:tag
          envFrom:
            # For any NON-secret environment variables. Name is automatically updated by Kustomize
            - configMapRef:
                name: my-app-config

            # Defined separately OUTSIDE of Kustomize. Just modify SECRETS_VERSION=[number] in the my-app-config ConfigMap
            # to trigger an update in both the config as well as the secrets (since the pod will get restarted).
            - secretRef:
                name: my-app-secrets

然后,像我上面所做的那样,在你的 ConfigMap 中添加一个变量,如 SECRETS_VERSION。每次更改 my-app-secrets 时,只需增加 SECRETS_VERSION 的值,除了触发 kustomize 的 ConfigMap 名称更改外,它没有任何其他作用,这也应该导致 pod 重新启动。所以现在变成了:


2

我也在这个问题上纠结了很长时间,希望能以一种优雅而快速的方式解决它。

这是我的20美分

  • 使用标签作为答案,如here所述,如果您正在更新标签,则不起作用。但如果您始终添加标签,则可以起作用。更多详细信息请参见here

  • 提到的答案here是我认为最优雅的快速解决方法,但它处理删除时会有问题。我补充了这个答案:

解决方案

我正在其中一个Kubernetes Operator中执行此操作,其中在一个协调循环中只执行一个任务。

  • 计算配置地图数据的哈希值。假设结果为v2
  • 如果不存在,则创建标签为version: v2product: prime的ConfigMap cm-v2并返回。如果存在,请执行下一步。
  • 查找所有具有标签product: prime但不具有version: v2的部署。如果找到此类部署,请删除它们并返回。否则,执行下一步。
  • 删除所有具有标签product: prime但不具有version: v2的ConfigMap。否则,执行下一步。
  • 创建标签为product: primeversion: v2的Deployment deployment-v2,并将配置地图附加为cm-v2,然后返回。否则,不执行任何操作。
这就是了!看起来很长,但这可能是最快的实现方式,并且基本上是将基础设施视为 Cattle(不可变性)的原则。
此外,上述解决方案适用于您的 Kubernetes 部署具有重新创建更新策略的情况。对于其他情况,逻辑可能需要进行一些微调。

2

本地选项无需第三方

Kubernetes如果将配置映射作为挂载(如果有子路径则不适用),它会自动重新加载

示例:https://medium.com/@harsh.manvar111/update-configmap-without-restarting-pod-56801dce3388

第三方选项

当更改/更新其configmap时,如何自动重启与部署相关的Kubernetes pod和pod?

如果您将configmap用作环境,则必须使用外部选项。

当卷中使用的 ConfigMap 被更新时,投影键也被最终更新。kubelet 检查每个周期同步时装载的 ConfigMap 是否是最新的。但是,kubelet 用其本地缓存获取 ConfigMap 的最新值。可以使用 KubeletConfiguration 结构中的 ConfigMapAndSecretChangeDetectionStrategy 字段配置缓存类型。ConfigMap 可以通过 watch (默认),基于 ttl 或将所有请求直接重定向到 API server 进行传播。因此,从 ConfigMap 更新到新键被投影到 Pod 的时间总延迟可能长达 kubelet 同步周期 + 缓存传播延迟,其中缓存传播延迟取决于所选缓存类型(它等于 watch 传播延迟、缓存的 ttl 或零,分别对应不同缓存类型)。
官方文档:https://kubernetes.io/docs/concepts/configuration/configmap/#mounted-configmaps-are-updated-automatically 作为环境变量使用的 ConfigMaps 不会自动更新,需要重新启动 Pod。 简单示例 Configmap
apiVersion: v1
kind: ConfigMap
metadata:
  name: config
  namespace: default
data:
  foo: bar

POD配置

spec:
  containers:
  - name: configmaptestapp
    image: <Image>
    volumeMounts:
    - mountPath: /config
      name: configmap-data-volume
    ports:
    - containerPort: 8080
  volumes:
    - name: configmap-data-volume
      configMap:
        name: config

这个方法虽然能用,但是很糟糕:“最终也会更新”有时候意味着几分钟。 - undefined
这取决于kubelet的刷新频率,如果你正在运行自己的K8s集群,你可以调整它,但如果是托管的集群,我想你就不能这么做了。 - undefined

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