(この記事はあしたのクラウド のインフラ移行に関する連載記事です)
最近セサミ4を購入してスマートロックに感動している@hidenbaです。
今日は、あしたのクラウドのインフラ移行した際に利用したTerraformのモジュール分割について書いていこうと思います
ディレクトリ構成
Terraform用のリポジトリは作らずに、あしたのクラウドのリポジトリ内に内包する形にしています。リポジトリのルートに iacディレクトリを作成しました。
iac
staging
main
main.tf
variables.yml
production
main
main.tf
variables.yml
demo main
main.tf
variables.yml
modules
alb
main.tf
outputs.tf
variables.tf
network
...
ecs
...
rds
...
ステージ毎に分割
あしたのクラウドのステージ構成は本番環境に加えてステージング環境とお客様が利用するデモ環境に分かれています。それぞれの環境で構成の差異があるのでワークスペースは利用せずにディレクトリを分けるようにしました。ワークスペースは各ステージのテスト用の環境を作成するために利用しています。
iac/staging: ステージング環境用iac/demo: デモ環境用iac/production: 本番環境用
各環境のmain.tfでは変数の読み込みとメインモジュールの実行を行うのですが、ステージング環境にだけベーシック認証を掛ける必要があるので差分として現れます。
差分の切り替え方法として変数を利用してcountでON/OFFを行う方法もあるのですが、条件分岐を作ることでコードの複雑性があがるのを避けるためにこの方法を採用しました。
本番環境
module "variables" { source = "../../modules/variables" } module "main" { source = "../../modules/main" input = module.variables.var suffix = module.variables.suffix } output "task_def" { value = module.main.ecs }
ステージング環境
module "variables" { source = "../../modules/variables" } module "main" { source = "../../modules/main" input = module.variables.var suffix = module.variables.suffix } module "basic_auth" { source = "../../modules/basic_auth" for_each = toset(keys(module.variables.var.hosts)) region = module.variables.var.region host = each.value target_group_basic_auth = module.main.alb[each.value].target_group_basic_auth[0] suffix = module.variables.suffix } output "task_def" { value = module.main.ecs }
モジュール分割方針
iac/modules 配下にモジュールを作成して各ステージから利用するようにしています。各ステージで作成する構成は基本的には同じなので iac/modules/main モジュールにて必要なモジュールを呼び出します。各モジュールは利用するリソースとそれに付随するリソースをまとめて一つのモジュールとして利用するようにしています。ネットワークのモジュールだとVPC・サブネット等ネットワーク周りのリソースを束ねて作成しています。他のリソースへの依存や利用方法による差異は変数にて受け渡しを行うことで再利用性を高めるようにしています。
簡単な例としてSNSトピックの構成をみてみましょう
sns_topic/main.tf
resource "aws_sns_topic" "this" { name = "${var.name}_${var.host}_${var.type}" } data "aws_iam_policy_document" "this" { statement { actions = ["sns:Publish"] principals { type = "Service" identifiers = ["codestar-notifications.amazonaws.com", "cloudwatch.amazonaws.com"] } resources = [aws_sns_topic.this.arn] } } resource "aws_sns_topic_policy" "this" { arn = aws_sns_topic.this.arn policy = data.aws_iam_policy_document.this.json } resource "aws_sns_topic_subscription" "this" { count = var.type == "lambda" ? 1 : 0 topic_arn = aws_sns_topic.this.arn protocol = "lambda" endpoint = var.lambda_endpoint }
SNSトピックモジュールの本体です。IAMの設定も内包しています。サブスプリプションリソースのaws_sns_topic_subscription では変数を使いTypeが lambda の場合のみリソースを作成します。
main/main.tf
module "sns_topic" { source = "../sns_topic" for_each = toset(local.hosts) name = var.input.name host = each.value type = "alert" } module "sns_topic_lambda" { source = "../sns_topic" for_each = toset(local.hosts) name = var.input.name host = each.value type = "lambda" lambda_endpoint = var.input.lambda_endpoint } module "sns_topic_deploy" { source = "../sns_topic" for_each = toset(local.hosts) name = var.input.name host = each.value type = "deploy" }
mainモジュールでSNSトピックモジュールを呼び出しています。SNSトピックは監視アラート用、LambdaFunctionの発火用、デプロイの通知用と用途別に作成しています。
今後の展望
同一リポジトリに収まっているが iac/modulesは別のリポジトリに切り出して再利用できるようにしていきたいと思っています!