Edited at

DNSimpleをTerraform+GitHub+CircleCIで管理する

More than 3 years have passed since last update.

処理の流れとしては次のような感じ。


  • GitHubにpushされるとCircleCI上でterraform planが走る

  • そのPRがmasterにマージされるとterraform applyが走る


Terraformのテンプレート

まずはdnsimpleプロバイダについて記述する。


dnsimple.tf

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レコードを設定する場合は次のように記述する。


dnsimple.tf

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として保存する場合、まず下準備として次のことを行う。


  1. S3で example-terraform-state バケットを作る

  2. そのバケットにファイルを書き込める権限(S3FullAccessとか)を持ったIAMユーザを作成する

  3. 鍵情報とバケットのリージョン情報として、次の3つの環境変数を設定する


    • AWS_ACCESS_KEY_ID

    • AWS_SECRET_ACCESS_KEY

    • AWS_DEFAULT_REGION



それが終わったらterraform remote config2で保存先を設定する。

$ 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.tfstateterraform.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




circle.yml

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しなきゃいけないっていう手間を省くものとしてはアリな気がしている。


参考





  1. Terraform v0.5.0以上が必要 



  2. https://www.terraform.io/docs/commands/remote.html