Terraformにおけるディレクトリ構造のプラクティスと記述事例ビヨンド勉強会 #242020/06/17 株式会社ビヨンド 寺岡 佑樹
View Slide
自己紹介resource “my_profile” “nezumisannn” {name = “Yuki.Teraoka”nickname = “ねずみさん家。”company = “beyond Co., Ltd.”job = “Site Reliability Engineer”twitter = “@yktr_sre”skills = [“Terraform”,”Packer”]}
Terraformにおけるディレクトリ構造● 皆さんはどうしていますか?● (恐らく) 構築するインフラの環境や組織体制によって異なる● 複数のプラクティスと現実解としての戦略を共有したい
インフラ構成
考えられるプラクティス1. 1 モジュール + 1 tfstate パターン2. 1 モジュール (tfファイル分割) + 1 tfstate パターン3. 1 モジュール (環境分割) + N tfstate パターン4. N モジュール (環境分割) + N tfstate パターン5. 1 モジュール (子モジュール) + 1 tfstate パターン6. N モジュール (子モジュール) + N tfstate パターン
プラクティス評価基準● DRYなコードを書けるか● オペレーションミスを抑えられるか● 運用フェーズで管理しやすいか
1 モジュール + 1 tfstate パターン● 最小モジュールとして main.tf / variables.tf / outputs.tf を記述していく○ インフラの規模が小さければ最小の労力で記述できる○ 規模が大きいとmain.tfが肥大化する + 共通化できない○ 間違った環境に反映してしまうミスが起こる可能性が高い○ 実際はこの設計で事足りる構成はほぼないと言ってもいい$ tree.├── main.tf├── outputs.tf└── variables.tf
1 モジュール(tfファイル分割) + 1 tfstate パターン● 環境・リソースごとにtfファイルを分割する○ 1つのtfファイル内の記述はシンプルになるが共通化できない○ 間違った環境に反映してしまうミスが起こる可能性が高い○ 1 tfstateで全リソースを管理するため 1環境のオペミスが全環境に及ぶ$ tree.├── dev_vpc.tf├── outputs.tf├── prd_vpc.tf├── mng_vpc.tf└── variables.tf
1 モジュール(環境分割) + N tfstate パターン● WorkSpaceを利用して環境ごとにtfstateを分割する○ 複数環境のインフラ構成において最小の労力で記述できる理想とするパターン○ 環境ごとにtfstateが分割されているため影響範囲を局所化できる○ 特定環境のみ存在するリソースがあるなどの環境差分を許容できず運用が難しい$ tree.├── vpc.tf├── outputs.tf├── terraform.tfstate.d│ ├── dev│ │ └── terraform.tfstate│ ├── mng│ │ └── terraform.tfstate│ └── prd│ └── terraform.tfstate└── variables.tf
N モジュール(環境分割) + N tfstate パターン● WorkSpaceは利用せずディレクトリ自体を分割する○ 環境ごとにtfstateが分割されているため影響範囲を局所化できる○ 大きな環境差分を許容できるがコード自体が冗長になってしまう○ 環境ごとにApplyする必要がありApply漏れが発生する可能性がある% tree.├── develop│ ├── terraform.tfstate│ ├── variables.tf│ └── vpc.tf├── manage│ ├── terraform.tfstate│ ├── variables.tf│ └── vpc.tf└── production├── terraform.tfstate├── variables.tf└── vpc.tf
1 モジュール(子モジュール) + 1 tfstate パターン● 環境ごとの共通部分を子モジュールとして切り出す○ コードの冗長性をなくしながら大きな環境差分も許容できる○ 1 tfstateで全リソースを管理するため 1環境のオペミスが全環境に及ぶ○ 特定の環境のみ先行して Applyするといったことができない$ tree.├── dev_vpc.tf├── mng_vpc.tf├── modules│ └── vpc│ ├── main.tf│ ├── outputs.tf│ └── variables.tf├── outouts.tf├── prd_vpc.tf├── terraform.tfstate└── variables.tf
N モジュール(子モジュール) + N tfstate パターン● 環境ごとのディレクトリ分割と共通部分の子モジュールのハイブリット○ コードの冗長性をなくしながら大きな環境差分も許容できる○ tfstate分割による影響範囲の局所化が可能で特定環境のみ Applyも可能○ 子モジュールの切り出し方が難しくしっかり設計しないと運用が破綻する$ tree.├── develop│ ├── terraform.tfstate│ ├── variables.tf│ └── vpc.tf├── manage│ ├── terraform.tfstate│ ├── variables.tf│ └── vpc.tf├── modules│ └── vpc│ ├── main.tf│ ├── outputs.tf│ └── variables.tf└── production├── terraform.tfstate├── variables.tf└── vpc.tf● manageとdevelop / productionでsubnetの構成に差分がある● 同じモジュールを読み込んだときに処理を分岐する○ manageの場合はpublicのみ作成して他は作成しない○ Conditionalを多用することになり難読化する● public / dmz / privateで子モジュール自体を分けてしまう○ 読み込むモジュールが増える○ コード量が増加して構成が複雑になる
記述事例● N モジュール (環境分割) + N tfstate パターンを利用● 環境差分の許容とtfstateの分割を行いたかった● 環境差分を0にするのは現実的に無理● N モジュール (子モジュール) + N tfstate パターンは運用しきれなかった
● 環境ごとにリソース単位でファイルを分割している● tfstateはもちろん環境ごとに存在している● 環境横断型のリソースは terraform_remote_stateで管理○ aws_vpc_peering_connection○ aws_key_pair● 環境ごとにApplyを実行する必要がある○ CI/CDツールでオペミスを減らしている○ Terraform Cloud● コードが冗長になることは現状は許容している● 子モジュールの設計を頑張りたい○ 特定のチームで中央集権的に管理しないと辛そう○ 既存コードのリファクタリングが発生する○ terraform state mv 地獄$ tree.├── develop│ ├── alb.tf│ ├── ec2.tf│ ├── outputs.tf│ ├── provider.tf│ ├── rds.tf│ ├── remotes.tf│ ├── securitygroup.tf│ ├── terraform.tfstate│ ├── terraform.tfstate.backup│ ├── variables.tf│ └── vpc.tf├── manage│ ├── ec2.tf│ ├── keys│ │ ├── id_rsa│ │ └── id_rsa.pub│ ├── outputs.tf│ ├── provider.tf│ ├── remotes.tf│ ├── securitygroup.tf│ ├── terraform.tfstate│ ├── terraform.tfstate.backup│ ├── variables.tf│ └── vpc.tf└── production├── alb.tf├── ec2.tf├── outputs.tf├── provider.tf├── rds.tf├── remotes.tf├── securitygroup.tf├── terraform.tfstate├── terraform.tfstate.backup├── variables.tf└── vpc.tfTerraform Cloudを利用したGitOps CI/CDパイプラインhttps://speakerdeck.com/nezumisannn/20200522-fgdc-terraform-cloudtegitopswoshi-yong-sitaci-cdhaihurainwogou-zhu-suruブログも書いてますhttps://beyondjapan.com/blog/2020/04/terraform-cloud-gitops-cicd/
まとめ● 運用していく上ではディレクトリ構造が結構大切● 実際の案件に合わせてパターンを選択しましょう● 子モジュールでの共通化をもっと頑張りたい
終わり