比较Terraform和CloudFormation在处理配置和实际基础设施差异的场景下的用例情况

首先

当在基础设施代码中进行基础设施运维时,通常会面临一个问题,即在Yaml文件中进行声明性配置与实际基础设施之间的差异,也被称为drift,当出现此类差异时需要进行对应的处理。

本文主要比较了Terraform和CloudFormation在处理漂移问题时的方法,分别从两个场景出发:一个是将配置内容应用于实际基础设施的用例,另一个是将实际基础设施配置应用于设置的用例。

在进行下文讨论之前,需要先提出一个前提。

在这个实验中,我们只会引发AWS EC2实例标签资源的差异进行验证。

因此,我认为在其他基础设施资源的情况下,可能会遗漏一些必须涵盖的观点。在此我将作为免责声明进行提及。

环境版本

MacOS版本:11.0.1

地形改造

$ terraform version
Terraform v0.13.5
+ provider registry.terraform.io/hashicorp/aws v3.24.1

云形成(AWS命令行接口)

aws-cli/2.1.32 Python/3.8.8 Darwin/20.1.0 exe/x86_64 prompt/off

为了实验而创建的EC2实例。

我们将使用Terraform和CloudFormation分别创建相同配置的EC2。

image.png

## 用中文将以下内容进行提炼:

## Terraform

resource "aws_instance" "test-instance" {
  instance_type = "t3.micro"
  ami = "ami-0ef85cf6e604e5650" # Ubuntu Server 18.04 LTS (HVM), SSD Volume Type (64-bit x86)
  subnet_id = "subnet-00301c4fabb0d2def"
  associate_public_ip_address = true
  security_groups = ["sg-08cb2b147c604dae4"]

  root_block_device {
    volume_type           = "standard"
    volume_size           = 8
    delete_on_termination = true
  }

  tags = {
    Name = "instance-by-terraform"
  }
}

## 云形成

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Cloud Formation example

Resources:
  MyEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: "ami-0ef85cf6e604e5650"
      InstanceType: "t3.micro"
      SubnetId: "subnet-00301c4fabb0d2def"
      SecurityGroupIds: ["sg-08cb2b147c604dae4"]
      Tags:
        - Key: Name
          Value: "instance-by-cloudformation"
      BlockDeviceMappings:
      - DeviceName: "/dev/sda1"
        Ebs:
          VolumeType: "standard"
          DeleteOnTermination: "true"
          VolumeSize: "8"

用例1: 希望将设定应用于实际基础设施/恢复 (现有设定为正当模式)

目前现有的设置与实际基础设施环境存在差异,需要将其应用到实际的基础设施环境中。这种情况可能是由于无意中手动操作或使用其他工具在目标基础设施工具之外进行了更改。

此时需要确认一下,目前存在哪些差别。

我們將在AWS控制台上更改EC2實例的標籤資源名稱並故意引發漂移。

image.png

## Terraform

## 用华夏语言重述

当您想要确认差异时,在Terraform中可以通过使用`terraform plan`命令来检查当前的差异。

Terraform可以通过terraform.tfstate文件来存储与实际基础架构的映射信息,从而了解差异并应用它们。

由于此次的对象是EC2,因此将进行替换操作,同时也确认可以手动更改标签名称的设置内容。

$ terraform plan
(略)
      ~ tags                         = {
          ~ "Name" = "MANUAL-DONE-instance-by-terraform-up" -> "instance-by-terraform"
        }
(略)

当您需要实际应用时,可以使用Terraform apply来进行应用。

## 云形成

要检查CloudFormation中的差异,可以使用Drift Detect功能。

在控制台上,可以在”Stack actions”下拉菜单中找到按钮,可以异步地确认结果。

image.png

从漂移结果查看,可以看到以下内容,表明资源标签不同。

image.png

