はじめに
先日、 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
# Terraform
*.log
*.tfstate
*.tfstate.backup
account.json
Google Provider の設定
provider "google" {
credentials = "${file("account.json")}"
project = "${var.project}"
region = "${var.region}"
}
Zone と Record の設定
resource "google_dns_managed_zone" "example_com" {
name = "example-com"
dns_name = "example.com."
}
dns_name
は末尾に .
(ピリオド) が必要なので注意。
Record の設定
Zone の設定と同じファイルに追記します。
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
variable "region" {
default = "asia-northeast1"
}
variable "project" {}
project = "project-id"
今回 region
は東京リージョン以外には変えないので default 値を設定しておきます。
認証情報
サービスアカウントキー (JSON) を作成します。
新しいサービスアカウントを作成します。
作成すると認証情報が JSON 形式でダウンロードされるので、 account.json
にリネームして同じ階層に配置します。
nameservers
Google Cloud DNS や Amazon Route 53 では、新規ゾーンを作成した際にネームサーバが割り当てられます。
そこで、リソースが作成された後にネームサーバ一覧を出力するようにしておくと、 Web コンソールを開いて確認する手間が省けて便利です。
Zone の設定と同じファイルに追記します。
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
Terraform をインストールしておきます。
Mac なら brew で入れるのがおすすめです。
terraform plan -var-file=variables.tfvars
syntax に問題がある場合はこの時点でエラーが出ます。
問題なく、更新されるリソースが確認できたら apply します。
terraform apply -var-file=variables.tfvars
次のように、ネームサーバ一覧が出力されます。
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.tf
と wercker.yml
です。
remote state
ローカルで実行する場合は terraform.tfstate
というファイルで設定状態が保存されています。ローカル以外で適切に反映させるには、この state を何らかの形で同期する必要があります。
それが、 remote state という機能です。
remote state の backend として S3 や GCS (Google Cloud Storage) が対応しています。ここでは GCS を選定しました。
remote state backend
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_bucket
と remote_state_path
を追加します。
variable "region" {
default = "asia-northeast1"
}
variable "project" {}
variable "remote_state_bucket" {}
variable "remote_state_path" {
default = "terraform.tfstate"
}
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 を作成する
リポジトリを登録して Wercker Application を作成します。
Make my app public を選択してしまうと、プライベートリポジトリだったとしても公開されてしまうので、注意が必要です。
build step の設定
wercker.yml
で build と deploy の設定をします。
build では terraform plan
を実行して tf ファイルを検証し、 deploy では terraform apply
で実際に反映させます。
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_name
と project_id
は適宜変更してください。
拙作ですが ww24/terraform
を使って terraform のインストールと plan, apply を実行しています。
remote_config
に -backend
や -backend-config
を記述することで、 plan や apply の前に必ず terraform remote config
が実行されます。
Workflow の設定
master branch に変更があった際に deploy (terraform apply) されるように設定します。
Add new pipeline から deploy
を追加します。
↓ pipeline に deploy が追加されました。
Workflow editor の build の右にある + ボタンを押して、 branch に master
、 Execute pipeline に deploy
を指定して Add します。
以下のようになれば Workflow の設定は完了です。
Environment の設定
認証情報を wercker.yml
に直接するわけにはいかないので、環境変数に設定します。
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
(ディレクトリ構成は若干異なります)