Oracle Cloud Hangout Cafe Season7 #2「IaC のベストプラクティス」(2023年7月5日開催)
リソースは作られたが…
ここまで、Terraformを用いてクラウドリソースの作成から削除まで一連の操作を実施してきましたが、いくつか改善点があります。具体的には以下の通りです。
- 現在使用しているプロバイダーのバージョンが不明確なこと
- コードの再利用性がないこと
- チームで開発することが考慮されていないこと
- コードの品質に関する保証がされていないこと
プロバイダーのバージョンが不明確
Terraformでは、Terraformブロックを用いてTerraform自体の動作設定を行うことができます。
terraform { # Terraform の動作に関する設定 }
具体的には、Terraform Cloud*3を使用するための設定、Terraform自身やプロバイダーに要求するバージョンの設定などを行うことができます。その他の設定や詳細についてはhttps://developer.hashicorp.com/terraform/language/settingsを参照してください。
ここでは、例としてTerraform自身と使用しているプロバイダーに設定を加えます。前述の「クラウドリソースをプロビジョニングしてみよう」でプロバイダーの初期化処理(ライブラリのインストール等)を実行した際の実行ログは以下のようでした。
*3: Terraformをチームで利用するためのアプリケーションのことで、Terraformの実行環境やStateファイルの管理を行うInitializing the backend... Initializing provider plugins... - Finding latest version of hashicorp/oci... - Installing hashicorp/oci v5.21.0... - Installed hashicorp/oci v5.21.0 (signed by HashiCorp) Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. ╷ │ Warning: Additional provider information from registry │ │ The remote registry returned warnings for registry.terraform.io/hashicorp/oci: │ - For users on Terraform 0.13 or greater, this provider has moved to oracle/oci. Please │ update your source in required_providers. ╵ Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
記載の通り、Terraform 0.13以降は、プロバイダーの取得場所がregistry.terraform.io/hashicorp/oci
からoracle/oci
へ移されたため、required_providers
を更新してください、と警告ログが出力されています。今回はプロバイダーの取得場所を変えることに加えて、2023年12月現在の最新バージョンである5.12.0を明示的に指定し、Terraform自身も1.6.0 以上を使うことを要求します。
provider "oci" { region = var.region } terraform { required_version = ">=1.6.0" required_providers { oci = { source = "oracle/oci" version = "5.21.0" } } }
この状態で再度、初期化処理を実行します。
$ terraform init Initializing the backend... Initializing provider plugins... - Finding oracle/oci versions matching "5.21.0"... - Installing oracle/oci v5.21.0... - Installed oracle/oci v5.21.0 (signed by a HashiCorp partner, key ID 1533A49284137CEB) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://www.terraform.io/docs/cli/plugins/signing.html Terraform has made some changes to the provider dependency selections recorded in the .terraform.lock.hcl file. Review those changes and commit them to your version control system if they represent changes you intended to make. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
これで、Terraformの動作に設定を加えることができました。意図せぬ動作を防止するために、自身が使用するバージョンなどを明示的に指定しておくことは重要です。
コードの再利用性
アプリケーションのコードを実装するときは、似たような処理をメソッドや関数として実装し、入力の引数を与えて戻り値を得ます。Terraformにも似たような仕組みが存在し、Terraform Moduleと呼びます。Terraform Moduleは一緒に使用する複数リソースが書かれた.tf
ファイルをディレクトリとしてまとめたもののことで、Terraformでリソースの設定をパッケージ化し再利用するための主な方法となります。
シンプルなモジュールの実装例は、以下の通りです。
provider "oci" { region = var.region } # 引数相当 variable "region" {} variable "compartment_ocid" {} variable "vcn_display_name" {} locals { vcn_cidr_block = "10.0.0.0/16" } # メソッド、関数の処理相当 resource "oci_core_vcn" "vcn" { compartment_id = var.compartment_ocid cidr_block = local.vcn_cidr_block display_name = var.vcn_display_name } # 戻り値相当 output "vcn" { value = oci_core_vcn.vcn }
上記のモジュールで仮想ネットワーク(VCN)をステージング環境は大阪リージョン(ap-osaka-1)、本番環境は東京リージョン(ap-tokyo-1)に作る例は、以下の通りです。
module "vcn-stage" { source = "../modules" compartment_ocid = "..." region = "ap-osaka-1" vcn_display_name = "vcn-stage" }
module "vcn-prod" { source = "../modules" compartment_ocid = "..." region = "ap-tokyo-1" vcn_display_name = "vcn-prod" }
また、Terraform Moduleにはhttps://developer.hashicorp.com/terraform/language/modules/develop/structureに記載の通り標準のディレクトリ構造が存在します。
$ tree complete-module/ . ├── README.md ├── main.tf ├── variables.tf ├── outputs.tf ├── ... ├── modules/ │ ├── nestedA/ │ │ ├── README.md │ │ ├── variables.tf │ │ ├── main.tf │ │ ├── outputs.tf │ ├── nestedB/ │ ├── .../ ├── examples/ │ ├── exampleA/ │ │ ├── main.tf │ ├── exampleB/ │ ├── .../
上記の全体構造のうち、以下の部分をルートモジュールと呼びます。ファイル名も通例これに従いますが、この限りではありません。
├── README.md ├── main.tf ├── variables.tf ├── outputs.tf ├── ...
main.tf
はルートモジュールのエントリーポイントとなります。複雑なモジュールの場合にはmain-network.tf
、main-compute.tf
のように複数に分割する場合もあります。variables.tf
は、ルートモジュールに対する入力変数を宣言します。このとき、入力変数に対する説明(description)は可能な限り記述する方が良いと言及されています。output.tf
はルートモジュールに対する出力変数を宣言します。こちらも入力変数と同様に可能な限りdescriptionを記述する方が良いと言及されています。
次に、以下の部分です。
├── modules/ │ ├── nestedA/ │ │ ├── README.md │ │ ├── variables.tf │ │ ├── main.tf │ │ ├── outputs.tf │ ├── nestedB/ │ ├── .../
Terraform Moduleは通常、modules/*
に配置します。ここは小さなモジュールに分割し、ユーザーが選択可能とするべきだと言及されています。また、通常README.md
が存在するモジュールは外部のユーザーが使用可能なモジュールで、README.md
が存在しないモジュールは内部利用に限定されたモジュールとみなされます。
モジュールの使用例は、examples/*
に配置します。各サンプルの目的や使い方を説明するREADME.md
と合わせて置くのも良いでしょう。
├── examples/ │ ├── exampleA/ │ │ ├── main.tf │ ├── exampleB/ │ ├── .../
そして、作成したTerraform Moduleはいくつかの条件を満たすと公開することができます。
- Terraform ModuleがGitHubのパブリックリポジトリとして公開されていること
- リポジトリが命名規則に従っていること
- 命名規則: terraform-[PROVIDER]-[NAME]
- リポジトリにdescriptionを設定していること
- モジュールの標準構造に従っていること
- リソースのタグ名がセマンティックバージョンであること
チーム開発に関する考慮
アプリケーション開発において、チーム内でソースコードを共有する場合はGit等のバージョン管理システムを用いることが一般的だと思いますが、Terraform Stateに関してはこの方法は良くないとされています。理由は、最新のStateファイルのコミット漏れが発生したり、同一Stateファイルにterraform apply
を実行することをロックする機構が存在しないことや、State ファイルが平文で保存されるため機密情報の扱いが複雑になることです。そのため、Terraform Stateの保存は上記のような課題が解決できるリモートデータストアが活用されます。代表的なのはTerraform Cloud、HashiCorp Consul、 Amazon S3、Azure Blob Storage、Google Cloud Storage、Alibaba Cloud OSS等です。当日のセッションでは、OCI Object Storageをリモートデータストアとして活用する例を紹介していますので、関心があれば合わせて参照してください。
コードの品質向上のための取り組み
通常、アプリケーション開発では期待される結果が得られることを確認するために、Unit、Integration、End-to-endテストを実装して自動テストに取り組みます。特に、モジュール開発の文脈ではそのモジュールの品質を担保するために、このような取り組みを行うことは有効だと考えます。下表にモジュール開発におけるTerraformのテストの粒度を記します。
Test | Description |
---|---|
Unitテスト | 再利用可能な単一のモジュールに正しく動作することを確認する(※Terraformを扱う以上、外部への依存をゼロにする方法はないため純粋な意味でのUnitテストとは異なる) |
Integrationテスト | 複数のモジュールをデプロイし、それらが正しく動作することを確認する |
End-to-endテスト | すべてのモジュールをデプロイし、それが正しく動作することを確認する |
テストピラミッドの考え方はアプリケーションの開発、Terraformを用いた開発で変わらないため、以下の指針に従ってテストを実装していくと良いでしょう。
また、当日のセッションでは、TerratestというGruntworkht社が開発するIaCの自動テスト用のGolangライブラリを用いた Terraformの自動テストのデモも実施しています。モジュールのコードに変更が加わるとGitHub Actions上でTerratestによりOCI Object Storage - Bucketを作成し、それが期待通りの名前であることを確認して、後処理として削除するという流れです。当日のデモ動画は、こちらから参照してください。
品質を向上させる方法としては、静的解析ツールを用いるアプローチも存在します。実際にリソースを作成するわけではないため検証可能な項目は限定的ですが、高速で安定的に動作するというメリットがあります。また、プロバイダーの認証が不要で使えるのもメリットの1つです。下表で、Terraformのコードに使用できる主な静的解析ツールを簡単に紹介します。
terraform validate | tfsec | tflint | Terrascan | |
---|---|---|---|---|
Overview | Terraform組み込みコマンド | Terraformコードのセキュリティスキャン | Terraform用のリンター | コンプライアンス、セキュリティ違反チェック |
Licence | Terraformと同じ | MIT | MPL 2.0 | Apache 2.0 |
Built-in-checks | 構文チェックのみ | AWS、Azure、OCI、Kubernetes、etc. | AWS、Azure、Google Cloud | |
Custom checks | 非対応 | YAML or JSON | Go plugin | Rego |
なお、当日はtfsecを用いて、可視性がパブリック(ObjectRead
or ObjectReadWithoutList
)のバケットの作成をツールで指摘するというデモも実施しました。デモの様子は、こちらから参照してください。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- TerraformからPulumiへの移行
- Iacツール「Terraform」の基本的な使い方
- 既に存在するリソースをPulumiで管理してみよう
- PulumiでAWSリソースをデプロイしよう
- Policy as Codeでインフラのコンプライアンスを自動実現! 「Pulumi CrossGuard」を活用してみよう
- 「Pulumi Stack」とは ー Pulumiによるマルチステージ環境の構築方法
- 「Pulumi Automation API」でPulumi CLIの機能をコード化しよう
- Pulumi Kubernetes Operatorを活用してPulumiのCI/CDを実現しよう
- Infrastructure-as-Codeアプローチと「Pulumi」の概要
- SecretもPulumiで使いこなしたい! PulumiのSecurityを試してみよう