【k8s】使用 Reloader 实现热部署

1,017 阅读6分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

一. 概述

我们在 k8s 中使用 ConfigMap 作为配置文件的时候会遇到一个问题:修改 ConfigMap 后无法实现热部署,也就是更改了 ConfigMap 配置文件后需要手动重启 Pod 配置才会生效,为了处理这个问题 github 专门有个开源的项目 Reloader 来解决这个问题,项目地址如下:

Reloader 项目地址:github.com/stakater/Re…

Reloader 可以观察 ConfigMap 和 Secret 中的变化,并对 pod 及其关联的 DeploymentConfigsDeploymentsDaemonsetsStatefulsets 和 Rollouts进行滚动升级。

本文主要对 Reloader 的使用进行一个简单的介绍,详细的配置与使用可以查看源码文档。

二. Reloader 实现滚动升级的原理

当 Reloader 检测到 ConfigMap 发生变化的时候,会使用 SHA1 计算 ConfigMap 的哈希值(使用 SHA1 是因为它高效且不易发生冲突),计算完哈希值之后,Reloader 获取所有的 DeploymentsDaemonsetsStatefulsets 和 Rollouts 列表,并查找其 anotations 中是否配置了 Reloader 相关的注解,比如配置了如下 annotations :

metadata:
  annotations:
    reloader.stakater.com/auto: "true"

接着 Reloader 会查找配置了 Reloader 相关 annotations 的 DeploymentsDaemonsetsStatefulsets 中一个特殊的环境变量。

如果找到这个环境变量,则获取其值并将其与前面计算的新 ConfigMap 哈希值进行比较,如果环境变量中的旧值与新哈希值不同,则 Reloader 会更新环境变量。

如果环境变量不存在,那么它会从 ConfigMap 创建一个具有最新哈希值的新环境变量并更新相关的deploymentdaemonset或者statefulset

k8s 检测到这个环境变量发生变化,则会触发 pod 关联的 deploymentdaemonset或者statefulset 的滚动升级。

修改 Secret 实现滚动升级的原理上述相同

环境变量的名字

这个环境变量的名字定义如下:

  • 生成 ConfigMap 的环境变量的名称为:STAKATER_{configmap_name}_CONFIGMAP ,比如 ConfigMap 的名称为 foo,则生成的环境变量的名称为:STAKATER_FOO_CONFIGMAP

  • 生成 Secret 的环境变量的名称为:STAKATER_{secret_name}_SECRET ,比如 Secret 的名称为 foo,则生成的环境变量的名称为:STAKATER_FOO_SECRET

环境变量的值

这个环境变量的值为使用 SHA1 计算的 ConfigMap 或者 Secret 的哈希值。

Reloader 监控特定命名空间

默认情况下,reloader 部署在默认命名空间中并监视所有命名空间中的更改,要监视特定命名空间中的更改,请在该命名空间中部署 Reloader,并将watchGlobally标志设置为false

三. 在 k8s 中安装 Reloader

在 Reloader 的源码文档中提供了三种安装方式:

  1. 使用 Manifests 安装
  2. 使用 kustomize 安装
  3. 使用 helm 安装

这里只介绍使用 helm 安装,个人觉得使用 helm 安装的优点是方便管理、升级和修改配置,你可以根据自己的需求选择其他的安装方式,详细的说明可以查看源码文档。

使用下面的命令添加 reloader 仓库地址:

helm repo add stakater https://stakater.github.io/stakater-charts

使用下面的命令更新仓库:

helm repo update

使用下面的命令搜索 reloader:

 helm search repo reloader

为了方便修改配置,我们可以使用下面的命令下载 Reloader 的 chart 包:

helm pull stakater/reloader

下载成功后获取到一个压缩包 reloader-v0.0.105.tgz,使用下面的命令解压:

tar -zxvf reloader-v0.0.105.tgz 

解压后得到 reloader 文件夹,其中的内容如下:

[root@node01 reloader]# ll
total 20
-rw-r--r-- 1 root root  789 Feb 13 20:16 Chart.yaml
drwxr-xr-x 2 root root 4096 Feb 17 10:26 templates
-rw-r--r-- 1 root root  341 Feb 13 20:16 values.schema.json
-rw-r--r-- 1 root root 4284 Feb 13 20:16 values.yaml

我们根据需要修改 values.yaml 文件的配置即可。

修改完成后,可以使用下面的命令根据修改后的配置进行安装:

helm install -name reloader -n default ./reloader

如果不通过 -n namespace 指定安装的命名空间,则默认安装在 default 命令空间,可以根据自己的需要安装到特定的命名空间。

使用下面的命令查看 reloader 是否安装成功:

[root@node01 ~]# kubectl get deploy
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
reloader-reloader   1/1     1            1           15m

四. 使用 Reloader 滚动升级

前面简单介绍了使用 helm 安装 reloader,接下来将介绍如何使用 reloader。

源码文档中介绍了三种 reloader 的使用场景:

