ウェブサイト検索

GitOps を使用して Terraform を自動化する方法


CI/CD パイプラインや Terraform Cloud を使用する代わりに、Flux と GitOps を使用して Terraform を自動化するこの代替アプローチを試してください。

ワークフローとしての GitOps は、主に Kubernetes 環境で使用されるアプリケーション配信に最適ですが、インフラストラクチャにも使用できます。典型的な GitOps シナリオでは、従来のインフラストラクチャのほとんどが依然として CI/CD パイプラインで使用されている一方で、Kubernetes ネイティブの代替手段として Crossplane などのソリューションを検討することをお勧めします。 Kubernetes をベースとしてデプロイメント プラットフォームを作成することにはいくつかの利点がありますが、より多くの人々がその特定のスキル セットを持っている必要があることも意味します。 Terraform のような Infrastructure-as-Code ツールの利点の 1 つは、学習が簡単で、専門知識をあまり必要としないことです。

私のチームがプラットフォーム サービスを構築していたとき、誰もが貢献できるようにしたいと考えていました。当社のエンジニアの全員ではないにしても、ほとんどが日常的に Terraform を使用しています。彼らは、複数のシナリオや複数の顧客向けに使用できる Terraform モジュールの作成方法を知っています。 Terraform を自動化するにはいくつかの方法がありますが、可能な限り適切な GitOps ワークフローを利用したいと考えています。

Terraform コントローラーはどのように機能するか

Kubernetes を使用して Terraform を実行するための代替手段を探しているときに、いくつかのコントローラーとオペレーターを見つけましたが、Weaveworks の tf コントローラーほど可能性があると感じたものはありませんでした。私たちはすでにGitOpsツールとしてFluxを使用しています。 tf コントローラーは、Flux のコア機能の一部を利用して動作し、Terraform デプロイメント用のカスタム リソースを備えています。ソース コントローラーはモジュールのフェッチを処理し、KusTOMize コントローラーは Terraform リソースを適用し、コントローラーは Terraform コマンドを実行する静的ポッド (ランナーと呼ばれる) を起動します。

Terraform リソースは次のようになります。

apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
  name: helloworld
  namespace: flux-system
spec:
  interval: 1m
  approvePlan: auto
  path: ./terraform/module
  sourceRef:
    kind: GitRepository
    name: helloworld
    namespace: flux-system

ここでの仕様に関して注意すべき点がいくつかあります。仕様の間隔は、コントローラーがランナー ポッドを起動する頻度を制御します。これにより、パス パラメーターで定義されたルート モジュールで terraform plan が実行されます。

この特定のリソースは、計画を自動的に承認するように設定されています。これは、計画とターゲット システムの現在の状態に差異がある場合、新しいランナーが実行され、変更が自動的に適用されることを意味します。これにより、プロセスは可能な限り「GitOps」になりますが、これを無効にすることもできます。無効にした場合は、計画を手動で承認する必要があります。これを行うには、Terraform コントローラー CLI を使用するか、適用する必要があるコミットへの参照を使用してマニフェストを更新します。詳細については、手動承認に関するドキュメントを参照してください。

tf コントローラーは Flux のソース コントローラーを利用します。 sourceRef 属性は、Flux Kustomization リソースと同様に、使用するソース リソースを定義するために使用されます。

高度な導入

上記の例は機能しますが、これは私のチームが通常行うような展開のタイプではありません。バックエンド ストレージを定義しない場合、状態はクラスターに保存されるため、テストや開発には問題ありません。しかし、実稼働環境では、状態ファイルはクラスターの外部のどこかに保存されることを好みます。ルートモジュールをいくつかのデプロイメントで再利用したいので、これをルートモジュールで直接定義したくありません。これは、Terraform リソースでバックエンドを定義する必要があることを意味します。

ここでは、カスタム バックエンド構成をセットアップする方法の例を示します。利用可能なすべてのバックエンドは、Terraform ドキュメントで見つけることができます。

apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
  name: helloworld
  namespace: flux-system
spec:
  backendConfig:
	customConfiguration: |
		backend "azurerm" {
		  resource_group_name  = "rg-terraform-mgmt"
		  storage_account_name = "stgextfstate"
		  container_name       = "tfstate"
		  key                  = "helloworld.tfstate"
		}
  ...

状態ファイルをクラスターの外に保存するということは、クラスターを再デプロイできることを意味します。ただし、ストレージへの依存はありません。バックアップや状態移行は必要ありません。新しいクラスターが起動するとすぐに、同じ状態に対してコマンドが実行され、業務に戻ります。

もう 1 つの高度な取り組みは、モジュール間の依存関係です。場合によっては、2 段ロケットのようにデプロイメントを設計することもあります。つまり、1 つのデプロイメントが次のデプロイメントで使用する特定のリソースを設定します。これらのシナリオでは、2 番目のモジュールの入力として必要なデータを出力し、最初のモジュールが最初に正常に実行されるように、Terraform がそのような方法で記述されていることを確認する必要があります。

これら 2 つの例は、依存関係を示すときに使用したコードからのもので、すべてのコードは私の GitHub で見つけることができます。簡潔にするために、コードの一部は省略されています。

apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
  name: shared-resources
  namespace: flux-system
spec:
  ...
  writeOutputsToSecret:
    name: shared-resources-output
  ...
apiVersion: infra.contrib.fluxcd.io/v1alpha1
kind: Terraform
metadata:
  name: workload01
  namespace: flux-system
spec:
  ...
  dependsOn:
    - name: shared-resources
	...
  varsFrom:
    - kind: Secret
      name: shared-resources-output
  ...

私が共有リソースと呼ぶデプロイメントでは、デプロイメントからの出力を保存する場所にシークレットを定義したことがわかります。この場合、出力は次のようになります。

output "subnet_id" {
  value = azurerm_virtual_network.base.subnet.*.id[0]
}

output "resource_group_name" {
  value = azurerm_resource_group.base.name
}

workload01 デプロイメントでは、最初に dependsOn 属性を使用して依存関係を定義します。これにより、共有リソースが正常に実行されることを確認してから、ワークロード01。その後、共有リソースからの出力がworkload01の入力として使用されます。これが待機する理由です。

パイプラインや Terraform Cloud を使用しない理由

Terraform を自動化する最も一般的なアプローチは、CI/CD パイプラインまたは Terraform Cloud を使用することです。 Terraform にパイプラインを使用すると問題なく機能しますが、通常はパイプライン定義を何度もコピーする必要があります。これに対する解決策はありますが、tf コントローラーを使用すると、命令的な方法でステップを定義するのではなく、デプロイメントをどのようにするかを定義するはるかに宣言的なアプローチが得られます。

Terraform Cloud には、GitOps ワークフローの使用と重複する多くの機能が導入されていますが、tf-controller を使用しても Terraform Cloud の使用が除外されるわけではありません。 Terraform Cloud をデプロイメントのバックエンドとして使用し、tf コントローラーによる実行のみを自動化することもできます。

私のチームがこのアプローチを使用している理由は、すでに GitOps を使用してアプリケーションをデプロイしており、これらの機能をサービスとして提供する方法についてより柔軟に対応できるためです。 API を通じて実装を制御できるため、オペレーターとエンドユーザーの両方がセルフサービスにアクセスしやすくなります。プラットフォームへのアプローチに関する詳細は非常に大きなトピックなので、別の記事で再度説明する必要があります。

この記事はもともと著者のブログに公開されたもので、許可を得て再公開したものです。

関連記事: