東京ガス内製開発チーム Tech Blog

東京ガス内製開発チームのTech Blogです!

「楽」する前のTerraformバージョンアップ方針と運用整理(とポエム)

はじめに、はじめまして。リビング戦略部SREチームのあおしょん(本名:青木)と申します。 2024年4月1日から弊チームにジョインしたピチピチの新人*1です。

入社から約一ヶ月過ぎまして、現在も盛りだくさんの情報量と圧倒的当事者意識を持っている弊社の優秀なエンジニアたちに日々圧倒されながらも一刻も早く事業に貢献出来るように歩を進めています。

大きな貢献が出来ている、とは未だ胸を張って言えないのですが入社したてで業務知識が無くてもまずは小さい貢献からコツコツと始めてみよう、ということで弊チームにおけるTerraformバージョンアップの方針と運用について整理したのでご紹介いたします。

ご紹介の前に申し上げておきたいこととして、今回はバージョンアップの運用をこんなテッキーなことをしてクールに自動化してやったぜ~ワイルドだろ~という内容ではございません。あくまで現状はこうなっているからこういうことを考慮してこうやってバージョンアップしていくことになった、ということを淡々と説明する泥臭い内容となっています。また、対象の読者は既にTerraformを利用しており一般的な機能や用語を把握している方々となります。

Terraformバージョンアップちゃんと出来てますか?

いきなりですが本記事をお読み頂いている、かつ業務でTerraformを活用されている方々に聞いてみたいのですが

「Terraformバージョンアップちゃんと出来てますか?」

ここで言うTerraformバージョンは下記のことを指します。

  • Terraform Version(Terraform本体のバージョン )
  • Provider Version

「当然出来てるよ」という方であれば貴重なお時間を割いてまで本記事を読む必要はないかと思います。

私の今までの経験ですと大体が下記のような感じでした。(GitHub ActionsやAzure PipelinesなどのCDパイプラインまたはHCP Terraform(旧Terraform Cloud)を利用している前提です。)

  1. バージョン固定(=)をしてサービスローンチ時から塩漬け。重い腰を上げてのバージョンアップは年一回程度。
  2. 男気溢れる(死語)常に最新バージョン(>=)でterraform init。もちろんterraform.lock.hcl.gitignoreに投入済み。

書いてて自分でもなかなかパンチが効いているなとは思った2点目はそこまで見かけなかったですが1点目はまあまあ遭遇しました。

そして漏れなく弊チームも1点目の状態となっていました。これはただ単にバージョンアップをしようとしていない、というわけではなくチームメンバーが少人数、かつ他に優先度が高いタスクがあり手が回っていない状態でした。

当然、このまま放置すると問題は発生します。特に下記の点が悩ましくもどかしい問題となります。

  • Terraform Versionがバージョンアップして便利な機能がリリースされても使えない日々。importブロックのfor_each、メチャクチャ便利で使えそうな場面があるのだけど使うことが出来ない…とか
  • terraform planしたら知らない差分が出ている。原因はProver Versionのアップグレードでいつの間にかパラメータの既定値が変わっていたり、新規パラメータが追加されていたりとか

上記の問題を解消してチームメンバーが優雅なTerraformライフを送るためにまずは無理なくバージョンアップ出来るような方針を検討しました。

Terraformバージョンアップの方針

結論

まずはバージョンアップ方針は下記の通りとなりました。

  • メジャーバージョンは破壊的変更(BREAKING CHANGE)、廃止(DEPRECATIONS)を伴う可能性があるため、下記を対応した上でバージョンアップをする。
    • Release Notesでバージョンアップによる影響の調査
    • 影響がある場合は、バージョンアップのPR (Pull Request) にコード修正を含める
    • バージョンアップのための対応期間は下位互換性の有無と影響度合い、チームメンバー全体のタスク状況を考慮してチームプランニング会*2で決める。
  • マイナーバージョンについても上記と同様の対応をする。但し、メジャーバージョンとは異なり定期的なバージョンアップサイクルを設ける。
  • パッチバージョンについては後方互換性が担保されるため、常に最新のバージョンを維持する。

以降で上記の方針に至った背景をご説明します。

背景

先述した通り本記事では下記の2種類*3をTerraformバージョンの対象としています。

  • Terraform Version
  • Provider Version

両方ともセマンティック バージョニングが採用されていますが、考慮点が異なるのでそれぞれ検討しました。

semver.org

なお、以降の項目ではHashiCorp公式ドキュメントの文章の引用、及び日本語訳を記載しています。 日本語訳はGoogle翻訳にて翻訳の上、私の方で必要に応じて若干の修正を加えております。

