Oracle Cloud Hangout Cafe Season7 #2「IaC のベストプラクティス」(2023年7月5日開催)

2024年1月18日(木)
川村 修平
第2弾の連載第2回では、2023年7月5日に開催された 「Oracle Cloud Hangout Cafe Season7 #2『IaC のベストプラクティス』」の発表内容に基づいて紹介していきます。

リソースは作られたが…

ここまで、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.tfmain-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を設定していること
  • モジュールの標準構造に従っていること
  • リソースのタグ名がセマンティックバージョンであること
    • [例]v1.0.4, 0.9.2, ...

    チーム開発に関する考慮
    アプリケーション開発において、チーム内でソースコードを共有する場合は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を用いた開発で変わらないため、以下の指針に従ってテストを実装していくと良いでしょう。

    test pyramid

    また、当日のセッションでは、TerratestというGruntworkht社が開発するIaCの自動テスト用のGolangライブラリを用いた Terraformの自動テストのデモも実施しています。モジュールのコードに変更が加わるとGitHub Actions上でTerratestによりOCI Object Storage - Bucketを作成し、それが期待通りの名前であることを確認して、後処理として削除するという流れです。当日のデモ動画は、こちらから参照してください。

    terraform testing by terratest

    品質を向上させる方法としては、静的解析ツールを用いるアプローチも存在します。実際にリソースを作成するわけではないため検証可能な項目は限定的ですが、高速で安定的に動作するというメリットがあります。また、プロバイダーの認証が不要で使えるのもメリットの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)のバケットの作成をツールで指摘するというデモも実施しました。デモの様子は、こちらから参照してください。

    tfsec demo

    日本オラクル株式会社

    Oracle Groundbreaker Advocate
    Cloud Solution Engineer

    日本オラクルに入社後、ソリューション・アーキテクトとしてクラウドでのアプリケーション開発、クラウドネイティブ技術、ストリーム処理、全文検索、AI 等に関する技術支援や案件支援に従事。現在、クラウドネイティブ技術に関連するコミュニティの運営にも参加中。

    Community:
    Oracle Cloud Hangout Cafe メンバー (#ochacafe)
    CloudNative Days - Observability チーム

    連載バックナンバー

    仮想化/コンテナ技術解説
    第6回

    Oracle Cloud Hangout Cafe Season 4 #5「Kubernetesのオートスケーリング」(2021年8月4日開催)

    2024/5/29
    第2弾の連載第6回では、2021年8月4日に開催された「Oracle Cloud Hangout Cafe Season4 #5『Kubernetesのオートスケーリング』」の発表内容に基づいて紹介していきます。
    仮想化/コンテナ技術解説
    第5回

    Oracle Cloud Hangout Cafe Season4 #4「Observability 再入門」(2021年9月8日開催)

    2024/4/23
    第2弾の連載第5回では、2021年9月8日に開催された「Oracle Cloud Hangout Cafe Season4 #4『Observability 再入門』」の発表内容に基づいて紹介していきます。
    仮想化/コンテナ技術解説
    第4回

    Oracle Cloud Hangout Cafe Season6 #4「Pythonで作るAPIサーバー」(2022年12月7日開催)

    2024/3/21
    第2弾の連載第4回では、2022年12月7日に開催された 「Oracle Cloud Hangout Cafe Season6 #4『Pythonで作るAPIサーバー』」の発表内容に基づいて紹介していきます。

    Think ITメルマガ会員登録受付中

    Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

    Think ITメルマガ会員のサービス内容を見る

    他にもこの記事が読まれています