即使Terraform有这9个坑,它还是值得一用

Terraform是国际著名的开源的资源编排工具,据不完全统计,全球已有超过一百家云厂商及服务提供商支持Terraform。Terraform是HashiCorp的代码软件基础设施。它允许用户使用高级配置语言定义数据中心基础架构,从中可以创建执行计划以构建OpenStack等基础架构,或者在IBM Cloud,AWS,Microsoft Azure,Google Cloud Platform等多种云服务中构建基础架构。

2015年,当Terraform第一次进入我的视野,它就像是我的瓦尔哈拉(北欧神话中死亡之神奥丁款待阵亡将士英灵的殿堂)。Terraform旨在解决复杂基础设施的配置问题——将多个云供应商汇聚在一起——从AWS这样的巨头到Logentries这样的单一解决方案供应商。

我们的团队认为我们需要一些解决方案来处理基础设施的复杂性问题。对于一个基于Heroku和AWS的平台来说,水平伸缩到四个Terraform似乎是一个完美的解决方案。我们希望有一些东西能够让我们意识到基础设施即代码这个概念的重要性,因为对于一个DevOps团队来说,这是必须的。Terraform功能齐全,但也并非十全十美,仍然有一些问题值得我们注意。

我将列举一些比较大的“坑”,并分享我们是如何填这些坑的。最后,我将会说明,尽管存在这些挑战,但Terraform在工具领域仍然有很大的发展空间。

1. 邪恶的状态

首先,Terraform是有状态的,我个人认为这会带来两个问题:

状态必须始终与基础设施保持同步——这意味着在配置时需要全盘考虑,不能在配置工具之外进行栈的修改。

你必须在某个地方维护状态——必须是一个安全的地方,因为状态可能会带有秘钥之类的东西。

但Terraform引入状态是有原因的,状态用来维护文件中定义的资源与在云供应商平台上创建的实际资源之间的映射关系。有了这个,Terraform就可以为我们提供一些好处:

从云供应商那里处读取状态(状态同步,也称为刷新)可能非常耗时。如果我们可以100%确定状态是准确的,我们完全可以不进行同步,并立即应用变更。

能够跟踪已创建的资源,可以更轻松地进行重命名和修改结构——这些都是基本的基础设施操作。

Terraform在应用变更之前会锁定状态,这意味着我们可以确保在应用变更时,没有其他人在做同样的操作。

我认为,在考虑配置工具时,你应该权衡一下上述的几个问题,并搞清楚你的栈是像工作簿之类的东西,每次做出变更后都可以进行重建,还是像是一个生物有机体,需要在它运行过程中做出变更。

2. 难以集成已有的栈

在Terraform的早期,很多人抱怨无法将Terraform用在已有的栈上。其原因在于,Terraform无法将已有栈纳入到它的状态管理中。所幸的是,Terraform通过引入import命令解决了这个问题(至少在系统级别)。

但又出现了另一个与此紧密相关的问题——如果你的栈很大,就必须为每个资源多次使用terraform import命令。如果不使用一些自动化脚本,这就变成了一个非常耗时且令人感到沮丧的活儿。我们完全可以通过更好的方式来导入这些东西,不过这要求Terraform将资源视为树,而不是扁平的结构。在某些情况下,它是完全合理的——比如heroku_app和heroku_domain(或heroku_drain)。当然,肯定还有很大的改进空间。

3. 复杂的状态修改

在重构基础设施定义时,到最后可能就是重命名资源(修改它们的标识符)或将资源移动到模块中。遗憾的是,Terraform很难跟踪这些变化,并将它们置于一种未知的错配状态。如果再次运行apply,会重新创建资源,但这可能不是你想要的。好在Terraform提供了terraform state mv命令,可用于移动逻辑资源。不好的地方在于,在大多数情况下,你需要大量使用这个命令。

4. 古怪的条件逻辑

Terraform并非真正的命令式编程语言,有些人不喜欢这一点​​。但说实话,我并不这么认为——我认为栈的定义应该尽可能是声明性的——这样可以减少定义之间的偏差。另一方面,Terraform提供的条件逻辑有点古怪。例如,如果要定义条件资源,需要将资源定义为列表,并使用count参数来控制它:

resource "heroku_app" "some_app" { count = "${var.create_app}" name = "some-app" ... }

这已经相当具体了,而且你不需要知道if/else是怎么回事。

resource "heroku_app" "some_app" { count = "${var.create_app}" name = "some-app" ... } resource "heroku_app" "some_app" { count = "${1 - var.create_app}" name = "some-app" ... }

不过需要注意的是,如果有可能,应该尽量避免使用这样的结构。当然,这不应该成为拒绝使用Terraform的理由,只需要知道存在这个问题即可。

在近期发布的一些版本中,可以使用resource for_each来简化这个问题。

5. 无法简单地遍历模块

模块的概念是非常棒的——它将可重用的资源集包含在可重用的工件中。我们来看一些经过简化的例子:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/356c1c93be25eaa0a2c6fc4d0c68181e.html