作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Radosław萨尔斯基的头像

雷达手表ław Szalski

Radosław是一名Python爱好者和全栈开发人员,拥有超过五年的专业web应用工程经验.

Expertise

以前在

STX Next
Share

编写应用程序只是故事的一部分. 为了让它有价值, 它需要部署到一个可以扩展的地方, 它必须以高可用性运行, 它需要备份, and so on.

越来越多的开发人员至少需要掌握这个部署过程. 这表现为,例如,在 DevOps 随着系统复杂性的增长,成为一个经常被要求的角色. 我们不能让自己忽视这些变化,需要意识到如何设计应用程序 轻松部署.

这也符合我们客户的利益:他们聘请我们作为我们所在领域的专家,并期望我们提供完整的产品, 通常从头到尾. 他们有自己的需求,但往往忽略了业务解决方案所运行的栈. 最后,重要的是产品的商业价值.

引入起程拓殖

部署和基础设施管理不是一个简单的过程. 除了我们需要学习的不断变化的领域专业知识之外 另一个工具 或者一个新的工作流程. 如果你一直在拖延这件事, 本文为您提供了一个熟悉基础设施管理方法的机会. 我希望最终你会对使用Terraform更有信心,并了解更多可能的方法和挑战. 您应该能够使用此工具至少开始管理云基础设施的某些部分.

Terraform是一个抽象层,是的,而且 抽象是有漏洞的, I agree. 但最终,我们的工作是解决问题和管理抽象. 这篇文章的目的是让我们在日常工作中更加理智.

The Goal

我会解释什么是Terraform, 它如何适应整个生态系统, 和其他的比较, 类似的工具. 然后,我将向您展示为团队配置多环境和生产就绪的Terraform设置所需的步骤. 我将解释编写Terraform配置的基础知识——如何使用shareable管理复杂性和重复代码 modules.

这些示例都将集中于一个云提供商:Amazon Web Services (AWS)。. 这只是我最有经验的一种云,但所有的信息也应该适用于其他云.

我将以一些我希望在开始时就知道的注释作为结束:一些语法陷阱, quirks, 以及在某些情况下,我不会选择Terraform作为工具.

我不会关注语法的细节. 你可以通过阅读来加快速度 神奇的文档 and guides HashiCorp为Terraform提供的. 我想把重点放在那些一开始可能不太明显的事情上,那些我希望在开始使用Terraform之前就知道的事情. Hopefully, 这将为您指明正确的方向,并允许您将Terraform视为将来项目中使用的工具.

基础设施管理困难

在云中为应用程序设置环境涉及多个步骤. 除非你把它们都写下来作为详细的清单并严格遵循, 一直以来, you will make mistakes; we are human, after all. 这些步骤很难分享. 您需要记录大量手工过程,而文档可能很快就会过时. 现在将所有这些乘以单个应用的环境总数: dev, test/qa, stage, and prod. 您还需要考虑它们的安全性.

是不是每个工具都有一个我可以使用的UI,而忽略了它的复杂性?

他们有us - aws管理控制台就是一个很好的例子. 但这些工具在幕后做了很多事情. 在UI上的一次点击实际上可能会调用一系列难以掌握的更改. 通常没有办法撤销你在UI中所做的事情(通常的“你确定吗?提示往往是不够的). Moreover, 有另一双眼睛来检查变化总是一个好主意, 但是当我们使用ui时,我们需要和这个人坐在一起,或者检查我们的更改 after 它们是被制作出来的,这更像是审计而不是审查. 每个云提供商都有自己的UI,您需要掌握这些UI. 用户界面设计得易于使用(不一定简单!),因此很容易产生妄想,比如“这只是一个小小的调整。,或者一个快速的产品修复程序,你会在48小时内忘记它. 这种手动点击也很难实现自动化.

如何使用CLI Tools?

对于我们的用例来说,它们比UI工具更好. However, 您仍然倾向于手工更改或编写bash脚本,这很容易失控. 此外,每个提供程序都有自己的CLI工具. 使用这些工具,您无法在提交之前看到更改. 值得庆幸的是,这不是一个新问题,有工具可以帮助解决这个问题.

编制和. 配置管理

我将定义一些用于管理基础架构的工具和实践的类别. 它们是编排和配置管理. 您可以将它们大致视为声明式和命令式编程模型(想想Prolog vs . Prolog). Python). 每种工具都有自己的优点和缺点,但最好了解它们,并将最好的工具应用于给定的工作. 此外,编排通常涉及比配置管理更高的抽象级别.

