Terraform のテスト環境を Terraform Workspaces で構築した
株式会社メドレーのエンジニアの笹塚です。 主にジョブメドレーのインフラを担当しています。
- 直近では、コンテナ化されていなかった環境の移行などをしていました。
- 休日は主にゲームをやっています。今は、日本語版がリリースされたばかりの「レムナント:フロム・ジ・アッシュ」に夢中です。
今回は Terraform のテスト環境を、Terraform Workspaces を使用して構築した事例を紹介します。
背景
ジョブメドレーにはいくつかのサービスがあり、Terraform によるコード化が進んでいるサービスと、Ansible、 itamae などで部分的にコード化はされているものの、Terraform の使用が遅れているサービスが混在しており、これらのサービスについても Terraform への移行を進めています。
Terraform への移行とあわせて、メンテナンスを担当するメンバーを増やす必要があるのですが
【作業担当】
- Terraform のコードから、実際に稼働させる環境を作るのは慣れていても難しい。
【レビュワー】
- Terraform のコードの差分だけで、内容をすぐに把握するのは難しい。
などの理由から、作業担当、レビュワーともにハードルが低いとは言えず、Terraform によるインフラの変更内容を事前に確認できる環境構築が必要だと感じるようになりました。
検討内容
事前に確認できる環境を作るにあたり、まず最初に検討したのは、各ステージのコードを共通化することでした。 ジョブメドレーの関連サービスでは、大きくわけて 3 つのステージで構成しています。
Sandbox(個人の検証用) → QA(リリース前の検証用) → Production
この各ステージのコードを共通化できれば、Production には QA 環境までで確認済みのコードを apply することができます。
ですが、Sandbox 環境、QA 環境、Production 環境はそれぞれ似てはいるものの、全てが同じ構成ではありません。
Terraform の HCL では
- if 構文がない( resource の作成を count で制御することはできる)
- module 単位でのリソース作成の制御ができない(Terraform 0.13 から module でも count や for_each が可能になるようです)
などの制限があり、構造の差分をコードで吸収しにくく、共通化できたとしてもメンテナンス性を下げる可能性が高いです。 また、すでに Terraform でコード化されている状態からの移行作業も楽ではないでしょう。
そこで、ステージごとのコードを共通化するのではなく、AWS Organizations で別アカウントを作り、各ステージのコードを試せる環境を作ることにしました。
<目標とする>
Terraform のコードをプロダクションアカウントで実行する前に、テストアカウントで実行し、設定した内容を AWS マネジメントコンソール や AWS CLI で確認することできる。
- テストアカウント上で設定を確認した後、プロダクションアカウントに同じコードを apply することができる。
- コストを抑えるため、テストアカウント上のリソースは、確認が終了したら destory することができる。
<目標としない>
- テストアカウント上でサービスが稼働することは目標としない。
- 必要なデータセットの作成など用意するものが増えるので、目標には含めませんでした。
設定作業
必要な作業は大きくわけて 2 つです。
- Terraform Workspaces の設定
- アカウントごとに必要なコードの追加、修正
Terraform Workspaces の設定
Terraform と AWS provider の設定を変更することで、workspace を切り替えることができます。
以下が作業イメージです。
workspace の追加
$ terraform workspace new production
$ terraform workspace list
default
* production
Terraform の環境変更
terraform {
required_version = "= 0.12.28"
backend "s3" {
bucket = "example-state"
workspace_key_prefix = "workspace"
dynamodb_table = "terraform-state-lock"
key = "example.tfstate"
region = "ap-northeast-1"
profile = "production"
}
}
provider "aws" {
region = "ap-northeast-1"
version = "= 2.70.0"
profile = var.workspace_profile[terraform.workspace]
}
variable "workspace_profile" {
type = map(string)
default = {
default = "test"
production = "production"
}
}
この例では、default workspace はテスト環境、Production を実際にサービスが稼働する環境のアカウントになるように設定しています。 それぞれ default は、aws config の test profile、Production は production profile を参照しています。
s3://example-state/example.tfstate
がテスト環境、s3://example-state/workspace/production/example.tfstate
が、プロダクション環境の state ファイルになります。
Terraform のコード内からは、現在の workspace 名を terraform.workspace で参照することができます。
ここまでの設定で
$ terraform workspace select [workspace 名]
で workspace を切り替えることができるようになりました。
あとは、アカウントごとに必要な変更をしていきます。
アカウントをまたいだ共通のリソースの定義
全アカウントでユニークにする必要があるリソース(S3 bucket、DNS など)の場合は、名称の分岐処理が必要です。 テストアカウントでは、リソース名に prefix をつけて作成するようにしました。
# 定義
variable "example_bucket_name" {
type = map(string)
default = {
default = "test-example-bucket"
production = "example-bucket"
}
}
# リソースでの参照時
resource "aws_s3_bucket" "example" {
bucket = var.example_bucket_name[terraform.workspace]
..
}
テストアカウントで起動するインスタンスタイプや台数の変更
テストアカウントでは EC2 のインスタンスを起動させなくても良い場合には、台数を変更するようにしました。
resource "aws_autoscaling_group" "example_web" {
name = "example-web"
max_size = 12
min_size = 6
desired_capacity = terraform.workspace == "production" ? 6 : 0
…
}
インスタンスの起動が必要な場合も、同様の分岐でインスタンスタイプの変更を行っています。
コード化をしないリソースの扱い
プロダクションアカウントで一旦コード化を保留したリソースについては data source で参照しますが、テストアカウントにはリソースが存在しないため作成しなければいけません。 テストアカウントのリソースは確認が終了した段階で destroy したいので
- ステージ用の Terraform コードとは別に、必要なリソースを作成するコードを用意する
- 必要なリソース用のコード → ステージ用のコードの順に apply する
ようにしました。 data source で参照できれば良い範囲でのコード化なので、この追加のリソースも最小限のコストになるようにしています。
以上の設定で、同じ Terraform のコードを workspace を切り替えて plan、apply ができるようになりました。
運用してみて
プロダクションアカウントに apply する前に、テストアカウントで事前に apply することができるようになったので、作業中の試行錯誤もしやすくなりました。
レビュワーも、テストアカウント上で実際に apply された結果を確認することができるようになり、apply の差分だけではわかりにくかった変更内容を確認できるようになりました。
これなら新しく担当するメンバーの不安を、少しかもしれませんが解消できそうです。
まとめ
今回は Terraform のテスト環境を、Terraform Workspaces を使用して構築した事例を紹介させていただきました。 テストアカウント上のリソースの自動 destroy や、 plan、apply の自動化については触れられていませんが、また別の機会に紹介できればと思います。
長らく運用しているサービスでは、サービスを稼働させたまま解決しなければいけない課題が数多くあります。 それらの課題を、一つずつ着実に解決していくことに楽しさを見いだせる方、ぜひメドレーで一緒に働きましょう!