Terraform Version

まずは下記のチュートリアルからバージョンアップに関する記載を参考にしました。

developer.hashicorp.com

HashiCorp uses the format major.minor.patch for Terraform versions. HashiCorp updates Terraform frequently, so it is common to use configuration written for an earlier version of Terraform. New minor and patch versions of Terraform are backward compatible with configuration written for previous versions. Because of this, you can upgrade to a newer minor version of Terraform and still use your existing configurations. However, upgrading your Terraform version can have other consequences, such as requiring you to update your provider versions. Some version updates may refresh your state file version or require configuration file edits to implement new features. Use the required_version setting to control which versions of Terraform will work with your configurations to ensure that updates to your infrastructure are safe and predictable.

日本語訳)

HashiCorp は、Terraform バージョンの形式として major.minor.patch を使用します。 HashiCorp は Terraform を頻繁に更新するため、以前のバージョンの Terraform 用に作成された構成を使用するのが一般的です。 Terraform の新しいマイナー バージョンとパッチ バージョンは、以前のバージョン用に作成された構成と下位互換性があります。 このため、Terraform の新しいマイナー バージョンにアップグレードしても、既存の構成を引き続き使用できます。 ただし、Terraform のバージョンをアップグレードすると、プロバイダーのバージョンの更新が必要になるなど、別の影響が生じる可能性があります。 一部のバージョン更新では、stateファイルのバージョンが更新されたり、新しい機能を実装するために構成ファイルの編集が必要になる場合があります。 required_version 設定を使用して、構成で動作する Terraform のバージョンを制御し、インフラストラクチャへの更新が安全かつ予測可能であることを確認します。

要点としては下記となります。

  • マイナーバージョンとパッチバージョンは前バージョンとの下位互換がある。
  • 但し、メジャー/マイナーバージョンのアップグレードについては下記影響の可能性がある。
    • Provider Versionのアップグレードが必要になる。
    • stateファイルのバージョンが更新される。
    • 新しい機能実装のために構成ファイルの編集が必要となる。

上記で「メジャー/マイナーバージョンのアップグレードについては…」としている根拠として、日本語訳中の「Terraform のバージョンをアップグレードすると…」のTerraformのバージョンとは何のことか?という疑問があったのですがマイナーバージョンのアップグレードガイドに下記の記載がありました。

Terraform v1.X honors the Terraform v1.0 Compatibility Promises, but there are some behavior changes outside of those promises that may affect a small number of users. Specifically, the following updates may require additional upgrade steps:

日本語訳)

Terraform v1.X は、Terraform v1.0 の互換性に関する約束を遵守していますが、これらの約束以外に、少数のユーザーに影響を与える可能性のある動作の変更がいくつかあります。具体的には、次の更新では追加のアップグレード手順が必要になる場合があります。

ということは結局マイナーバージョンのアップグレードでも少なからずTerraformコードの修正が発生する可能性があるので「メジャー/マイナーバージョンのアップグレードについては…」と定義しました。

また、チュートリアル内の下記の点についても考慮が必要となります。

Terraform will only update the state file version when a new version of Terraform requires a change to the state file's format. Terraform will update the terraform_version whenever you apply a change to your configuration using a newer Terraform version.

In general, Terraform will continue to work with a given state file across minor version updates. For major or minor releases, Terraform will update the state file version if required, and give an error if you attempt to run an older version of Terraform using an unsupported state file version.

If you were to attempt to apply this configuration again using an older version of Terraform that does not support the current state file version, Terraform returns a state lock error and displays the necessary version.

日本語訳)

Terraform は、Terraform の新しいバージョンでstateファイルの形式の変更が必要な場合にのみ、stateファイルの version を更新します。 新しい Terraform バージョンを使用して構成に変更を適用するたびに、Terraform は terraform_version を更新します。

一般に、Terraform は、マイナー バージョンが更新されても、指定されたstateファイルを引き続き使用します。 メジャー リリースまたはマイナー リリースの場合、Terraform は必要に応じてstateファイルのバージョンを更新し、サポートされていないstateファイル バージョンを使用して古いバージョンの Terraform を実行しようとするとエラーが発生します。

現在のstateファイルのバージョンをサポートしていない古いバージョンの Terraform を使用してこの構成を再度適用しようとすると、Terraform は状態ロック エラーを返し、必要なバージョンを表示します。

例えば 1.11 のTerraformでPlan/Apply実行した場合、場合によってはtfstate内のバージョンも "terraform_version": “1.11.X” となるため1.10 のTerraform Plan/Applyを実行したらエラーとなる可能性がある、ということです。(恐らく)