编制

编排更类似于声明性编程范例. 我喜欢把它想象成一个管弦乐队的指挥. 维基百科(Wikipedia)对指挥家的工作做了一个很好的总结:“用手势指挥几个演奏者或歌手同时演奏的艺术。.“指挥家传达节拍和速度, dynamics, 还有音乐的提示,但实际的工作是由擅长演奏乐器的音乐家个人完成的. 如果没有这种协调,他们就无法演奏出完美的曲子.

这就是Terraform适合的地方. 您使用它来管理it基础设施的一部分:您告诉它要部署什么, Terraform将它们连接在一起,并执行所有必要的API调用. There are 类似的工具 in this space; one of the most well-known is AWS云的形成. 与Terraform相比,它对错误的恢复和回滚有更好的支持, but also, 在我看来, 更陡峭的学习曲线. 它也不是与云无关的:它只适用于AWS.

配置管理

这些实践的补充方面是配置管理方法. 在这个范例中, 您指定工具必须执行的精确步骤以达到给定的目标, 想要的配置. 这些步骤本身可能很小,并且支持多个操作系统, 但你需要积极思考它们的执行顺序. 它们没有当前状态和周围环境的意识(除非你给它们编程), as such, 会盲目执行你给他们的任何步骤吗. 这可能会导致一个问题,称为 配置漂移, 您的资源将慢慢地与最初打算表示的内容不同步, 特别是如果你对它们进行了一些手动更改. 它们非常擅长管理和供应单个实例上的服务. 擅长此工作流的工具示例有Chef、Puppet、Ansible和Salt.

业务流程对您的基础设施实施了一种方法,在这种方法中,您将资源视为牛, 不是宠物. 而不是手动“培育”每个VPS, 当出现问题时,你可以用一模一样的复制品来替换它们. 我并不是说你根本不在乎,抱着最好的希望重新开始.

IT Crowd电视节目的梗和他们标志性的标语:你试过关机再开机吗?

相反,您应该调查并解决问题 在代码中 然后部署它.

Ansible(和其他配置管理工具)可用于管理AWS基础设施, 但这将涉及大量工作,并且更容易出错, 特别是当基础设施经常变化且复杂性不断增加时.

要记住的一件重要的事情是,编排和配置管理方法不会相互冲突. 它们是兼容的. 在由Terraform管理的AutoScaling组中拥有一组EC2 (VPS)实例,但运行AWS应用程序映像(AMI)是完全可以的。, 哪个是磁盘的快照, 这是用必要的步骤准备的, e.g., Ansible. Terraform甚至有一个“提供者”的概念 这允许您在机器启动后运行外部配置工具.

起程拓殖文档 做了一个很好的工作来进一步解释这一点,并帮助你把Terraform放在整个生态系统中.

什么是Terraform?

它是一个开源工具, 由HashiCorp创建,它允许您将基础设施编码为声明性配置文件,这些文件是版本控制和共享的,并且可以查看.

HashiCorp的名字应该听起来很耳熟——它们也做了同样的事情 Nomad, Vault, Packer, Vagrant, and Consul. 如果你用过其中任何一个, 工具你已经知道文档的质量, 充满活力的社区, 你可以从他们的解决方案中得到有用的东西.

基础设施即代码

Terraform is platform-agnostic; you can use it to manage bare metal servers or cloud servers like AWS, 谷歌云平台, OpenStack, and Azure. 在Terraform术语中,这些被称为 providers你可以通过阅读来了解规模 支持的提供程序的完整列表. 可以同时使用多个提供者, 例如提供商A为您配置虚拟机, 但是提供商B配置并委托DNS记录.

这是否意味着只需在配置文件中进行一次更改就可以切换云提供商? 不,我不认为你会想要那样,至少不会以自动的方式. 问题是不同的提供者可能具有不同的功能, 不同的产品, flows, ideas, etc. 这意味着您将不得不为不同的提供者使用不同的资源来表达相同的概念. However, 这一切仍然可以在一个单一的, 熟悉的配置语法,并成为内聚工作流的一部分.

地形设置的基本部分

  1. 你拥有的Terraform二进制文件本身 安装
  2. 源代码文件,i.e.,您的配置
  3. 表示Terraform管理的资源的状态(本地或远程)(稍后会详细介绍)

编写地形配置