1. 检测所有命名空间中的 ConfigMap 或者 Secret 的变化,并实现滚动升级

DeploymentConfigsDeploymentsDaemonsetsStatefulsets 和 Rollouts 中的 annotations 中添加如下内容:

metadata:
  annotations:
    reloader.stakater.com/auto: "true"

之后 reloader 会检测所有命名空间中其相关联的 ConfigMap 或者 Secret 的变化,并实现滚动升级。

2. 限制只检测带有特殊 annotations 的 ConfigMap 或者 Secret 的变化

首先在DeploymentConfigsDeploymentsDaemonsetsStatefulsets 和 Rollouts 中的 annotations 中添加如下内容:

kind: Deployment
metadata:
  annotations:
    reloader.stakater.com/search: "true"

并且在 ConfigMap 或者 Secret 中的 annotations 中添加如下内容:

kind: ConfigMap
metadata:
  annotations:
    reloader.stakater.com/match: "true"

3. 检测指定的 ConfigMap 或者 Secret 的变化

DeploymentConfigsDeploymentsDaemonsetsStatefulsets 和 Rollouts 中的 annotations 中指定多个要检测的 ConfigMap 的名称:

kind: Deployment
metadata:
  annotations:
    configmap.reloader.stakater.com/reload: "foo-configmap,bar-configmap,baz-configmap"
spec:
  template: 
    metadata:

DeploymentConfigsDeploymentsDaemonsetsStatefulsets 和 Rollouts 中的 annotations 中指定多个要检测的 Secret 的名称:

kind: Deployment
metadata:
  annotations:
    secret.reloader.stakater.com/reload: "foo-secret,bar-secret,baz-secret"
spec:
  template: 
    metadata:

五. 验证 Reloader 的滚动升级

这里使用 ConfigMap 作为 spring boot 项目的外部配置,它的原理就是将 ConfigMap 中配置的 application.yaml 文件挂载到容器中 spring boot 项目 jar 包所在目录的 config 文件夹中,因为 spring boot优先读取 config 目录中的配置文件并覆盖内部的配置。

挂载配置文件后,在容器中的目录内容如下:

root@springboot-demo-7ddbc4dfd5-sxnqv:/app# ls -l
total 99
drwxrwxrwx 3 root root   4096 Feb 16 19:28 config
-rw-r--r-- 1 root root 102043 Feb  16 15:48 springboot-demo.jar

其中 config 文件夹中的内容如下:

root@springboot-demo-7ddbc4dfd5-sxnqv:/app# ls -l config/
lrwxrwxrwx 1 root root 22 Feb 17 10:28 application.yaml -> ..data/application.yaml

定义一个 springboot-demo 项目,其 Dockerfile 文件内容如下:

FROM openjdk:8u232-jdk
WORKDIR /app
LABEL maintainer="peterwd" app="springboot-demo"
COPY target/springboot-demo.jar springboot-demo.jar
EXPOSE 8080
CMD java -jar springboot-demo.jar

相关的部署文件如下:

deployment.yaml 文件内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: springboot-demo
  namespace: default
  labels:
    app: springboot-demo
  annotations:
    reloader.stakater.com/auto: "true"
spec:
  replicas: 1
  selector:
    matchLabels:
      app: springboot-demo
  template:
    metadata:
      labels:
        app: springboot-demo
    spec:
      containers:
        - name: springboot-demo
          image: springboot-demo
          imagePullPolicy: Always
          env:
            - name: TZ
              value: Asia/Shanghai
            - name: NAMESPACE
              value:  default             
          ports:
            - containerPort: 8080
          volumeMounts:
            - mountPath: /app/config
              name: config
      volumes:
        - configMap:
            name: springboot-demo
          name: config
      imagePullSecrets:
        - name: docker-secret
---
apiVersion: v1
kind: Service
metadata:
  name: springboot-demo
  namespace: default
spec:
  ports:
    - name: http-port
      port: 80
      protocol: TCP
      targetPort: 8080
  selector:
    app: springboot-demo
  type: ClusterIP

springboot-demo-configmap.yaml 文件的内容如下:

apiVersion: v1
kind: ConfigMap
metadata:
  name: springboot-demo
  namespace: default
data:
  application.yaml: |-
    server:
      port: 8080
      servlet:
        context-path: /springboot-demo
    spring:
      application:
        name: demo

当我们修改 ConfigMap 中的 application.yaml 配置的 context-path 后,可以看到相关 pod 完成了自动重启,并且使用下面的命令查看 deployment 的 yaml 内容:

kubectl get deploy -o yaml

可以发现增加了一个环境变量:

    spec:
      containers:
        - env:
            - name: TZ
              value: Asia/Shanghai
            - name: NAMESPACE
              value: default
            - name: STAKATER_SPRINGBOOT_DEMO_CONFIGMAP
              value: 7b36c3bd0a7c0a87db028cf1037eb994df4de49e

参考文档

github.com/stakater/Re…