[译文]Terraform vs CloudFormation

1,013 阅读10分钟

原作者:Alexander Savchuk

原文地址:terraform-vs-cloudformation

这个问题偶尔出现——在aws已经提供了在本质上一样服务的情况下,为什么我们仍然可能会选择使用第三方工具?

在过去的几年中,CloudFormation已经有了巨大的改进,势必现在已经不如当初存在诸多令人信服的理由去使用Terraform,但是,如果再次让我选择的话,我还是愿意选择Terraform,以下我会罗列几个我做此选择的原因。

语言

Terraform使用HCL(HashiCorp Configuration Language),这种语言是为了平衡人类可读性和机器友好识别而开发出来的。

而CloudFormation使用的是JSON或YAML语言。

一般来说,相对于JSON来说,YAML是更为容易阅读和编写的,但是和JSON一样,YAML也会强迫你去使用多层嵌套域,如果某个地方不注意错误的使用缩进,那么你的代码可能会让你想吐血(译者:现在大多数的idea都有代码格式化功能,所以...)。相比之下,HCL一般只有一到两层的嵌套域,并执行一些基础的Go-inspired formatting hygiene,这会使得代码阅读起来更舒适。

Terraform拥有丰富的字符插值和内置函数集,包括条件和云原生支持,它允许在DSL中建立相当复杂的逻辑模型,而不必求助于完全成熟的编程语言(尽管这可以通过外部数据源完成)。而CloudFormation 的内置函数则明显受到限制。

倾向于Terraform的另一个原因是,它通过使用模块使得代码重用变得更加容易,并为您在按照对您最有意义的方式构建项目时提供了很大的灵活性。CloudFormation支持嵌套堆栈,但是一般来说,您必须将所有基础设施代码保存在一个项目仓库中的一个(或多个)大型文件中,而使用Terraform,您可以随意对其进行分割。对于普通人来说,使用多个100行的文件通常比使用单个5000多行的文件要容易得多。模块可以存储在GitHub或公共的Terraform模块存储库上,并且很容易跨多个项目进行版本控制和共享。

状态

Terraform将他的状态保存在一个文件中,这个文件可以被存储在你的电脑硬盘,也可以提交到你的代码管理仓库,还可以将它保存到S3或其他的配置管理系统中。无论你选择怎么保存,你必须确保你不能丢失或毁坏你的状态文件,你也要保证你不会一不小心将你的生产环境的状态文件应用到你的测试环境。不管怎么说,我们在使用Terraform 状态文件的历史过程中已经踩过足够多的坑,现在Terraform更是已经引入了工作空间(以前称之为“环境”),所以我们大概很难再搬起石头砸自己的脚了。

相比之下,CloudFormation运行在AWS基础设施上,并为您管理状态,因此你并不需要关心它是如何做到这一点的。与Terraform不同,CloudFormation还会尝试回滚无法应用的更改(除非它处于UPDATE_ROLLBACK_FAILED状态)(aws.amazon.com/blogs/mt/re…)。此外,CloudFormation后端可以接收来自您的资源的信号,这使得配置自动伸缩和滚动更新成为非常有用的选项。CloudFormation也不惧怕机器突然宕机(可替换的CI/CD服务器可能会发生这种情况),而Terraform需要从部分更新中恢复。

目前看从表面来仿佛是,CloudFormation更占上风。如果我们更深入的去了解Terraform的状态文件,我们会发现它是一个非常强大的存在,而这也就是Terraform主要的卖点。它允许我们导入、应用或转移资源,这意味着我们可以随意所欲的重构我们的基础设施,而不用销毁重建。

另一件在Terraform中可能做到的是配置管理漂变(原文中用的是managing configuration drift)。每次运行“terraform plan”时,它获取基础设施的最新“实际”状态,并将其与配置文件中定义的“目标”状态和“当前”状态进行比较,后者反映了上一次运行“terraform apply”时的实际状态,并存储在状态文件中。如果我们的基础设施不再匹配“目标”状态(通常由于一些外在更改导致),Terraform将计算差异并建议通过手动更改将其恢复到我们的配置中所描述的状态。只要我们保持幂等部署,周期性的运行Terraform plans,这会使我们检测和恢复配置漂变更为方便。事实上,这些事情可以在我们的基础设施供应管道中设置成计划任务。

与此同时,CloudFormation就傻傻的意识不到其状态之外的任何变化。可以简单的试试下面这个实验。

首先,我们部署一个新的CloudFormation来创建一个新的VPC和一个没有入口规则的安全组:

AWSTemplateFormatVersion: '2010-09-09'

Resources:
  VPC:
    Type: "AWS::EC2::VPC"
    Properties:
      CidrBlock: 10.0.0.0/16

  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupName: TestSG
      GroupDescription: Test security group
      VpcId: !Ref VPC

然后,转到Aws控制台,手动添加一个新的ingress规则。例如,让我们打开SSH端口来进行一些快速调试。

接下来,更新你的CloudFormation,你会得到下面这个错误:

这时候再次运行CloudFormation也不会检测到手动的修改,就CloudFormation而言,我们没有对基础设施做任何更改。

配置

参数使得在各种环境中重用同一个模板成为可能。例如,您可能希望在测试环境中使用更便宜的EC2实例,而在生产环境中使用更强大的实例。

CloudFormation支持最多60个参数,必须在运行时提供这些参数。有几种方法可以做到这一点。

