【技术版】编码基础设施配置。使用 Terraform 部署 AWS 实例。

我叫寺冈,是一名基础设施工程师。
这次,我们将着眼于技术方面,接续上次的知识部分。
基础设施即代码我们来亲自试一试。
上次只是有点长请查看以下知识博客:

【知识版】基础设施配置代码化。学习“基础设施即代码”的概念,让配置管理更智能。

在本文中,Terraform 的工具
在 AWS 上构建一个 EC2 实例

■简单介绍一下:什么是 Terraform?

Terraform 由 HashiCorp 开发

一个由 HashiCorp 开发的配置管理工具,它
通过代码实现基础设施的自动化构建和配置。HashiCorp
公司Vagrant(我一直对此心存感激)。
该工具的开发非常活跃,更新频繁,因此我个人非常关注它的发展。

好的,我们马上开始吧。

■准备 Terraform 执行环境

首先,准备运行环境。没有它您将无法开始(
压缩文件已分发,只需使用 wget 下载即可。简单方便。
解压缩并设置路径。

$ wget https://releases.hashicorp.com/terraform/0.9.0/terraform_0.9.0_linux_amd64.zip $ unzip terraform_0.9.0_linux_amd64.zip $ mv terraform /usr/bin/ $ terraform -v Terraform v0.9.0

……完成了!
这就是准备执行环境所需的全部步骤(

■ 创建模板文件

简单来说,

模板文件是一段代码,Terraform 可以解析这段代码,它描述了
使用此配置创建基础设施的步骤然后,Terraform 将基于此文件构建基础设施。

首先,准备一个工作目录,并在其中创建一个模板文件。

$ mkdir /var/tmp/terraform $ cd /var/tmp/terraform $ touch main.tf

模板文件扩展名应为“*.tf”。Terraform
会将具有此扩展名的文件识别为模板。

所以,这是我创建的用于构建一个 EC2 实例的模板文件:(
这是我提前创建的。这是一种作弊方法,烹饪节目里也会用到。)

变量 "aws_access_key" {} 变量 "aws_secret_key" {} 变量 "region" { default = "ap-northeast-1" } 变量 "images" { default = { us-east-1 = "ami-1ecae776" us-west-2 = "ami-e7527ed7" us-west-1 = "ami-d114f295" eu-west-1 = } } 提供程序 "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "${var.region}" } 资源 "aws_vpc" "default" { cidr_block = "172.30.0.0/16" instance_tenancy = "default" enable_dns_support = "true" enable_dns_hostnames = "false" tags { Name = "default" } } 资源"aws_internet_gateway" "gw" { vpc_id = "${aws_vpc.default.id}" } resource "aws_subnet" "public_subnet_a" { vpc_id = "${aws_vpc.default.id}" availability_zone = "ap-northeast-1a" cidr_block = "${cidrsubnet(aws_vpc.default.cidr_block, 4, 1)}" } resource "aws_route_table" "public_route" { vpc_id = "${aws_vpc.default.id}" route { cidr_block = "0.0.0.0/0" gateway_id = "${aws_internet_gateway.gw.id}" } } resource "aws_route_table_association" "public_route_a" { subnet_id = "${aws_subnet.public_subnet_a.id}" route_table_id = "${aws_route_table.public_route.id}" } resource "aws_security_group" "ec2_terraform_test" { name = "ec2_terraform_test" vpc_id = "${aws_vpc.default.id}" ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_instance" "terraform-test" { ami = "${var.images["ap-northeast-1"]}" instance_type = "t2.micro" key_name = "XXXXXXXX" vpc_security_group_ids = [ "${aws_security_group.ec2_terraform_test.id}" ] subnet_id = "${aws_subnet.public_subnet_a.id}" associate_public_ip_address = "true" root_block_device { volume_type = "gp2" volume_size = "8" } tags { Name = "terraform-test" } } output "public ip" { value = "${aws_instance.terraform-test.public_ip}" }

我明白了(
下面我会逐一解释代码的每个部分。)

定义提供者

provider "aws" { access_key = "${var.aws_access_key}" secret_key = "${var.aws_secret_key}" region = "${var.region}" }

我们先从这部分开始。在 Terraform 中,首先需要配置提供程序。
实际上,Terraform 也支持 Azure 和 Google Cloud 等云服务,因此
您需要先告诉它要写入哪个提供程序的配置。
在本例中,我们想在 AWS 上构建一个 EC2 实例,所以我们指定 AWS。

定义变量

变量“aws_access_key”{} 变量“aws_secret_key”{} 变量“region”{默认值=“ap-northeast-1”}

您可以使用变量块定义变量。
存储在此变量中的值可以从其他块调用和使用。
变量中的值会在定义提供程序的部分中被调用。
例如,“var.aws_access_key” 就是这部分。

顺便一提,光看代码你可能会疑惑“变量的值都存储在哪里了?”,但
使用 Terraform,你可以把想要存储到变量中的值写在一个单独的文件中,Terraform
会引用该文件并自动将值存储到变量中。
这个文件还需要指定一个扩展名,必须是“*.tfvars”。

aws_access_key = "AKIAXXXXXXXXXXXXXXXX" aws_secret_key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

内容如下所示:“变量名="值"”。

资源定义

resource "aws_vpc" "default" { cidr_block = "172.30.0.0/16" instance_tenancy = "default" enable_dns_support = "true" enable_dns_hostnames = "false" tags { Name = "default" } }

此部分用于编写通常通过管理控制台创建的资源的配置。
在 Terraform 中,“VPC”和“EC2”都被视为资源。
定义 VPC 时,需要指定“cidr_block”。
定义安全组时,需要指定“ingress”和“egress”;
定义 EC2 实例时,需要指定“ami”和“instance_type”。
需要注意的是,每个描述项并非由编写代码的人员随意决定。Terraform
有其独特的规范,如果编写错误,系统会发出语法错误警告(
更多信息,请参阅下方的官方文档,文档均为英文)。

AWS 提供商:HashiCorp 的 Terraform

输出

输出“公有 IP”{ 值 = "${aws_instance.terraform-test.public_ip}" }

如果部署代码的结果没有返回,您将无法得知发生了什么。
通过编写输出块,您可以接收部署结果。
上面的代码接收了已创建的 EC2 实例的公网 IP 地址。

■ 让我们实际部署它。但在此之前

当然,部署后结果会立即显现。
即使你编写的代码存在错误……(这可不是什么好事。)
所以,让我们使用 Terraform 的 dry run 功能。
这是一个非常方便的功能,可以让你提前检查执行计划,看看代码中是否存在任何错误。
如果存在任何语法错误,系统会发出错误警告,方便你发现问题(
尝试输入“terraform plan”)。

$ terraform plan 在执行计划之前,正在刷新内存中的 Terraform 状态…… 刷新后的状态将用于计算此计划,但不会持久化到本地或远程状态存储。Terraform 执行计划已生成,如下所示。资源按字母顺序排列,以便快速浏览。绿色资源将被创建(如果资源已存在,则会先销毁再创建),黄色资源将被就地更改,红色资源将被销毁。青色条目是要读取的数据源。注意:您 + aws_instance.terraform-test ami: "ami-cbf90ecb" associate_public_ip_address: "true" availability_zone: "<computed> " ebs_block_device.#: "<computed> " ephemeral_block_device.#: "<computed> " instance_state: "<computed> " instance_type: "t2.micro" ipv6_addresses.#: "<computed> key_name: "XXXXXXXXXX" network_interface_id: "<computed> “ placement_group:”<computed> private_dns:<computed>私有 IP:<computed> public_dns:<computed> public_ip:<computed> " root_block_device.#: "1" root_block_device.0.delete_on_termination: "true" root_block_device.0.iops: "<computed> " root_block_device.0.volume_size: "8" root_block_device.0.volume_type: "gp2" security_groups.#: "<computed> " source_dest_check: "true" subnet_id: "${aws_subnet.public_subnet_a.id}" tags.%: "1" tags.Name: "terraform-test" tenancy: "<computed> " vpc_security_group_ids.#: "<computed> + aws_internet_gateway.gw vpc_id: "${aws_vpc.default.id}" + aws_route_table.public_route route.#: "1" route.~2599208424.cidr_block: "0.0.0.0/0" route.~2599208424.egress_only_gateway_id: "" route.~2599208424.gateway_id: "${aws_internet_gateway.gw.id}" route.~2599208424.instance_id: "" route.~2599208424.ipv6_cidr_block: "" route.~2599208424.nat_gateway_id: "" route.~2599208424.network_interface_id: "" route.~2599208424.vpc_peering_connection_id: "" vpc_id: "${aws_vpc.default.id}" + aws_route_table_association.public_route_a route_table_id: "${aws_route_table.public_route.id}" subnet_id: "${aws_subnet.public_subnet_a.id}" + aws_security_group.ec2_terraform_test description: "Managed by Terraform" egress.#: "1" egress.482069346.cidr_blocks.#: "1" egress.482069346.cidr_blocks.0: "0.0.0.0/0" egress.482069346.from_port: "0" egress.482069346.ipv6_cidr_blocks.#: "0" egress.482069346.prefix_list_ids.#: "0" egress.482069346.protocol: "-1" egress.482069346.security_groups.#: "0" egress.482069346.self: "false" egress.482069346.to_port: "0" ingress.#: "1" ingress.2541437006.cidr_blocks.#: "1" ingress.2541437006.cidr_blocks.0: "0.0.0.0/0" ingress.2541437006.from_port: "22" ingress.2541437006.ipv6_cidr_blocks.#: "0" ingress.2541437006.protocol: "tcp" ingress.2541437006.security_groups.#: "0" ingress.2541437006.self: "false" ingress.2541437006.to_port: "22" name: "ec2_terraform_test" owner_id: "<computed> " vpc_id: "${aws_vpc.default.id}" + aws_subnet.public_subnet_a assign_ipv6_address_on_creation: "false" availability_zone: "ap-northeast-1a" cidr_block: "172.30.16.0/20" ipv6_cidr_block_association_id: "<computed> " map_public_ip_on_launch: "false" vpc_id: "${aws_vpc.default.id}" + aws_vpc.default assign_generated_ipv6_cidr_block: "false" cidr_block: "172.30.0.0/16" default_network_acl_id: "<computed> " default_route_table_id: "<computed> " default_security_group_id: "<computed> " dhcp_options_id: "<computed> " enable_classiclink: "<computed> " enable_dns_hostnames: "false" enable_dns_support: "true" instance_tenancy: "default" ipv6_association_id: "<computed> "ipv6_cidr_block:"<computed> " main_route_table_id: "<computed> " tags.%: "1" tags.Name: "default" 计划:添加 7 个,更改 0 个,销毁 0 个。.

如果执行成功且无任何错误,
结果将显示实际部署时将添加哪些资源。
在本例中,结果为“计划:添加 7 个资源”,因此总共将添加 7 个资源。
如果结果如下所示,那就很遗憾了。让我们修复代码中的错误(

发生 2 个错误:* aws_security_group.ec2_terraform_test:egress.0:无效或未知的密钥:cidr_block * aws_security_group.ec2_terraform_test:ingress.0:无效或未知的密钥:cidr_block

■ 尝试实际部署。

既然我们已经确认试运行没有错误,现在让我们正式部署它。
要部署,只需输入命令“terraform apply”即可。

$ terraform apply aws_vpc.default: 创建中... assign_generated_ipv6_cidr_block: "" => "false" cidr_block: "" => "172.30.0.0/16" default_network_acl_id: "" => "<computed> " default_route_table_id: "" => "<computed> " default_security_group_id: "" => "<computed> " dhcp_options_id: "" => "<computed> " enable_classiclink: "" => "<computed> " enable_dns_hostnames: "" => "false" enable_dns_support: "" => "true" instance_tenancy: "" => "default" ipv6_association_id: "" => "<computed> "ipv6_cidr_block: "" => "<computed> " main_route_table_id: "" => "<computed> tags.%: "" => "1" tags.Name: "" => "default" aws_vpc.default: 创建完成 (ID: vpc-XXXXXXXX) aws_internet_gateway.gw: 正在创建... vpc_id: "" => "vpc-XXXXXXXX" aws_security_group.ec2_terraform_test: 正在创建... description: "" => "由 Terraform 管理" egress.#: "" => "1" egress.482069346.cidr_blocks.#: "" => "1" egress.482069346.cidr_blocks.0: "" => "0.0.0.0/0" egress.482069346.from_port: "" => "0" egress.482069346.ipv6_cidr_blocks.#: "" => "0" egress.482069346.prefix_list_ids.#: "" => "0" egress.482069346.protocol: "" => "-1" egress.482069346.security_groups.#: "" => "0" egress.482069346.self: "" => "false" egress.482069346.to_port: "" => "0" ingress.#: "" => "1" ingress.2541437006.cidr_blocks.#: "" => "1" ingress.2541437006.cidr_blocks.0: "" => "0.0.0.0/0" ingress.2541437006.from_port: "" => "22" ingress.2541437006.ipv6_cidr_blocks.#: "" => "0" ingress.2541437006.protocol: "" => "tcp" ingress.2541437006.security_groups.#: "" => "0" ingress.2541437006.self: "" => "false" ingress.2541437006.to_port: "" => "22" name: "" => "ec2_terraform_test" owner_id: "" => "<computed> " vpc_id: "" => "vpc-XXXXXXXX" aws_subnet.public_subnet_a: 创建中... assign_ipv6_address_on_creation: "" => "false" availability_zone: "" => "ap-northeast-1a" cidr_block: "" => "172.30.16.0/20" ipv6_cidr_block_association_id: "" => "<computed> " map_public_ip_on_launch: "" => "false" vpc_id: "" => "vpc-XXXXXXXX" aws_internet_gateway.gw: 创建完成 (ID: igw-XXXXXXXX) aws_route_table.public_route: 正在创建... route.#: "" => "1" route.3460203481.cidr_block: "" => "0.0.0.0/0" route.3460203481.egress_only_gateway_id: "" => "" route.3460203481.gateway_id: "" => "igw-XXXXXXXX" route.3460203481.instance_id: "" => "" route.3460203481.ipv6_cidr_block: "" => "" route.3460203481.nat_gateway_id: "" => "" route.3460203481.network_interface_id: "" => "" route.3460203481.vpc_peering_connection_id: "" => "" vpc_id: "" => "vpc-XXXXXXXX" aws_subnet.public_subnet_a: 创建完成 (ID: subnet-XXXXXXXX) aws_route_table.public_route: 创建完成 (ID: rtb-XXXXXXXX) aws_route_table_association.public_route_a: 正在创建... route_table_id: "" => "rtb-XXXXXXXX" subnet_id: "" => "subnet-XXXXXXXX" aws_route_table_association.public_route_a: 创建完成 (ID: rtbassoc-XXXXXXXX) aws_security_group.ec2_terraform_test:创建完成(ID:sg-XXXXXXXX) aws_instance.terraform-test:正在创建... ami:"" => "ami-cbf90ecb" associate_public_ip_address:"" => "true" availability_zone:"" => "<computed> " ebs_block_device.#: "" => "<computed> " ephemeral_block_device.#: "" => "<computed> " instance_state: "" => "<computed> " instance_type: "" => "t2.micro" ipv6_addresses.#: "" => "<computed> key_name: "" => "XXXXXXXX" network_interface_id: "" => "<computed> “ placement_group: “” => “<computed> " private_dns: "" => "<computed> " private_ip: "" => "<computed> " public_dns: "" => "<computed> " public_ip: "" => "<computed> " root_block_device.#: " => "1" root_block_device.0.delete_on_termination: " => "true" root_block_device.0.iops: " => "<computed> " root_block_device.0.volume_size: "" => "8" root_block_device.0.volume_type: "" => "gp2" security_groups.#: "" => "<computed> " source_dest_check: "" => "true" subnet_id: "" => "subnet-XXXXXXXX" tags.%: "" => "1" tags.Name: "" => "terraform-test" tenancy: "" => "<computed> " vpc_security_group_ids.#: "" => "1" vpc_security_group_ids.766820655: "" => "sg-XXXXXXXX" aws_instance.terraform-test: 仍在创建...(已耗时 10 秒) aws_instance.terraform-test: 仍在创建...(已耗时 20 秒) aws_instance.terraform-test: 创建完成(ID:i-XXXXXXXXXXXXXXXX) 应用完成!资源:新增 7 个,更改 0 个,销毁 0 个。您的基础设施状态已保存到以下路径。修改和销毁您的基础设施需要此状态,因此请妥善保管。要查看完整状态,请使用 `terraform show` 命令。 状态路径: 输出: 公网 IP = XX.XX.XXX.XX

是的,输出部分返回了所创建实例的公网 IP 地址。
我们来检查一下管理控制台中是否真的创建了该实例。

……成功了(太棒了)

同时,一个名为“terraform.tfstate”的文件会被静默创建。
该文件以JSON格式存储“当前基础设施状态”
,Terraform会将此文件与定义文件(.tf文件)以及资源的实际状态进行比较,以
确定要对目标资源执行哪些操作,例如创建、修改或删除。
换句话说,如果此文件中存在不一致,则无法正常部署,因此
在处理此文件时需要格外小心。

■概要

我尝试使用 Terraform 来“编写基础设施配置代码”。
编写的代码可读性很高,基本命令也很简单易用,所以我感觉它很容易上手。
这些工具经常与 DevOps 一词一起提及。
如果开发人员和运维人员要共同合作,防止系统变成黑匣子,
我非常希望能好好利用这个工具!
我不会告诉你这篇文章的篇幅比知识部分还要长。
就这些了,谢谢阅读!

如果您觉得这篇文章有用,请点击【点赞】!
0
加载中...
0票,平均分:0.00/10
2,711
X Facebook Hatena书签 口袋

这篇文章的作者

关于作者

寺冈由纪

他于 2016 年加入 Beyond 公司,目前已是第六年
担任 MSP(托管服务提供商)的基础设施工程师,负责故障排除以及
使用 AWS 等公有云设计和构建基础设施。近期,他开始

使用 HashiCorp 的 Terraform 和 Packer 等工具,用于构建和运维
Docker 和 Kubernetes 等容器基础设施的自动化流程。此外,他还扮演着技术推广者的角色,经常在外部学习小组和研讨会上发表演讲。

・GitHub
https://github.com/nezumisannn

・讲述历史
https://github.com/nezumisannn/my-profile

・演示材料(SpeakerDeck)
https://speakerdeck.com/nezumisannn

・认证:
AWS认证解决方案架构师 - 助理级、
Google Cloud专业云架构师