以上のことからメジャーバージョン、マイナーバージョンは固定として定期的にアップグレードするという結論に至りました。

Provider Version

Provider Versionについては正直調べるまでもなく…、という気もしましたが念のためちゃんと調べて検討しました。

まずは下記のチュートリアルでVersionを制約しない場合の影響が記載されていました。

developer.hashicorp.com

If you do not scope provider version appropriately, Terraform will download the latest provider version that fulfills the version constraint. This may lead to unexpected infrastructure changes. By specifying carefully scoped provider versions and using the dependency lock file, you can ensure Terraform is using the correct provider version so your configuration is applied consistently.

日本語訳)

プロバイダーのバージョンを適切にスコープしないと、Terraform はバージョン制約を満たす最新のプロバイダー バージョンをダウンロードします。 これにより、予期しないインフラストラクチャの変更が発生する可能性があります。 プロバイダーのバージョンを慎重に指定し、依存関係ロック ファイルを使用することで、Terraform が正しいプロバイダー バージョンを使用していることを確認し、構成が一貫して適用されるようにすることができます。

予期しないインフラストラクチャの変更が発生する可能性があります…恐ろしいですね。

また、Style Guideではメジャーバージョン、マイナーバージョンの固定が推奨されていました。

developer.hashicorp.com

To prevent providers and modules upgrades from introducing unintentional changes to your infrastructure, use version pinning.

Specify provider versions using the required_providers block. Terraform version constraints support a range of accepted versions.

Pin modules to a specific major and minor version as shown in the example below to ensure stability. You can use looser restrictions if you are certain that the module does not introduce breaking changes outside of major version updates.

日本語訳)

プロバイダーとモジュールのアップグレードによってインフラストラクチャに意図しない変更が加えられるのを防ぐには、バージョン固定を使用します。

required_providers ブロックを使用してプロバイダーのバージョンを指定します。 Terraform のバージョン制約は、許容されるさまざまなバージョンをサポートします。

安定性を確保するには、以下の例に示すように、モジュールを特定のメジャー バージョンとマイナー バージョンに固定します。 メジャー バージョンの更新以外でモジュールに重大な変更が導入されないことが確実な場合は、より緩やかな制限を使用できます。

最後にTerraform Providerに登録されるPluginの開発ドキュメントにはバージョン管理のベストプラクティスについての記載がありました。

developer.hashicorp.com

Observing that Terraform plugins are in many ways analogous to shared libraries in a programming language, we adopted a version numbering scheme that follows the guidelines of Semantic Versioning. In summary, this means that with a version number of the form MAJOR.MINOR.PATCH, the following meanings apply:

  • Increasing only the patch number suggests that the release includes only bug fixes, and is intended to be functionally equivalent.
  • Increasing the minor number suggests that new features have been added but that existing functionality remains broadly compatible.
  • Increasing the major number indicates that significant breaking changes have been made, and thus extra care or attention is required during an upgrade. To allow practitioners sufficient time and opportunity to upgrade to the latest version of the provider, we recommend releasing major versions no more than once per year. Releasing major versions more frequently could present a barrier to adoption due to the effort required to upgrade.

Version numbers above 1.0.0 signify stronger compatibility guarantees, based on the rules above. Each increasing level can also contain changes of the lower level (e.g. MINOR can contain PATCH changes).

日本語訳)

Terraform プラグインは多くの点でプログラミング言語の共有ライブラリに似ていることに気づき、セマンティック バージョニングのガイドラインに従ったバージョン番号付けスキームを採用しました。 要約すると、これは、MAJOR.MINOR.PATCH 形式のバージョン番号では、次の意味が適用されることを意味します。

  • パッチ番号のみが増加することは、リリースにバグ修正のみが含まれており、機能的に同等であることを意図していることを示しています。
  • マイナー番号が大きくなるということは、新しい機能が追加されたものの、既存の機能には広範な互換性が残っていることを示しています。
  • メジャー番号の増加は、重大な破壊的変更が加えられたことを示しているため、アップグレード中に特別な注意が必要です。 実務者がプロバイダーの最新バージョンにアップグレードするための十分な時間と機会を確保するために、メジャー バージョンのリリースは年に 1 回までにすることをお勧めします。 メジャー バージョンをより頻繁にリリースすると、アップグレードに必要な労力が原因で導入に障壁が生じる可能性があります。

1.0.0 より上のバージョン番号は、上記のルールに基づいて、より強力な互換性保証を意味します。 増加する各レベルには、下位レベルの変更を含めることもできます(例: MINOR には PATCH の変更を含めることができます)。

