仕事で時々terraformを書くんですが割と雰囲気で触ってるので自分の中で怪しい概念とかを勉強しなおしました
基本的には公式ドキュメントの翻訳メモです
英語のドキュメント読みながら書いたけどもしかしたらおれが探してないだけで日本語対応してるのかも
Terraformについて
Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.
Introduction - Terraform by HashiCorp
terraformはインフラをコード管理するためのツール
コードで管理できるのでgitによるversion管理やレビューも行えるし、同じ構成のインフラを作りたい時は既存のものを使い回すのも簡単
AWS、Azure、GCPはもちろん、多くのサービスに対応している
terraformではそれらのサービスをproviderと呼ぶ
ざっと数えた感じ使用可能なproviderの数は100は余裕で超えてそう
以下は全部AWSについて書いていく (AWSしか使ったことない)
resourceとdata source
resource
resourceは名前の通り、VPCやEC2インスタンスなどのAWSリソースを定義する
以下のtfファイルを作成しterraform applyすると、実際にAWS上にEC2インスタンスが作成される
resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}
data source
data sourceはterraform内で利用可能なデータを定義する
実際にAWS上にあるデータをfetchしてきて使うことも可能
リソース自体はterraform管理したくないけど、そのリソースを参照して新たなAWSリソースをterraformで定義したい時などに使う
以下のtfファイルを作成しterraform applyすると、terraform内で使えるamiのデータが作成されるがAWSリソースは作成されない
その後このdata resourceのidを使ってEC2インスタンスを立てるなどして利用する
data "aws_ami" "example" {
most_recent = true
owners = ["self"]
tags = {
Name = "app-server"
Tested = "true"
}
}
terraformはdata sourceのREADのみ可能
そもそもAWSリソースとして存在しないため、resourceのようにCREATE、UPDATE、DELETEを行うことはできない
module
A module is a container for multiple resources that are used together.
Modules - Configuration Language - Terraform by HashiCorp
moduleは一緒に使われるリソースをまとめたコンテナ
全てのterraform configurationはroot moduleというmoduleを持っていて、その中にはmain working directory (おそらくterraform initをしたdirectory)にあるtfファイルに定義されたリソースが含まれている
moduleの呼び出しは、moduleの中身のリソースをconfigurationに追加することを意味する
moduleを呼び出す際には、呼び出すmoduleのpathを指定するsourceと、呼び出しの際に使用するinput valueを宣言する
以下のtfファイルは、./app-cluster配下のterraformリソースをserversという変数と共に呼び出す
module "servers" {
source = "./app-cluster"
servers = 5
}
module内のリソースはカプセルかされているため、リソースの要素 (id, nameなど)に直接アクセスすることはできない
アクセスするためには、module内でoutput valueを宣言しておく必要がある
以下のtfファイルでは/app-cluster/ec2.tfで定義したEC2インスタンスのidをinstance_idという名前でoutput valueとして宣言している
メインのworking directoryにあるelb.tfファイルでは、module.servers.instance_idという呼び方でoutputしたinstance_idにアクセスしている
#./app-cluster/ec2.tf
resource "aws_instance" "web" {
ami = "ami-a1b2c3d4"
instance_type = "t2.micro"
}
output "instanse_id" {
value = aws_instance.web.id
}
#./elb.tf
resource "aws_elb" "example" {
# ...
instances = module.servers.instance_id
}
remote state
terraformでは、localにterraform.tfstateというファイルを作成し、そこに実際のprovider上のリソースの状態を記録している
tfstateとworking directoryを比較することで、新たに作成されたリソースや変更の加わったリソースを検知し、providerのAPIをcallしてその差分を反映している
一人で開発している分にはいいが、チーム開発していると誰かが新たにリソースを作ったらちゃんと自分のtfstateにも反映しないと誤って消してしまうことがあるのでなかなか面倒
remote stateは、AWSであればS3などのremote datastoreにtfstateを置いておくことができる
そのため自分のローカルのtfstateを最新にしなくてもterraform planやterraform applyの際にremote stateを参照するだけで意図しない変更を防ぐことができる
また、remote stateでは変数のoutputをすることができるため、workspaceをまたいだ変数の参照を行うことができる
例えば社内のインフラチームがremote stateを利用した上でVPCやSubnetの設定を行い、VPC ID等をoutputしておく
社内の別チームが、会社のVPC内に新たにリソースを作成したい場合はインフラチームのworkspaceのremote stateからVPC IDを参照して開発を行うことができる
data "terraform_remote_state" "vpc" {
backend = "remote"
config = {
organization = "hashicorp"
workspaces = {
name = "vpc-prod"
}
}
}
# Terraform >= 0.12
resource "aws_instance" "foo" {
# ...
subnet_id = data.terraform_remote_state.vpc.outputs.subnet_id
}
ちなみにremote state側でのoutputと、resource側での参照を一度にapplyすることはできないらしい
まとめ
基本的には作りたいリソース、行いたい変更を宣言的に記述していくだけなので難しくはない (はず)
ハマりポイントとしては変数の渡し方や参照方法でつまづくことが個人的には多かったのできちんと仕組みを理解してコードを書けばいける (はず)
tfstateがわかればremote stateも怖くない (はず)