在这个EnvVar配置模式中,我们研究了配置应用程序的最简单方法。对于小的配置值集,最简单的外部化配置方式是将它们放入普遍支持的环境变量中。我们看到了在Kubernetes中不同方式声明环境变量,但也看到了使用环境变量进行复杂配置的局限性。
存在问题
每个重要的应用程序都需要一些配置来访问数据源、外部服务或生产级调优。而在《十二要素应用程序宣言》之前,我们就知道在应用程序中硬编码配置是一件坏事。相反,配置应该外部化,这样我们即使在构建了应用程序之后也可以更改它。这为支持和促进不可变应用程序构件共享的容器化应用程序提供了更多的价值。但是,在容器化的世界里,如何才能做到最好呢?
解决方案
《十二要素应用程序宣言》建议使用环境变量来存储应用程序配置。这种方法很简单,适用于任何环境和平台。每个操作系统都知道如何定义环境变量以及如何将它们传播给应用程序,每个编程语言也都允许轻松访问这些环境变量。可以说环境变量是普遍适用的。在使用环境变量时,一个典型的使用模式是在构建时定义硬编码的默认值,然后我们可以在运行时覆盖这些默认值。让我们来看看一些具体的例子,看看这在Docker和Kubernetes中是如何工作的。
对于Docker镜像,环境变量可以直接在Dockerfiles中使用ENV指令进行定义。你可以逐行定义环境变量,也可以在一行中全部定义,如例1-1所示。
实例 1-1. Dockerfile中定义环境变量。
FROM openjdk:11
ENV PATTERN "EnvVar Configuration"
ENV LOG_FILE "/tmp/random.log"
ENV SEED "1349093094"
# Alternatively:
ENV PATTERN="EnvVar Configuration" LOG_FILE=/tmp/random.log SEED=1349093094
...
在这样的Java应用程序容器中就可以通过对Java标准库的调用轻松地访问变量,如例1-2所示。
1-2实例,Java中读取环境变量
publicRandom initRandom(){
longseed=Long.parseLong(System.getenv("SEED"));
returnnewRandom(seed); #1
}
#1 用EnvVar的seed参数初始化一个随机数发生器。
直接运行这样的镜像将使用默认的硬编码值,但在大多数情况下,你想从镜像外部覆盖这些参数。
当直接用Docker运行这样的镜像时,可以通过调用Docker从命令行设置环境变量,如例1-3。
实例 1-3. Docker容器启动时添加环境变量
docker run -e PATTERN="EnvVarConfiguration"
-e LOG_FILE="/tmp/random.log"
-e SEED="147110834325"
k8spatterns/random-generator:1.0
对于Kubernetes来说,这些类型的环境变量可以直接在Deployment或ReplicaSet等控制器的Pod规范中设置(如例1-4)。
实例1-4,环境变量在Deployment设置
apiVersion: v1
kind: Pod
metadata:
name: random-generator
spec:
containers:
- image: k8spatterns/random-generator:1.0
name: random-generator
env:
- name: LOG_FILE
value: /tmp/random.log #1
- name: PATTERN
valueFrom:
configMapKeyRef: #2
name: random-generator-config #3
key: pattern #4
- name: SEED
valueFrom:
secretKeyRef: #5
name: random-generator-secret
key: seed
#1 EnvVar 字面值
#2 EnvVar ConfigMap
#3 ConfigMap命名
#4 在ConfigMap中寻找EnvVar值的键
#5 来自Secret的EnvVar(查询语义与ConfigMap相同)
在这样的Pod模板中,你不仅可以直接将值附加到环境变量上(比如LOG_FILE),还可以使用授权给Kubernetes Secrets(用于敏感数据)和ConfigMaps(用于非敏感配置)。ConfigMap和Secret间接的好处是,环境变量可以从Pod定义中独立管理。
在前面的例子中,SEED变量来自于Secret资源。虽然这是Secret的一个完全有效的使用,但同样重要的是要指出,环境变量并不安全。将敏感的、可读的信息放到环境变量中,会使这些信息很容易被读取,甚至可能泄露到日志中。
关于缺省值
缺省值使生活变得更轻松,因为它们消除了为一个你可能根本不知道存在的配置参数选择值的负担。它们在约定俗成的配置范式中也发挥了重要作用。然而,默认值并不总是一个好主意。有时,对于一个不断发展的应用程序,它们甚至可能是一个反模式。
这是因为追溯性地改变默认值是一项困难的任务。首先,改变默认值意味着在代码中替换它们,这需要重新构建。其次,当一个默认值发生变化时,依赖默认值的人(无论是习惯还是有意识地)总会感到惊讶。我们必须传达这种变化,而这种应用程序的用户可能也要修改调用代码。
然而,默认值的变化往往是有意义的,因为很难从一开始就得到默认值。我们必须把对默认值的修改看作是重大的修改,如果使用语义版本号,这样的修改就有理由在重大版本号中进行凸显。如果对给定的默认值不满意,如果用户没有提供配置值,通常最好是完全删除默认值,并抛出一个错误。这样至少可以尽早地、突出地破坏应用程序,而不是它默默地做一些不同的、意想不到的事情。
考虑到所有这些问题,如果你不能90%确定一个合理的默认值会长期存在,那么从一开始就避免使用默认值往往是最好的解决方案。密码或数据库连接参数是不提供默认值的很好的候选者,因为它们高度依赖于环境,而且往往无法可靠地预测。另外,如果我们不使用默认值,就必须明确提供配置信息,这也是文档的作用。
讨论
环境变量很容易使用,而且每个人都知道它们。这个概念可以顺利地映射到容器上,而且每个运行时平台都支持环境变量。但是环境变量并不安全,它们只适用于相当数量的配置值。而当有很多不同的参数需要配置时,对所有这些环境变量的管理就会变得很笨重。
在这些情况下,许多人使用额外的间接层次,将配置放入不同的配置文件中,每个环境一个。然后单个环境变量来选择其中一个文件。Spring Boot中的配置文件就是这种方法的一个例子。由于这些配置文件通常存储在应用程序本身,也就是容器内,所以它将配置与应用程序紧密耦合。这往往导致开发和生产的配置最终并排在同一个Docker镜像中,这就需要为任何一个环境中的每一个变化重建镜像。所有这些都告诉我们,环境变量只适合小范围的配置集。
当出现比较复杂的配置需求时,下面几章所介绍的配置资源、不可更改配置和配置模板这几个页面是不错的选择。
环境变量是普遍适用的,正因为如此,我们可以在不同的层次上设置它们。这种选择会导致配置定义的分散,并且很难追踪某个环境变量的来源。当没有一个集中的地方定义所有的环境变量时,很难调试配置问题。
环境变量的另一个缺点是,它们只能在应用程序启动前设置,我们不能在之后更改它们。一方面,它的缺点是你不能在运行时 “热 “地改变配置来调整应用程序。然而,许多人认为这是一个优点,因为它促进了甚至对配置的不可改变性。这里的不可变性意味着你扔掉正在运行的应用容器,然后用修改后的配置启动一个新的副本,很可能采用滚动更新等平滑的Deployment策略。这样一来,你始终处于一个确定的、众所周知的配置状态。
环境变量使用起来很简单,但主要适用于简单的用例,对于复杂的配置要求有一定的局限性。接下来的模式将说明如何克服这些限制。
原文始发于微信公众号(云原生内经):Kubernetes 设计模式之环境变量配置
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之家整理,本文链接:https://www.bmabk.com/index.php/post/167968.html