当 Kubernetes 中的 configmap 更新时重新启动 pods?

Restart pods when configmap updates in Kubernetes?

当配置映射为 changed/updated 时,如何自动重启与部署关联的 Kubernetes pods 和 pods?


我知道有人谈论过当配置映射更改时自动重启 pods 的能力,但据我所知,这在 Kubernetes 1.2 中尚不可用。

那么(我认为)我想做的是 deployment 资源的 "rolling restart" 与使用配置映射的 pods 关联。是否有可能,如果可以,如何在不更改实际模板中的任何内容的情况下强制滚动重启 Kubernetes 中的部署?这是目前最好的方法还是有更好的选择?

在配置映​​射更新时向 pod 发送信号是一项正在开发中的功能 (https://github.com/kubernetes/kubernetes/issues/22368)。

您始终可以编写一个自定义 pid1 来通知 confimap 已更改并重新启动您的应用程序。

您还可以例如:在 2 个容器中安装相同的配置映射,在第二个容器中公开一个 http 健康检查,如果配置映射内容的哈希值发生变化,该检查将失败,并将其推送为第一个容器的 liveness 探测器(因为 pod 中的容器共享相同的网络命名空间)。当探测失败时,kubelet 会为你重启你的第一个容器。

当然,如果您不关心 pods 在哪些节点上,您可以简单地删除它们,复制控制器将为您 "restart" 它们。

当前解决此问题的最佳解决方案(在 https://github.com/kubernetes/kubernetes/issues/22368 兄弟回答中链接的深处引用)是使用 Deployments,并考虑您的 ConfigMaps 是不可变的。

当您想要更改您的配置时,使用您想要进行的更改创建一个新的 ConfigMap,并将您的部署指向新的 ConfigMap。如果新配置被破坏,Deployment 将拒绝缩减你的工作 ReplicaSet。如果新配置有效,那么您的旧 ReplicaSet 将缩放到 0 个副本并删除,新的 pods 将使用新配置启动。

不如就地编辑 ConfigMap 快,但安全得多。

Helm 3 doc page

配置映射或机密通常作为配置文件注入容器中。根据应用程序的不同,如果使用后续 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 }}

另一种方法是将其粘贴到 Deployment 的命令部分:

...
command: [ "echo", "
  option = value\n
  other_option = value\n
" ]
...

或者,为了让它更像 ConfigMap,使用一个额外的 Deployment,它将只在 command 部分托管该配置,并在其上执行 kubectl create,同时添加一个独特的 'version' 到它的名字(比如计算内容的哈希值)并修改所有使用该配置的部署:

...
command: [ "/usr/sbin/kubectl-apply-config.sh", "
  option = value\n
  other_option = value\n
" ]
...

我可能会 post kubectl-apply-config.sh 如果它最终能正常工作。

(不要那样做;看起来太糟糕了)

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

例如:

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

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

它允许您定义要监视的配置映射或机密,当它们更新时,将对您的部署执行滚动更新。这是一个例子:

您有一个部署 foo 和一个名为 foo-configmap 的 ConfigMap。每次更改 configmap 时,您都希望滚动部署的 pods。您需要 运行 重新加载:

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

在 Deployment 位于子图表中并且控制它的值位于父图表的值文件中时遇到了这个问题。这是我们用来触发重启的:

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

显然,这将在 任何 值更改时触发重启,但它适用于我们的情况。只有当子图表中的 config.yaml 本身发生变化时,子图表中最初的内容才会起作用:

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

我也在这个问题上苦苦思索了一段时间,希望以一种优雅而快速的方式解决这个问题。

这是我的 20 美分:

  • 使用上述标签的答案 won't work if you are updating labels. But would work if you always add labels. More details

  • 提到的答案是我认为最优雅的快速执行此操作的方法,但存在处理删除的问题。我正在添加这个答案:

解决方案

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

  • 计算配置映射数据的哈希值。假设它是 v2.
  • 创建具有标签的 ConfigMap cm-v2version: v2product: prime(如果不存在)和 RETURN。如果存在,请往下看。
  • 查找所有具有标签 product: prime 但没有 version: v2 的部署,如果找到此类部署,则删除它们并 RETURN。否则往下看。
  • 删除所有具有标签 product: prime 但没有 version: v2 的 ConfigMap,否则请往下看。
  • 使用标签 product: primeversion: v2 创建部署 deployment-v2 并将配置映射附加为 cm-v2 和 RETURN,否则什么也不做。

就是这样!它看起来很长,但这可能是最快的实现,并且原则上处理 infrastructure as Cattle(不变性)。

此外,当您的 Kubernetes 部署具有 Recreate 更新策略时,上述解决方案有效。对于其他场景,逻辑可能需要稍作调整。

将不可变 属性 添加到配置映射完全避免了这个问题。使用配置哈希有助于无缝滚动更新,但无助于回滚。你可以看看这个开源项目 - 'Configurator' - https://github.com/gopaddle-io/configurator.git .'Configurator' 使用自定义资源通过以下方式工作:

  1. Configurator 将部署生命周期与 configMap 联系起来。什么时候 配置映射已更新,为此创建了一个新版本 配置映射。所有附加到 configMap 的部署 获取绑定最新 configMap 版本的滚动更新。

  2. 当您将部署回滚到旧版本时,它会反弹到 执行滚动更新之前的 configMap 版本。

通过这种方式,您可以维护配置映射的版本,并促进滚动和回滚到您的部署以及配置映射。

如果k8>1.15;然后做 rollout restart 最适合我作为 CI/CD 的一部分,应用程序配置路径与卷安装挂钩。部署清单 YML 中的重新加载器插件或设置 restartPolicy: Always 对我不起作用。无需更改应用程序代码,适用于静态资产和微服务。

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

考虑使用 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),但您可以将配置映射 您的秘密捆绑在一起,例如在您的 deployment:

# ...
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 名称的更改外,没有其他用途,应该 [=34] =]也 会导致您的 pod 重新启动。那么它就变成了:

How do I automatically restart Kubernetes pods and pods associated with deployments when their configmap is changed/updated?

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

Kubernetes auto-reload config map 如果挂载为 volume(如果有子路径,它将无法使用)。

When a ConfigMap currently consumed in a volume is updated, projected keys are eventually updated as well. The kubelet checks whether the mounted ConfigMap is fresh on every periodic sync. However, the kubelet uses its local cache for getting the current value of the ConfigMap. The type of the cache is configurable using the ConfigMapAndSecretChangeDetectionStrategy field in the KubeletConfiguration struct. A ConfigMap can be either propagated by watch (default), ttl-based, or by redirecting all requests directly to the API server. As a result, the total delay from the moment when the ConfigMap is updated to the moment when new keys are projected to the Pod can be as long as the kubelet sync period + cache propagation delay, where the cache propagation delay depends on the chosen cache type (it equals to watch propagation delay, ttl of cache, or zero correspondingly).

官方文档: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

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