Terraform
参考
クラウド上のリソースを定義ファイルの状態になるように生成・操作するツール
インフラストラクチャ定義ツールと呼ばれるもの
これでインスタンスを必要な時に立ち上げられる
ずっとお金を取られる不安に駆られない
$ terraform apply一発でサーバを作成できる
まだツールの更新が激しいので,古い情報もある.以下などを参照
準備
AWS IAMを使ってユーザを作成し,AWSのアクセスキーと秘密鍵を取得しておく 導入
Mac: $ brew install terraform
Windowsなど: ここから適切なインストーラを落とし,binにパスを通す code: bash
$ terraform version
Terraform v0.12.5
テンプレートファイル
terraformは*.tf形式のファイルをHCL(HashiCorp Configuration Language)で書く
JSONと互換性があるが,より可読性の高いDSLである.
code: bash
$ mkdir terraform-test
$ cd terraform-test
$ touch main.tf
プロバイダー
TerraformはAWSだけでなくHerokuやGoogleCloudなど多くのクラウドインフラに対応
まずはどのプロバイダーを使うのかを宣言する
code: main.tf
provider "aws" {
access_key = "ACCESS_KEY"
secret_key = "SECRET_KEY"
region = "ap-northeast-1"
}
クレデンシャル情報の切り出し
上記のACCESS_KEYのようなクレデンシャル情報は環境変数に入れる.
access_key -> AWS_ACCESS_KEY_ID
secret_key -> AWS_SECRET_ACCESS_KEY
region -> AWS_DEFAULT_REGION
の環境変数を設定することで,自動で読み込んでくれる.
code: main.tf
provider "aws" {
}
このとき,.gitignoreした次のようなcredentials.shを用意しておくといいかも
code: credentials.sh
export AWS_ACCESS_KEY_ID=your_access_key
export AWS_SECRET_ACCESS_KEY=your_secret_key
export AWS_DEFAULT_REGION=your_region
code: .gitignore
credentials.*
aws configureで設定されているクレデンシャル情報を読み取ってもくれる
また,divenvというディレクトリごとに環境変数を切り替えるツールもある このようにプロバイダを設定した後,terraformがAWSプロバイダを扱えるようにする.
code: bash
$ terraform init
$ terraform --version
Terraform v0.12.5
+ provider.aws v2.22.0
変数
tfファイルでの変数の扱い方
variable "variable_name" {}: 変数の宣言
variable "variable_name" {default = "default_value"}: デフォルト値
var.variable_name: 変数の参照
"${var.variable_name}": 変数の文字列内展開
デフォルト値はMap(辞書みたいなもの)によって指定することもできる.
code: main.ts
variable "images" {
default = {
us-east-1 = "ami-abcdefgh"
us-west-2 = "ami-12345678"
...
}
...
hoge-attr = var.images.ap-northeast-1
...
tfファイルへの変数の渡し方
コマンドオプション
code: bash
$ terraform apply -var 'variable_name=variable_value'
環境変数
TF_VARで始まる環境変数を自動読み込み
code: bash
$ export TF_VAR_variable_name="variable_value"
terrarofm.tfvarsファイル(推奨)
このファイル名だと自動読み込み
コマンドオプション -var-fileでも読み込み可能
code: terraform.tfvars
variable_name = "variable_value"
外部ファイル
code: main.tf
variable_name = "${file("./variable_value.json")}"
template = "${file("${path.module}/kms_policy.json")}"
リソース
resource "resource_type" "resource_name" {configs = "config_value"}
resource_type: リソースの種類,aws_*といったもの
resource_name: リソースの名前,任意,わかりやすい名前
code: main.tf
resource "aws_vpc" "myVPC" {
cidr_block = "10.1.0.0/16"
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_host_names = "false
tags {
Name = "myVPC"
}
}
これがEC2ならamiやinstance_typeといった設定項目になる
リソースごとの設定項目は公式のリソースごとのArgument Referenceを参照 他リソースの属性の参照
code: main.tf
resource "aws_internet_gateway" "myGW" {
vpc_id = "${aws_vpc.myVPC.id}" # myVPCのid属性を参照
}
リソースごとの参照可能属性は公式のAttributes Referenceを参照 依存関係の明示
属性の参照先のリソースは,参照元より先に存在する必要がある
そのような依存関係は自動的に解決されるが,明示することができる
code: main.tf
resource "aws_internet_gateway" "myGW" {
vpc_id = "${aws_vpc.myVPC.id}"
depends_on = "${aws_vpc.myVPC}" # myVPCへの依存を明示
}
アウトプット
EC2インスタンスのパブリックIPなど,リソースに割り当てられた属性値をコンソール出力
code: main.tf
output "public ip of cm-test" {
value = "${aws_instance.cm-test.public_ip}"
}
public ip of cm-test = 52.69.227.18のように出力される
リソースの作成
Dry-Run
テンプレートに誤りがないかと,意図したリソースが作成されるか(実行計画)を確認
code: bash
$ terraform plan
結果の見方
+: 新規に作成されるリソース
-: 削除されるリソース
-/+: リソースの削除と再作成
~: 設定変更されるリソース
存在しないVPCのIDや選択不可なAMIを選んでもDryRunではエラーにならないので注意
そういったエラーはterraform applyで初めて発覚する
実際にテンプレートを適用
code: bash
$ terraform apply
terraform.tfstateというリソースの状態を示すJSONファイルが作成される.
テンプレートの更新があれば共に更新される.
結果の確認
terraform.tfstateの内容を整形して表示
$ terraform show
特定のoutputのみを表示
$ terraform output "public ip of cm-test"
リソースの変更
例えば,InboundのHTTP(80)を解放し,EC2のインスタンスタイプをm3.mediumにする
code: main.tf
...
resource "aws_security_group" "admin" {
...
# HTTPのInbound通信を許可
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
}
...
}
...
resource "aws_instance" "cm-test" {
...
# インスタンスタイプをm3.mediumに変更
instanc_type = "m3.medium"
...
...
code: bash
$ terraform plan
$ terraform apply
リソースの削除
テンプレートファイルにあるリソース一式の削除
code: bash
$ terraform destroy
削除の実行計画
code: bash
$ terraform plan -destroy
実行確認のメッセージが表示されるので,yesを返すと実行
テンプレートファイルの分割
terraformでは,.tfのファイルを自動的にテンプレートとして認識する
main.tf: リソースの定義を記述
variables.tf: 変数の定義を記述
outputs.tf: アウトプットの定義を記述
のようにテンプレートファイルを分割できる.
実践
EC2インスタンス2台を作成
EC2インスタンスの名前を変更
EC2インスタンスを1台にする
EC2インスタンスのもう一台を削除
code: variables.tf
provider "aws" {
version = "~> 2.0"
region = "ap-northeast-1"
}
code: ec2.tf
resource "aws_instance" "sandbox" {
count = 2
ami = "ami-785c491f" # Ubuntu 16.04 LTS official ami
instance_type = "t2.micro"
tags = {
Name = "${format("sandbox-%02d", count.index + 1)}"
}
}
terraform v0.11 までは tags に = は不要でしたが、v0.12 より一部の構文が変更となり tags = と書くようになりました
code: bash
$ source credentials.sh
$ terraform init
$ terraform plan
... Plan: 2 to add, 0 to change, 0 to destroy. ...
$ terraform apply
... Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
https://gyazo.com/027578cf364b8c87598c000ed89d10f3
生成されたインスタンスの確認
code: bash
$ terraform show
resource "aws_instance" "sandbox" {
ami = "ami-785c491f"
arn = "arn:aws:ec2:ap-northeast-1:...
...
resource "aws_instance" "sandbox" {
ami = "ami-785c491f"
arn = "arn:aws:ec2:ap-northeast-1:...
...
名前の変更
code: ec2.tf
Name = "${format("modified sandbox-%02d", count.index + 1)}"
code: bash
$ terraform plan
...
~ tags = {
~ "Name" = "sandbox-02" -> "modified sandbox-02"
}
...
$ terraform apply
... Apply complete! Resources: 0 added, 2 changed, 0 destroyed.
https://gyazo.com/4d3f53d2136f480f1ccbbb10ec5d9cea
EC2サーバを1台にする
code: ec2.tf
count = 1
code: bash
$ terraform plan
... # aws_instance.sandbox1 will be destroyed ... ... Plan: 0 to add, 0 to change, 1 to destroy. ...
$ terraform apply
... Apply complete! Resources: 0 added, 0 changed, 1 destroyed.
https://gyazo.com/a549cd6c820abbdd0edf675f60d69acc
作成したリソースの全削除
code: bash
$ terraform destroy
... Plan: 0 to add, 0 to change, 1 to destroy. ...
Destroy complete! Resources: 1 destroyed.
https://gyazo.com/8218d60854590e4018002f23b39bf608
terraform showしても何も出ない
その他
イコールの位置を自動修正
terraform fmt
組み込み関数
挙動を確認するには,terraform consoleでreplを使える
code: bash
$ terraform console
coalesce("","hoge")
hoge
coalesce("fuga","hoge")
fuga
coalesce(string1, string2, ...) - Returns the first non-empty value from the given arguments. At least two arguments must be provided.
lifecycleブロックを使う
リソースの差分を無視
リソースを新しく生成してから古いのを削除
うっかり削除の保護
モジュール化
条件分岐
バージョンアップ
より詳細なテンプレート設定の例
Provider: AWS,クレデンシャル,リージョン
VPC: AWSコンポーネントのベースになるネットワーク空間
Subnet: VPCをさらに分割したもの
Internet Gateway: 外部インターネットとの出入り口
Route Table: Subnetとインターネットゲートウェイなどを繋げる
Security Group: アクセスの制限を管理する
EC2: 仮想サーバ
以上の設定を行う例
code: main.tf
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {
default = "ap-northeast-1"
}
variable "images" {
default = {
us-east-1 = "ami-abcdefgh"
us-west-2 = "ami-12345678"
...
}
provider "aws" {
access_key = "${var.aws_access_key}"
secret_key = "${var.aws_secret_key}"
region = "${var.region}"
}
resource "aws_vpc" "myVPC" {
cidr_block = "10.1.0.0/16"
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_hostnames = "false"
tags {
Name = "myVPC"
}
}
resource "aws_internet_gateway" "myGW" {
vpc_id = "${aws_vpc.myVPC.id}"
}
resource "aws_subnet" "public-a" {
vpc_id = "${aws_vpc.myVPC.id}"
cidr_block = "10.1.1.0/24"
availabirity_zone = "ap-northeast-1a"
}
resource "aws_route_table" "public-route" {
vpc_id = "${aws_vpc.myVPC.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.myGW.id}"
}
}
resource "aws_security_group" "admin" {
name = "admin"
description = "Allow SSH inbound traffic"
vpc_id = "${aws_vpc.myVPC.id}"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
}
}
resource "aws_instance" "cm-test" {
ami = "${var.images.ap-northeast-1}"
instance_type = "t2.micro"
key_name = "cm-yawata.yutaka"
vpc_security_group_ids = [
"${aws_security_group.admin.id}"
]
subnet_id = "${aws_subnet.public-a.id}"
associate_public_ip_address = "true"
root_block_device = {
volume_type = "gp2"
volume_size = "20"
}
ebs_block_device = {
device_name = "/dev/sdf"
volume_type = "gp2"
volume_size = "100"
}
tags {
Name = "cm-test"
}
}
output "public ip of cm-test" {
value = "public ip of cm-test: ${aws_instance.cm-test.public_ip}"
}
DryRunの結果
code: bash
$ terraform plan
Refreshing Terraform state prior to plan...
...
+ aws_instance.cm-test
ami: "" => "ami-cbf90ecb"
associate_public_ip_address: "" => "1"
availability_zone: "" => "<computed>"
ebs_block_device.#: "" => "1"
ebs_block_device.2659407853.delete_on_termination: "" => "1"
ebs_block_device.2659407853.device_name: "" => "/dev/sdf"
ebs_block_device.2659407853.encrypted: "" => "<computed>"
ebs_block_device.2659407853.iops: "" => "<computed>"
ebs_block_device.2659407853.snapshot_id: "" => "<computed>"
ebs_block_device.2659407853.volume_size: "" => "100"
ebs_block_device.2659407853.volume_type: "" => "gp2"
ephemeral_block_device.#: "" => "<computed>"
instance_type: "" => "t2.micro"
key_name: "" => "cm-yawata.yutaka"
...
+ aws_internet_gateway.myGW
vpc_id: "" => "${aws_vpc.myVPC.id}"
...
Plan: 7 to add, 0 to change, 0 to destroy.
applyの結果
code: bash
aws_vpc.myVPC: Creating...
cidr_block: "" => "10.1.0.0/16"
default_network_acl_id: "" => "<computed>"
default_security_group_id: "" => "<computed>"
dhcp_options_id: "" => "<computed>"
...
Apply complete! Resources: 7 added, 0 changed, 0 destroyed.
...
Outputs:
public ip of cm-test = 52.69.227.18
showの結果
code: bash
aws_instance.cm-test:
id = i-e9860e1b
ami = ami-cbf90ecb
associate_public_ip_address = true
availability_zone = ap-northeast-1a
...
Outputs:
public ip of cm-test = 52.68.24.133