Terraform(テラフォーム)について
最近、Terraformを利用する機会があったので、調査した内容を記載します。
Terraformとは
Terraformは、IaCを実現するツールです。Terraformはオープンソースであり、HashiCorpによってGo言語で開発されました。具体的にはTerraformではインフラの構成をコードで宣言します。構造化された構成ファイルでは、手動で操作することなくインフラ構成を自動で管理できます。インフラの初期プロビジョニング、更新、破棄、いずれもTerraformではコードにより宣言し、実行します。
公式サイト
インフラのコード化のデメリット
- 小規模なものであれば、CUIやクラウドの提供するマネージメントコンソールでのGUI操作で構築してしまった方が早く、IaCで構築する方が時間がかかってしまう
- きちんと保守性を考えた設計し、運用手法やルールをチームで共有していかなければ、ただ管理する範囲が増えるだけで終わってしまう
インフラのコード化のメリット
- ソースコードでインフラを管理することで、VCSなりCI/CDといったソフトウェア開発のシステムをインフラにも適用でき、保守性やスケーラビリティを高めることができる
- デメリットの裏返しで、多リージョン、多環境への対応が必要なシステムであれば、複製や再利用しやすいためコード化してしまった方が素早く正確に環境を用意できる
インストール
以下に従って OS 毎のビルド済みバイナリをダウンロードしてきてパスを通すだけ
https://www.terraform.io/intro/getting-started/install.html
Mac の場合
Homebrew でインストールできます
Homebrewでインストール
$ brew update
$ brew install terraform
terraformのバージョン確認
$ terraform -v
Terraform v0.14.2
初期設定
作業ディレクトリ作成
$ mkdir <作業ディレクトリ名>
$ vim main.tf
provider "aws" {
access_key = "ACCESS_KEY_HERE"
secret_key = "SECRET_KEY_HERE"
region = "ap-northeast-1"
}
※以下の環境変数を設定しておくと、terraformコマンド実行時にクレデンシャル情報とリージョンが環境変数から自動的に読み込まれます。
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION
この場合、providerブロックには設定しなくとも動作します。
$ terraform init
Terraformの仕組み&記述方法
tfファイルというファイルをHCLで記述し、terraformコマンドを実行すると、Providerというmoduleが 指定したサービス上のリソースをAPI経由で作成/計画/実行し、その結果の状態をtfstateというファイルで保持する
Terraformでの変数の利用
変数の定義
Terraformでは、変数はvariableブロックで定義します。variable"<変数名>"{}のように、空のブロックを与えて変数を宣言します。
変数の値の参照
変数の値はvar.<変数名>で参照することができます。${}を使うと、文字列に変数の値を埋め込むことができます。
例)
variable "test" {}
provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
region = "ap-northeast-1"
}
値の渡し方
変数の値の参照 コメント
変数の値はvar.<変数名>で参照することができます。${}を使うと、文字列に変数の値を埋め込むことができます。
例)
variable "test" {}
provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
region = "ap-northeast-1"
}
パラメータの渡し方
コマンドオプションで渡す
terraform apply -var 'container_name=test4’
環境変数で渡す
TF_VAR_
のプレフィックスを付けて環境変数を設定すると、その値が変数にロードされる。TF_VAR_foo='env-test' terraform plan
を実行すると同様の結果が得られる。
ファイルで渡す
変数の値を指定する設定ファイルを作成し、-var-file
で指定すると同様のことが出来る。
vars.tfvars
foo = "tfvars-file"
terraform plan -var-file=vars.tfvars
を実行すると同様の結果が得られる。
ディレクトリ構成検討
Terraformはコードをどう配置するかというルールがありません。 だからといって自由にコードを書いてしまうと、コードを管理しづらくなったり、メンバーがコードの変更をキャッチアップしづらくなってしまいます。
Terraformのコードを扱うときは、どう管理するかというルールを定めることが大切です。 ここでは、Terraformにおけるディレクトリ構成の例を示します
├── examples
│ └── basic
│ └── main.tf [可能な限り] このモジュールのミニマムな使用例を書きます
│ 何も変更せずともこのbasicディレクトリで
│ terraform init && terraform apply が通るようにします
├── modules [必要に応じて] ネストしたモジュール(このモジュール内でのみ使用するモジュール)はここで宣言します
│ ├── master-nodes [必要に応じて]
│ └── worker-nodes [必要に応じて]
├── LICENSE [必要に応じて] 公開モジュールの場合は必ず置きましょう
├── README.md [必須] モジュールの概要と用途を書きます。場合によっては図を含めましょう
│ 使用例は examples/basic 以下に実際に動くコードとして書き、
│ ここからはリンクするにとどめたほうがよいです
├── variables.tf [必須] 何もvariableがない場合でも空のファイルを作ります。
│ またvariableのdescriptionは必ず書きます。
│ 単にリソースのargumentへ引き回しており自明に思えるときは
│ https://www.terraform.io/docs/providers/aws/r/instance.html#ami
│ などそのargument項目へのリンクを書くと良いです。
├── main.tf [必須] 基本的にはここへリソース宣言を置きます。
├── another.tf [必要に応じて] main.tfが長くなりすぎた場合はRoot Moduleと同じく
│ 種別ごとにtfファイルを分けて書きます。
└── outputs.tf [必須] 何もoutputがない場合でも空のファイルを作ります。
またoutputのdescriptionは必ず書きます。
単にリソースのoutputを引き回しており自明に思えるときは
https://www.terraform.io/docs/providers/aws/r/instance.html#id
などそのoutput項目へのリンクを書くと良いです。
tfstateファイルの保存場所
運用的にtfstateをローカルの環境に保存して置くのは良く無いのでs3などの共通の場所に保存する
- Terraformが認識しているリソースの状態をStateと言う。それを保持するファイルをtfstateファイルと言う
- バージョニング設定ありで、名前はなんでも構わない
- tfstateの置く場所を、Backendと呼ぶ
コマンド一覧
# 初期化
$ terraform init
# 構成をフォーマットして検証
$ terraform fmt
# dry-run
$ terraform plan
# 特定のリソースだけ実行
$ terraform plan -target={type.local-name01,type.local-name02}
# 実行
$ terraform apply
# 既存のインフラリソースをインポート
$ terraform import aws_vpc.vpc vpc-09a9f1827bfd851f4
※予め対応するリソースをtfファイルに記述しておく必要がある
デバッグ方法
$ TF_LOG=DEBUG terraform plan
文字列操作、演算子、組み込み関数
Terraform には、文字列操作やリスト操作、よくある処理が組み込み関数として提供されている。
文字列操作
文字列結合
main_api_gw = "app.api.${var.env_name}.mydomain.com"
演算子
四則演算
# integer
add = "${1 + 2 + 3}" # add = 6
sub = "${10 - 5}" # sub = 5
mul = "${2 * 5}" # mul = 10
div = "${10 / 5}" # div = 2
mod = "${11 % 3}" # mod = 2
# float
add_f= "${1.5 + 2.5 + 3.5}" # add_f = 7.5
sub_f = "${10 - 4.5}" # sub_f = 5.5
mul_f = "${2.5 * 5}" # mul_f = 12.5
div_f = "${12.5 / 5.0}" # div_f = 2.5
等価演算子
true_result_eq = "${1 == 1}" # true_result_eq = true
false_result_eq = "${1 == 2}" # false_result_eq = false
true_result_ne = "${1 != 2}" # true_result_ne = true
false_result_ne = "${1 != 1}" # false_result_ne = false
比較演算子
result_gt = "${10 > 10}" # result_gt = false
result_ge = "${10 >= 10}" # result_ge = true
result_lt = "${5 < 10}" # result_lt = true
result_le = "${10 <= 5}" # result_le = false
二値論理演算子
true_result_and = "${true && true}" # true_result_and = true
false_result_and = "${truel && false}" # false_result_and = false
true_result_or = "${true || false}" # true_result_or = true
fasle_result_or = "${false || false}" # false_result_or = false
true_result_unary = "${true && ! false}" # true_result_unary = true
false_result_unary = "${!(true || false)}" # false_result_unary = false
三項演算子
true_cond = "${true ? 10 : 0}" # true_cond = 10
false_cond = "${false ? 10 : 0}" # false_cond = 0
組み込み関数
cidrhost(iprange, hostnum)
iprangeで与えられたCIDRブロック内で有効なIPアドレスのうちhostnum番目のIPアドレスを返します。hostnumが負の値の場合は後ろから数えてIPアドレスを返します。
concat(list1, list2, …)
引数で与えられたlist型の値を全て結合したlistを返します。
contains(list, element)
listがelementを要素として含んでいる場合にtrueを返します。
element
listのindex番目の値を返します。list[index – 1]と同じ挙動です。ネストされたリストに対しては正常に振る舞いません。
format(format, args, …)
一般的なフォーマットストリングと同じ使い方です。Go言語のfmtパッケージにあるSprintf()を使って実装されているので、サポートされている記法もそれに準拠しています。
index(list, elem)
element(list, index)の逆で、listの中の指定された要素のindexを返します。ネストされたリストに対しては正常に振る舞いません。
join(delim, list)
listの要素をdelimを区切り文字にして結合した文字列を返します。ネストされたリストに対しては正常に振る舞いません。
length(list)
listの要素数を返します。ネストされているリストは1として数えられます。
list(items, …)
引数で受け取った値をlistにして返します。
lookup(map, key, [default])
mapの中でkeyに対応する値を返します。keyがない場合はエラーになりますが、defaultが定義されている場合はdefaultを返します。
map(key, value, …)
2N個の引数を受け取り、奇数番目の引数にそれぞれ次の引数の値を対応づけてmapを生成し返します。奇数番目のkeyになる引数は全てstring型でなければなりません。
merge(map1, map2, …)
2つ以上のmapを結合したmapを返します。keyが重複している場合、対応する値は順に上書きされていきます。
split(delim, string)
join(delim, list)とは逆にstringをdelimで区切った文字列のリストを返します。
timestamp()
関数がコールされたときのタイムスタンプを返します。実行の度に値が変わるので、リソースの作成日時をタグ付けしたい場合やリソースの更新日時を常に記録しておきたいような場合に有用です。
title(string)
引数のstringに含まれる全ての単語の頭文字を大文字にした文字列を返します。アルファベットと数字とアンダースコア(_)以外のアスキー文字は文章のセパレータとして認識され、セパレータの前後の文字列は全て別々の単語としてパースされます。
values(map)
引数のmapが持つkeyに対応する全ての値をリストにして返します。
ディスカッション
コメント一覧
まだ、コメントがありません