中编写Terraform配置代码 *.tf 文件使用 HCL语言. 有一个使用JSON格式的选项(*.tf.json),但它针对的是机器和自动发电,而不是人类. 我建议你坚持使用HCL. 我不会深入研究HCL语言的语法; 官方文件 做一个奇妙的工作描述如何编写HCL和如何使用变量和插值. 我将只提及理解示例所需的最基本内容.

在Terraform文件中,您主要处理的是 resources and 数据源. 资源表示基础设施的组件,例如.g.例如,AWS EC2实例、RDS实例、Route53 DNS记录或安全组中的规则. 它们允许您在云架构内配置和更改它们.

假设您已经设置了Terraform,如果您发出 起程拓殖应用,下面的代码将创建一个功能齐全的EC2实例(只显示某些属性):

资源"aws_instance" "bastion" {
  ami = "ami-db1688a2" # Amazon Linux 2 LTS候选ami 2017.12.0 (HVM), SSD卷类型- ami-db1688a2
  Instance_type = "t2.nano"
  Key_name =“${var.key_name}”
  Subnet_id =“${模块.network.public_subnets[0]}”

  Vpc_security_group_ids = ["${aws_security_group . cn.bastion.id}"]
  监控= "false"
  Associate_public_ip_address = "true"
  Disable_api_termination = "true"

  tags = {
    名称=“${var.project_tag}堡垒- $ {var.env}"
    Env = "${var.env}"
    ApplicationID = "${var.api_app_tag}”
    ApplicationRole = "Bastion Host"
    项目=“${var.project_tag}”
  }
}

另一方面, 有一些数据源允许您在不更改给定组件的情况下读取有关它们的数据. 您需要获取acm颁发的证书的AWS ID (ARN)? 您使用数据源. 不同之处在于数据源的前缀是 data_ 在配置文件中引用它们时.

数据"aws_acm_certificate" "ssl_cert" {
  域名= "* ".example.com"
  状态=["已发布"]
}

以上引用了一个已颁发的ACM SSL证书,该证书可以与AWS alb一起使用. 在完成所有这些之前,您需要设置环境.

文件夹结构

地形环境(及其状态)由目录分隔. 地形加载所有 *.tf 一个目录中的文件放到一个命名空间中,所以顺序无关紧要. 我推荐使用以下目录结构:

/起程拓殖/
     |---> default_variables.tf (1)
   / /(2)阶段
       |---> terraform.tfvars (3)
       |---> default_variables.tf (4)
       |---> terraform.tf (5)
       |---> env_variables.tf (6) 
   /prod/
   //
  1. default_variables.tf -定义所有顶级变量和可选的默认值. 它们可以通过符号链接在每个环境(嵌套目录)中重用.
  2. /stage/ -保存整个独立环境配置的目录(这里名为 stage但它可以是任何东西). 在此文件夹内所做的任何更改都完全独立于其他环境(如环境) prod),这是您想要的,以避免由于对的更改而弄乱生产环境 stage!
  3. terraform.tfvars -定义可变值. .tfvars 文件类似于 .env 文件中包含了 key=val 已定义变量对. 例如,这指定了AWS profile, AWS key_name and AWS key_path 我用的. 它可以在Git中被忽略.
  4. default_variables.tf -这是一个符号链接到文件(2), 这样我们就可以共享与环境无关的变量,而不用重复自己.
  5. terraform.tf – this is the main configuration of each env; it holds the 起程拓殖{} 块,用于配置后端. 我还配置了 providers here.
  6. env_variables.tf -该文件保存特定于env的变量. 我将所有资源都标记为 Env= ,所以这个文件通常只定义一个变量: env.

当然,这并不是构建环境的唯一方法. 这对我来说很有效,因为它使我能够清楚地分离关注点.

后端配置

我已经提到过地形状态. 这是Terraform工作流程的重要组成部分. 您可能想知道是否真的需要state. Terraform不能一直通过查询AWS API来获取基础设施的实际状态吗? Well, 如果你仔细想想, Terraform需要维护它在声明性配置文件中管理的内容与这些文件实际对应的内容之间的映射(在云提供商的环境中)。. 注意,在编写Terraform配置文件时,您不关心,e的id.g.、将为您发布的安全组创建的单个EC2实例或arn. 在内部, however, Terraform需要知道一个给定的资源块代表一个带有ID/ARN的具体资源. 这是检测更改所必需的. Moreover, 状态用于跟踪资源之间的依赖关系(这也是您通常不需要考虑的事情)!). 它们被用来构造一个可以(通常)并行化和执行的图. 与往常一样,我建议您阅读有关的优秀文档 地形状态及其目的.

