プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

Terraformの特性を知る

Terraformは優れたツールだ。

Ansibleにはない、純粋な宣言型の振る舞いを経験するとある種の感動を覚える。

しかし使い込んでいくとわかるが癖がある。

ツールを使いこなすにはそのツールの背景にある思想、設計、そして癖を理解することが重要だ。

ここではそういったことをいくつか簡単に紹介したい。

Terraformの基礎についてはこちらの投稿で説明した。必要に応じて参照して欲しい。

architecting.hateblo.jp


Terraform全般

純粋な宣言型

  • Terraformの最大の特徴は宣言型であること
    • 例えばリソースA、Bを作成したとする。
    • リソースBが不要になったら宣言ファイルからBを削除して再度、applyすればAだけ残る
    • AnsibleであればBを削除するPlaybookを作成する必要があるがTerraformでは不要だ
    • 管理対象システムの変更が頻繁に発生する場合、工数削減が期待できる

インフラの細部までは管理できないことがある

  • Terraformは制御対象システムの細部まで管理することを目的としていない
Terraform is not intended to give low-level programmatic access to providers,
but instead provides a high level syntax for describing how cloud resources and services should be created, provisioned, and combined.

出典:https://www.terraform.io/intro/vs/boto
  • 実際できるケースは少なくないが、できないからといって騒いでも改善される可能性は低い
  • 細かい制御が必要なケースは他のツールに任せることになる

設定ファイル関連

進化が激しい

  • 年々、便利な機能が追加され、過去の制約がなくなり、以前は必要だった小技が不要になっている
  • 1年前の情報は信じず、時間はかかるが公式ドキュメントで地道に勉強することが望ましい

汎用プログラミング言語ほど柔軟な制御構造を持たない

  • HCLは一見、柔軟な言語に見える
  • 設定ファイル記述言語としては便利だが過度な期待は禁物だ
  • 例えばLoop機能はあるが任意の範囲の処理を繰り返すような使い方はできない
    • countfor_each
    • これらは厳密にはresource、moduleの複製回数を指定する機能
  • Loopを利用してif文的なConditionも実現できるがLoop同様、resource、module単位での条件分岐になる
  • 任意の範囲の処理を条件分岐するような使い方はできない
resource XXX "this" {
    count = var.some_flag ? 1 : 0
    :
}       
  • ネストされたLoop、Conditionはいずれも苦手だ
    • データ構造をフラットにして1ループに収まるよう工夫する

制御対象システムに対する深い知識が必要

  • 制御対象システムに対する理解がないと設定ファイルが書けない
    • Terraform RegistryのProvider Documentの中から適切なresourceを探せない
    • resourceの各argumentの意味と使い方を理解できない
    • apply後、正しい設定が投入できたか調査できない
    • 問題発生時に管理対象システムをデバッグできない
  • Terraform自体は実はかなりシンプルで、Providerや制御対象に対する理解度の方が重要

module compositionの階層は2〜3くらいが推奨

  • moduleから他のmoduleを利用できる
    • module compositionと呼ばれている
  • 呼び出しの階層が深すぎると可読性が落ちる
  • 2階層以内が推奨
    • 最大でも3階層に収める

循環依存に注意

  • 呼び出しがmodule間で循環するとterraform planでエラーが発生し、terraform applyが実行できなくなる
  • module間の呼び出しは便利で推奨されているが循環しないように注意が必要
  • 循環した場合はmoduleを分割する
    • 分割が進んでいくと小粒なモジュール同士の実行順序をどう制御するかという課題が生まれる
      • terragrunt、terrafileなどのOSSが視野に入ってくる

Listは注意

  • Listのデータをもとにリソースを作成した場合、TerraformはList内の順序を覚えている
  • List内の順序が変わるとTerraformは設定がなくなったと勘違いして設定の削除、再作成を行う
  • Map、Setを使った場合は起こらない

State関連

版数管理する

  • versioning機能があるbackendを使うこと
  • 作業履歴になる
  • 問題があった場合に戻れる
  • Gitは後述するロック機能がないので注意

plan/apply時はロックする

  • ロック機能があるbackendを使うこと

大きくなったら分割する

  • 大きくなるとplan/applyに時間がかかる
  • ロック時間が長くなるとチーム全体の生産性に影響する
  • 適切な単位に分割した方がよい

Terraform以外から投入した設定を残したい場合

  • 以下の選択肢がある
    • Stateの内容と干渉しないのならば無視することができる
    • 干渉する場合、terraform importでTerraformの管理化に組み込める
      • 面倒なので定常業務として実施するのは厳しい
  • Terraformを導入した場合、Terraform以外から設定を投入することはやめた方がよい

構造

envsとmodulesを分ける

  • 1つのディレクトリにすべての設定ファイルを集めると柔軟性、管理性、安全性が損なわれる
  • 環境ごとの情報をenvsに配置する
  • 各環境が共通的に利用するmoduleをmodulesに配置する
sample_project
├── envs
│   ├── production
│   │   ├── locals.tf.txt
│   │   ├── main.tf.txt
│   │   ├── outputs.tf.txt
│   │   ├── provider.tf.txt
│   │   └── version.tf.txt
│   └── staging
│       ├── locals.tf.txt
│       ├── main.tf.txt
│       ├── outputs.tf.txt
│       ├── provider.tf.txt
│       └── version.tf.txt
└── modules
    ├── module_A
    │   ├── main.tf.txt
    │   ├── outputs.tf.txt
    │   └── variables.tf.txt
    └── module_B
        ├── main.tf.txt
        ├── outputs.tf.txt
        └── variables.tf.txt