1. 概要

以前、Terraformの基本的な概念と使用法について説明しました。 それでは、この人気のあるDevOpsツールを使用する際のベストプラクティスのいくつかをさらに深く掘り下げて説明しましょう。

2. リソースファイルの編成

Terraformの使用を開始するとき、すべてのリソース定義、変数、および出力を1つのファイルに入れることは珍しくありません。 ただし、このアプローチでは、コードの保守が難しく、再利用がさらに困難になります。

より良いアプローチは、モジュール内でTerraformが任意の「.tf」ファイルを読み取り、その内容を処理するという事実を利用することです。 それらのリソースを宣言する順序は関係ありません。結局のところ、これがTerraformの仕事です。 何が起こっているのかをよりよく理解できるように、それらを整理しておく必要があります。

ここでは、ファイル内のリソースをどのように編成するかよりも、一貫性が重要です。 一般的な方法は、モジュールごとに少なくとも3つのファイルを使用することです。

  • variables.tf :すべてのモジュールの入力変数が、該当する場合はデフォルト値とともにここに表示されます
  • main.tf :ここにリソース定義を配置します。 単一責任の原則を使用していると仮定すると、そのサイズは制御されたままである必要があります
  • モジュール:モジュールにサブモジュールが含まれている場合は、ここに移動します
  • outputs.tf :エクスポートされたデータ項目はここに移動する必要があります
  • Providers.tf:トップレベルディレクトリでのみ使用され、バージョンを含め、プロジェクトで使用するプロバイダーを宣言します

この組織により、モジュールを使用したいチームメンバーは、必要な変数を見つけてデータをすばやく出力できます。

また、モジュールが進化するにつれて、main.tfファイルのサイズに注意する必要があります。 サブモジュールへのリファクタリングを検討する必要がある良い兆候は、サイズが大きくなり始めたときです。 この時点で、EC2インスタンスや接続されたEBSボリュームなど、緊密に結合されたリソースをネストされたモジュールに移動して、リファクタリングする必要があります。 結局、トップレベルの main.tf ファイルには、つなぎ合わされたモジュール参照のみが含まれている可能性があります。

3. モジュールの使用法

Modules は強力なツールですが、他の大規模なソフトウェアプロジェクトと同様に、プロジェクト間での再利用を最大化できるように、適切なレベルの抽象化を取得するには時間がかかります。 Terraformは、Infrastructure-as-Codeのプラクティス全体としては比較的新しいという事実を考えると、これは多くの異なるアプローチが見られる領域です。

そうは言っても、適切なモジュール編成に役立つアプリケーションコードベースから学んだいくつかの教訓を再利用することができます。 これらのレッスンの中で、SOLIDの一連の原則からの単一責任は非常に役立ちます。

私たちのコンテキストでは、これは、モジュールがインフラストラクチャの単一の側面、たとえばVPCのセットアップや仮想マシンの作成などに焦点を当てる必要があることを意味します。

この原則を使用するサンプルのTerraformプロジェクトディレクトリレイアウトを見てみましょう。

$ tree .
├── main.tf
├── modules
│   ├── ingress
│   │   └── www.petshop.com.br
│   │       ├── main.tf
│   │       ├── outputs.tf
│   │       └── variables.tf
... other services omitted
│   └── SvcFeedback
│       ├── main.tf
│       ├── outputs.tf
│       └── variables.tf
├── outputs.tf
├── terraform.tfvars
└── variables.tf

ここでは、インフラストラクチャの重要な側面(データベース、入力、メッセージング、外部サービス、バックエンドサービス)ごとにモジュールを使用しました。 このレイアウトでは、 .tf ファイルを含む各フォルダーは、次の3つのファイルを含むモジュールです。

  • variables.tf –モジュールの入力変数
  • main.tf –リソース定義
  • outputs.tf –出力属性の定義

この規則には、モジュールコンシューマーが、実装の詳細をスキップして、必要に応じてその「コントラクト」(変数と出力)に直接アクセスできるという利点があります。

4. プロバイダー構成

Terraformのほとんどのプロバイダーは、リソースを操作できるように有効な構成パラメーターを提供する必要があります。 たとえば、AWSプロバイダーには、アカウントにアクセスしてタスクを実行できるように、アクセスキー/シークレットとリージョンが必要です。

これらのパラメーターには機密情報とデプロイメントターゲット固有の情報が含まれているため、プロジェクトのコードの一部としてそれらを含めることは避けてください。 代わりに、変数またはプロバイダー固有のメソッドを使用して構成する必要があります。

4.1. 変数を使用したプロバイダーの構成

このアプローチでは、必要なプロバイダーパラメーターごとにプロジェクト変数を定義します。

variable "aws_region" {
  type = string
}
variable "aws_access_key" {
  type = string
}a
variable "aws_secret_key" {
  type = string
}

ここで、プロバイダー宣言でそれらを使用します。

provider "aws" {
  region = var.aws_region
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
}

最後に、.tfvarファイルを使用して実際の値を提供します。

aws_access_key="xxxxx"
aws_secret_key="yyyyy"
aws_region="us-east-1"

planapplyなどのTerraformコマンドを実行するときに、.tfvarファイルと環境変数を組み合わせることもできます。

$ export TF_VAR_aws_region="us-east-1"
$ terraform plan -var="access_key=xxxx" -var-file=./aws.tfvars