つらつらと公式ドキュメントを引用しましたが落とし穴考慮点はマイナーバージョンの互換性はあくまで「広範」となっていることだと思いました。

この記載が気になりいくつかのProviderのRelease Notesを眺めましたが恐らくProviderによって「広範」の捉え方が異なるんだな…と個人的に感じました。

何のことかといいますと、あるProviderのマイナーバージョンのアップグレードにおけるRelease Notesは一貫して

  • FEATURES
  • ENHANCEMENTS
  • BUG FIXES

のみなのですが別のあるProviderでは上記と併せて

  • BREAKING CHANGE
  • DEPRECATIONS

が含まれていることがありました。

互換性とは一体…と突っ込みたくなりますが「広範」な互換性であるということはやはりコード修正が発生する可能性があるので度合いは違えどメジャーバージョンのアップグレードとほぼ同じ対応方針でよいのでは、と考えました。

以上のことからメジャーバージョン、マイナーバージョンは固定として定期的にアップグレードするという結論に至りました。

ということでTerraform Versionと同じ結論になったので統一した方針となりました。

.terraform.lock.hcl の取り扱い

バージョンアップの方針を取り決めて運用をしていく上で.terraform.lock.hclを管理するかはチーム内で少し議論になりました。 公式ドキュメントではリポジトリ管理を推奨していますね。

developer.hashicorp.com

Terraform automatically creates or updates the dependency lock file each time you run the terraform init command. You should include this file in your version control repository so that you can discuss potential changes to your external dependencies via code review, just as you would discuss potential changes to your configuration itself.

日本語訳)

Terraform は、terraform init コマンドを実行するたびに、依存関係ロック ファイルを自動的に作成または更新します。構成自体に対する潜在的な変更について議論するのと同じように、コード レビューを通じて外部依存関係に対する潜在的な変更について議論できるように、このファイルをバージョン管理リポジトリに含める必要があります。

結論としてはあえて.gitignoreの対象に含めて管理しないことにしました。 理由としては下記となります。

  • メジャーバージョン、マイナーバージョンはバージョン制約で固定している。
  • 方針に則り、常に最新のパッチバージョンでTerraformを実行する。
  • Pull Request前にGitHub ActionsでTerraform Planは必須とするのでローカルの.terraform.lock.hclは管理不要とする。

バージョンアップの流れ、すなわち運用

では実際に日々の業務の中でどうやってバージョンアップをしていくことになったのかを説明します。

0. バージョンアップサイクル

  • 実働メンバーが少人数(本記事執筆時点で3人)、かつ現時点ではほぼ手動作業が主となるためマイナーバージョンのアップグレードサイクルは月次とする。
  • 先述の通り、メジャーバージョンのアップグレードはチームプランニング会で決める。

担当者はチームプランニング会でLinearチケットをアサイン、SREチームの全メンバーでローテーション、担当者は以降のプロセスに従いバージョンアップを実施します。

1. アップデート情報の確認

  1. 現在の利用バージョンからアサイン時点までのアップグレード情報をGitHubリポジトリのRelease Notesで調査する。
  2. BREAKING CHANGE, DEPRECATIONSの有無を確認し、影響有りであれば備忘として内容をチケットにコメントする。(後続のPull Request Descriptionにも掲載する。)

2. 事前Terraform Planのためのコード更新

作業ブランチをチェックアウトして下記を更新する。

  • Terraform Blockの下記を更新する。
    • required_version
    • required_providers.<provider>.version

パッチバージョンは常に最新とするため下記の通り ~> x.y.0 のようにバージョン指定する。

terraform {
  required_version = "~> 1.8.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.100.0"
    }
  }
}
  • Terraform Versionがアップグレード対象の場合、GitHub ActionsのTerraform Plan/Apply実行用Workflowファイルの下記を更新する。
    • terraform_version

パッチバージョンは常に最新とするため下記の通り~1.yのようにバージョン指定する。

- name: Setup Terraform
  uses: hashicorp/setup-terraform@v2
  with:
    terraform_version: ~1.8
  • Release Notesの内容からバージョンアップ以外の修正が必要であれば更新する。

3. 事前Terraform Plan結果確認

  1. 作業ブランチで全環境(develop, staging, prroductionなど)に対してTerraform Plan用Workflowを実行する。
  2. Plan結果に差分があれば(すなわち No changes. ではないのであれば)適宜修正する。

ここでTopicです。

Provider VersionのアップグレードはRelease NoteのBREAKING CHANGE, DEPRECATIONS以外に下記の点でTerraformコード修正が必要な場合があります。

  • パラメータの既定値が変わることがある。
  • tfstateに新規パラメータが追加されることがある。

