Terraform と Wercker で DNS レコードを管理する

More than 1 year has passed since last update.


はじめに

先日、 Terraform で Google Cloud DNS を管理して GitHub 経由で Wercker から反映する環境を構築し、その便利さに感動しました。

これには、大きく2つの恩恵があります。


  • Terraform と Git による DNS レコードのバージョン管理

  • GitHub と Wercker による自動反映

  • チーム開発などで複数人がレコードを管理する事が容易になる (Pull Request を出してもらってレビュー後に master へ merge するだけ)

割と嵌ったので、手順を纏めておきます。

尚、 GCP のアカウントは発行済で、プロジェクトも作成済みとします。

https://console.cloud.google.com/


余談

筆者はこれまで Gehirn DNS を利用していました。このサービスは、自前で Git などを使うことなく DNS レコードのバージョン管理が行えます。API も用意されている上に指定した時刻に任意のバージョンへ切り替える機能(マイグレーション)まで備えています。

今回 Google Cloud DNS へ移行する理由は、 Gehirn DNS にしかない機能を活用していない事と、 Amazon Route 53 を含めた3つのサービスでコスト試算して一番安かったという理由があります。

Web コンソール上から手軽に DNS レコードのバージョン管理がしたい、 API さえ用意されていれば Terraform は使えなくても構わない方は Gehirn DNS が向いているかもしれません。


Terraform から Google Cloud DNS を設定する


ファイル構成

┬ .gitignore

├ account.json
├ dns_example_com.tf
├ gcp.tf
├ variables.tf
└ variables.tfvars

※ tf ファイルが同じディレクトリにあれば構成は自由なので、参考までに。


.gitignore

.*

!.gitignore

# Terraform
*.log
*.tfstate
*.tfstate.backup

account.json



Google Provider の設定

https://www.terraform.io/docs/providers/google/


gcp.tf

provider "google" {

credentials = "${file("account.json")}"
project = "${var.project}"
region = "${var.region}"
}


Zone と Record の設定

https://www.terraform.io/docs/providers/google/r/dns_managed_zone.html


dns_example_com.tf

resource "google_dns_managed_zone" "example_com" {

name = "example-com"
dns_name = "example.com."
}

dns_name は末尾に . (ピリオド) が必要なので注意。


Record の設定

https://www.terraform.io/docs/providers/google/r/dns_record_set.html

Zone の設定と同じファイルに追記します。


dns_example_com.tf

resource "google_dns_record_set" "a_example_com" {

managed_zone = "${google_dns_managed_zone.example_com.name}"
name = "${google_dns_managed_zone.appcloud_info.dns_name}"
type = "A"
ttl = 300
rrdatas = [
"192.30.252.153",
"192.30.252.154"
]
}


Variable


variables.tf

variable "region" {

default = "asia-northeast1"
}
variable "project" {}


variables.tfvars

project = "project-id"


今回 region は東京リージョン以外には変えないので default 値を設定しておきます。


認証情報

https://console.cloud.google.com/apis/credentials

サービスアカウントキー (JSON) を作成します。

service account key

新しいサービスアカウントを作成します。

create service account

作成すると認証情報が JSON 形式でダウンロードされるので、 account.json にリネームして同じ階層に配置します。


nameservers

Google Cloud DNS や Amazon Route 53 では、新規ゾーンを作成した際にネームサーバが割り当てられます。

そこで、リソースが作成された後にネームサーバ一覧を出力するようにしておくと、 Web コンソールを開いて確認する手間が省けて便利です。

Zone の設定と同じファイルに追記します。


dns_example_com.tf

resource "null_resource" "nameservers_example_com" {

triggers {
name_servers = "${join("\n", google_dns_managed_zone.example_com.name_servers)}"
}

provisioner "local-exec" {
command = <<EOF
echo "nameservers: ${google_dns_managed_zone.example_com.dns_name}"
echo "${self.triggers.name_servers}"
EOF
}
}



plan & apply

https://www.terraform.io/downloads.html

Terraform をインストールしておきます。

Mac なら brew で入れるのがおすすめです。

terraform plan -var-file=variables.tfvars

syntax に問題がある場合はこの時点でエラーが出ます。

問題なく、更新されるリソースが確認できたら apply します。

terraform apply -var-file=variables.tfvars

次のように、ネームサーバ一覧が出力されます。


null_resource

null_resource.nameservers_example_com (local-exec): nameservers: example.com.

null_resource.nameservers_example_com (local-exec): ns-cloud-d1.googledomains.com.
null_resource.nameservers_example_com (local-exec): ns-cloud-d2.googledomains.com.
null_resource.nameservers_example_com (local-exec): ns-cloud-d3.googledomains.com.
null_resource.nameservers_example_com (local-exec): ns-cloud-d4.googledomains.com.

ローカルから実行する分にはこれで完了です。


Wercker で自動反映する

GitHub か Bitbucket でリポジトリを作成します。

Wercker ならプライベートリポジトリでも無料で利用することが出来ます。(2017年1月現在)


ファイル構成

┬ .gitignore

├ account.json
├ dns_example_com.tf
├ gcp.tf
├ gcs.tf
├ variables.tf
├ variables.tfvars
└ wercker.yml

