如何用Docker实现"One Binary"原则
How to implement the "One Binary" principle with Docker
这里解释了一个二进制原则:
http://programmer.97things.oreilly.com/wiki/index.php/One_Binary 声明应该...
"Build a single binary that you can identify and promote through all the stages in the release pipeline. Hold environment-specific details in the environment. This could mean, for example, keeping them in the component container, in a known file, or in the path."
我看到许多开发运营工程师可能违反了这一原则,他们为每个环境(即 my-app-qa、my-app-prod 等)创建一个 docker 图像。我知道 Docker 支持不可变的基础设施,这意味着部署后不更改图像,因此不上传或下载配置 post 部署。不可变的基础设施和二元原则之间是否存在权衡,或者它们可以相互补充?当谈到将配置与代码分开时,Docker 世界中的最佳实践是什么???应该采取以下哪一种方法...
1) 创建基本二进制映像,然后创建一个配置 Docker 文件,通过添加特定于环境的配置来扩充此映像。 (即我的应用 -> 我的应用产品)
2) 将仅二进制 docker 映像部署到容器并在部署时通过环境变量等传递配置。
3) 将Docker文件部署到容器后上传配置
4) 从容器内的 运行 docker 镜像的配置管理服务器下载配置。
5) 将配置保留在主机环境中,并通过绑定安装使其可供 运行 Docker 实例使用。
有没有上面没有提到的其他更好的方法?
如何使用不可变的基础架构来执行二元原则?可以做到还是有取舍?什么是最佳做法?
我过去的做法是在执行构建后将标记化合并到打包过程中。这些令牌可以在位于顶部的编排层中进行管理,以管理您的平台工具。所以对于给定的标记,有一个匹配的正则表达式或 xpath 表达式。该令牌链接到一个或多个配置文件,具体取决于所选择的关系。然后,当此构建部署到容器时,平台服务(即配置管理)将根据其环境使用正确的值来标记这些令牌。这些 poke 值很可能会从金库中提取。
我现在有大约 2 年的 Docker 容器部署经验,所以我要谈谈我所做的 and/or 知道如何工作。
所以,首先让我说容器绝对应该是不可变的(我什至将我的容器标记为只读)。
主要方法:
- 通过设置静态入口点并通过覆盖容器启动命令来覆盖配置文件位置来使用配置文件——这不太灵活,因为必须提交更改并重新部署才能启用它;不适合密码、安全令牌等
- 通过使用环境变量覆盖配置文件的位置来使用配置文件——同样,这取决于提前准备好配置文件; ;不适合密码、安全令牌等
- 使用环境变量 - 这可能需要更改部署代码,从而缩短配置更改生效的时间,因为它不需要经过应用程序构建阶段(在大多数情况下),部署这样的改变可能很容易。这是一个示例 - 如果将容器化应用程序部署到 Marathon,更改环境变量可能只是从上次使用的容器映像(甚至可能在同一主机上)启动一个新容器,这意味着这可以在几秒钟内完成;不适合密码、安全令牌等,尤其是 Docker
- 将配置存储在像 Consul 这样的 k/v 存储中,让应用程序知道这一点,甚至让它可以动态重新配置。同时启动功能的好方法——甚至可能跨多个服务;如果使用诸如 HashiCorp Vault 之类的解决方案来为敏感信息提供安全存储,您甚至可以拥有短暂的秘密(例如 Vault 的 PostgreSQL 秘密后端 - https://www.vaultproject.io/docs/secrets/postgresql/index.html)
- 让应用程序或脚本在启动主应用程序之前创建配置文件 - 将配置存储在像 Consul 这样的 k/v 存储中,使用类似 consul-template 的东西来填充应用程序配置;更安全一点——因为你没有把所有东西都作为代码
- 在启动主应用程序之前让应用程序或脚本填充环境变量——例如 envconsul;不适合敏感信息 - 有权访问 Docker API 的人(通过 TCP 或 UNIX 套接字)将能够读取那些
- 我什至遇到过这样一种情况,我们将变量填充到 AWS 的实例 user_data 中,并在启动时将它们注入到容器中(使用脚本在启动时修改容器的 json 配置)
我要考虑的主要事项:
- 我公开的变量是什么以及我何时何地从中获取它们的值(可能是 CD 软件或其他东西)- 例如,您可以将 AWS RDS 端点和凭证发布到实例的 user_data,甚至可能带有一些 IAM 实例配置文件魔法的 EC2 标签
- 我们需要管理多少个变量以及我们多久更改其中一些变量 - 如果我们有几个变量,我们可能只使用环境变量,或者对最常更改的变量和变量使用环境变量将那些我们不经常更改的文件存储在文件中
- 以及我们希望看到它们更改的速度有多快 - 如果它是一个文件,通常需要更多时间才能将其部署到生产环境中;如果我们使用环境变量
s,我们通常可以更快地部署这些更改
- 我们如何保护它们中的一些——我们在哪里注入它们以及如何注入它们——例如 Ansible Vault、HashiCorp Vault,将它们保存在一个单独的 repo 中,等等
- 我们如何部署 - 可能是 JSON 配置文件发送到部署框架端点、Ansible 等
- 我们所拥有的环境是什么 - 使用像 Consul 这样的东西作为配置数据存储是否现实(Consul 有 2 种不同的代理 - 客户端和服务器)
我倾向于最复杂的情况,将它们存储在一个中心位置(k/v 存储、数据库)并动态更改它们,因为我遇到过以下情况:
- 缓慢的部署管道 - 这使得更改配置文件和部署它真的很慢
- 环境变量太多 - 这真的会失控
- 必须同时在整个队列(由数十项服务组成)中打开一个功能标志
- 真正努力通过更好地处理敏感配置数据来提高安全性的环境
我可能遗漏了一些东西,但我想这应该足以引发思考什么对您的环境最有利
这里解释了一个二进制原则: http://programmer.97things.oreilly.com/wiki/index.php/One_Binary 声明应该...
"Build a single binary that you can identify and promote through all the stages in the release pipeline. Hold environment-specific details in the environment. This could mean, for example, keeping them in the component container, in a known file, or in the path."
我看到许多开发运营工程师可能违反了这一原则,他们为每个环境(即 my-app-qa、my-app-prod 等)创建一个 docker 图像。我知道 Docker 支持不可变的基础设施,这意味着部署后不更改图像,因此不上传或下载配置 post 部署。不可变的基础设施和二元原则之间是否存在权衡,或者它们可以相互补充?当谈到将配置与代码分开时,Docker 世界中的最佳实践是什么???应该采取以下哪一种方法...
1) 创建基本二进制映像,然后创建一个配置 Docker 文件,通过添加特定于环境的配置来扩充此映像。 (即我的应用 -> 我的应用产品)
2) 将仅二进制 docker 映像部署到容器并在部署时通过环境变量等传递配置。
3) 将Docker文件部署到容器后上传配置
4) 从容器内的 运行 docker 镜像的配置管理服务器下载配置。
5) 将配置保留在主机环境中,并通过绑定安装使其可供 运行 Docker 实例使用。
有没有上面没有提到的其他更好的方法?
如何使用不可变的基础架构来执行二元原则?可以做到还是有取舍?什么是最佳做法?
我过去的做法是在执行构建后将标记化合并到打包过程中。这些令牌可以在位于顶部的编排层中进行管理,以管理您的平台工具。所以对于给定的标记,有一个匹配的正则表达式或 xpath 表达式。该令牌链接到一个或多个配置文件,具体取决于所选择的关系。然后,当此构建部署到容器时,平台服务(即配置管理)将根据其环境使用正确的值来标记这些令牌。这些 poke 值很可能会从金库中提取。
我现在有大约 2 年的 Docker 容器部署经验,所以我要谈谈我所做的 and/or 知道如何工作。
所以,首先让我说容器绝对应该是不可变的(我什至将我的容器标记为只读)。
主要方法:
- 通过设置静态入口点并通过覆盖容器启动命令来覆盖配置文件位置来使用配置文件——这不太灵活,因为必须提交更改并重新部署才能启用它;不适合密码、安全令牌等
- 通过使用环境变量覆盖配置文件的位置来使用配置文件——同样,这取决于提前准备好配置文件; ;不适合密码、安全令牌等
- 使用环境变量 - 这可能需要更改部署代码,从而缩短配置更改生效的时间,因为它不需要经过应用程序构建阶段(在大多数情况下),部署这样的改变可能很容易。这是一个示例 - 如果将容器化应用程序部署到 Marathon,更改环境变量可能只是从上次使用的容器映像(甚至可能在同一主机上)启动一个新容器,这意味着这可以在几秒钟内完成;不适合密码、安全令牌等,尤其是 Docker
- 将配置存储在像 Consul 这样的 k/v 存储中,让应用程序知道这一点,甚至让它可以动态重新配置。同时启动功能的好方法——甚至可能跨多个服务;如果使用诸如 HashiCorp Vault 之类的解决方案来为敏感信息提供安全存储,您甚至可以拥有短暂的秘密(例如 Vault 的 PostgreSQL 秘密后端 - https://www.vaultproject.io/docs/secrets/postgresql/index.html)
- 让应用程序或脚本在启动主应用程序之前创建配置文件 - 将配置存储在像 Consul 这样的 k/v 存储中,使用类似 consul-template 的东西来填充应用程序配置;更安全一点——因为你没有把所有东西都作为代码
- 在启动主应用程序之前让应用程序或脚本填充环境变量——例如 envconsul;不适合敏感信息 - 有权访问 Docker API 的人(通过 TCP 或 UNIX 套接字)将能够读取那些
- 我什至遇到过这样一种情况,我们将变量填充到 AWS 的实例 user_data 中,并在启动时将它们注入到容器中(使用脚本在启动时修改容器的 json 配置)
我要考虑的主要事项:
- 我公开的变量是什么以及我何时何地从中获取它们的值(可能是 CD 软件或其他东西)- 例如,您可以将 AWS RDS 端点和凭证发布到实例的 user_data,甚至可能带有一些 IAM 实例配置文件魔法的 EC2 标签
- 我们需要管理多少个变量以及我们多久更改其中一些变量 - 如果我们有几个变量,我们可能只使用环境变量,或者对最常更改的变量和变量使用环境变量将那些我们不经常更改的文件存储在文件中
- 以及我们希望看到它们更改的速度有多快 - 如果它是一个文件,通常需要更多时间才能将其部署到生产环境中;如果我们使用环境变量 s,我们通常可以更快地部署这些更改
- 我们如何保护它们中的一些——我们在哪里注入它们以及如何注入它们——例如 Ansible Vault、HashiCorp Vault,将它们保存在一个单独的 repo 中,等等
- 我们如何部署 - 可能是 JSON 配置文件发送到部署框架端点、Ansible 等
- 我们所拥有的环境是什么 - 使用像 Consul 这样的东西作为配置数据存储是否现实(Consul 有 2 种不同的代理 - 客户端和服务器)
我倾向于最复杂的情况,将它们存储在一个中心位置(k/v 存储、数据库)并动态更改它们,因为我遇到过以下情况:
- 缓慢的部署管道 - 这使得更改配置文件和部署它真的很慢
- 环境变量太多 - 这真的会失控
- 必须同时在整个队列(由数十项服务组成)中打开一个功能标志
- 真正努力通过更好地处理敏感配置数据来提高安全性的环境
我可能遗漏了一些东西,但我想这应该足以引发思考什么对您的环境最有利