当您希望将配置文件应用于实际基础设施时,在使用CloudFormation时,您需要在AWS控制台或AWS CLI上手动修改实际基础设施。与terraform拥有state文件不同,CloudFormation具有差异检测功能,但这些差异数据本身不受CloudFormation管理。

因此,通过漂移检测,我们可以明确差异,然后手动修复实际基础架构。由于差异是手动或工具产生的,所以我们也应该以类似的方式进行修正。

### 如果想要在CloudFormation中自动修正差异,请执行以下操作

如果想要在CloudFormation中自动修复差异,可以像以下官方博客文章中那样使用Lambda和CloudWatch Event来实现。

用例2: 我想将实际基础设施应用到配置文件中(将现有的实际基础设施正确应用的情况)

这是当真正的基础设施首先进行了必要的修正,然后进行了基础设施配置的情况。

土建

在Terraform中,你需要使用terraform refresh命令将当前状态更新到tfstate文件中,并通过查看差异来确定要应用于tf文件设置的已更改项目。使用terraform state show aws_instance.test-instance等命令可以使差异稍微更易于理解一些。

你可以手动更改后使用terraform plan来确认修正是否正确处理差异。

$ terraform plan
(略)
        tags                         = {
            "Name" = "MANUAL-DONE-instance-by-terraform-up"
        }
(略)

使用terraform import的情况下

如果您希望使用Terraform来管理原本不属于Terraform管辖的资源,您可以使用terraform import。

在以下的官方教程中,提供了详细的步骤说明。

以下是步骤(↓是我的翻译)。因此,最终需要手动将state文件的内容转移到tf文件中。如上述URL页面所述,无需将state文件的所有内容都转移到tf文件中。

    1. 确定导入现有基础设施资源

 

    1. 将基础设施导入到Terraform状态中

 

    1. 编写与基础设施资源匹配的Terraform配置

 

    1. 使用Terraform plan确认所编写的配置是否与预期状态的基础设施资源匹配

 

    执行设置以更新Terraform状态(terraform apply)

云化构建

对于CloudFormation来说,您需要使用漂移检测功能来检测差异并手动更新想要更改的模板部分,就像之前一样。

对于CloudFormation,如果您不将配置更改应用到CloudFormation上的Stack中,它将永远被视为漂移状态。因此,您需要使用AWS CloudFormation create-change-set => execute-change-set或者update-stack来更新Stack。

$ aws cloudformation create-change-set --stack-name TestCFnEC2 --change-set-name test-cs --template-body file://template.yml
$ aws cloudformation execute-change-set --change-set-name test-cs --stack-name TestCFnEC2

我希望在CloudFormation中将设置应用到实际基础设施中,而无需进行替换(删除和新建)。

当CloudFormation表示在执行上述步骤更新Stack时,要替换目标资源时,如果想避免删除资源并更新template.yml后执行Update Stack,AWS文档在以下页面展示了相应的步骤。

如果要将步骤概要翻译成日语,将会是以下这样。

步骤1:更新Stack以将Deletion Policy设置为Retain。
步骤2:删除template.yml中与偏移相关的内容,并通过执行更新Stack操作应用更改。
步骤3:使用导入功能更新template.yml。

# 最后

从漂移的角度来比较Terraform和CloudFormation,显然可以看出CloudFormation的麻烦之处更加明显。我们确认了Terraform利用tfstate对实际环境和配置进行映射管理的强大功能,尽管这会增加管理成本。

由于没有提及特定的terraform删除限制(destroy lock),所以我考虑在另一篇文章中进行比较整理。

# 请提供以下内容的汉语译文。

    • https://www.hashicorp.com/blog/detecting-and-managing-drift-with-terraform

 

    • https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-drift.html

 

    • https://medium.com/@cep21/after-using-both-i-regretted-switching-from-terraform-to-cloudformation-8a6b043ad97a

 

    https://speakerdeck.com/dena_tech/techcon2021-19?slide=87
bannerAds