接着上篇文章的话题,我们继续讨论应用程序容器化部署后的配置管理。容器化应用除了可以在启动的时候通过command和args来指定启动命令和参数,很多时候我们也需要为每个POD设置环境变量(env variables),来提供应用运行的上下文信息,如下图所示:
如上图所示,我们可以为每一个容器实例设置一套环境变量。这里需要读者注意的是,环境变量在最新版本的Kubernetes版本上,只支持设置在容器级别,这就意味着你无法设置POD级别的全局环境变量,但是后续Kubernetes版本有可能支持。环境变量可以被设置为”字面值“,或者引用另外一个环境变量的值,以及依赖外部的信息来初始化。接下来我们分别来介绍一下不同的设置方式。
我们可以在Dockerfile中使用ENV参数来进行环境变量的设置,但是修改环境变量就需要重新构建镜像,因此更好的做法是在POD的YAML文件定义中,如下图所示:
从上图可以看出,env字段支持的是数组类型,因此我们可以通过name/value组合定义多个环境变量并赋值。话不多说,我们赶紧把上边的YAML文件部署到自己的集群中,然后运行kubectl exec yunpan-env -- env,在笔者的机器上输出如下:
如上图所示,env命令输出的环境变量远超我们的预期,读者也可以看到我们在POD的YAML文件中设置的环境变量显示在返回的清单中。环境变量的来源有多个,有些定义在容器镜像中,有些属于Kubernetes管理追加的,还有很多其他的源头。不幸的是我们无法从输出的环境变量列表确定具体的源头,但是我们一般情况下会只关心在Dockerfile或者POD的YAML文件中定义的配置项。
一般情况下,环境变量给应用程序提供配置信息,来支持特定的业务场景,接下来我们在我们的代码中直接读取环境变量,并返回给客户端的http请求,来看看我们的配置是否生效。如下图所示:
从上图可以看到,我们从node应用程序中可以获取在POD的YAML文件或者Dockerfile中定义的环境变量,我们通过修改这些参数,就可以提供不同的数据给容器实例。但是,不知道大家是否已经发现了问题,无论是在Dockerfile中设置环境变量,还是在POD的YAML文件中设置环境变量,本质上是另外一种形式的"硬编码“,如何理解这句话呢?
在Dockerfile配置的环境变量,如果我们需求修改,更新这些变量的值,我们需要重新构建容器镜像,耗时耗力耗带宽和时间啊,这和直接修改源代码重新打包编译有相同的”坏味道“。聪明的你可能会说,那我写POD的YAML文件里啊。没错,如前边的介绍,我们可以在POD的YAML文件中重写环境变量,但是这又是另外一种代码修改,区别只是你不需要编译而已,本质上编译的过程是Kubernetes,从这个角度讲,你可以把Kubernetes看成是解析执行YAML文件的”编译+运行时“,也耗费时间和精力啊,因为我们要分别为不同的环境,不同的版本拷贝不同的YAML文件,唯一的区别就是几个字段的值,大部分内容都相同,如果你改了某个共用的字段,你可能需要同时维护10-20个不同的YAML文件,对文本文件进行修改,风险是极高的,比如0和O,眼神不好的同学,几乎无感。
那么有没有好的做法,让我们把容器化应用的配置和代码进行解耦呢?Kubernetes社区当然也知道这个问题,因此设计了ConfigMap对象,来解耦容器化应用的配置,接下来我们介绍一下ConfigMap。
在Kubernetes平台上,ConfigMap对象用来保存一组key-value值,并且value可以是字符串,也可以是一大块结构化的文本,常见于应用配置文件中。POD可以在运行的过程中,读取这些key的值,并且一个POD可以在YAML文件中引用多个ConfigMap,多个POD也可以引用到相同的ConfigMap。
为了让引用程序不强依赖于Kubernetes平台,通常情况下应用程序不直接读取ConfigMap中的配置项信息,取而代之的是,ConfigMap对象会作为环境变量注入到容器的上下文中,或者作为文件挂载到容器中,如下图所示:
对于云原生应用来说,将配置信息和POD定义解耦到不同的文件中可以提升部署的灵活性,这样我们就可以用一套YAML文件和不同的环境配置信息ConfigMap的方案,来应对环境配置的差异。由于应用程序读取configMap中配置信息是通过key,因此我们在不同的环境加载对应的configMap配置信息,应用就可以获得对应环境的配置,按预期的方式运行,如下图所示:
如上图所示,我们可以通过不同的configMap来提供配置信息给对应的运行环境,这样就可以不用修改POD的YAML文件和Dockerfile文件,满足在多个环境中部署的需求。接下来,我们来实际在自己的集群上实际操作一下,看看是否符合预期。
由于我们只是演示之用,因此我们的configMap对象会非常简单,只包含POD初始化键值对。并且正常情况下,我们需要通过YAML文件来创建configMap,但是一种更加高效的方法就是直接从kubectl命令行来创建。我们可以通过命令:kubectl create configmap yunpan-config --from-literal status-message="从configMap读取到的应用启动信息"。
注:定义configMap的时候,key只能包含英文字母,下划线和点,其他字符不被允许。运行上边的命令会在集群中创建一个叫yunpan-config的configMap对象,并且包含status-message为key的键值对。通常情况下ConfigMap包含一组键值对,并且除了使用--from-literal之外,Kubernetes还支持--from-file和--from-env-file,来分别从不同的源注入键值对。
大部分情况下,开发和运维人员都是通过编写YAML文件来创建configMap文件,如下图所示,我们可以通过YAML文件来定义相同的配置项信息:
接下来我们把yunpan-config.yaml定义的configMap部署到本地集群,并通过kubect get cm来罗列机器上所有的configMap,输出如下:
➜ Kubernetes配置管理 kubectl apply -f yunpan-config.yaml
configmap/yunpan-config created
➜ Kubernetes配置管理 kubectl get cm
NAME DATA AGE
(删除若干.....)
yunpan-config 1 3s
另外我们可以通过命令kubectl get cm kiada-config -o yaml来查看configMap的详细信息,为了不给文章注水,我就不贴大段的输出了,笔者可以在自己的集群上验证。我们可以从输出的信息中,找到代表配置项的键值对。
有了配置信息,接下来我们来把配置信息用起来。我们可以在POD的YAML文件定义中,使用valueFrom来将配置信息注入到容器的运行环境中,如下图所示:
如上图所示,高亮部分显示了如何从configMap来注入配置信息,简单明了,笔者就不浪费时间解释了。接下来我们来创建这个POD,验证一下环境变量已经被成功注入,如下图所示所示:
好了,今天的内容就这么多了,我们下篇文章介绍如何将configMap以及文件的形式挂载到容器中,敬请关注!