Terraform要比这个灵活得多。它有一个数据源的概念,它提供了一些资源的只读表示,允许您在不影响其状态的情况下访问其属性。数据源提供了广泛的选项来方便的集成相关的Stacks和避免对所需的参数进行硬编码。例如,您可以访问外部管理资源的属性(如找到最新的AMI),CloudFormation Stack中获取输出,重用被其他Terraform Stack输出的属性(比如一个数据库的Endpoint或DNS记录),如果这还不够,运行一些可以提供所需的值的自定义脚本。真相只有一个,这个真相是基础设施的当前状态,而不是不久前的快照,这是配置文件经常发生的情况。在下一次Terraform运行期间,对资源的所有更改将传播到依赖于它的所有其他资源,而不担心某个地方忘记复制了,同时保持关注点的清晰分离。另一个好处是,您可以使用相同的CI/CD管道来管理所有Terraform项目,而不必担心向每个资源传递正确的参数集。

由于Terraform是跨云的,所以您可以将来自不同云提供商的基础设施与来自第三方服务(如PagerDuty或New Relic)的资源组合在一起。本质上,Terraform为您提供了一种方便的方式来管理所有您的基础结构,将其作为代码,而不必学习每个平台和服务的独立工具和配置语法。

其他

Terraform在运行更新之前会验证配置文件。它不仅检查所有文件是否使用了正确的语法(CloudFormation也有类似的验证),而且还检查所有参数是否可访问,整个配置是否有效。

在Terraform中,您可以(也应该)在应用任何更改之前运行一个“terraform plan”步骤。这一步会精确地告诉你什么会改变,为什么会改变。

terraform paln运行后表明,Terraform必须销毁和重新创建两个ECR存储库,原因是它们的名称发生了变化。

CloudFormation支持更改集,这非常不友好,而且很难理解

Terraform可以自动格式化代码,帮助维护一致的代码风格,使拉请求更容易阅读。JSON(或者现在的YAML)的痛苦困扰着CloudFormation开发人员,而使用Terraform的开发人员很难想象这种痛苦的。

无论是Terraform还是CloudFormation都100%覆盖了可用的AWS资源,但是一般来说,Terraform支持新可用资源的时间要比CloudFormation早几个月。所有新的AWS功能在发布时必须为主要语言提供REST API和SDK支持。另一方面,CloudFormation支持是可选的。但Terraform开发人员会积极加入进来,并添加他们想要使用的功能,所以开源软件在这种情况下表现突出,。

结语

所以,我的上述是否就意味着Terraform 永远要优于CloudFormation?并非如此,还是有很多原因让我们愿意去选择CloudFormation。

比如,在管理s3存储桶来存储Terraform state文件时,我们最好不要手动创建,因为在这种情况下,在你的基础设施中一个最重要的存储桶将会变得不可管理和不可审计。当然,您可以以Terraform的形式提供它,但是它似乎马上就变成了一个鸡生蛋还是蛋生鸡的问——在哪里存储用于存储状态文件的桶的状态文件?一种选择是将其提交到一个源代码控制系统,如果您不经常修改这个资源,那么它可能可以正常工作,但是这通常不是最好的选择。CloudFormation可能是管理这个存储桶的一个很有吸引力的选择,因为这样您就完全不必担心状态文件。或者,您可以首先使用本地状态在Terraform中创建它,然后将S3后端添加到刚刚创建的桶中。通过运行“terraform init”重新初始化项目后,terraform会将状态文件迁移到S3。

再比如,自动缩放和滚动更新功能。不幸的是,它只作为CloudFormation API的一部分可用,因此除了使用CloudFormation管理它之外没有其他选择。作为一种变通方法,您可以将此CloudFormation Stack资源打包为Terraform,从而两全其利。

resource "aws_cloudformation_stack" "autoscaling_group" {
  name = "${var.cfn_stack_name}"
 
  template_body = <<EOF
Description: "${var.cfn_stack_description}"
Resources:
  ASG:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier: ["${join("\",\"", var.subnets)}"]
      AvailabilityZones: ["${join("\",\"", var.availability_zones)}"]
      LaunchConfigurationName: "${aws_launch_configuration.ecs.name}"
      MinSize: "${var.asg_min_size}"
      MaxSize: "${var.asg_max_size}"
      DesiredCapacity: "${var.asg_desired_capacity}"
      HealthCheckType: EC2
 
    CreationPolicy:
      AutoScalingCreationPolicy:
        MinSuccessfulInstancesPercent: 80
      ResourceSignal:
        Count: "${var.cfn_signal_count}"
        Timeout: PT10M
    UpdatePolicy:
    # Ignore differences in group size properties caused by scheduled actions
      AutoScalingScheduledAction:
        IgnoreUnmodifiedGroupSizeProperties: true
      AutoScalingRollingUpdate:
        MaxBatchSize: "${var.asg_max_size}"
        MinInstancesInService: "${var.asg_min_size}"
        MinSuccessfulInstancesPercent: 80
        PauseTime: PT10M
        SuspendProcesses:
          - HealthCheck
          - ReplaceUnhealthy
          - AZRebalance
          - AlarmNotification
          - ScheduledActions
        WaitOnResourceSignals: true
    DeletionPolicy: Retain
  EOF
}

译者语

打算用来技能学习+英语锻炼的,但是没想到的是翻译过程这么痛苦,而且翻得也很烂(可能会让人一脸懵逼),不过还是要感谢有道词典。

同事撰Terraform文若干,有兴趣可以前去观摩,传送: