概要

TerraformでAWSのリソース管理をしていますが,(少人数でやるには1)まぁまぁ良い感じに落ちついたのでご紹介します.

以下,話を簡単にするためにTerraformでAWSのリソースを管理しているという前提で話をすすめます.

問題

Terraformは便利で,使いはじめると何でもかんでもコード化してやろう!!などと思ってしまいますが,すぐに以下のような問題にぶち当たります.

  1. コード化をすすめるとplan/applyにかかる時間が増大していく
  2. planが通ってもapplyでコケる場合がままある

コード化をすすめるとplan/applyにかかる時間が増大していく

コード化されてるリソースが少ないうちはあまり問題にならないのですが,時がたちコード量が増えてくると,plan/applyにかかる時間がどんどん伸びていきます.

巨大なmoduleがあると,コード量の増加は数行だったとしても所要時間は一気に増えたりするので要注意です.

planが通ってもapplyでコケる場合がままある

プルリクエスト作ったらplanして,マージされたapplyする,というフローをよく(?)みかけます.
一見すると分かりやすいフローではありますが,planが成功していようが結局applyしないとどうなるかわかりません.一度この状態に陥ると,

  • applyを通すために微修正したプルリクエストを何度も作ることになってつらい
  • applyが通っても意図した通りの結果になっていなくてつらい

みたいなことになり,加えてコード量がそれなりに多い場合は(1)の問題がクリーンヒットしてきます.

どうしたか

大きくわけて2つです.

  • AWSアカウントを開発(dev)/ステージング(stg)/本番(prd)で分ける
  • Terraform Providerを使う

AWSアカウントを開発(dev)/ステージング(stg)/本番(prd)で分ける

よくある話だと思います.(IAMを完全にマスターすれば1アカウントで頑張れるのかもしれませんが,ちょっと自分には無理です...)

各アカウントの用途は以下のような感じになります.

  • devはローカルマシンからplan/applyできるようにしてしまう
  • CIからapplyする確認はstgで取る
  • stgで確認できたらprdにもapplyする

devを,対象プロジェクト専用のsandboxとみなしてしまうのがポイントです.
そもそもdevは壊れる前提で作ってるわけですし,ぶっ壊れたら作りなおせばいいよね?(それをしないなら何のためにコード化を...?)
新規で何かリソースを追加するにしても,既存のリソースがある前提で考えられますし,
何よりdevでapplyまでやってしまうことにより,無駄なプルリクエストを作りまくらなくても良くなりCI待ちなども無くサクサク開発できるようになります.

※冒頭で少人数でと書いたのはこのあたりの事情にもよります.複数人で1つの環境に同時にapplyすると多分よくわからない状態になります.

:thinking: Terraform検証用AWSアカウントについて

以前は,Terraformの検証は調査/学習用の(共通の)AWSアカウントでやる,という感じでした.その場合はローカルマシンからapplyできるようになっていると思いますが,以下のような問題もあります.

  • 関係ない他のリソースが作られていたりする
  • 検証したいリソースに必要な依存先があればそれも含めて作る必要がある
  • 検証終わったらちゃんと消さないと無駄に金がかかる

また,通常は必要最低限のリソースしか作らないので,そのコードを加えた際に増加するapply時間がまったく見えなくなります.

というわけで,一切プロジェクトと関係ないリソース(個人の趣味とか将来的には使うかもとか...)の確認には良いですが,現在のプロジェクトで使う前提のリソースを確認する目的なのであればそのプロジェクトのdevでやるのが良いのでは,という感じです.

Terraform Providerを使う

Terraform ProviderというProviderがあります.一言でいうと,他で実行されたTerraformのtfstateを参照できるProviderです.イメージは下記の通りです.

terraform_A
resource "aws_s3_bucket" "foo" {
 :
} 

output "s3_bucket_foo" {
  value = "${aws_s3_bucket.foo.id}"
}
terraform_B
data "terraform_remote_state" "tf_a" {
  backend     = "s3"

  config {
   :
  }
}

data "aws_s3_bucket" "foo" {
  bucket = "${data.terraform_remote_state.tf_a.s3_bucket_foo}"
}

これで,terraform_Aapplyした結果できたS3バケットfooを,別のterraform_Bが参照できるようになります.
terraform_B内ではS3バケットfooは存在することを前提として動けるため,plan/applyが爆速になります.

基本はmoduleで完結させ,module外で必要なリソースはoutputを定義する,という感じにする2と,どこに何があるのか把握しやすくて良い感じです.
terraform outputコマンドでoutputされている値を一覧できる3ので,必要なときにぱっと探しやすいという利点もあります.

まとめ

  • Terraformはapplyしやすい環境を整えると捗る
  • Terraform Providerをつかってtfstateを分割すると時短になる4


  1. 自分自身がそんなに大勢の人がTerraformを同時にいじくるという環境にいたこと無いというのと,現実的にはインフラ構成いじるの少人数じゃない?という憶測があるためです. 

  2. 具体的な構成はまた別の記事に書くかも... 

  3. terraform_remote_stateで参照するためにはrootレベルでoutputされてなければいけない(仕様)せいで若干めんどくさかったりもする. 

  4. TerraformのBest Practice的にも推奨っぽい