广告

特定于环境的Docker镜像是解决部署问题的关键

  • 浏览(433)
  • 评论(0)
  • 译者:k8s

一个简单的事实是,你的部署环境并不是彼此的精确副本。环境之间最常见的区别是用于访问数据库等服务的凭据,因为在测试环境和生产环境之间共享相同的密码被视为不好的做法。用于公开服务的主机名通常也因环境而异。

这意味着应用程序需要灵活性,以适应其在部署生命周期中所经历的每个新环境的具体细节。这允许根据需要配置数据库连接字符串或外部服务URL等详细信息。

创建环境无关部署的公认解决方案是通过外部可配置选项公开所有特定于环境的设置,通常是通过环境变量(env vars)。这是12因子应用程序方法详述的解决方案。

因此,你今天在互联网上找到的绝大多数建议都强烈支持使用环境变量在运行时配置的与环境无关的部署工件(无论是Docker镜像还是更传统的包,如ZIP文件)。然而,这个建议并不普遍,在本文中,我们将了解为什么特定于环境的Docker镜像是解决许多常见部署问题的有效解决方案。

了解传统部署流程

Octopus最初是作为IIS(互联网信息服务)等更传统平台的部署工具而存在的,并且始终接受发展与环境无关的部署工件的概念。为了支持特定于环境的配置,在每个环境的部署过程中,配置文件中的值由Octopus替换,修改后的包随后部署到其目标。

这个变量替换功能很容易成为Octopus中最常用的功能之一,这个概念经受住了时间的考验。

这种方法的有趣之处在于,它实际上意味着将特定于环境的包交付到目的地。如下图所示,在每个环境中部署了一个由原始部署工件和特定于环境的设置组成的独特包:

图片

从该工作流中需要注意的重要事项包括:

这一过程已经确立并经过了考验。

单个环境无关工件是部署过程的输入。

创建特定于环境的工件,即使它们是整个部署过程的不透明副作用。

考虑到这种传统的部署过程,让我们看看当作为多环境工作流的一部分生成时,特定于环境的Docker镜像可能是什么样子。

特定于环境的Docker镜像工作流

部署特定于环境的Docker镜像的过程如下所示:

图片

传统部署工作流与包含特定于环境的Docker镜像的工作流之间的唯一区别是,特定于环境的Docker镜像必须存储在特定于环境的Docker存储库中,而不是在部署期间直接复制到目标的不透明文件。否则,这两个过程实际上是相同的。

如果传统的部署过程已经建立并经过了测试,那么就没有理由相信将ZIP文件交换为Docker镜像会带来任何重大缺陷。这是一个需要进行的重要比较,因为许多反对特定于环境的Docker镜像的论点往往会援引脆弱性或不受欢迎性的不具体说法,而实际上传统部署过程与特定于环境的镜像之间存在不可否认的相似之处。

现在,我们可以看到特定于环境的镜像如何反映一个成熟且经过测试的过程,接下来要问的问题是,它们何时比将所有特定于环境的配置外部化为环境变量更好。

特定于环境的镜像可能不是最佳解决方案

重要的是要预先承认,在大多数情况下,特定于环境的镜像不是最佳解决方案。所有现代框架都非常支持通过env-vars外部化配置值,托管Docker容器的每个平台都支持设置env-vars。如果你的代码可以读取环境变量,那么这应该是默认选项,因为环境变量是解决特定于环境的设置问题的可靠解决方案。

也就是说,你应该小心不要将环境变量用作解决所有问题的灵药,在许多情况下,特定于环境的镜像是有意义的。

迁移遗留应用程序

特定于环境的镜像的一个明显用例是迁移在部署期间依赖于操作配置文件的遗留应用程序。

你不必回顾Java或.NET应用程序的历史,就可以找到没有读取环境变量概念的XML配置文件。这些应用程序受到直接编辑配置文件的传统部署过程的良好支持,这些应用程序到Docker的最直接迁移路径是创建特定于环境的镜像,并使用新层覆盖具有特定于环境的版本的原始配置文件。

将脚本与可执行镜像捆绑在一起

Docker正在迅速成为基于CLI的工具的通用包管理器。每个主要工具都有一个受支持的Docker镜像,Docker注册表提供了下载镜像的标准化过程。DevOps完全是关于可靠性和自动化的,对任何镜像运行“docker pull”的能力是一个非常值得赞赏的进化,从必须定位工具的下载URL、下载包、提取包到在生成的文件结构中挖掘以执行适当的文件。

Docker镜像中托管的更高级工具可以执行脚本或读取复杂的配置文件,允许最终用户装载卷或文件。示例包括newman,Postman的CLI工具,它提供了运行本地集合文件的说明。

在本地运行Docker镜像时,装载文件和目录很简单,但在托管平台(如Kubernetes、ECS、App Runner、Azure Container实例等)上运行这些相同的镜像时,会变得很有挑战性。如果这些平台支持文件装载,你通常需要提供一个云文件存储解决方案,以便在选择的容器编排平台中可靠地装载文件。为了在容器中装载数千字节的文件,需要考虑设置NFS文件服务器是否值得。

特定于环境的镜像是解决此问题的完美解决方案。例如,你可以基于官方的Cypress Docker镜像创建特定于环境的镜像,然后在端到端测试脚本中分层。生成的镜像可以由任何能够运行Docker的平台运行,因为特定于环境的镜像只是普通的老Docker镜像。

多环境自定义

特定于环境的配置可以扩展到简单的键/值对之外。如果我们将环境的概念扩展到包含租户的概念,那么很容易想象“环境特定配置”扩展到租户特定网站的CSS文件之类的场景。CSS是一个声明性语法的示例,它从来没有被设计为从外部值(如env vars)模板化。虽然有一种方法可以做到这一点,但更现实的方法是在通用基础镜像上分层租户特定的样式表。

12因子方法要求将配置与代码严格分离。描述这一点的更好方法是将配置和自定义与代码分离。这种可组合性非常自然地适合特定于环境的镜像,其中特定于环境或租户的定制在部署过程中“分层”。

试图在入口点自定义文件是个坏主意

在谷歌搜索“docker envsubst entrypoint”时,我们发现了大量关于如何在docker容器启动时将环境变量注入文件的建议。从表面上看,这似乎是合理的,但随着配置文件的复杂性增加,它的扩展性不好。

在Octopus,我们看到了编辑配置文件的愿望是如何演变的。从几个简单的替换开始。随着时间的推移,需要编辑的文件越来越多,原始替换迫使你根据目标配置文件格式处理转义空格和引号。接下来是希望将值注入到JSON或YAML等结构化文件中,这允许你发布未经模板化的默认配置文件,然后以声明方式更改属性。

试图将此功能烘焙到Docker镜像的入口点,可能会引入定制的复杂脚本,处理角色转义或调用jq或Augeas等工具以结构化方式修改文件。

另一方面,特定于环境的镜像可以在Docker镜像之外使用专门为此目的设计的工具执行此文件操作,然后将生成的文件分层为新镜像。创建特定于环境的配置文件的部署时间问题留给部署工具处理,而不是在容器运行时尝试将其嵌入Docker执行的自定义脚本中。

结论

创建特定于环境的镜像的过程在概念上与传统部署中使用的过程相同,在部署到多个环境时,这已证明是一个可靠的解决方案。虽然使用env-vars为环境定制容器是一个很好的解决方案,也可以说是更好的解决方案,但在许多情况下,特定于环境的镜像非常有意义。

原文链接:

https://thenewstack.io/the-case-for-environment-specific-docker-images/

  • 分享到:
  • icon
  • icon
  • icon
  • icon
箭头