Terraformにまつわる運用tips的なもの

この記事は Enigmo Advent Calendar 2021 の12日目の記事です。

こんにちは。BUYMAの検索やMLOps基盤周りを担当している竹田です。
この一年間はTerraformを業務で利用することが多かったため、普段気を付けていることなどを運用tipsとして紹介したいと思います。

Terraform

Terraformは言わずと知れたInfrastructure as Code (IaC)を実現するためのツールです。
先日v1 🎉 になり、安定してきた印象があります。 f:id:enigmo7:20211207145027p:plain

Terraformに限った話ではありませんが、コード管理されている安心感は何ものにも変えがたいものがあります。 f:id:enigmo7:20211207151117p:plain

本記事はTerraform自体の説明は割愛し、ある程度利用したことのある方を対象としています。
個人の主観を多分に含んでいる、およびGoogleCloudに寄った内容のため他クラウドベンダーとは若干記述の毛色が違うといった可能性があります。あくまで参考程度としていただけると幸いです。
また、 terraform Advent Calender もあるようですので、より多くの情報を得たいという場合はそちらも参考にしていただくと良いかと思います。

定期的なバージョンアップ

Terraformはv1化により安定してきたように思いますが、クラウドベンターのproviderは変化のサイクルが早く、新機能の追加やベータ版のGA化など定期的に追従した方が良いです。

  • 気付かない内に有用な機能がリリースされており、無効の状態で運用を継続している可能性がある
  • 新機能があった場合に気付きを得られる
  • 項目の定義方法が変更になっている場合がある

設定理由を残す

インフラ面のライフサイクルとして定義変更の頻度が少ないこともあり、いざ定義を変更しようとした際に設定理由を忘れていることが多々あります。
「この機能は何々の理由で有効・無効にした」といった内容を何かしら文章として残しておくことをお勧めします。

terraform applyの際にエラーとなってしまう場合がある

主に認証周りや、providerサイドで項目のvalidationがされていないものについて発生することがあります。terraform planした際には正常終了するものの、いざterraform applyするとエラーになるというものです。
すでにコード管理上にcommitしていると二度手間になるため、開発環境やテスト可能な環境でterraform applyまで実施しておくことをお勧めします。

書き方のtips

tfファイルの記法は若干クセがあるため、どの書き方が適切なのか、どう書くとやりたいことを実現できるのか分からない状況に遭遇することが稀にあります。
以下に一部例を挙げてみます。


変数参照

ハッシュ記述value["preemptible"]なのかシンボル的記述value.preemptibleなのか迷います。大抵はどちらでも問題ありません。
ただし、混在すると見にくくなったり、記述箇所によっては意図があるように感じたりする場合があるため、統一感を持たせた方が良いように思います。
↓こんな混在した書き方ができてしまう

  node_config {
    preemptible     = each.value["preemptible"]
    machine_type    = each.value.machine_type
    disk_type       = each.value["disk_type"]
    oauth_scopes    = each.value.oauth_scopes

dynamicブロック

環境毎にリソース内の定義有無を設定できるため非常に便利なdynamicブロックですが、リソースによっては後で削除できないパターンが存在します。
例えば GoogleCloud のノードプールに設定可能なtaint定義などが該当します。taintの説明はこちらを参照ください。
なお、ここで例として挙げているtaintの変更は、変更の際にノードプール自体が作り直されてしまいますのでご注意ください。

taint定義を以下のようにdynamicブロック定義していたとします。

    dynamic "taint" {
      for_each = contains(keys(local.gke_nodepools), "taints") ? local.gke_nodepools.taints : []
      content {
        key    = taint.value.key
        value  = taint.value.value
        effect = taint.value.effect
      }
    }

variablesは以下のようになっており、terraform applyにより実環境にtaintが設定されていたとします。

    locals {
      gke_nodepools = {
        taints = [
          {
            key    = "testkey"
            value  = "testvalue"
            effect = "NO_SCHEDULE"
          }
        ]
      }

あるタイミングでtaint定義が不要となり、削除したいと考えてvariablestaints項目自体を除去してterraform planを実施します。すると

No changes. Infrastructure is up-to-date.

削除差分が出ると思っていたのに差分が一切検出されませんでした 😢

これはdynamicブロック対象のオブジェクトを定義しない場合に、そもそも定義が存在しないものと解釈されて空の内容では更新してくれないためです。
こういった場合は、taint定義を以下のようにします。

    taint = [
      for item in local.gke_nodepools.taints :
      {
        key    = item.key
        value  = item.value
        effect = item.effect
      }
    ]

続いて、variablesを空に設定します。

    locals {
      gke_nodepools = {
        taints = []
      }

このように定義することで、taint定義を空で更新することが可能となります。
以下はterraform plan時の差分結果です。

          ~ taint             = [ # forces replacement
              - {
                  - effect = "NO_SCHEDULE"
                  - key    = "testkey"
                  - value  = "testvalue"
                },
            ]

最後に

システム規模としては小〜中規模のため、規模面で困ったことは今のところありません。
大規模システムでは自動化などを考慮する必要がありそうなので、その辺りの悩みがない分は楽かもしれませんね。
今後はTerraform以外にもIaC関連のツールを利用するかもしれませんが、しっかり追従していきたいなと思います!

明日の記事担当はエンジニアマネージャーの木村さんです。お楽しみに!


株式会社エニグモ すべての求人一覧

hrmos.co