使用terraform来管理现有的AWS基础架构
太长不看。
-
- 既存awsインフラを手動でterraformに起こすのは大変面倒くさい.
-
- dtan4氏によるterraformingはimport作業をある程度自動化するが,問題もある.
- terraformingを改造/手でファイルを書く を併用してimport作業を進めると良いと思う.
使用”terraform import”命令将现有的基础设施导入到terraform中。
-
- tfファイルに既存インフラの設定を記述する
- tfstateに既存インフラの状態を追加する
需要这件事。
在AWS上将现有的基础架构纳入terraform的管理下,有几种方法。以下是对这些方法的整理。
前提 – 前提条件
-
- Terraform v0.11.11+ provider.aws v2.0.0
- AWS上には,terraform管理下に置くべきインフラとそうでないインフラが混在している.
目标
将部署在AWS上的生产环境基础架构转化为代码形式,
-
- 使维护更加容易。
- 将本正式环境(production environment)搭建成与之相同配置的环境(测试环境)尽快完成。
用手动方法执行的方式
以设置vpc为例,考虑将其放入terraform中。有关aws_vpc设置的详细信息,请参阅官方文档。
准备一个tf文件的模板。
创建一个合适的tf文件(在这里称为vpc.tf),首先只需描述设置的框架。
# resource "awsサービス名" "名前(aws上の名前と一致している必要はない)"
resource "aws_vpc" "first_vpc" {
}
如果有多个目标,请准备相应数量的外壳。
导入状态到tfstate。
在这里我们使用Terraform提供的terraform import命令。
基本格式是terraform import <服务名称>.<资源名称> <在AWS上的ID>。
每个服务的import命令的详细信息也可以在官方文档中找到,请参考。例如,对于之前提到的VPC,
terraform import aws_vpc.first_vpc vpc-xxxxxxxxx
这样,first_vpc已经被导入到tfstate中了。
一边查看diff,一边编写tf文件。
terraform import命令可以自动生成tfstate文件,但是tf文件必须手动编写。我们将参考官方文档和AWS控制台,填写vpc.tf文件。首先可以适当地复制粘贴示例用法来创建文件。
在写了一部分之后,我们执行terraform plan命令。这样,将显示当前tf文件与AWS上的配置之间的差异。我们需要在tf文件中添加项目,以将差异降低到零。
------------------------------------------------------------------------
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
~ aws_vpc.first_vpc
tags.%: "2" => "0"
tags.Name: "first-vpc" => ""
tags.STATUS: "IN USE" => ""
Plan: 0 to add, 1 to change, 0 to destroy.
------------------------------------------------------------------------
当tf文件中准确描述了所有项目,并且与AWS上的配置没有任何差异时,执行terraform plan的结果应该是一致的。
------------------------------------------------------------------------
No changes. Infrastructure is up-to-date.
This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
在中文中,它会显示为“。通过这样做,AWS 基础架构已经纳入Terraform的管理范围内。”
使用地球創造法的方式
最初的方法是对每个基础架构手动编写tf文件,并执行terraform import命令以创建tfstate。 如果基础架构数量只有10个左右,可以通过这种方法来勉强应对,但是当数量增多时,手动编写tf文件将变得非常困难。
因此,我们将使用dtan4提供的terraforming工具。terraforming可以自动化创建tf文件并批量创建tfstate文件,是一款出色的工具。
比如创建aws_vpc的tf文件,可以使用以下命令完成。terraforming的输出将在标准输出上进行,因此当需要将其输出到文件时,请进行重定向。
terraforming vpc > vpc.tf
创建tfstate文件可以使用以下命令进行。
# ---------ディレクトリ内にterraform.tfstateがない場合-----------
terraforming vpc --tfstate > terraform.tfstate
terraform refresh
# ---------ディレクトリ内に既にterraform.tfstateが存在する場合-----------
terraforming vpc --tfstate --merge=terraform.tfstate > tmp.tfstate
cp tmp.tfstate terraform.tfstate
rm tmp.tfstate
terraform refresh
只需要这个操作就可以完成整个VPC的导入。我相信你会发现,与手动编写tf文件并逐个执行terraform import相比,这种方法简单得令人惊讶。
改造terraforming。
我发现使用 terraforming,可以在瞬间创建 tf 文件和 tfstate 文件。但是这并不意味着可以轻松将现有基础设施迁移到 AWS。
在对行星进行地形改造时存在一些问题。
terraform planすると一部のサービスでtfstateとの差分が残る.dtan4/terraformingの最終コミットが2018年9月であり,一部のサービスについてterraform本家のバージョンアップについていけていない.
例)nat_gatewayおよびelastic_ipでtfファイルにtagsが出力されない.
対象アカウント・対象サービスのすべての設定を取り込むため,terraform管理下におく設定項目を自分で指定することができない.
referenceがID直書きされているため,出力されたtfファイルを使って新しい環境にインフラを立てることはできない.
例)internet_gatewayはvpcを参照するが,terraformingで出力されるtfファイルにはvpc_idが直接数字で書き出される.新しい環境にvpcを立てるときは当然vpc_idが異なるので,internet_gatewayからvpcを参照することができない.
因此,我们将改进terraforming以应对这些问题。从dtan4 / terraforming的fork库中可以看出,似乎有一些人已经进行了自己的修改并使用了它。
在着手改造之前,首先让我们来看看地球改造在内部做了什么。
首先,我们来看erb文件。在terraforming/lib/terraforming/resource/目录下,有每个服务对应的rb文件。而在terraforming/lib/terraforming/template/tf/目录下,有每个服务对应的erb文件。
比如,vpc.erb的内容如下所示。
<% vpcs.each do |vpc| -%>
resource "aws_vpc" "<%= module_name_of(vpc) %>" {
cidr_block = "<%= vpc.cidr_block %>"
enable_dns_hostnames = <%= enable_dns_hostnames?(vpc) %>
enable_dns_support = <%= enable_dns_support?(vpc) %>
instance_tenancy = "<%= vpc.instance_tenancy %>"
tags {
<% vpc.tags.each do |tag| -%>
"<%= tag.key %>" = "<%= tag.value %>"
<% end -%>
}
}
<% end -%>
从这个形式上来看,可以知道erb文件中写有每个服务的tf文件输出。对于存在问题的服务的tf文件输出,可以考虑编辑erb文件。
接下来,让我们来查看terraforming/lib/terraforming/resource/目录中的rb文件。作为示例,我们将查看vpc.rb文件。在文件的某一部分,有关于tfstate的描述。
def tfstate
vpcs.inject({}) do |resources, vpc|
attributes = {
"cidr_block" => vpc.cidr_block,
"enable_dns_hostnames" => enable_dns_hostnames?(vpc).to_s,
"enable_dns_support" => enable_dns_support?(vpc).to_s,
"id" => vpc.vpc_id,
"instance_tenancy" => vpc.instance_tenancy,
"tags.#" => vpc.tags.length.to_s,
}
resources["aws_vpc.#{module_name_of(vpc)}"] = {
"type" => "aws_vpc",
"primary" => {
"id" => vpc.vpc_id,
"attributes" => attributes
}
}
resources
end
end
在进行terraform环境配置时,似乎直接编写JSON文件而不是使用terraform import来创建tfstate。
实际上,没有必要在terraforming中认真制作tfstate的JSON。因为只要具有服务类型和ID等最基本的信息,就可以通过terraform refresh自动修复terraform.tfstate。
要使用Ore Ore永久更改地貌技術
使用rbenv命令切换到不同版本的Ruby,并通过gem安装自定义的terraforming。在terraforming存储库中执行以下命令。
rbenv local <適当なバージョン>
gem uninstall terraforming
gem build terraforming.gemspec
gem install terraforming-0.16.0.gem
填补tf文件中的缺失项目
一旦简要地查看完terraforming的内部情况后,开始进行必要的改造以使terraforming正常运作。
首先,在导出tf文件的时候,添加缺失的项目。举例来说,我们将对nat_gateway进行改进。在原始的terraforming代码中,tags没有被输出到tf文件中,导致在运行terraform plan时会提示有差异。因此,在nat_gateway.erb文件中添加以下代码来补充tags。
<% nat_gateways.each do |nat_gateway| -%>
<% unless nat_gateway.nat_gateway_addresses.empty? -%>
resource "aws_nat_gateway" "<%= module_name_of(nat_gateway) %>" {
allocation_id = "<%= nat_gateway.nat_gateway_addresses[0].allocation_id %>"
subnet_id = "<%= nat_gateway.subnet_id %>"
#----追記ここから----
tags {
<% nat_gateway.tags.each do |tag| -%>
"<%= tag.key %>" = "<%= tag.value %>"
<% end -%>
}
#----追記ここまで----
}
<% end -%>
<% end -%>
好像是从AWS SDK中获取实例相关信息。需要边查阅AWS SDK的文档边撰写。
注)<% ~~~ -%>是用于执行用括号括起来的Ruby代码的语法。当将结果作为文本输出时,请使用<%= ~~~ %>。
将tf文件中的id转换为参考形式。
如果只是将现有基础设施转换为代码进行管理,直接在代码中使用id进行引用写入也没有什么问题。但是,如前所述,如果要在另一个环境中建立相同的配置,则需要将tf文件中的引用转换为引用形式。引用形式的示例如下(参考:https://learn.hashicorp.com/terraform/getting-started/dependencies)。
vpc_id = "vpc-xxxxxxxx" #id直書きされた形
vpc_id = "${aws_vpc.example-vpc.id}" #参照形
我們以VPC為例來解釋。為了解決VPC的ID參考形式,我們準備了Terraforming :: Resource :: VPC的name函數。
module Terraforming
module Resource
class VPC
include Terraforming::Util
def self.name(id, client: Aws::EC2::Client.new)
self.new(client, []).name(id)
end
def name(id)
v = vpcs.select { |e| e.vpc_id==id }
if v.length > 0
"${aws_vpc.#{module_name_of(v[0])}.id}"
else
id
end
end
end
end
end
在erb文件中输出vpc_id的方法如下。(下面的示例是将subnet的vpc_id转换为引用形式。)
vpc_id = "<%= Terraforming::Resource::VPC.name(subnet.vpc_id) %>"
您可以使用相同的方法将ID转换为参考形式,适用于其他服务。
改造地球的示例-地貌改造
對於土地改造的觀點
-
- tfファイル出力項目の修正
- id直書きを参照形にする
我在GitHub上发布了进行改造的terraforming存储库。我所修改的AWS服务如下:
-
- application load balancer
-
- auto scaling group
-
- auto scaling schedule(新設)
-
- ec2 instance
-
- elastic ip
-
- internet gateway
-
- launch configuration
-
- nat gateway
-
- route table
-
- security group
-
- subnet
- vpc
由于我只对使用了terraforming的范围进行了修改,所以即使使用了上述提到的服务,仍可能存在差异。
整理
使用terraform将现有的基础设施纳入管理需要创建tf和tfstate文件,手动操作非常繁琐。我介绍了使用terraforming可以稍微简化一些工作量,但仍存在一些问题,因此我介绍了修改terraforming的方法。
目前来说,将AWS现有基础设施轻松转移到terraform管理下的方法可能还不存在。如果目标基础设施较小,可以手动操作;如果较大,可能需要改造terraforming,逐步完善tf文件和tfstate文件。
terraform 是一个使用广泛的自动化工具,我们认为由于每个人并不会频繁进行现有基础架构的导入工作,因此在这方面自动化的动力较弱,很难出现一个万能的自动化工具。我们是否能期待有一天 hashicorp 推出官方的工具呢?
完成tf和tfstate这两个方面的讨论后,我们将在另一篇文章中进行详述。