新規パラメータ追加についてはApplyせざるを得ないのでその場合はPull Request Descriptionに実機影響ないことを含めてその旨を記載することにしました。

4. 既定ブランチへのPull Request, Merge

下記をDescriptionに記載してPull Requestを作成、承認後Mergeする。

  • BREAKING CHANGE, DEPRECATIONSによる更新概要
  • Plan結果に差分がある場合の説明

5. Terraform Apply, 動作確認

  • サンドボックス環境、開発環境、本番環境といったユーザー影響の無い順番でTerraform Apply、動作確認を実施する。
  • Terraform Applyにより変更が発生してもあくまでtfstateのみが更新される想定ではあるが念のためアプリケーションの動作確認を行う。

全体像

まとめとしてバージョンアップの流れの全体をざっくりフローチャートにすると下記の通りとなりました。

特に複雑な作業項目はありませんが、どのような作業があるかを可視化出来たのでチームメンバーへ進捗状況の共有がしやすくなりました。

これから取り組んでいくこと

今回はバージョンアップをちゃんとはじめるために終始方針を決めて運用を整理したことについてご紹介しましたが先述の通りほぼ手動作業が主となるため実際に数回のサイクルを回してチームメンバー全員が流れを把握出来たら「この部分はこれで自動化出来るよね」とか「ここはこうした方がみんなの負担が減って楽に回していけるよね」ということを議論して運用の改善を続けていきます。

少し先のことにはなりますが、改善のフェーズが進んだらテッキーなことをしてクールに自動化してやったぜ~ワイルドだろ~(もちろん、左記が本質ではないですが)という内容についても本ブログでご紹介したいと思います。

終わりに

最後はまとめではなく自身への戒めを込めたポエムで締めます。

Terraformバージョンアップの内容からは外れますが日々の業務の中で別のチームからの依頼、または自発的にそれとなく誰かがこなしている作業というのは少なからずあると思います。所謂「業務の属人化」ですね。普段はその対応者がそれとなく作業をこなしているので他のチームメンバーが意識することはあまりないんじゃないかと思います。ただ言うまでもなく「業務の属人化」はたくさんのリスクをはらんでいます。例えば、その対応者が退職したり事故や病気などで突然の長期休暇になってしまったときなどですね。当然その対応者がいなくなってもシステムは稼働し続けるので他の誰かがその対応者の代わりに属人化されていた作業をしなければなりません。例えほとんどの作業が自動化されており極端な話、クリック一つで完了する形になっていても中身のプロセスを知らなければイレギュラーな事象が発生した時に直ぐ対応することはなかなか難しいと思います。

従って、どんなに単純な作業でも下記を念頭に置くことが大事です。

  • 「チームメンバー全員」が作業の流れを把握するためのドキュメントを作成する。不明点が残らないように「チームメンバー全員」でそのドキュメントをレビューする。
  • 自動化の仕組みはシンプルで分かりやすいようにする。可能な限り誰かの手組した魔改造ツールには頼らない。「チームメンバー全員」が把握出来る構成にする、かつREADMEを作成する。

上記の「チームメンバー全員」というのは、今回のTerraformバージョンアップの件はSREチームメンバーのことを指しています。当たり前のことではありますが開発チームやビジネスチームまで対象にするとドキュメントに書くべき情報量が無限に広がっていくので最初にその作業を対応する対象チームの範囲を決めた上でチームのレベルを考慮したドキュメントを作成すれば良いと思います。例えば、弊SREチームの場合は全員Terraformの仕組みや基本的なコマンドは熟知しているのでそういった情報は全て省きました。

上記が整うことで初めて「チームメンバー全員」が本当の意味で「楽」できるようになります。実際Terraformバージョンアップ一つとってもドキュメントを執筆するのは「楽」ではないですしちょいちょいコーディング関連の作業に逃げたくなりました(泣)

また、自動化の仕組みはこれからの取り組みになるので「楽」するのはもう少し先になりそうです。

ということで全くTechなBlogになっておらず恐縮ですが、「楽」する前のTerraformバージョンアップ方針と運用整理、でした。

私たちのチームでは、ともに働く仲間を積極的に募集しています。少しでも興味をお持ちいただけましたら、ぜひカジュアル面談からご応募ください!

Application Developers www.wantedly.com

SRE www.wantedly.com

*1:新卒社員ではございません

*2:SREチームでは一週間をサイクルとして各メンバーのタスクを計画しています。その計画をメンバー全員で話し合って決める会です。

*3:本記事執筆時点で、弊チームではPublic RegistryのModule、HCP TerraformのPrivate RegistryのModuleは利用していないのでModuleのバージョンについては対象外としています。