环境变量 env,相信每个运维工程师都耳熟能详并能在 linux 环境下娴熟应用,但在 k8s集群环境中,灵活应用环境变量并不是一件很容易的事情,需要考虑的东西远比 linux 环境下多。
用得好,可以维持发布、监控和日志中心等运维平台的统一规范与逻辑,降低k8s 集群管理成本,否则将增加运维难度,同时给故障诊断带来不必要的干扰。
一、静态环境变量
1、显式定义
可以直接在 Dockerfile 中定义环境变量,语法为 ENV,比如我们为了设定容器时区,往往会在镜像 Dockerfile 里面设定“ENV TZ=Asia/Shanghai”,或者预先指定一个环境变量用于后续处理,比如在镜像里指定一个JVM 的预置运行参数:
ENV JVM_OPTS="-Xms1024m -Xmx1024m -Xmn386m -XX:metaspaceSize=256m -XX:MaxmetaspaceSize=256m -Xss256k"
2、隐式包含
除了显式定义环境变量,也可以在镜像里从其它文件引入。比如 tomcat 的官方镜像,tomcat 的启动脚本为 catalina.sh,catalina.sh 里面会去调用 setenv.sh,这个里面可以定义环境变量。
二、动态环境变量
静态环境变量由于缺乏灵活性,往往不能满足实际工作中的运维需求,应用较多的是动态环境变量。需要注意的是:动态环境变量的语义不能与静态的发生冲突,比如我们在 Dockerfile 中定义一个环境变量 APP_NAME,它的意思是微服务名称,后面可以覆盖其内容,但不能改变它的含义。下面基于运维场景,列举两种对于 k8s 动态环境变量的典型应用:
1、在微服务部署中的应用
通过发布平台结合helm等工具注入环境变量,前面提到的环境变量JVM_OPTS,针对同一个微服务,不同环境往往需要配置不同的内存容量,比如测试环境配置为512M,生产为2G。如何利用环境变量来统一配置逻辑呢?处理过程如下:
以 Jenkins pipeline 为例,其定义是一种 groove 脚本语言,可以直接在里面用 def 语法定义变量(或者基于用户下拉框选项等),然后用 helm set 的方式注入到 k8s deployment,实现多环境配置逻辑统一。以下为部分配置片段:
1)、Jenkins流水线配置多分支
2)、helm模板注入
3)、启动脚本应用
在 docker-entrypoint.sh 启动脚本中正式应用前面的环境变量:
2、在微服务日志中的应用
在应用日志处理过程中,往往需要知道一些k8s元数据,比如 pod 所在命名空间等,这种情况显然无法使用静态环境变量来处理,也是需要在pod启动的时候动态注入,helm模板如下:
三、基于 configmap 的环境变量
《k8s concepts》中关于configmap的定义如下:A ConfigMap is an API object used to store non-confidential data in key-value pairs. Pods can consume ConfigMaps as environment variables,command-line arguments,or as configuration files in a volume。
显然,这里的意思是我们可以使用 configmap 的数据,将其作为环境变量。在实际运维过程中,如何使用呢?可以通过 configMapKeyRef 引用 configmap 的 key 值,将其注入到 env 中,也可以直接挂载基于configmap的配置文件,将其封装为动态环境变量。比如,我们有一个log4j2的configmap,里面是项目组基于log4j2日志框架的配置,在helm模板中挂载如下:
后面也是类似之前的处理,把这个 mount 到的文件作为环境变量引入到启动脚本。一般来说,项目组的日志框架和格式定型后,很少会再有变化,如此处理,开发人员不再需要专门维护 log4j2 配置文件。需要注意的是:一般微服务架构都有自己的配置中心,比如nacos 或者 apollo 等,业务配置信息建议一定要放到配置中心里面去,不要放进configmap。
四、小结
合理使用k8s环境变量不仅有利于统一运维逻辑,降低复杂性,而且可以最大限度复用已有规则规范,做到极简化配置。