因为状态是架构的唯一真实来源, 您需要确保您和您的团队始终在其最新版本上工作,并且您不会通过不同步访问状态来创建冲突. 你不会想在国家文件上解决合并冲突的,相信我.

默认情况下, Terraform将状态存储在磁盘上的文件中, 位于(每个env的)当前工作目录中 terraform.tfstate file. 如果你知道自己是唯一的开发人员,或者只是在学习和试验Terraform,这是可以的. 从技术上讲,您可以让它在团队中工作,因为您可以将状态提交到VCS存储库. But then, 您需要确保每个人都在使用状态的最新版本,并且没有人同时进行更改! 这通常是一个令人头痛的问题,我强烈建议不要这样做. Besides, 如果有人加入你的单一开发操作, 您仍然必须为状态配置一个替代位置.

幸运的是,这个问题有一个很好的解决方案内置到Terraform:所谓的 偏远的国家. 要使远程状态工作,您需要使用其中一个来配置后端 可用的后端提供者. 下面的后端示例将基于AWS S3和AWS DynamoDB (AWS NoSQL数据库). 您可以只使用S3,但这样会失去状态锁定和一致性检查机制(不推荐)。. 如果您以前只使用本地状态, 配置远程后端将为您提供第一次迁移状态的选项, 所以你不会失去任何东西. 您可以阅读有关后端配置的更多信息 here.

不幸的是, 这里有一个先有鸡还是先有蛋的问题:S3桶和DynamoDB表必须手动创建. 由于还没有状态,Terraform不能自动创建它们! 有一些解决方法,比如 http://github.com/gruntwork-io/terragrunt 使用AWS CLI实现自动化,但我不想偏离这篇博文的主题.

关于S3和DynamoDB后端配置需要了解的重要事项是:

  1. 启用S3存储桶上的版本控制以避免人为错误和 墨菲定律.
  2. DynamoDB表对读写有速率限制(称为容量)。. 如果对远程状态进行了大量更改, 确保为该表启用DynamoDB AutoScaling或配置足够高的R/W限制. 否则,Terraform在执行大量调用时将从AWS API获得HTTP 400错误.

总而言之,可以放入以下后端配置 terraform.tf 在S3和DynamoDB上配置远程状态.

起程拓殖{
  #有时你可能需要一个特定版本的Terraform
  required_version = ">= 0.11.7"

  存储远程状态,分布式团队需要
  # Bucket & 如果dynamoDB表不存在,则必须手动创建
  #参见:http://github.com/hashicorp/起程拓殖/issues/12780
  后端"s3" {
    Bucket = "my-company- terrform -state"
    关键字= "app-name/stage"
    地区= "eu-west-1"

    # 5/5 R/W容量可能不够重,突发工作(导致400). 考虑在表上启用Auto Scaling.
    #参见:http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ProvisionedThroughput.html
    Dynamodb_table = " terrform -state-lock-table"
  }
}

这让我一下子接受不了, 但请记住, 你对每个嫉妒都这样做一次,然后就可以忘记它了. 如果您需要对状态锁定进行更多的控制, 有HashiCorp Terraform 企业, 但我不会在这里讲.

Providers

为了这个后端做是可访问的,并能够与我们的云提供商在所有通信, 我们需要配置所谓的 provider. 下面的块可以放在 terraform.tf 文件(每个env):

提供程序"aws" {
  配置文件=“${var.profile}"
  区域=“${var.region}"
  version = "~> 1.23.0"
}

变量 profile and region 都存储在 terraform.tfvars 文件,可以忽略. The profile 变量指的是一个已命名的配置文件,该配置文件持有使用该标准的AWS云的安全凭证 证书文件. 注意,我也设置了一些 版本约束. 您不希望Terraform在您不知道每个后端初始化的情况下升级您的提供商插件. 特别是考虑到有一个版本2.x的AWS提供商 需要谨慎升级.

后端初始化

每个后端配置在开始和结束时都需要一个初始化步骤 每次都有变化. 初始化还可以配置Terraform模块(稍后会详细介绍), 当你把这些加起来, 您还需要重新运行init步骤. 该操作可多次执行,安全可靠. Note that, 在后端初始化期间, 不是所有的变量都可以被Terraform读取来配置状态, 也不应该是出于安全原因.g.,秘钥). 要克服这个问题(在我们的示例中,使用与默认配置不同的AWS配置文件),可以使用 -backend-config 带有accept的选项 k=v 变量对. 这被称为部分配置.