追加されたのは gcs.tfwercker.yml です。


remote state

ローカルで実行する場合は terraform.tfstate というファイルで設定状態が保存されています。ローカル以外で適切に反映させるには、この state を何らかの形で同期する必要があります。

それが、 remote state という機能です。

remote state の backend として S3 や GCS (Google Cloud Storage) が対応しています。ここでは GCS を選定しました。


remote state backend


gcs.tf

resource "google_storage_bucket" "wercker_tfstate" {

name = "${var.remote_state_bucket}"
location = "${var.region}"
storage_class = "REGIONAL"

provisioner "local-exec" {
# Enable versioning (not supported by terraform)
# https://cloud.google.com/storage/docs/object-versioning
command = <<EOF
gcloud auth activate-service-account --key-file account.json
gsutil versioning set on "${var.remote_state_bucket}"
gsutil versioning get "${var.remote_state_bucket}"
EOF
}
}


state が何らかの事故により誤った内容で上書きされてしまった時の対策として、 GCS のバージョニング機能を設定します。しかしながら、現在 terraform ではサポートされていないので local-exec provisioner を使って直接 gsutil コマンドを叩いて有効化します。

次に、 variables.tf と variables.tfvars に remote_state_bucketremote_state_path を追加します。


variables.tf

variable "region" {

default = "asia-northeast1"
}
variable "project" {}
variable "remote_state_bucket" {}
variable "remote_state_path" {
default = "terraform.tfstate"
}


variables.tfvars

project = "project-id"

remote_state_bucket = "bucket-name"

bucket-name は他のユーザが同じ名前で登録していると使えません。重複しないように設定する必要があります。


ローカル環境で remote state を試す

まずは、 GCS bucket を作るために apply しておきます。

terraform plan -var-file=variables.tfvars

terraform apply -var-file=variables.tfvars

次に terraform remote config を実行して、ローカルの tfstate を同期します。

terraform remote config \

-backend=gcs \
-backend-config='bucket=bucket_name' \
-backend-config='path=terraform.tfstate' \
-backend-config='project=project_id' \
-backend-config="credentials=$
(cat account.json)"


Wercker Application を作成する

https://app.wercker.com/applications/create

リポジトリを登録して Wercker Application を作成します。

make-my-app-public

Make my app public を選択してしまうと、プライベートリポジトリだったとしても公開されてしまうので、注意が必要です。


build step の設定

wercker.yml で build と deploy の設定をします。

build では terraform plan を実行して tf ファイルを検証し、 deploy では terraform apply で実際に反映させます。


wercker.yml

box: python:2.7

build:
steps:
- script:
name: generate account.json
code: |
cat <<EOF > account.json
{
"private_key": "$GCP_PRIVATE_KEY",
"client_email": "$GCP_CLIENT_EMAIL"
}
EOF

- ww24/terraform:
command: plan
var_file: variables.tfvars
remote_config: |
-backend=gcs \
-backend-config='bucket=bucket_name' \
-backend-config='path=terraform.tfstate' \
-backend-config='project=project_id' \
-backend-config='credentials={ \
"private_key": "$GCP_PRIVATE_KEY", \
"client_email": "$GCP_CLIENT_EMAIL" \
}'

deploy:
steps:
- script:
name: install gsutil
code: |
pip install gsutil

- ww24/terraform:
command: apply
var_file: variables.tfvars
remote_config: |
-backend=gcs \
-backend-config='bucket=bucket_name' \
-backend-config='path=terraform.tfstate' \
-backend-config='project=project_id' \
-backend-config='credentials={ \
"private_key": "$GCP_PRIVATE_KEY", \
"client_email": "$GCP_CLIENT_EMAIL" \
}'


bucket_nameproject_id は適宜変更してください。

拙作ですが ww24/terraform を使って terraform のインストールと plan, apply を実行しています。

remote_config-backend-backend-config を記述することで、 plan や apply の前に必ず terraform remote config が実行されます。


Workflow の設定

master branch に変更があった際に deploy (terraform apply) されるように設定します。

image

Add new pipeline から deploy を追加します。

image

↓ pipeline に deploy が追加されました。

image

Workflow editor の build の右にある + ボタンを押して、 branch に master、 Execute pipeline に deploy を指定して Add します。

image

以下のようになれば Workflow の設定は完了です。

image


Environment の設定

認証情報を wercker.yml に直接するわけにはいかないので、環境変数に設定します。

image

Protected に☑を入れると、基本的に Web 上から見れなくなるので有用です。

しかし、 build や deploy 時に思い掛けないエラーが発生して、エラーメッセージとして出力されてしまう場合があるので、 Wercker Application を public にしない等、対策が必要です。

あとは、 git push して GitHub 上のリモートリポジトリに変更があれば、 Wercker で build (terraform plan) が走ります。master に merge すると build + deploy (terraform apply) が走ります。


さいごに

Terraform ではありませんが、業務で似たような構成で DNS レコードの管理を行っているのを拝見し、触発されました。

実際に動いているものがあるので、設定で分からないことがあれば参考にどうぞ。

https://github.com/ww24/dns

(ディレクトリ構成は若干異なります)