ここでは、環境変数とコマンドライン引数を組み合わせて変数値を渡しました。 これらのソースに加えて、Terraformは、terraform.tfvarsファイルおよびプロジェクトのフォルダーにある「.auto.tfvars」拡張子の付いたファイルで定義された変数も調べます。

4.2. プロバイダー固有の構成の使用

多くの場合、 Terraformプロバイダーは、ネイティブツールで使用されているのと同じ場所からクレデンシャルを選択できます。 典型的な例はKubernetesプロバイダーです。 環境に、ターゲットクラスターを指すように構成されたネイティブユーティリティ kubectl がすでにある場合は、追加情報を提供する必要はありません。

5. 状態管理

テラフォーム状態ファイルには通常機密情報が含まれているため、適切な対策を講じて保護する必要があります。 それらのいくつかを見てみましょう:

  • VCS構成ファイルの*。tfstateファイルには常に除外ルールを使用してください。 Gitの場合、これはグローバル除外ルールまたはプロジェクトの.gitignoreファイルに含めることができます。
  • デフォルトのローカルバックエンドの代わりに、できるだけ早くリモートバックエンドを採用します。 また、選択したバックエンドへのアクセス制限を再確認してください。

デフォルトの状態のバックエンド(ローカルファイル)からリモートに移行するのは簡単な作業です。 プロジェクトのファイルの1つにバックエンド定義を追加する必要があります。

terraform {
  backend "pg" {}
}

ここでは、PostgreSQLバックエンドを使用して状態情報を保存することをTerraformに通知しています。 通常、リモートバックエンドには追加の構成が必要です。 プロバイダーと同様に、推奨されるアプローチは、環境変数または「.auto.tfvars」ファイルを介して必要なパラメーターを渡すことです。

リモートバックエンドを採用する主な理由は、複数のコラボレーター/ツールが同じターゲット環境でTerraformを実行できるようにすることです。 これらのシナリオでは、同じターゲット環境で複数のTerraformを実行しないようにする必要があります。これにより、あらゆる種類の競合状態や競合が発生し、大混乱が発生する可能性があります。

リモートバックエンドはロックの概念をサポートしているため、リモートバックエンドを採用することでこれらの問題を回避できます。 つまり、 terraformplanterraformapplyなどのコマンドを順番に実行できるのは1人の共同作業者だけです。

状態ファイルの適切な管理を実施する別の方法は、専用サーバーを使用してTerraformを実行することです。 これには、Jenkins、GitLabなどのCI/CDツールを使用できます。 小規模なチーム/組織の場合、TerraformのSaaSオファリングの永久無料利用枠を使用することもできます。

6. ワークスペース

ワークスペースを使用すると、1つのプロジェクトに複数の状態ファイルを保存できます。 VCSブランチの例えに基づいて、複数のターゲット環境を処理する必要があるとすぐに、プロジェクトでそれらを使用し始める必要があります。 このようにして、Terraformをどこに向けても、同じリソースを再作成するための単一のコードベースを持つことができます。

もちろん、環境および何らかの方法で変化します。たとえば、マシンのサイズ設定/カウントなどです。 それでも、apply時に渡される入力変数を使用してこれらの側面に対処できます。

これらの点を念頭に置いて、一般的な方法は、環境名にちなんでワークスペースに名前を付けることです。 たとえば、DEV、QA、PRDなどの名前を使用できるため、既存の環境と一致します。

同じプロジェクトで複数のチームが作業している場合は、それらの名前を含めることもできます。 たとえば、新機能に取り組んでいるチーム用のDEV-SQUAD1ワークスペースと、本番環境の問題を再現して修正するための別のチーム用のDEV-SUPPORTを作成できます。

7. テスト

インフラストラクチャを処理するために標準のコーディング手法を採用し始めると、その特徴の1つである自動テストも採用するのは当然です。 これらのテストは、さまざまなシナリオで期待どおりに機能するという確信を高めるため、モジュールのコンテキストで特に役立ちます。

一般的なテストは、テスト構成を一時的な環境にデプロイし、それに対して一連のテストを実行することで構成されます。 テストは何をカバーする必要がありますか? まあ、それは私たちが作成しているものの詳細に大きく依存しますが、いくつかは非常に一般的です:

  • アクセシビリティ:リソースを正しく作成しましたか? それらは到達可能ですか?
  • セキュリティ:必須ではないネットワークポートを開いたままにしましたか? デフォルトのクレデンシャルを無効にしましたか?
  • 正しさ:モジュールはそのパラメーターを正しく使用しましたか? 欠落しているパラメーターにフラグを立てましたか?

この記事を書いている時点で、Terraformテストはまだ進化しているトピックです。 必要なフレームワークを使用してテストを作成できますが、統合テストに焦点を当てたものは、通常、このタスクに適しています。 たとえば、 FitNesse Spock Protractorなどがあります。 通常のシェルスクリプトを使用してテストを作成し、CI/CDパイプラインに追加することもできます。

8. 結論

この記事では、Terraformを使用する際のいくつかのベストプラクティスについて説明しました。 これはまだ比較的新しい分野であるため、これらを出発点としてとらえる必要があります。 より多くの人々がInfrastructure-as-Codeツールを採用するにつれて、新しいプラクティスやツールが出現する可能性があります。

いつものように、すべてのコードはGitHubを介して利用できます。