terraform init -backend-config=profile=

Terraform文件共享一个限定于给定目录的作用域. 这意味着子文件夹没有直接连接到父目录代码. 然而,它定义了一个允许代码重用、复杂性管理和共享的模块.

工作流

使用Terraform代码的一般工作流程如下:

  1. 为基础结构编写配置.
  2. 看看它会带来什么实际变化(起程拓殖计划).
  3. 可选地,执行 the exact 您在步骤2中看到的更改(起程拓殖应用).
  4. GOTO 1

起程拓殖计划

在起程拓殖 plan 命令将为您提供一个更改列表,这些更改将在发出 apply command. 发行是安全的 plan 很多次,因为它本身不会改变任何东西.

如何阅读计划

Terraform中的对象(资源和数据源)很容易通过它们的完全限定名称来识别.

  1. 在资源的情况下,ID可能看起来像: .—e.g., aws_ecs_service.this.
  2. 在模块内的资源的情况下,我们有一个额外的模块名称: module...—e.g., module.my_service_ecs_service_task.aws_ecs_service.this.
  3. 数据源(模块内部和外部): (module.).data..—e.g., module.my_service_ecs_service_task.data.aws_ecs_task_definition.this.

资源类型特定于给定的提供程序,通常包括其名称(aws_…). 可用的AWS资源的完整列表可在 the docs.

对于给定的资源,计划将显示五个行动:

  1. [+] 添加-一个新的资源将被创建.
  2. [-] 破坏-资源将被完全破坏.
  3. [~] 就地修改—资源将被修改,一个或多个参数将被更改. 这通常是安全的. Terraform将显示哪些参数将被修改以及如何修改(如果可能的话)。.
  4. [- / +] —资源将被移除 然后重新制作 使用新的参数. 如果对不能就地更改的参数进行了更改,就会发生这种情况. Terraform将显示哪些更改强制重新创建资源,并显示以下红色注释: (强制使用新资源). 这是潜在的危险,因为在一段时间内资源将根本不存在. 它还可以破坏其他连接的依赖项. 我建议你绕开这些改变,除非你知道后果是什么,或者你不关心停机时间.
  5. [<=] —将读取数据源. 这是一个只读操作.

Terraform AWS -一个示例“计划”

以上是一个示例计划. 我所做的改变是:

  • 改变了 instance_type 第一个堡垒的实例
  • 增加了第二个堡垒实例
  • 日志含义修改安全组名称

注意,最后一个更改是自动检测父组名称更改的安全组规则. 在我看来,整个计划很好读. 一些参数值显示为 -这并不意味着他们会被改变, 而是无法在此阶段检索和显示它们(例如尚未创建的SG名称)。. 的过程中只会显示已更改的资源 plan command. 任何被省略的东西都不会被触及.

通常,Terraform命令接受附加参数. plan命令最重要的参数是 -out 选项,该选项将在磁盘上保存您的计划. 然后可以通过apply命令执行这个保存的计划(与保存的完全一样). 这一点非常重要,因为如果不这样做,e可能会做出改变.g.,同事在你之间发出一个 plan 以及发行 apply. Example:

  1. Issue plan 并验证它看起来不错.
  2. 有人改变了状态,你却不知道.
  3. Issue apply 哎呀,它做了一些超出计划的事情!

值得庆幸的是,这个工作流程在Terraform v0中得到了改进.11.0. 从这个版本开始, apply 命令自动向您显示一个计划,然后您必须批准该计划(通过显式键入) yes). 优点是,如果您应用此计划,它将完全按照所提供的执行.

另一个有用的选择是 -destroy,它会向你展示摧毁Terraform管理的所有资源的计划. 你也可以用 -target option.

起程拓殖应用

当我们应用给定的计划时,Terraform会出去并锁定我们的状态以确保独占性. 然后,它继续更改资源,并最终推送更新后的状态. 需要注意的一点是,有些资源比其他资源需要更长的时间来完成. 例如, 创建一个AWS RDS实例可能需要超过12分钟, terrform会等这一切结束. 显然,Terraform很聪明,不会因此而阻碍其他的操作. 它创建请求更改的有向图, 如果没有相互依赖, 使用并行来加速执行.

