基础设施即代码 - 使用 Terraform 管理 AWS
介绍
基础设施即代码
Terraform 实战
总结与结语
这篇博文是我AWS系列文章的一部分:
- 基础设施即代码 - 使用 Terraform 管理 AWS
- 使用 Lambda 和 API Gateway 在 AWS 上部署 HTTP API
- 使用 Elastic Beanstalk 在 AWS 上部署 HTTP API
- 部署和测试 AWS RDS MySQL 实例
- 在 AWS 中使用 SNS、SQS 和 Lambda 进行事件处理
- 在 AWS 上使用 Terraform 和 Travis CI 实现持续交付
- 在 AWS 上使用 IoT Core、Kinesis 和 ElastiCache 进行传感器数据处理
- 使用 CloudWatch 监控 AWS Lambda 函数
介绍
如今,企业往往无需再自行运营数据中心。公有云和私有云提供商在不同抽象层级上提供优质的产品。无论您需要访问虚拟机管理程序来创建虚拟机,还是只想运行一段 Python 代码,一切皆有可能。这些不同的抽象层级通常被称为 XaaS(X 即服务)。从最高抽象层级开始,常用的分类如下:
- 软件即服务 (SaaS)
- 平台即服务 (PaaS)
- 基础设施即服务 (IaaS)
虽然使用亚马逊云服务(AWS) 或谷歌云等服务可以简化很多复杂性,但管理不同资源以及各个组件之间的依赖关系可能会变得非常混乱。如何处理故障部件?如何执行升级和回滚?维护又该如何进行?
你可能听说过把服务器比作牛,而不是猫。如果猫不舒服,你会抚摸它,让它开心起来。但如果牛不舒服,你会宰杀它,再买一头新的。你不会投入资源去救治牛群中的某一头。我是素食主义者,我不赞成宰杀动物,但我觉得把这个概念应用到基础设施上是合理的。
在软件工程领域,人们已经开发出许多用于软件模块依赖管理和版本控制的技术。我们是否也可以将这些技术应用到基础设施中呢?答案是肯定的。实现这一目标的关键要素之一是将基础设施视为代码。本文将探讨这一主题,并提供一个使用 Terraform 管理 AWS 资源的示例。
这篇博文的结构如下。第一部分我们将深入探讨基础设施即代码的概念。第二部分将通过一个简单的示例,演示如何使用 Terraform 管理 AWS 资源。最后,我们将总结文章的主要内容。
基础设施即代码
基础设施即代码(IaC) 指的是通过定义文件而非交互式配置工具来管理 IT 基础设施的过程。它与“牛羊而非猫”的原则紧密相关,因为基于定义文件,基础设施的部分重建可以完全自动化。
定义文件通常会纳入版本控制。结合持续集成和持续交付流水线,可以实现高度自动化。基础设施即代码 (IaC) 与自动化相结合,带来以下三个优势:
- 可复现性。可以基于定义文件从头开始重建部分或全部基础架构。通过为每个组件使用明确的版本号并对定义文件进行版本控制,可以确保部署结果的可复现性。
- 速度。计算机在执行预定义任务时速度很快。在设置和配置新环境时,尽可能减少人为干预将加快部署速度。
- 质量。由于前两项优势,为您的基础设施实施自动化测试成为可能,且成本效益相当高。这提高了部署的稳健性和质量。减少人为干预也有助于避免错误。
有很多工具可以实现基础设施即代码 (IaC) 方法,但它们的功能各有不同。Ansible 、Puppet、Chef和SaltStack就是几个值得注意的例子。Ansible 是命令式的,也就是说,你需要声明如何设置基础设施,并且它采用推送机制,即主动访问组件并进行更改。而 Puppet 则是声明式的,也就是说,你需要描述期望的状态,并且它采用拉取机制,即每个服务器上都运行一个 Puppet 代理,从 Puppet 主服务器拉取最新的配置。
上述所有工具的共同之处在于它们都是配置管理工具。它们旨在安装软件并管理不同服务器上的配置。问题是,如何获取要管理的服务器?答案是编排管理。
编排管理侧重于按正确的顺序创建不同的资源(例如虚拟机),并根据需要将它们连接起来。结合不可变基础设施(这与我们“牛羊而非猫”的原则密切相关),问题就出现了:你真的还需要配置管理吗?每个云提供商都以容器或虚拟机镜像的形式提供某种形式的不可变组件存储。
HashiCorp Terraform是一款功能强大的基础设施编排工具。在接下来的章节中,我们将深入了解 Terraform 的实际应用。
Terraform 实战
问题
为了演示 Terraform 的工作流程,我们将在 AWS 上部署一个简单的基础设施。目标是使用 Amazon Elastic Compute Cloud (EC2) 创建一个虚拟机,并使用 SSH 连接到它。为此,我们需要配置防火墙以允许 22 端口上的 TCP 流量,并将我们的公钥复制到该虚拟机。
AWS 设置
在开始之前,我们需要确保可以访问AWS 控制台。为此,如果您还没有 AWS 账户,请先创建一个。然后,您应该创建一个新用户,并为其分配对 EC2 实例的完全访问权限,以便我们能够执行所需的操作。
在将要运行 Terraform 的机器上(例如我们目前的笔记本电脑),我们需要进行一些基本配置。有很多方法可以做到这一点,但我们将选择基于文件的方法。我们需要在文件夹内创建一个文件credentials,内容如下:config~/.aws
# ~/.aws/credentials
[default]
aws_access_key_id=<access_key>
aws_secret_access_key=<secret_access_key>
# ~/.aws/config
[default]
region=eu-central-1
output=json
访问密钥和秘密访问密钥均可从 AWS 控制台获取。请注意,不建议使用 root 用户凭证,而应使用特定用户的凭证。您也可以安装AWS CLI,但 Terraform 运行并不需要它。这里我们还配置了 AWS CLI 的区域和输出格式,但这实际上并不影响 Terraform 的运行。
请注意,我们使用的是默认配置文件。如果您已将 AWS 用于其他用途,则可能需要创建一个新的配置文件。
Terraform部署
设置
Terraform 管理的基础设施定义在文件中,这些文件可以用HashiCorp 配置语言.tf(HCL) 或 JSON编写。Terraform 支持基于不同来源(例如文件、环境变量、其他资源等)的变量插值。
定义文件采用声明式方法,描述最终画面应有的样子。如果当前状态不符合定义,则会调整资源(例如销毁并重新创建),以匹配期望状态。
要初始化 Terraform 目录,您需要执行相应的terraform init命令。当然,前提是您必须已经安装了Terraform。Terraform会将状态存储在所谓的后端(backend)中。在这个简单的示例中,我们将使用本地后端,它将状态存储terraform.tfstate在工作目录下的一个文件中。在生产环境中,您可以根据需要选择不同的后端。
初始化过程还会下载所需的提供程序,在本例中是 AWS 提供程序。提供程序用作与资源交互的抽象层。AWS 提供程序使我们能够管理 AWS 资源。
让我们创建一个名为 . 的新定义文件example.tf。
# example.tf
provider "aws" {
region = "eu-central-1"
}
运行terraform init后将输出以下结果:
Initializing provider plugins...
...
* provider.aws: version = "~> 1.21"
...
Terraform has been successfully initialized!
基本命令
目前我们需要四个基本命令:plan,,,和。与命令类似,apply它们可以使用 terraform CLI()执行。showdestroyinitterraform <command>
terraform plan它用于评估基础设施的当前状态和期望状态,并告知您需要执行哪些操作才能使两者匹配。为了确保完全可预测性,您可以保存计划,以便在运行计划时,即使当前状态在此期间发生变化,也不会发生意外情况。如果 Terraform 检测到任何差异,它将中止应用程序。
terraform apply用于执行必要的操作,以达到所需的架构状态。缺失的资源将被创建,而对现有资源的修改将触发相应的变更。
terraform show可用于检查基础设施的当前状态。虽然它主要用于调试,但在某些情况下也非常有用。
terraform destroy将移除所有已定义的资源。如果您不再需要这些基础设施,或者您的老板要求您削减成本,这将非常有用。
步骤
添加 EC2 实例
首先,我们将添加一个基于 Ubuntu 14.04 镜像的新 EC2 实例。请注意,我们配置了法兰克福区域(eu-central-1),该区域标识符ami-23a48cc8与区域相关。您可以使用Ubuntu AMI 定位器查找适用于您所在区域的正确镜像。接下来,我们将把这个镜像安装到一个t2.micro包含在免费套餐中的实例上。
# example.tf
provider "aws" { ... }
resource "aws_instance" "example" {
ami = "ami-23a48cc8" # Ubuntu 14.04 LTS AMD64 in eu-central-1
instance_type = "t2.micro"
}
运行程序terraform plan会告诉我们,应用更改后它将创建新实例:
Terraform will perform the following actions:
+ aws_instance.example
id: <computed>
ami: "ami-23a48cc8"
instance_type: "t2.micro"
除了该id字段之外,还有许多其他<computed>字段,我暂时将其从粘贴的输出中排除。计算字段是指 Terraform 在执行计划后会生成一个值。稍后我们将看到除了使用 `.` 之外,如何直接访问这些计算值terraform show。
现在我们已经定义了实例,接下来让我们为 SSH 服务器准备公钥认证。
添加公钥
除了在 EC2 中管理虚拟机之外,AWS 还允许您管理公钥。为了使用公钥/私钥对登录,我们需要在 AWS 中创建一个新的公钥。我们可以aws_key_pair向定义文件中添加一个新的资源:
# example.tf
provider "aws" { ... }
resource "aws_instance" "example" { ... }
resource "aws_key_pair" "my-key" {
key_name = "my-key"
public_key = "${file("~/.ssh/id_rsa.pub")}"
}
如您所见,我们从公钥文件中获取了密钥。您也可以将密钥存储在单独的变量文件中,或者直接粘贴密钥。
然而,仅仅添加公钥是不够的。我们需要将一组防火墙规则与我们的实例关联起来,才能将 SSH 流量正确路由到 22 端口。
添加安全组
在 AWS 中,您可以通过定义安全规则来管理传入和传出的流量。让我们添加一条新aws_security_group规则,转发端口 22 上的所有传入 TCP 连接。
# example.tf
provider "aws" { ... }
resource "aws_instance" "example" { ... }
resource "aws_key_pair" "my-key" { ... }
resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
# SSH access
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
看起来不错!现在我们已经有了 EC2 实例、公钥和安全规则,唯一需要做的就是将它们连接起来。
更新 EC2 实例
要声明 Terraform 资源之间的依赖关系,只需使用来自另一个资源的变量即可。通过访问资源内部的aws_key_pair.my-key.key_name变量,Terraform 就知道它需要先创建密钥和安全组。现在我们可以修改示例实例,使其使用已定义的密钥和安全组:aws_security_group.allow_ssh.nameaws_instance.example
# example.tf
provider "aws" { ... }
resource "aws_instance" "example" {
ami = "ami-23a48cc8" # Ubuntu 14.04 LTS AMD64 in eu-central-1
instance_type = "t2.micro"
key_name = "${aws_key_pair.my-key.key_name}"
security_groups = ["${aws_security_group.allow_ssh.name}"]
}
resource "aws_key_pair" "my-key" { ... }
resource "aws_security_group" "allow_ssh" { ... }
让我们执行一下terraform apply。首先它会打印计划并要求确认:
Terraform will perform the following actions:
+ aws_instance.example
...
+ aws_key_pair.my-key
...
+ aws_security_group.allow_ssh
...
如果获得批准,资源将按正确的顺序创建。Terraform 可以并行创建独立资源。
aws_key_pair.my-key: Creating...
aws_security_group.allow_ssh: Creating...
aws_key_pair.my-key: Creation complete after 0s (ID: my-key)
aws_security_group.allow_ssh: Creation complete after 2s (ID: sg-0231ae1c04f2f9556)
aws_instance.example: Creating...
aws_instance.example: Still creating... (10s elapsed)
aws_instance.example: Still creating... (20s elapsed)
aws_instance.example: Creation complete after 22s (ID: i-0761f0d410df33154)
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
太棒了!但是我们怎么知道要连接到哪里呢?服务器名称是什么?我们可以使用terraform showAWS 控制台来查找,但还有更便捷的方法。
结果
我们可以output在定义文件中添加声明,这样就能在程序成功执行后立即访问计算值。最终结果example.tf如下所示:
# example.tf
provider "aws" {
region = "eu-central-1"
}
resource "aws_instance" "example" {
ami = "ami-23a48cc8" # Ubuntu 14.04 LTS AMD64 in eu-central-1
instance_type = "t2.micro"
key_name = "${aws_key_pair.my-key.key_name}"
security_groups = ["${aws_security_group.allow_ssh.name}"]
}
resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_key_pair" "my-key" {
key_name = "my-key"
public_key = "${file("~/.ssh/id_rsa.pub")}"
}
output "example_public_dns" {
value = "${aws_instance.example.public_dns}"
}
现在terraform apply会显示服务器名称:
Outputs:
example_public_dns = ec2-18-184-130-203.eu-central-1.compute.amazonaws.com
执行ssh ubuntu@<server-name>结果符合预期:
Welcome to Ubuntu 14.04.5 LTS (GNU/Linux 3.13.0-149-generic x86_64)
ubuntu@ip-172-31-47-208:~$
完工后,请不要忘记销毁基础设施,terraform destroy因为它不再需要了。
总结与结语
在这篇博文中,我们了解了如何将基础设施视为在版本控制代码中定义的不可变组件。基础设施即代码可以提高部署的可复现性、速度和质量。目前有很多工具可以支持这种模式。
使用 Terraform 等编排工具结合不可变容器或虚拟机镜像,是替代 Ansible 或 Puppet 等传统配置管理工具的有效方案。Terraform 支持多种不同的资源提供程序,并允许使用可配置的后端来存储其状态。
您使用哪些工具来管理您的基础设施?您之前使用过上面提到的任何工具吗?您更倾向于配置管理工具、编排管理工具,还是两者结合使用?请在评论区告诉我您的想法!
封面图片根据 CC BY-SA 3.0 许可,https://commons.wikimedia.org/w/index.php? curid=1194108 。
如果你喜欢这篇文章,可以在 ko-fi 上支持我。
文章来源:https://dev.to/frosnerd/infrastruction-as-code---managing-aws-with-terraform-i9o