前提
- 小規模というほどではないが決して大規模ではない環境を組むときにちょうどいいTerraformのディレクトリ構成について悩んだときのメモです。
- 環境ごとにVPCとかのネットワークを切って、それぞれにEC2(ECS)+RDS+S3とかとかを数台ずつ乗せていくくらい。
観点
- [効率]環境ごとの差異を比較しやすいこと
- dev→stg→prodとデプロイしていくとして、差分反映・確認がしやすいこと
- [安定]安全にデプロイしやすいこと
├── env
│ └── stg01
│ |── network
│ | └── main.tf
│ └── service-aaa
│ └── main.tf
└── modules
└── vpc
└── main.tf
ファイル例
terraform {
required_version = "0.13.2"
backend "s3" {
bucket = "system-stg01-deploy-tf-999999999"
region = "ap-northeast-1"
key = "tfstate"
encrypt = true
}
}
provider "aws" {
region = "ap-northeast-1"
}
data "terraform_remote_state" "XXX" {
backend = "s3"
config = {
region = "ap-northeast-1"
bucket = "system-stg01-deploy-tf-999999999"
key = "tfstate.XXX"
}
}
locals {
env = "stg01"
}
module "vpc" {
source = "../../modules/vpc"
vpc_cidr_block = "192.168.10.0/24"
vpc_tags_Name = "system-${local.env}-vpc"
}
module "public-subnet01" {
source = "../../modules/subnet"
subnet_vpc_id = module.vpc.vpc_id
subnet_cidr_block = "192.168.10.0/26"
subnet_map_public_ip_on_launch = "true"
subnet_tags_Name = "system-${local.env}-public-subnet01"
}
module "public-subnet02" {
source = "../../modules/subnet"
subnet_vpc_id = module.vpc.vpc_id
subnet_cidr_block = "192.168.1.64/26"
subnet_map_public_ip_on_launch = "true"
subnet_tags_Name = "system-${local.env}-public-subnet02"
}
variable "vpc_cidr_block" {}
variable "vpc_tags_Name" {}
resource "aws_vpc" "this" {
cidr_block = var.vpc_cidr_block
instance_tenancy = "default"
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = var.vpc_tags_Name
}
}
output "vpc_id" {
description = "The ID of the VPC"
value = concat(aws_vpc.this.*.id, [""])[0]
}
実装方針
- モジュール切り出し+環境分離+コンポーネント分割
- 変数の値は4箇所に集約(全て環境ファイルに)
- 環境ファイルに直書き
- 同一環境ファイルの別モジュールの値(output)
- 環境ファイルのLocal Values
- 別コンポーネントの値(outputをRemote Stateとして拾う)
- 全てmain.tfに寄せる
- 環境ファイルとmoduleファイル
- variable.tfとか切り出さない
検討経緯
モジュール切り出し+環境分離+コンポーネント分割
- 推奨されてそうなのでモジュール切り出しにして見た
- 今後のことを考えて受け身が取れるように
- モジュール切り出しは、ダイナミックとかロジック入れるときに役立つ?
- ただ、ロジックは極力使わずベタ書きしている
- EC2のcountとかあるが、tagのインデックスくらいならいいがEIPとか絡むとややこしくなる。だったら環境ファイル側でベタ書きする。
- →が、環境ごとに下手書きで十分な気がする(モジュールを繰り返し利用したところで、記述量が減らない)
- 結局、コードとは違う。設定値の羅列で、ロジックがないから。ベタ描きで十分 or 思い切ってWorkspace?
- moduleはあくまでパーツ。システム側の都合を考慮せず、疎結合に組み合わせられるように実装する
- tf.stateの分割
変数の値は4箇所に集約(全て環境ファイルに)
- 同一環境ファイルの別モジュールの値(output)
module.vpc.vpc_id
data.terraform_remote_state.network.outputs.vpc_vpc_id
全てmain.tfに寄せる
- 「観点」記載の通り、環境ごとの差分を比較しやすくしたいので、分けることにあまり意味がない
参考
共通コードのディレクトリにはclassの定義が、 環境ごとのディレクトリにはclassのインスタンスを作る際に渡す実際の値が定義されていると イメージすると分かりやすいと思います。
# コンポーネント分割例
* network
* vpc,subnet
* routing
* route53,alb,acm
* services
* ecsのサービス単位(ecs cluster,taskdifinition,service,それに付随するec2系リソース ecr pipeline系)
* opsserver
* session manager,ec2
* events
* やりたい処理単位(lambda cloudwatchEvents ここはApex+Terraform)
* モニタリングとかもここに
* state machine
* step function
* storage
* s3
* datastore
* elasticache,RDS
* elasticache,RDSを別々に
* iam user
# コンポーネント分割例
* network
* ネットワーク系の設定VPC、Subnet、NAT、Route Tables等の設定を管理
* securitygroup
* セキュリティグループの管理
* iam
* IAMの管理
* s3
* S3回りの管理
* backend
* DB等のデータストア関連のリソースを管理
* webservers
* webサーバやALB等の管理
* opsservers
* 運用系のサーバの管理