进口资源

Often, Terraform和其他“配置即代码”的解决方案逐渐被引入, 进入一个已经存在的环境. 这种转变真的很容易. 基本上,在Terraform中未定义的任何内容都将保持未管理和不变. Terraform只关心它所管理的东西. Of course, 这可能会带来一些问题,例如, 如果Terraform依赖于某些存在于其配置之外的端点,那么它将被手动销毁. Terraform不知道这些, 因此,在状态更改期间无法重新创建此资源, 在计划执行期间,哪些会导致API出错. This is not something I’d worry about; the benefits of introducing Terraform far outweigh the cons.

为了使介绍过程更容易一些,Terraform包含了 import command. 它用于将已经存在的外部资源引入Terraform状态,并允许它管理这些资源. 不幸的是, Terraform不能为那些导入的模块自动生成配置, 至少在撰写本文时是这样. 有这个功能的计划, 但是现在, 您需要首先在Terraform中编写资源定义,然后导入该资源,告诉Terraform开始管理它. 一旦导入到状态(以及在执行计划期间), 你会看到你在 .tf 文件和云中实际存在的东西. 这样,您就可以进一步调整配置. Ideally, 不应该显示任何更改, 这意味着Terraform的配置将以1:1的比例反映云上已经存在的东西.

Modules

模块是Terraform配置的重要组成部分, 我建议你接受它们,并经常使用它们. 它们为您提供了一种重用某些组件的方法. 一个例子就是用于运行Docker容器的AWS ECS集群. 这样的集群才能工作, 您需要配置许多独立的资源:集群本身, 启动配置,用于管理单独的EC2实例, 映像的容器存储库, 自动伸缩组和策略, and so on. 通常需要为不同的环境和/或应用程序使用不同的集群.

解决这个问题的一种方法是复制并粘贴配置, 但这显然是一种目光短浅的解决方案. 做最简单的更新也会出错.

模块允许您将所有这些独立的资源封装在一个配置块(称为模块)下。. 模块定义 inputs and outputs 哪些是它与“外部世界”(或调用代码)通信的接口. Moreover, 模块可以嵌套在其他模块中, 允许您快速启动整个独立的环境.

本地和远程模块

与状态类似,您可以拥有本地模块或远程模块. 本地模块与Terraform配置一起存储(在单独的目录中), 在每个环境之外,但在同一个存储库中). 如果您有一个简单的体系结构并且不共享这些模块,那么这是可以的.

然而,这有其局限性. 很难对这些模块进行版本化和共享. 版本控制很重要,因为您可能希望使用v1.0.您的ECS模块的0的生产,但想用v1试验.1.在登台环境中为0. 如果模块与代码一起存储, 对模块代码的每次更改都会反映在每个环境中(一次) apply 这通常是不受欢迎的.

对模块进行版本控制的一种方便方法是将它们全部放在单独的存储库中.g.你的公司/ terraform-modules. 然后,在Terraform配置中引用这些模块时,可以使用 VCS链接作为源:

模块"my-module" {
  来源= "git@github ".com:贵公司/ terraform-modules.git / /模块/我的模块?ref=v1.1.0"
  ...
}

这里我引用的是v1.1.我可以在不同的环境中独立于同一模块的其他版本进行测试.

除此之外,还存在模块的可发现性和可共享性问题. 您应该努力编写文档完备且可重用的模块. Usually, 对于不同的应用程序,您将有不同的Terraform配置,并且可能希望在它们之间共享相同的模块. 如果不将它们提取到单独的回购中,这将非常困难.

使用模块

通过定义一个特殊的模块块,可以在Terraform环境中轻松引用模块. 下面是一个假设的ECS模块的示例:

模块“my_service_ecs_cluster”{
  来源= "../模块/ 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 . name}.env}"

  Ecs_Instance_type = "t2.nano"
  Min_size = "1"
  Max_size = "1"

  Use_autoscaling = false

  al_target_group_arn = "${模块.my_alb.target_group_arn}”
  子网=“${local.my_private_subnets}”
  Security_groups = "${aws_security_group . net.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”
}

所有被传递的选项(除了一些全局选项,比如 source)在该模块的配置中定义为输入(变量).

模块注册表

最近HashiCorp推出了一个 官方Terraform模块注册表. 这是一个好消息,因为您现在可以从已经开发出经过战斗测试的模块的社区中汲取知识. Moreover, 其中一些有“HashiCorp验证模块”的徽章, 这意味着他们经过审查并积极维护,给了你额外的信心.

