注意:1.使用这些原则、模式和实践需要一定程度的组织成熟度。本文不关注事物的文化方面,但它对于成功采用这些东西是非常重要的。2.本文中使用的示例使用的是Terraform和AWS,但这些原则、模式和实践是通用的,大多可以应用于其他IAC工具,如Pulumi、CloudForment等,以及云提供商,如GCP和Azure,甚至是On-Premise。
基础设施即代码(IAC)是一种采用软件系统使用的成熟编码技术并将其扩展到基础设施的方法。它是使团队能够快速、可靠地大规模交付基础设施和在其上运行的软件的关键DevOps实践之一。
如果您想要实现应用程序的持续交付,为您的基础设施提供快速可靠的配置机制非常重要。
在本文中,我们将介绍多年来帮助我和我工作过的组织的各种原则、模式和实践。
在我们开始讨论模式和实践之前,让我们先看看有效IAC的关键原则。
幂等意味着无论您运行IAC多少次,不管您的起始状态是什么,您最终都会得到相同的结束状态。这简化了基础设施的调配,并降低了结果不一致的可能性。
等价性可以通过将有状态工具与声明性语言(如Terraform)结合使用来实现,在该工具中,您可以定义所需的基础设施的结束状态,然后Terraform的工作就是将您带到该结束状态。如果它不能达到期望的状态,它就会失败。
在下面的图表#1中,您可以看到,对于非等幂IAC,如果您运行两次,它将调配6个虚拟机,而不是所需的3个。对于等幂IAC,即使您多次运行,它也只调配3个虚拟机,从而使其更加可靠和一致。
配置漂移是基础设施的一个巨大问题。当在一段时间内对基础设施进行了未记录的更改,并且您的各种环境以不易重现的方式彼此漂移时,就会发生这种情况。如果您有一个可长期使用的可变基础设施,通常会发生这种情况。对于寿命较长的基础设施,系统通常更脆弱,因为在一段时间内可能会出现内存泄漏缓慢、日志积累导致磁盘空间不足等问题。这还意味着您不会像您的应用程序或配置那样频繁地配置基础设施,因此您不会对自己的能力有信心。这些问题可以通过使用不可变的基础设施来解决。
不可变的基础设施意味着不是改变现有的基础设施,而是用新的基础设施取代它。通过每次配置新的基础设施,您可以确保它是可重现的,并且不允许配置随时间漂移。
在云环境中调配基础设施时,不变基础设施还可实现可扩展性。您可以在下面的图表#2中看到,对于可变基础架构,应用程序的v2部署在与v1相同的服务器上,但是对于不变的基础架构,它为新VM提供应用程序的v2。
不用说,一切都应该在源代码控制范围内。甚至是您偶尔运行以修复问题的脚本,以及用于配置基础设施和部署软件的管道(管道作为代码)。我曾经去过一些没有人知道在生产中运行的脚本驻留在哪里、谁创建了它以及更改历史的地方。这是你不想陷入的境地。
确保公司中的每个人都可以访问代码,即使是不更改IAC代码库的开发人员也可以访问。例外情况可能是你有很强的理由不做这件事,比如法律上的原因。这为在您的基础设施上运行应用程序的用户提供了可见性和更好的理解,因此当您的消费者想要解决问题,并想要了解基础设施是如何配置的时,他们可以通过查看代码轻松地做到这一点。他们应该能够查看代码,了解基础设施是如何配置的,甚至可以做出贡献(如果他们选择这样做的话)。我曾与一些团队合作过,在这些团队中,IAC存储库不仅对组织的其他成员不可访问,而且还存储在单独的源代码控制工具上,这些工具将它们隐藏起来。这是一种反模式。
像软件代码一样模块化IAC有助于维护、可读性和所有权。它还有助于保持更改较小且可独立部署。与软件相比,重构IAC相对困难,特别是对于DNS记录、CDN、网络、数据库等关键部分,因此偏向于预先过度抽象效果更好,即使这意味着要处理比所需的稍微复杂的IAC。
在许多拥有不同团队(如网络、安全和平台工程)的组织中,将基础设施的各个层分开并将所有权授予适当的团队以实现更好的控制可能是有意义的。由于上面提到的其他原因,如果是一个管理软件和基础设施的跨职能团队,我还将各层分开。在下面的图3中,我展示了一个部署到Amazon Elastic Kubernetes Service(EKS)的示例,每个基础架构层中都有不同的模块及其所有权。根据您的设置,这些模块/层可能会有所不同。
模块的版本控制非常重要,以确保您不会破坏生产中的东西,除非您使用monorepo,在这种情况下,您总是使用来自同一repo的最新版本。
使用IAC,您应该不需要大量的文档,因为所有内容都已编码,但有些文档仍然是必不可少的。更高质量的文档不仅对维护IAC的团队有帮助,而且对基础设施的使用者也有帮助。
文档记录很难。就像代码一样,确保您有足够的文档来传达您想要的消息,这一点很重要。更多的文档并不意味着它更好。过期的文档更糟糕。
在需要时方便地获取文档非常重要。例如,如果您显示一条错误消息,最好包含一个指向文档的链接来解决此类问题。此外,拥有典型场景的Runbook有助于在生产问题期间进行故障排除。
文档应该更贴近代码。如果它与代码在一起或更靠近代码,您会有更多的机会使其保持更新。例如,在与IAC相同的存储库中添加自述文件,而不是在存储库之外的某个外部位置(如Confluence或Wiki)添加自述文件会更好。这样,当代码更改时,您将有更多机会记住在同一提交中更新文档,并且它还可以在拉请求过程中起到提醒作用。如果您可以从代码生成文档或使用测试作为文档,那就很理想了。
与软件开发一样,您需要考虑在不同级别测试IAC。如果你不知道测试金字塔,这里有一篇文章在马丁·福勒的网站上。下面是我为IAC做的一个测试金字塔的尝试。
这里的想法是,随着测试金字塔的攀登,测试成本更高,更脆弱,运行时间更长,需要更高的维护。因此,出于这些原因,为了获得更快的反馈,您应该尽可能频繁地在金字塔底部运行测试,而在顶部运行测试的频率要低一些。
静态分析:因为这是获得反馈的最快方式,所以您应该尽可能频繁地运行它,即使在您的机器上也是如此。当您在文本编辑器或IDE中保存文件时,有一些集成可以自动执行此操作。您可以使用Terraform Valify或TFLint等工具进行静态分析。
单元测试:由于大多数工具(如terraform和Ansible)都是声明性的,因此IAC通常不需要单元测试。不过,在某些情况下,单元测试可能会有帮助,比如当您有条件或循环时。如果您正在编写bash脚本,您可以使用BATS进行单元测试,或者如果您使用的是Pulumi,它支持TypeScript、Python、Go或C#等语言,您可以使用语言测试框架。
集成测试:这是在环境中调配资源并验证是否满足特定要求时进行的测试。请记住,不要为您的工具负责的事情编写测试,尤其是在编写声明性代码时。例如,您应该编写自动测试以确保没有任何S3存储桶是公共的,而不是验证是否应用了IAC中指定的策略。另一个示例是测试所有EC2实例中是否只有某些端口是打开的。您还可以配置一个临时²环境(稍后可以拆卸)来运行这些测试。根据这些测试所需的时间长短,您可能希望在每次提交之后或每晚构建时运行这些测试。Chef Inspec和Goss等工具对这些类型的测试很有帮助。
使用虚拟应用程序进行冒烟测试:最后但并非最不重要的一种测试方式是配置环境、部署虚拟应用程序并运行快速冒烟测试,以验证应用程序部署是否正确。使用虚拟应用程序来测试您的实际应用程序将具有但未配置用于生产的场景。例如,如果您的应用程序连接到外部托管的数据库,则应尝试在虚拟应用程序中连接到该数据库。这使您确信您正在调配的基础架构允许您运行您打算在其上运行的应用程序。因为这些测试速度很慢,所以您可以在配置新环境之后运行这些测试,然后定期运行这些测试。
确保您的基础设施和在其上运行的应用程序是安全和合规的,这是一个重要但经常被忽视的方面。传统上,许多组织对此都有手动检查和关卡,这很耗时,通常会在部署周期的后期阶段进行,但使用IAC,您可以自动执行这些检查和关卡,以提供更好的安全性/合规性,并在周期中更频繁、更快地运行这些检查和关口。
身份和访问管理:确保您的IAC及其提供的基础设施拥有强大的身份和访问管理。对IAC使用基于角色的访问控制(RBAC)来调配基础设施有助于减少总体受攻击面。使用RBAC,您仅授予IAC足够的权限来执行它应该执行的操作。
机密管理:IAC通常需要机密来配置任何基础设施。例如,如果您在AWS中调配资源,则需要AWS凭据才能连接到它。确保您使用可靠的机密管理工具,如Hashicorp Vault或AWS Secrets Manager。
如果您需要在状态文件中输出或存储任何秘密(尽管您应该尽量避免这样做),请确保它们是加密的,这样,如果有人获得了状态文件,他们就不能从中提取秘密。
安全扫描:在较低或短暂的环境中调配或更改基础设施后运行安全扫描有助于避免生产中的安全问题。使用CIS Benchmark和Amazon Inspector等工具有助于查找常见漏洞/暴露,并确保遵循安全最佳实践。
合规性:许多公司都有合规性要求,但如果您在医疗保健或金融领域工作,则会有更严格的要求。我相信您知道其中的一些(如果不是全部的话):HIPAA、PCI、GDPR和SOX。如上所述,传统上,遵从性团队习惯于手动完成所有检查和文书工作。使用各种工具(如Chef Inspec或Hashicorp Sentinel)自动执行这些遵从性要求有助于更频繁地运行它并更快地发现问题。例如,您可以在每次更改IAC时运行这些合规性测试,方法是配置一个临时环境,以便在投入生产之前查明新代码是否存在任何问题。
上面提到的所有步骤应该结合在一起,并按照一定的顺序使用适当的检查执行IAC,以便在各种环境中放心地配置基础设施。为此,我将在下面讨论两个选项。
请参见下面的示例,该示例演示了IAC管道中的典型步骤序列。我在下面的示例中使用了CircleCI,但是您可以使用任何管道工具来执行此操作。管道为依赖于配置的基础设施的每个人提供可见性,并在出现故障时通知相应的团队。
另一种执行IAC的方式是使用GitOps,它扩展了IAC并添加了一个工作流(Pull Request Process)以将更改应用到生产或任何环境中。它还可以有一个控制循环,定期验证基础设施的实际状态是否与所需状态相同。例如,它将确保如果直接对基础设施进行任何更改,它将按照源代码控制恢复到所需的状态。可以使用GitOps代替上面定义的IAC管道。有关GitOps的更多信息,您可以在这里阅读Weaveworks网站上的文档。
感谢您阅读这篇文章,希望它对您有所帮助。如果您知道可以添加到本文中的其他IAC原则、模式或实践,或者有任何问题,请在下面的评论中告诉我,或者在Twitter上联系我,我将对其进行研究。
致谢:Matt Kuritz、Michael Wytock和Arielle Sullivan阅读了本文的草稿,并提供了改进意见。