本文概述
编写应用程序只是故事的一部分。为了使其有价值, 它需要部署在可以扩展的地方, 必须以高可用性运行, 需要备份等等。
需要越来越多的开发人员至少掌握此部署过程。例如, 随着系统复杂性的增加, DevOps成为当今经常需要的角色。我们不能让自己忽略这些变化, 而需要意识到如何设计易于部署的应用程序。
这也符合我们客户的利益:他们聘请我们担任我们领域的专家, 并期望我们从头到尾交付整个产品。他们有要求, 并且常常不理会其业务解决方案的运行堆栈。最后, 重要的是产品的商业价值。
介绍Terraform
部署和基础架构管理不是一个简单的过程。除了不断变化的领域专业知识以外, 我们还需要学习”另一种工具”或新的工作流程。如果你推迟了这一步, 那么本文将是你熟悉一种基础架构管理方法的机会。我希望最终你对使用Terraform更有信心, 并且对可能的方法和挑战有更多的了解。你应该能够使用此工具至少开始管理你的云基础架构的某些部分。
Terraform是一个抽象层, 是的, 我同意抽象是泄漏的。但是最后, 我们的业务是解决问题和管理抽象。这样做的目的是为我们的日常工作提供更多的理智。
目标
我将解释什么是Terraform, 它如何适合整个生态系统, 以及如何与其他类似工具进行比较。然后, 我将向你展示为团队配置多环境且可投入生产的Terraform设置所需的步骤。我将解释编写Terraform配置的基础知识-如何管理复杂性以及使用可共享模块重复代码。
所有示例都将集中于一个云提供商:Amazon Web Services(AWS)。这只是我经验最丰富的云, 但所有信息也应适用于其他云。
我将以我希望在开始时就已经知道的一些笔记结束:一些语法上的陷阱, 怪癖, 以及某些情况下Terraform并不是我的首选工具。
我不会关注语法的细节。通过阅读HashiCorp为Terraform提供的出色文档和指南, 你可以快速掌握这一点。我想专注于一开始可能并不明显的事情, 并且希望在开始使用Terraform之前就已经知道了。希望这将为你指明正确的方向, 并允许你考虑将Terraform用作将来项目中使用的工具。
基础架构管理很难
为云中的应用程序设置环境涉及多个步骤。除非你将它们全部记录下来作为详细的核对清单, 并且始终不停地遵循它们, 否则你会犯错误。毕竟, 我们是人类。这些步骤很难共享。你需要记录大量的手动程序, 而文档可能会很快过时。现在, 将所有这些乘以单个应用程序的环境总数:开发人员, 测试/质量保证, 阶段和产品。你还需要考虑每个安全性。
并不是每个工具都具有我可以使用并忘记复杂性的UI吗?
它们确实具有UI — AWS管理控制台就是一个很好的例子。但是这些工具在后台发挥了很大作用。一次单击UI可能实际上会调用一系列难以掌握的更改。通常无法撤消在UI中所做的操作(通常的”确定吗?”提示通常是不够的)。此外, 总是有另一双眼睛来检查更改是一个好主意, 但是当我们使用UI时, 我们需要与这个人坐在一起或检查更改后的更改, 这更多的是审核而不是审核。评论。每个云提供商都需要拥有自己的UI。 UI设计为易于使用(不一定简单!), 因此容易产生妄想性的方法, 例如”这只是一个微小的调整”, 或者是你会在48小时内忘记的快速生产修复程序。这种手动点击也很难自动化。
CLI工具呢?
对于我们的用例, 它们将比UI工具更好。但是, 你仍然倾向于手动进行更改或编写bash脚本, 而这些脚本很容易失控。此外, 每个提供程序都有自己的CLI工具。使用这些工具, 在提交更改之前, 你将看不到更改。幸运的是, 这不是一个新问题, 有一些工具可以帮助你解决此问题。
编排与配置管理
我将定义一些用于管理基础结构的工具和实践。这些是业务流程和配置管理。你可以将它们粗略地视为声明性和命令性编程模型(想想Prolog与Python)。每个人都有自己的优点和缺点, 但是最好都了解它们, 并为给定的工作应用最好的工具。而且, 业务流程通常比配置管理涉及更高的抽象级别。
编排
编排更像是声明性编程范例。我喜欢认为它是乐队的指挥。维基百科(链接)将指挥家的工作很好地概括为”通过手势指导多个演奏者或歌手同时表演的艺术。”指挥可以传达音乐的节拍, 节奏, 动态和提示, 但是实际的工作由演奏乐器的专家个人音乐家完成。没有这种协调, 他们将无法完成完美的作品。
这就是Terraform适合的地方。你使用它来管理IT基础架构:告诉它部署什么, 然后Terraform将它们链接在一起并执行所有必要的API调用。这个领域有类似的工具。最著名的之一是AWS云形成。在出现错误的情况下, 它比Terraform更好地支持恢复和回滚, 但在我看来, 它的学习曲线也更陡峭。它也不是不可知论的:它仅适用于AWS。
配置管理
这些实践的补充是配置管理方法。在此范例中, 你指定工具为达到给定的所需配置而必须执行的确切步骤。这些步骤本身可能很小, 并且支持多个操作系统, 但是你需要积极考虑它们的执行顺序。他们不了解当前状态和周围环境(除非你用它们编程), 因此, 他们会盲目执行你给他们的任何步骤。这可能会导致称为配置漂移的问题, 在此情况下, 资源将缓慢地与它们最初要表示的内容不同步, 特别是如果你对它们进行了一些手动更改。他们擅长在单个实例上管理和配置服务。在此工作流程中出色的工具示例包括Chef, Puppet, Ansible和Salt。
编排对你的基础结构实施了一种方法, 在该方法中, 你将资源视为牛而不是宠物。当出现问题时, 你无需手动”培育”每个VPS, 而是可以用精确的副本替换它们。我并不是说你根本不在乎并重新启动希望达到最佳效果的事情。
相反, 你应该调查并修复代码中的问题, 然后进行部署。
Ansible(和其他CM工具)可用于管理AWS基础架构, 但这将涉及大量工作, 并且更容易出错, 尤其是在基础架构经常更改且复杂性不断提高的情况下。
要记住的重要一件事是, 编排和配置管理方法不会相互冲突。它们是兼容的。完全可以在Terraform管理的AutoScaling组中拥有一组EC2(VPS)实例, 但运行一个AWS Application Image(AMI), 这是磁盘快照, 它是通过命令性步骤(例如, 使用Ansible)准备的。 Terraform甚至具有”提供商”的概念, 一旦计算机启动, 你就可以运行外部的预配工具。
Terraform文档做了出色的工作, 可以进一步解释这一点, 并帮助你将Terraform放置在整个生态系统中。
什么是地形?
它是由HashiCorp创建的一个开源工具, 使你可以将基础结构编成声明性配置文件, 并进行版本控制和共享, 并可以对其进行检查。
HashiCorp的名称应按时响起, 它们也可以命名为Nomad, Vault, Packer, Vagrant和Consul。如果你使用了这些工具中的任何一种, 则你已经知道文档的质量, 活跃的社区以及可以从其解决方案中获得的实用性。
基础架构即代码
Terraform与平台无关;你可以使用它来管理裸机服务器或云服务器, 例如AWS, Google Cloud Platform, OpenStack和Azure。在Terraform术语中, 这些称为提供程序。你可以通过阅读受支持的提供程序的完整列表来了解规模。可以同时使用多个提供程序, 例如, 当提供程序A为你配置VM, 但是提供程序B配置并委派DNS记录时。
这是否意味着只需更改配置文件即可切换云提供商?不, 我什至不认为你会想要, 至少不是以自动化的方式。问题在于, 不同的提供程序可能具有不同的功能, 不同的产品, 流程, 想法等。这意味着你将不得不为不同的提供程序使用不同的资源来表达相同的概念。但是, 所有这些仍可以用一种熟悉的配置语法完成, 并且可以成为内聚工作流程的一部分。
Terraform设置的基本部分
- 必须安装的Terraform二进制文件本身
- 源代码文件, 即你的配置
- 表示Terraform管理的资源的状态(本地或远程)(稍后会详细介绍)
编写Terraform配置
你可以使用HCL语言在* .tf文件中编写Terraform配置代码。有一个使用JSON格式(* .tf.json)的选项, 但它针对的是机器和自动生成, 而不是人类。我建议你坚持使用HCL。我不会深入探讨HCL语言的语法;官方文档在描述如何编写HCL以及如何使用变量和插值方面做得非常出色。我只会提及理解示例所需的最低要求。
在Terraform文件中, 你主要是在处理资源和数据源。资源代表基础架构的组件, 例如AWS EC2实例, RDS实例, Route53 DNS记录或安全组中的规则。它们允许你在云体系结构内部进行配置和更改。
假设你已设置Terraform, 则如果发出Terraform应用, 则下面的代码将创建一个功能齐全的EC2实例(仅显示某些属性):
resource "aws_instance" "bastion" {
ami = "ami-db1688a2" # Amazon Linux 2 LTS Candidate AMI 2017.12.0 (HVM), SSD Volume Type - ami-db1688a2
instance_type = "t2.nano"
key_name = "${var.key_name}"
subnet_id = "${module.network.public_subnets[0]}"
vpc_security_group_ids = ["${aws_security_group.bastion.id}"]
monitoring = "false"
associate_public_ip_address = "true"
disable_api_termination = "true"
tags = {
Name = "${var.project_tag}-bastion-${var.env}"
Env = "${var.env}"
ApplicationID = "${var.api_app_tag}"
ApplicationRole = "Bastion Host"
Project = "${var.project_tag}"
}
}
另一方面, 有些数据源使你可以读取有关给定组件的数据而无需更改它们。你想获取ACM颁发的证书的AWS ID(ARN)吗?你使用数据源。区别在于, 在配置文件中引用数据源时, 数据源以data_为前缀。
data "aws_acm_certificate" "ssl_cert" {
domain = "*.example.com"
statuses = ["ISSUED"]
}
上面引用了已发行的ACM SSL证书, 该证书可与AWS ALB一起使用。在执行所有操作之前, 你需要设置环境。
资料夹结构
地形环境(及其状态)由目录分隔。 Terraform将目录中的所有* .tf文件加载到一个名称空间中, 因此顺序无关紧要。我建议使用以下目录结构:
/terraform/
|---> default_variables.tf (1)
/stage/ (2)
|---> terraform.tfvars (3)
|---> default_variables.tf (4)
|---> terraform.tf (5)
|---> env_variables.tf (6)
/prod/
/<env_name>/
- default_variables.tf –定义所有顶级变量以及可选的默认值。使用符号链接可以在每个环境(嵌套目录)中重用它们。
- / stage / –保存整个单独环境(这里称为stage, 但可以是任何东西)的配置的目录。在此文件夹内进行的任何更改都完全独立于其他环境(例如prod), 这是你要避免的, 以避免将生产环境与舞台上的更改搞混!
- terraform.tfvars –定义变量值。 .tfvars文件与.env文件相似, 因为它们保存已定义变量的key = val对。例如, 这指定了我使用的AWS配置文件, AWS key_name和AWS key_path。在Git中可以忽略它。
- default_variables.tf –这是文件(2)的符号链接, 它使我们可以共享独立于env的变量, 而无需重复自己。
- terraform.tf –这是每个环境的主要配置;它包含用于配置后端的terraform {}块。我还在这里配置提供程序。
- env_variables.tf –该文件包含特定于环境的变量。我在AWS中使用Env = <env_name>标记所有资源, 因此此文件通常仅定义一个变量:env。
当然, 这不是构造环境的唯一方法。通过清楚地分离关注点, 这对我来说很有效。
后端配置
我已经提到了Terraform状态。这是Terraform工作流程的重要组成部分。你可能想知道状态是否实际上是必需的。 Terraform不能一直查询AWS API以获取基础架构的实际状态吗?好吧, 如果你考虑一下, Terraform需要在声明性配置文件中管理的内容与这些文件实际对应的内容(在云提供商的环境中)之间保持映射。请注意, 在编写Terraform配置文件时, 你不必关心单个EC2实例的ID或将为你发布的安全组创建的ARN。但是在内部, Terraform需要知道给定的资源块代表具有ID / ARN的具体资源。这是检测更改所必需的。此外, 状态用于跟踪资源之间的依赖关系(这也是你通常不必考虑的!)。它们用于构造可以(通常)并行化和执行的图。与往常一样, 我建议你阅读有关Terraform状态及其用途的出色文档。
由于状态是架构的唯一真理, 因此你需要确保你和你的团队始终在开发其最新版本, 并且不要通过对状态的不同步访问而不会产生冲突。相信我, 你不想解决状态文件上的合并冲突。
默认情况下, Terraform将状态存储在磁盘上的文件中, 该文件位于terraform.tfstate文件中(位于每个环境的当前工作目录中)。如果你知道自己将是唯一的开发人员, 或者只是在学习和试验Terraform, 那也可以。从技术上讲, 你可以使其在团队中工作, 因为你可以将状态提交到VCS存储库。但是然后, 你需要确保每个人都始终在使用最新状态, 并且没有人同时进行更改!通常这是一个严重的头痛, 我强烈建议不要这样做。此外, 如果有人加入你的单开发操作, 你仍然必须为该状态配置替代位置。
幸运的是, 这是Terraform内建好的解决方案的问题:所谓的远程状态。为了使远程状态正常工作, 你需要使用可用的后端提供程序之一配置后端。以下后端示例将基于AWS S3和AWS DynamoDB(AWS NoSQL数据库)。你只能使用S3, 但是会丢失状态锁定和一致性检查的机制(不推荐)。如果你以前仅使用本地状态, 则配置远程后端将为你提供一次迁移状态的选项, 因此你不会丢失任何东西。你可以在此处阅读有关后端配置的更多信息。
不幸的是, 这里有一个鸡蛋的问题:S3存储桶和DynamoDB表必须手动创建。 Terraform无法自动创建它们, 因为还没有状态!嗯, 有一些解决方案, 例如https://github.com/gruntwork-io/terragrunt, 可以使用AWS CLI自动执行, 但我不想偏离本博客文章的主题。
关于S3和DynamoDB后端配置要了解的重要事项是:
- 在S3存储桶上启用版本控制, 可以避免人为错误和墨菲定律的影响。
- DynamoDB表具有读写速率限制(称为容量)。如果对远程状态进行了大量更改, 请确保为该表启用DynamoDB AutoScaling或配置足够高的R / W限制。否则, 在执行大量调用时, Terraform将从AWS API中获取HTTP 400错误。
总结起来, 可以将以下后端配置放在terraform.tf中, 以在S3和DynamoDB上配置远程状态。
terraform {
# Sometimes you may want to require a certain version of Terraform
required_version = ">= 0.11.7"
# Stores remote state, required for distributed teams
# Bucket & dynamoDB table have to be created manually if they do not exist
# See: https://github.com/hashicorp/terraform/issues/12780
backend "s3" {
bucket = "my-company-terraform-state"
key = "app-name/stage"
region = "eu-west-1"
# 5/5 R/W Capacity might not be enough for heavy, burst work (resulting in 400s). Consider enabling Auto Scaling on the table.
# See: http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html
dynamodb_table = "terraform-state-lock-table"
}
}
一次要花很多时间, 但是请记住, 你对每个环境都做一次, 然后就可以忘记了。如果你需要对状态锁定进行更多控制, 可以使用HashiCorp Terraform Enterprise, 但在此不做介绍。
提供者
为了使该后端能够访问并能够与我们的云提供商进行通信, 我们需要配置所谓的提供商。可以将以下块放置在terraform.tf文件中(对于每个环境):
provider "aws" {
profile = "${var.profile}"
region = "${var.region}"
version = "~> 1.23.0"
}
轮廓和区域的变量存储在terraform.tfvars文件中, 可以忽略。配置文件变量是指使用标准凭证文件保存AWS云安全凭证的命名配置文件。请注意, 我还设置了一些版本限制。你不希望Terraform在不了解每个后端初始化的情况下升级你的提供程序插件。特别是考虑到需要仔细升级AWS提供商的2.x版本。
后端初始化
每个后端配置在开始时以及每次对其进行更改时都需要初始化步骤。初始化还配置Terraform模块(稍后会详细介绍), 因此在添加模块时, 还需要重新运行init步骤。此操作可以安全运行多次。请注意, 在后端初始化期间, 并非所有变量都可以被Terraform读取以配置状态, 也不是出于安全原因(例如, 秘密密钥)。为了解决这个问题, 在我们的情况下, 使用与默认配置不同的AWS配置文件, 你可以将-backend-config选项与接受k = v个变量对一起使用。这称为部分配置。
terraform init -backend-config=profile=<aws_profile_name>
Terraform文件共享一个范围, 该范围限制在给定目录中。这意味着子文件夹没有直接连接到父目录代码。但是, 它正在定义一个模块, 该模块允许代码重用, 复杂性管理和共享。
工作流程
使用Terraform代码时的常规工作流程如下:
- 为你的基础架构编写配置。
- 查看它将进行哪些实际更改(地形计划)。
- (可选)执行你在步骤2中看到的确切更改(适用于Terraform)。
- 转到1
地形图
Terraform plan命令将向你显示在发出apply命令时将对基础结构进行的更改的列表。多次发布计划很安全, 因为它本身不会改变任何内容。
如何阅读计划
Terraform中的对象(资源和数据源)可以通过其完全限定的名称轻松识别。
- 对于资源, 该ID可能类似于:<resource_type>。<resource_name>-例如aws_ecs_service.this。
- 对于模块内部的资源, 我们还有一个附加模块名称:module。<模块名称>。<资源类型>。<资源名称>-例如, module.my_service_ecs_service_task.aws_ecs_service.this。
- 数据源(模块内部和外部):(module。<模块名称>)。data。<资源类型>。<资源名称>-例如, module.my_service_ecs_service_task.data.aws_ecs_task_definition.this。
资源类型特定于给定的提供程序, 通常包括其名称(aws_…)。可在文档中找到AWS可用资源的完整列表。
对于给定的资源, 计划将显示五项操作:
- [+]添加–将创建一个新资源。
- [-]销毁–资源将被完全销毁。
- [〜]就地修改–将修改资源并更改一个或多个参数。这通常是安全的。 Terraform将向你显示将修改哪些参数以及如何修改(如果可能)。
- [-/ +] –资源将被删除, 然后使用新参数重新创建。如果对无法就地更改的参数进行了更改, 则会发生这种情况。 Terraform将通过红色以下注释向你显示哪些更改会强制重新创建资源:(强制新资源)。这有潜在的危险, 因为在一段时间内根本没有资源。它还可能破坏其他已连接的依赖项。我建议你进行此类更改, 除非你知道后果是什么, 或者不关心停机时间。
- [<=] –将读取一个数据源。这是只读操作。
以上是一个示例计划。我所做的更改是:
- 更改了第一个堡垒实例的instance_type
- 添加了第二个堡垒实例
- 更改安全组的名称
请注意, 最后一个更改是安全组规则, 该规则会自动检测父组名称的更改。我认为整个计划可读性强。一些参数值显示为<computed>-并不意味着它们将被更改, 而是在此阶段无法检索和显示(例如, 尚未创建的SG名称)。请记住, 在计划命令期间将仅显示更改的资源。遗漏的任何内容都不会被触及。
通常, Terraform命令接受其他参数。 plan命令最重要的参数是-out选项, 它将把你的计划保存在磁盘上。然后可以通过apply命令执行(完全保存)该保存的计划。这非常重要, 因为在其他情况下, 例如, 你的同事可能会在你发布计划和发布申请之间进行更改。例:
- 发布计划并验证其外观是否良好。
- 有人更改了你不知道的状态。
- 问题已解决, 哎呀, 它做的事情超出了计划!
值得庆幸的是, 此工作流程已在Terraform v0.11.0中得到了改进。从此版本开始, apply命令会自动为你提供你必须批准的计划(通过显式键入yes)。这样做的好处是, 如果你应用此计划, 它将完全按照所示执行。
另一个有用的选项是-destroy, 它将向你显示计划, 该计划将导致破坏Terraform管理的所有资源。你还可以使用-target选项将特定资源作为销毁目标。
地形应用
当我们应用给定的计划时, Terraform会退出并锁定我们的状态以确保排他性。然后, 它继续更改资源, 最后推送更新的状态。需要注意的一件事是, 某些资源比其他资源需要更长的时间。例如, 一个AWS RDS实例的创建可能花费超过12分钟, 并且Terraform将等待此过程完成。显然, Terraform很聪明, 因此不会阻塞其他所有操作。它创建所请求更改的有向图, 并且如果没有相互依赖性, 则使用并行性来加快执行速度。
导入资源
通常, 将Terraform和其他”配置为代码”解决方案逐步引入到已经存在的环境中。过渡真的很容易。基本上, 未在Terraform中定义的任何内容都将保持不受管理和不变。 Terraform仅关注其管理内容。当然, 这很可能会带来问题, 例如, 如果Terraform依赖于配置之外的某个端点, 然后将其手动销毁。 Terraform对此一无所知, 因此在状态更改期间无法重新创建该资源, 这将导致在计划执行期间API产生错误。这不是我担心的事情;引入Terraform的好处远大于缺点。
为了使引入过程更容易一些, Terraform包含了import命令。它用于将现有的外部资源引入Terraform状态并允许其管理这些资源。不幸的是, 至少在编写本文时, Terraform无法为这些导入的模块自动生成配置。有计划使用此功能, 但是现在, 你需要先在Terraform中编写资源定义, 然后将其导入, 以告诉Terraform开始对其进行管理。导入状态后(在执行计划期间), 你将看到在.tf文件中编写的内容与云中实际存在的内容之间的所有差异。这样, 你可以进一步调整配置。理想情况下, 不应该显示任何更改, 这意味着Terraform配置可以1:1反映云中已有的内容。
模组
模块是Terraform配置的重要组成部分, 我建议你拥抱它们并经常使用它们。它们为你提供了重复使用某些组件的方法。一个示例是AWS ECS集群, 该集群用于运行Docker容器。为了使这样的集群正常工作, 你需要配置许多单独的资源:集群本身, 将管理单独的EC2实例的启动配置, 图像的容器存储库, 自动伸缩组和策略, 等等。通常, 你需要针对单独的环境和/或应用程序使用单独的群集。
解决此问题的一种方法是复制并粘贴配置, 但这显然是一种短视解决方案。执行最简单的更新会出错。
模块允许你将所有这些单独的资源封装在一个配置块(称为模块)下。模块定义输入和输出, 这些输入和输出是与”外界”(或调用代码)进行通信的接口。此外, 模块可以嵌套在其他模块中, 从而使你可以快速启动整个单独的环境。
本地和远程模块
与状态类似, 你可以具有本地模块或远程模块。本地模块与Terraform配置一起存储(在每个环境之外的单独目录中, 但在同一存储库中)。如果你具有简单的体系结构并且不共享这些模块, 则可以。
然而, 这具有局限性。很难对这些模块进行版本控制和共享。版本控制很重要, 因为你可能想在生产环境中使用ECS模块的v1.0.0, 但想在登台环境中试用v1.1.0。如果模块与你的代码一起存储, 则对模块代码的每次更改都会反映在每个env中(一旦运行应用程序), 这通常是不希望的。
一种方便的版本控制模块方法是将它们全部放在单独的存储库中, 例如your-company / terraform-modules。然后, 在引用Terraform配置中的那些模块时, 可以使用VCS链接作为源:
module "my-module" {
source = "[email protected]:your-company/terraform-modules.git//modules/my-module?ref=v1.1.0"
...
}
在这里, 我指的是我的模块v1.1.0(特定路径), 可以在不同环境中与同一模块的其他版本进行独立测试。
除此之外, 还存在模块的可发现性和可共享性的问题。你应该努力编写有据可查且可重复使用的模块。通常, 你将为不同的应用程序使用不同的Terraform配置, 并且可能希望在其中共享相同的模块。如果不将它们提取到单独的仓库中, 这将非常困难。
使用模块
通过定义特殊的模块块, 可以轻松在Terraform环境中引用模块。这是假设的ECS模块的此类块的示例:
module "my_service_ecs_cluster" {
source = "../modules/ecs_cluster"
cluster_name = "my-ecs-service-${var.env}"
repository_names = [
"my-ecs-service-${var.env}/api", "my-ecs-service-${var.env}/nginx", "my-ecs-service-${var.env}/docs", ]
service_name = "my-ecs-service-${var.env}"
ecs_instance_type = "t2.nano"
min_size = "1"
max_size = "1"
use_autoscaling = false
alb_target_group_arn = "${module.my_alb.target_group_arn}"
subnets = "${local.my_private_subnets}"
security_groups = "${aws_security_group.my_ecs.id}"
key_name = "${var.key_name}"
env_tag = "${var.env}"
project_tag = "${var.project_tag}"
application_tag = "${var.api_app_tag}"
asg_tag = "${var.api_app_tag}-asg"
}
传递的所有选项(来自某些全局选项(如源)除外)都在此模块的配置中定义为输入(变量)。
模块注册表
最近, HashiCorp启动了正式的Terraform模块注册表。这是个好消息, 因为你现在可以从已经开发了经过战斗测试的模块的社区中汲取经验。此外, 其中一些具有” HashiCorp认证模块”徽章, 这意味着它们已经过审核并得到积极维护, 并给你带来额外的自信。
以前, 你要么必须从头开始编写自己的模块(并从错误中学习), 要么使用在GitHub和其他地方发布的模块, 而不能保证它们的行为(除了阅读代码!)
在环境之间共享数据
理想情况下, 即使使用不同的AWS账户, 环境也应该完全分开。实际上, 在某些情况下, 一个Terraform环境可能在另一环境中使用某些信息。如果你正在逐渐将体系结构转换为使用Terraform, 则尤其如此。一个示例可能是你拥有一个全局环境, 该环境为其他环境提供了某些资源。
假设env global与stage共享数据。为此, 你可以在环境的主要级别定义输出, 如下所示:
output "vpc_id" {
value = "${module.network.vpc_id}"
}
然后, 在舞台环境中, 定义一个数据源, 该数据源指向global的远程状态:
data "terraform_remote_state" "global" {
backend = "s3"
config {
bucket = "my-app-terraform-state"
key = "terraform/global"
region = "${var.region}"
dynamodb_table = "terraform-state-lock-table"
profile = "${var.profile}"
}
}
现在, 你可以将此数据源用作任何其他数据源, 并访问global的输出中定义的所有值:
vpc_id = "${data.terraform_remote_state.global.vpc_id}"
注意的话
Terraform有很多优点。我每天在生产环境中使用它, 并认为它对于这样的工作足够稳定。话虽如此, Terraform仍在积极开发中。因此, 你会发现错误和怪癖。
在哪里报告问题并监视更改
首先, 请记住:Terraform为每个提供程序(例如AWS)都有一个单独的核心存储库和存储库。如果遇到问题, 请确保同时检查核心存储库和单独的提供程序存储库是否存在问题和/或打开的带修复程序的拉取请求。 GitHub确实非常活跃并且热情好客, 因此它实际上是搜索错误和修复的最佳场所。
这也意味着提供程序插件分别进行了版本控制, 因此请确保你遵循其更新日志以及核心日志。我遇到的大多数错误都可以通过升级已提供修复程序的AWS提供程序来解决。
无法通过云知识欺骗自己
如果你不了解给定提供程序的工作方式, 则不能使用Terraform来配置和管理基础结构。我想说这是一个误解, 而不是弊端, 因为Terraform旨在增强和改善配置管理的工作流程, 而不是让你随意撒布的魔法尘土– po!环境在成长!你仍然需要对每个云的安全模型, 如何编写(例如)AWS策略, 可用的资源以及它们如何交互的扎实知识。
优先使用明确链接的单独资源
有某些资源(例如AWS安全组或AWS路由表)可让你直接在其自己的块内分别配置安全规则和路由。这很诱人, 因为看起来工作量较少, 但实际上会给你带来麻烦。当你在后续遍中更改这些规则时, 问题就开始了。即使仅引入一个路由/安全规则, 整个资源也将被标记为已更改。它还为这些规则提供了隐式排序, 并使其难以遵循更改。幸运的是, 现在不允许将这两种方法混合使用(请参阅注释)。
最佳实践示例, 具有显式链接的资源:
resource "aws_security_group" "my_sg" {
name = "${var.app_tag}-my-sg"
...
}
resource "aws_security_group_rule" "rule_one" {
security_group_id = "${aws_security_group.my_sg.id}"
...
}
resource "aws_security_group_rule" "rule_two" {
security_group_id = "${aws_security_group.my_sg.id}"
...
}
Terraform计划并不总是能发现问题和冲突
在你使用Terraform管理依赖于其他非托管基础结构的资源的情况下, 我已经提到了这一点。但是还有更多琐碎的示例, 例如, 即使你的EC2实例启用了终止保护, 即使计划表明你可以销毁它, 也会出现错误。你可以说这是Termination Protection的设计目的, 我同意, 但是在理论上/按计划可以做的事情还有更多的例子, 但是当执行时会死锁或出错。例如, 如果某人正在使用某个网络接口, 则无法删除它-死锁而无法正常恢复。
语法怪癖
还有一些与如何设计HCLv1(Terraform使用的语法语言)有关的怪癖。它有几个令人沮丧的怪癖。正在为HCLv2提供解析器的改进版本。阅读这个当前限制以及克服这些限制的计划的最佳方法是这个很棒的博客系列。同时, 大多数问题都有解决方法。它们不漂亮, 一旦v0.12发布, 它们就会失败, 但是嘿, 就是这样。
状态更新失败时
有时会发生Terraform无法正确推送更新状态的情况。这通常是由于潜在的网络问题。解决方案是重试状态更新, 而不是再次运行Apply, 这将派生该状态。
Terraform无法取消状态锁定(防止多个用户更新同一状态的同步原语)时, 可能会发生另一个问题。这涉及使用锁定ID运行Terraform强制解锁以手动将其取下。
幸运的是, 万一出现此类问题, Terraform为你提供了良好的说明和修复步骤。
通过Terraform管理并非一切都有趣
在某些情况下, Terraform不是我选择的工具。例如, 通过Terraform进行配置时, 配置AWS CodePipeline和CodeBuild项目(AWS等效于CI / CD管道)非常麻烦。你需要通过非常冗长的配置块来定义每个步骤, 并且诸如”通过GitHub登录”之类的事情比使用UI要复杂得多。当然, 如果你希望将其编纂, 仍然有可能。好吧, 我想这是写得好的模块的不错选择!
管理AWS API Gateway终端节点也是如此。在这种情况下, 使用专用的无服务器框架将是更好的选择。
在使用Terraform配置AWS资源时, 你会发现自己编写了很多策略。否则, 这些策略通常会为你自动生成(使用UI时)。对于这些, 我建议使用AWS Visual Editor, 然后将结果策略JSON复制到Terraform。
总结
使用Terraform很有趣, 我将继续这样做。初始步骤可能会遇到麻烦, 但是越来越多的资源可以帮助你轻松入行。
我绝对建议你试用Terraform并简单地玩一下。不过请记住, 要安全, 并在非必需帐户上进行测试。如果你有资格获得AWS免费套餐, 则可以将其用作12个月的免费试用期。请注意, 它在可提供的内容方面有局限性。否则, 只需确保旋转最便宜的资源, 例如t3.nano实例。
我强烈建议在各种代码编辑器中扩展Terraform支持。对于Visual Studio Code, 其中一种具有语法突出显示, 格式设置, 验证和棉绒支持。
学习新事物和评估新工具总是很有价值的。我发现Terraform在管理基础架构方面极大地帮助了我。我认为使用Terraform只会变得更加轻松和有趣, 尤其是在v0.12.0附带对HCL语法的重大升级并解决了大多数怪癖之后。 Terraform周围的牵引力和社区活跃而充满活力。你可以在很多我无法在单个博客文章中介绍的内容上找到很多不错的资源, 例如, 有关如何编写模块的详细指南。
评论前必须登录!
注册