以前, 你要么从头开始编写自己的模块(并从错误中吸取教训),要么使用GitHub和其他地方发布的模块, 对它们的行为没有任何保证(除了阅读代码之外)!)

在不同环境之间共享数据

理想情况下,即使使用不同的AWS帐户,环境也应该是完全独立的. 在现实中, 在某些情况下,一个Terraform环境可能会在另一个环境中使用某些信息. 如果您正在逐步将您的架构转换为使用Terraform,则尤其如此. 举个例子,你有一个 global 为其他环境提供某些资源的环境.

比方说嫉妒 global 与…共享数据 stage. 为此,您可以定义输出 在环境的主要层面上 like so:

输出“vpc_id”{
  Value = "${模块.network.vpc_id}"
}

然后,在 stage 的远程状态,定义指向的远程状态的数据源 global:

数据“terraform_remote_state”“global”{
  后台= "s3"

  config {
    Bucket = "my-app-terraform-state"
    关键字= "terraform/global"
    区域=“${var.region}"
    Dynamodb_table = " terrform -state-lock-table"
    配置文件=“${var.profile}"
  }
}

现在,您可以像使用任何其他数据源一样使用该数据源,并访问中定义的所有值 global输出:

Vpc_id = "${数据.terraform_remote_state.global.vpc_id}"

注意事项

Terraform有很多优点. 我每天都在生产环境中使用它,并且认为它对于此类工作来说足够稳定. 话虽如此,Terraform仍在积极开发中. 因此,你会被错误和怪癖绊倒.

在哪里报告问题和监视更改

首先,记住:Terraform有一个 单独的核心仓库 以及每个提供者的存储库(例如.g., AWS). 如果遇到问题, 确保检查核心回购和单独的提供商存储库的问题和/或打开的拉请求与修复. GitHub确实是搜索bug和修复的最佳场所,因为它非常活跃和欢迎.

这也意味着提供程序插件的版本是分开的,所以一定要遵守 他们的更新日志 as well as 核心部分. 我遇到的大多数错误都是通过升级已经修复的AWS提供商来解决的.

不能欺骗你的云知识

如果您不了解给定提供程序的工作原理,则无法使用Terraform来配置和管理基础设施. 我想说这是一种误解,而不是缺点, 因为Terraform的设计是为了增强和改进配置管理的工作流程,而不是一些你随意撒在周围的魔法尘埃! 环境中成长! 您仍然需要对每个云的安全模型、如何编写、如何使用、如何使用等方面有扎实的了解.g.AWS策略、可用的资源以及它们之间的交互方式.

优先选择显式链接的单独资源

例如,有一些资源, AWS安全组或AWS路由表,可以分别配置安全规则和路由, 直接在他们自己的街区内. 这很诱人,因为它看起来工作量少,但实际上会给你带来麻烦. 当你在随后的回合中改变这些规则时,问题就开始了. 即使只引入了一条路由/安全规则,整个资源也会被标记为被更改. 它还为这些规则提供了隐含的秩序,使人们更难遵循这些变化. 值得庆幸的是,现在不允许混合使用这两种方法(见注释).

带有明确链接资源的最佳实践示例:

资源“aws_security_group”“my_sg”{
  名称=“${var.app_tag} -my-sg”
  ...
}

资源“aws_security_group_rule”“rule_one”{
  Security_group_id = "${aws_security_group . net.my_sg.id}"
  ...
}

资源“aws_security_group_rule”“rule_two”{
  Security_group_id = "${aws_security_group . net.my_sg.id}"
  ...
}

Terraform plan 不能总是发现问题和冲突

在使用Terraform管理资源时,我已经提到了这一点, 非托管的基础设施. 但是还有更琐碎的例子,例如, 如果您的EC2实例启用了终止保护,您将得到一个错误, 尽管 plan 会告诉你毁掉它是可以的吗. 你可以争辩说,这就是终止保护的目的, 我同意, 但还有更多的例子,你可以在理论上/计划中做的事情,但在执行时会死锁或出错. 例如, 如果有东西正在使用网络接口,您就不能删除它——这样就会出现死锁,而没有优雅恢复的选项.

语法怪癖

