【k8s】使用 Reloader 实现热部署
一. 概述
我们在 k8s 中使用 ConfigMap
作为配置文件的时候会遇到一个问题:修改 ConfigMap
后无法实现热部署,也就是更改了 ConfigMap
配置文件后需要手动重启 Pod 配置才会生效,为了处理这个问题 github 专门有个开源的项目 Reloader 来解决这个问题,项目地址如下:
Reloader 可以观察 ConfigMap 和 Secret 中的变化,并对 pod 及其关联的 DeploymentConfigs
,Deployments
,Daemonsets
,Statefulsets
和 Rollouts
进行滚动升级。
本文主要对 Reloader 的使用进行一个简单的介绍,详细的配置与使用可以查看源码文档。
二. Reloader 实现滚动升级的原理
当 Reloader 检测到 ConfigMap 发生变化的时候,会使用 SHA1 计算 ConfigMap 的哈希值(使用 SHA1 是因为它高效且不易发生冲突),计算完哈希值之后,Reloader 获取所有的 Deployments
,Daemonsets
,Statefulsets
和 Rollouts
列表,并查找其 anotations 中是否配置了 Reloader 相关的注解,比如配置了如下 annotations :
metadata:
annotations:
reloader.stakater.com/auto: "true"
接着 Reloader 会查找配置了 Reloader 相关 annotations 的 Deployments
,Daemonsets
,Statefulsets
中一个特殊的环境变量。
如果找到这个环境变量,则获取其值并将其与前面计算的新 ConfigMap 哈希值进行比较,如果环境变量中的旧值与新哈希值不同,则 Reloader 会更新环境变量。
如果环境变量不存在,那么它会从 ConfigMap 创建一个具有最新哈希值的新环境变量并更新相关的deployment
,daemonset
或者statefulset
。
k8s 检测到这个环境变量发生变化,则会触发 pod 关联的 deployment
,daemonset
或者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 的源码文档中提供了三种安装方式:
- 使用 Manifests 安装
- 使用 kustomize 安装
- 使用 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 的变化,并实现滚动升级
在DeploymentConfigs
,Deployments
,Daemonsets
,Statefulsets
和 Rollouts
中的 annotations 中添加如下内容:
metadata:
annotations:
reloader.stakater.com/auto: "true"
之后 reloader 会检测所有命名空间中其相关联的 ConfigMap 或者 Secret 的变化,并实现滚动升级。
2. 限制只检测带有特殊 annotations 的 ConfigMap 或者 Secret 的变化
首先在DeploymentConfigs
,Deployments
,Daemonsets
,Statefulsets
和 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 的变化
在DeploymentConfigs
,Deployments
,Daemonsets
,Statefulsets
和 Rollouts
中的 annotations 中指定多个要检测的 ConfigMap 的名称:
kind: Deployment
metadata:
annotations:
configmap.reloader.stakater.com/reload: "foo-configmap,bar-configmap,baz-configmap"
spec:
template:
metadata:
在DeploymentConfigs
,Deployments
,Daemonsets
,Statefulsets
和 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
参考文档
https://github.com/stakater/Reloader/blob/master/docs/How-it-works.md