処理の流れとしては次のような感じ。
- GitHubにpushされるとCircleCI上で
terraform plan
が走る - そのPRがmasterにマージされると
terraform apply
が走る
Terraformのテンプレート
まずはdnsimpleプロバイダについて記述する。
variable "dnsimple_email" {} # 環境変数TF_VAR_dnsimple_email
variable "dnsimple_token" {} # 環境変数TF_VAR_dnsimple_token
provider "dnsimple" {
token = "${var.dnsimple_token}"
email = "${var.dnsimple_email}"
}
DNSimpleに接続する際のtokenとemailをTerraformに渡す必要があるのだけれど、それらはリポジトリに載せたくないので環境変数として渡すようにしておく。その際環境変数には接頭辞TF_VAR_が必要で、例えば環境変数TF_VAR_foo
は$var.foo
として参照する。
続けてwww.example.com
に対してexample.herokuapp.com
へのALIASレコードを設定する場合は次のように記述する。
resource "dnsimple_record" "example-com" {
domain = "example.com" # 設定する対象のドメイン
name = "www" # サブドメイン部分。ルートドメインなら空文字を渡す
value = "example.herokuapp.com" # ALIAS先。AレコードならIPアドレス、CNAMEなら転送先を渡す
type = "ALIAS" # レコードのタイプ
}
dnsimple_recordリソースにはほかにもTTLのオプションなどがある。設定値についてはDNSimpleのレコード作成のAPIも参考になる。
DNSimpleへの反映
先のテンプレートファイルのあるディレクトリでterraform apply
コマンドを実行する。
$ terraform apply
tfstateをAWS S3に保存する
terraform apply
でリソースへ変更を行うと、最新の状態を記録したterraform.tfstate
というファイルが作成・更新される。詳しくはドキュメントに任せるとして、このファイルがCircleCI上で毎回作成されては破棄されるようだといざ手元で作業したときに困りそう。ということでS3に記録するようにする。1
たとえばs3://example-terraform-state/terraform.tfstate
として保存する場合、まず下準備として次のことを行う。
- S3で example-terraform-state バケットを作る
- そのバケットにファイルを書き込める権限(S3FullAccessとか)を持ったIAMユーザを作成する
- 鍵情報とバケットのリージョン情報として、次の3つの環境変数を設定する
- AWS_ACCESS_KEY_ID
- AWS_SECRET_ACCESS_KEY
- AWS_DEFAULT_REGION
それが終わったらterraform remote config
2で保存先を設定する。
$ terraform remote config \
-backend=S3 \
-backend-config="bucket=example-terraform-state" \
-backend-config="key=terraform.tfstate"
そして反映の前後で、S3から取得(pull)、更新(push)を行う。
$ terraform remote pull
$ terraform apply # もしくはplan
$ terraform remote push
ちなみにterraform remote config
すると、それまでカレントディレクトリにできていたterraform.tfstate
とterraform.tfstate.backup
が、.terraform
というディレクトリに作られるようになる。このディレクトリの中身はS3に上がるものと同じなので.gitignore
に追加しても問題ないと思う。
circle.yml
内容としては次のことが行われるようにする。
- Terraformをダウンロードしてきて~/.terraformに展開、パスを通す
- ~/.terraformをキャッシュする
- testで
- terraform remote config
- terraform remote pull
- terraform plan
- terraform remote push
- masterブランチにマージされたら
- terraform remote config
- terraform remote pull
- terraform apply
- terraform remote push
machine:
environment:
PATH: $HOME/.terraform:$PATH
TERRAFORM_VERSION: 0.5.1
dependencies:
cache_directories:
- ~/.terraform
pre:
- |
mkdir -p $HOME/.terraform
if [ -z "$(ls -A $HOME/.terraform)" ]; then
cd $HOME/.terraform
curl -LO https://dl.bintray.com/mitchellh/terraform/terraform_${TERRAFORM_VERSION}_linux_amd64.zip
unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip
rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip
fi
test:
pre:
- |
terraform remote config -backend=S3 -backend-config="bucket=example-terraform-state" -backend-config="key=terraform.tfstate"
terraform remote pull
override:
- |
terraform plan
post:
- |
terraform remote push
deployment:
production:
branch: master
commands:
- |
terraform remote config -backend=S3 -backend-config="bucket=example-terraform-state" -backend-config="key=terraform.tfstate"
terraform remote pull
terraform apply
terraform remote push
ちなみに、circle.ymlに処理を記述するだけだといざ手元で実行しようとしたときに不便なので、実際にはrakeタスクとして定義し、それを呼び出すようにしている。
CircleCIでの設定
GitHubとCircleCIを連携する。
またこれまでに出てきた次の5つの環境変数をCircleCIのプロジェクトごとの画面から設定する。
変数名 | 概要 |
---|---|
TF_VAR_dnsimple_email | DNSimpleに登録してるemail |
TF_VAR_dnsimple_token | DNSimpleのAPI Token |
AWS_ACCESS_KEY_ID | AWS IAMで発行したユーザのAccess Key ID |
AWS_SECRET_ACCESS_KEY | AWS IAMで発行したユーザのSecret Access Key |
AWS_DEFAULT_REGION | AWSで作ったS3のバケットのリージョン。Tokyoだとap-northeast-1 |
所感
- Terraformで管理する前にすでに同じレコードが登録されている場合、applyで上書きできずに失敗する。遊びで使っているドメインなら削除してからapplyして作りなおせばいいけど、業務で使っているような場合には厳しい。
- ちなみに今回の場合はtfファイルで一度でも記述したドメインのみが管理対象になるのに対し、Roadworkerの場合はドメイン(Hosted Zone)以下全てが管理対象になる。
- テストとして
terraform plan
が走るだけだと、実際の記述が正しいかどうかはわからない。ただtfファイルの記述の時点で処理が入り乱れるようなものでもないし、あくまでも誰か人間がterraform apply
しなきゃいけないっていう手間を省くものとしてはアリな気がしている。
参考
- Amazon S3 で Terraform の状態管理ファイル terraform.tfstate を管理 / 共有する
- Terraform + GitHub + CircleCI + Atlasを利用してAWSの操作を自動化した
- CircleCIで任意のツールをインストールして使う際の定番スニペット
- Route53のレコードをRoadworkerとCircleCIを使ってPull Requestベースで管理する
-
Terraform v0.5.0以上が必要 ↩