还有一些与HCLv1 (Terraform使用的语法语言)的设计有关的怪癖. 它有几个 令人沮丧的怪癖. 工作正在进行中 为HCLv2提供一个改进版本的解析器. 阅读当前的限制和克服它们的计划的最好方法是 这个奇妙的博客系列. 与此同时, 有一些变通办法 大多数情况下 issues. 他们不漂亮,一旦失败就会失败.12出来了,但是,嘿,它就是它.

状态更新失败时

有时,Terraform不能正确地推送更新状态. 这通常是由于潜在的网络问题. 解决方案是重试状态更新 而不是再次运行apply,这将分叉状态.

另一个问题可能发生在状态锁(防止多个用户更新相同状态的同步原语)无法被Terraform取下时. 这包括跑步 起程拓殖force-unlock 用锁的ID手动打开它.

值得庆幸的是, 万一出现这样的问题, Terraform为您提供了一个很好的描述和修复它所需的步骤.

通过Terraform管理并非所有事情都有趣

在某些情况下,Terraform并不是我的首选工具. 例如, 通过Terraform配置AWS CodePipeline和CodeBuild项目(相当于AWS的CI/CD管道)是很麻烦的. 你需要通过非常繁琐的配置块来定义每一步,而像“通过GitHub登录”这样的东西比使用UI要复杂得多. 当然,如果你愿意的话,这也是可能的. 嗯,我想这是一个编写良好的模块的好候选人!

管理AWS API网关端点也是如此. 在本例中,使用 专用无服务器框架 会是更好的选择吗.

在使用Terraform配置AWS资源时,您会发现自己编写了大量策略. 通常会自动为您生成的策略(在使用UI时). 对于这些,我推荐 AWS可视化编辑器 然后将结果策略JSON复制到Terraform中.

结论

使用Terraform很有趣,我将继续这样做. 最初的步骤可能是一段颠簸的旅程,但有越来越多的资源可以帮助你轻松进入.

我绝对推荐你带着Terraform去玩一玩. 但请记住,要注意安全,并在非必要的帐户上进行测试. 如果你有资格 AWS免费套餐,免费试用12个月. 只是要注意,它对您可以提供的内容有限制. 否则,只要确保使用最便宜的资源,比如t3.纳米实例.

我强烈推荐在各种代码编辑器中支持Terraform的扩展. 对于Visual Studio Code, 有一个 支持语法高亮显示、格式化、验证和检查.

学习新事物和评估新工具总是有价值的. 我发现Terraform在管理基础设施方面帮了我很大的忙. 我认为使用Terraform只会变得更容易,更有趣,尤其是在v0之后.12.0附带了对HCL语法的重大升级,并解决了大多数奇怪的问题. 围绕Terraform的牵引力和社区是活跃和充满活力的. 你可以找到很多很棒的资源,这些资源是我在一篇博客文章中无法涵盖的.g.,一个关于如何编写模块的详细指南.

了解基本知识

  • Terraform是用来做什么的?

    Terraform用于管理各种云平台上的基础设施. 它是基于控制创建的配置文件来完成的, changing, 破坏了它们覆盖的所有资源. 它使您不必手动执行此操作,因为手动操作容易出错并且通常不可复制

  • 我为什么要使用Terraform?

    你应该使用Terraform,因为, with it, 您可以在多个云平台上自动创建复杂的环境. 因为配置是以代码形式编写的, 可以回顾一下, 合作, 并在VCS中进行版本控制. 它还为您提供了不易出错的可重复部署.

  • 什么是AWS?

    AWS代表亚马逊网络服务. 它是领先的云平台之一,它允许人们.g.、提供虚拟机、数据库,以及存储和操作大量数据. 所有的资源都是按需可用的,并且是可扩展的,而不必担心物理基础设施.

  • 我是否需要了解AWS或其他平台才能使用Terraform?

    Yes, 您至少需要基本了解给定的平台提供了什么,以及它的组件如何相互交互以创建功能设置.

  • 在使用Terraform执行更改之前,我可以预览更改吗?

    是的,使用“plan”命令. 这是一种演练模式,它总结了在应用它们时可能做出的所有更改. 请记住保存此计划,以便稍后它将完全按照您所看到的应用.

聘请Toptal这方面的专家.
Hire Now
Radosław萨尔斯基的头像
雷达手表ław Szalski

位于 Poznań,波兰

成员自 2017年8月29日

作者简介

Radosław是一名Python爱好者和全栈开发人员,拥有超过五年的专业web应用工程经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

以前在

STX Next

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® community.