问题描述
在进行环境变量的存储时,希望得到一种可扩展的方法和建议。他具体询问:“如何有效存储环境变量?”用户的环境定义为包含3至10个服务器,是用来容纳特定客户基础设施的方式。在每个环境中,有一些变量主要从几个关键输入(如名称、大小等)自动生成。
当前,用户的环境变量都存储在类似以下结构的方式中:
<playbook>.yml # 各种部署playbooks
roles/windows # Ubuntu的Ansible角色
roles/ubuntu # Ubuntu的Ansible角色
config/hosts/<name>.yml # Ansible主机清单
config/hosts/vars/<name>.json # 特定环境变量
现在,配置以Git存储库中的子模块形式初始化。由于变量文件的频繁更改,这导致数据在提交之间发生变化一次、两次甚至三次,使得跟踪变更变得越来越困难。
用户认为,未来的做法应该是将所有的客户变量以集中、可扩展的方式存储,然后使用动态清单与Ansible进行连接。
他提到了一些可以完成部分所需的技术,如Consul,但似乎更适用于为一个大型应用提供服务,而不是为许多稍有不同的小型应用提供服务。他认为最终可能需要编写一个清单脚本,然后将所有数据存储在一些不适用于此目的的数据库中,然后继续进行,就好像什么都没有发生过。他认为这可能是一个潜在的方法,可以在不仅仅是扩展现有服务的情况下,还可以探索不同的数据存储方式。
他希望能够听到有关在处理许多小型环境时如何实现基础设施即代码的经验,而不是只有一个、两个或三个大型环境。
解决方案
用户提到了两种尝试过的可扩展环境变量存储方法,下面将对这两种方法进行总结:
方法1:使用Consul KV Pairs
在这种方法中,用户的环境变量从一个存储库(与原始代码存储库分开,但仍作为子模块进行)加载,并加载到一个有命名空间的KV对树中,例如:
/env/dev1/my/application/v1.1.1
这里的dev1是环境的名称,my/application是应用程序命名空间,v1.1.1是要使用的环境变量版本。
在运行时,平台会检查当前Consul集群中是否存在环境(如果不存在,会出现问题并报错),然后检查应用程序命名空间的子树(以确保没有跨应用程序的变量引用)以及与可部署工件相关联的标签的版本号。更新此标签是关键,因为这意味着如果我们丢失了所有生产数据中心,我们可以通过简单地读取我们的可部署工件的元数据并将所有环境变量加载到KV存储中来重新启动环境。
此方法的问题: 开发人员总是,我是说每一次,都会找到一种方法来滑入会对应用程序运行方式产生重大影响的环境更改。因为往往更容易获得批准的是配置更改而不是代码更改。
方法2:存储带有嵌入式变量的“部署”工件
这种方法将工件的确切版本与配置版本紧密耦合。如果更改了配置,则必须重新构建此部署工件。
部署工件本身实质上是一个YAML文件,其中包含了指向可发布二进制文件的URL以及与其关联的所有配置。
平台包含了读取变量并在应用程序启动时将其放入进程树中的组件。
这种方法迄今为止更加成功,因为存在一个工件,我们可以追踪其历史,并且可以将其提供给任何审核委员会,说“这是唯一关心的工件,我们不需要查看任何其他更改,只需查看此工件的更改”(即部署应用程序的版本、包括环境变量等)。
这使得开发人员稍微难以构建将根据变量更改其行为的逻辑,以便可以在未经适当测试周期的情况下滑入更改的逻辑变得稍微困难。
附加注意事项: 考虑应用程序机密。迄今为止,我们的解决方案是提供一个公共RSA密钥,开发团队使用它加密扩展的Java密钥存储(几乎每种语言都有某种库可以读取Java密钥存储),然后将其视为第三种类型的工件,并将其拉到服务器上,使用平台私钥进行解密,并在运行时提供给应用程序。
请注意: 机密管理是一个独立的问题。但是考虑它可能是值得的。
自动化
自动化非常重要,因为应尽可能避免由人工更新环境变量,而是由脚本生成。确保在每个客户清单中几乎没有手动更新,开发人员只