TerraformとCloudFormation:決定的なガイド

公開: 2022-03-11

私のように、決定的な答えを見つけることなく、次のInfrastructure-as-Code(IaC)ツールとしてCloudFormationとTerraformのどちらかを選択できるようにインターネットを調べた場合、私は長い間あなたの苦痛を共有しました。 今、私は両方のツールについて豊富な経験を持っており、どちらを使用するかについて情報に基づいた決定を下すことができます。

TL; DR

AWSでのIaCプロジェクトには、CloudFormationを選択します。理由は次のとおりです。

  1. CloudFormationは、コード(つまり、テンプレート)とコードのインスタンス化(つまり、スタック)を区別します。 Terraformでは、そのような区別はありません。 これについては、次のセクションで詳しく説明します。
  2. Terraformは、基本的な依存関係管理をうまく処理しません。 これについては、後のセクションで詳しく説明します。

コードとインスタンス化を区別する

CloudFormationとTerraformの違いの1つは、各サービス内でコードとインスタンス化が互いにどのように関連しているかです。

CloudFormationには、テンプレートのインスタンス化であるスタックの概念があります。 同じテンプレートは、特定のアカウントの特定のクライアント、アカウント間、または異なるクライアントによって無限にインスタンス化できます。

Terraformにはそのような概念がなく、コードとそのインスタンス化の間に1対1の関係が必要です。 これは、実行するサーバーごとにWebサーバーのソースコードを複製することや、コンパイルされたバージョンを実行する代わりに、アプリケーションを実行する必要があるたびにコードを複製することに似ています。

この点は、セットアップが簡単な場合は非常に簡単ですが、中規模から大規模の運用ではすぐに大きな問題になります。 Terraformでは、既存のコードから新しいスタックを起動する必要があるたびに、コードを複製する必要があります。 また、スクリプトファイルのコピー/貼り付けは、自分自身を妨害し、触れるつもりのないリソースを破壊する非常に簡単な方法です。

Terraformには、実際にはCloudFormationのようなスタックの概念がありません。これは、Terraformがコードと管理するリソースを1対1で一致させるためにゼロから構築されていることを明確に示しています。 これは後に環境の概念(その後「ワークスペース」に名前が変更されました)によって部分的に修正されましたが、それらの使用方法により、不要な環境への展開が非常に簡単になります。 これは、デプロイする前にterraform workspace selectを実行する必要があるためです。この手順を忘れると、以前に選択したワークスペースにデプロイされます。これは、希望するワークスペースである場合とそうでない場合があります。

実際には、この問題はTerraformモジュールを使用することで軽減されることは事実ですが、最良の場合でも、かなりの量の定型コードが必要になります。 実際、この問題は非常に深刻であったため、この問題に対処するためにTerraformの周りにラッパーツールTerragruntを作成する必要がありました。

状態管理と権限

CloudFormationとTerraformのもう1つの重要な違いは、それぞれが状態とアクセス許可を管理する方法です。

CloudFormationはスタックの状態を管理し、オプションを提供しません。 しかし、CloudFormationスタックの状態は、私の経験では堅実です。 また、CloudFormationを使用すると、特権の少ないユーザーが、スタック自体に必要なすべてのアクセス許可を持たずにスタックを管理できます。 これは、CloudFormationが、スタック操作を実行しているユーザーからのアクセス許可ではなく、スタックにアタッチされたサービスロールからアクセス許可を取得できるためです。

Terraformでは、状態を管理するためにいくつかのバックエンドを提供する必要があります。 デフォルトはローカルファイルですが、次の場合は完全に不十分です。

  1. 状態ファイルの堅牢性は、それが保存されているマシンの堅牢性に完全にリンクされています。
  2. それはほとんどチームワークを不可能にします。

そのため、堅牢で共有された状態が必要です。これは、AWSでは通常、S3バケットを使用して状態ファイルを保存し、同時実行を処理するためのDynamoDBテーブルを使用することで実現されます。

つまり、インスタンス化するスタックごとにS3バケットとDynamoDBテーブルを手動で作成し、これら2つのオブジェクトのアクセス許可を手動で管理して、特権の低いユーザーがアクセスできないデータにアクセスできないようにする必要があります。 スタックが2つしかない場合はそれほど問題にはなりませんが、管理するスタックが20ある場合は、非常に面倒になります。

ちなみに、Terraformワークスペースを使用する場合、ワークスペースごとに1つのDynamoDBテーブルを持つことはできません。 つまり、最小限のアクセス許可を持つIAMユーザーにデプロイを実行させたい場合、DynamoDBのアクセス許可はアイテムレベルに細かく設定されていないため、そのユーザーはすべてのワークスペースのロックをいじることができます。

依存関係の管理

この点で、CloudFormationとTerraformはどちらも少し注意が必要です。 リソースの論理ID(つまり名前)を変更すると、両方とも古いリソースを破棄して新しいリソースを作成する必要があると見なします。 したがって、特にCloudFormationのネストされたスタックの場合、どちらのツールでもリソースの論理IDを変更することは一般的に悪い考えです。

最初のセクションで述べたように、Terraformは基本的な依存関係を処理しません。 残念ながら、Terraformの開発者は、回避策が明らかに不足しているにもかかわらず、長年の問題にあまり注意を払っていません。

適切な依存関係管理がIaCツールにとって絶対的に重要であることを考えると、Terraformのこのような問題は、実稼働環境への展開など、ビジネスに不可欠な操作が含まれるとすぐに、その適合性に疑問を投げかけます。 CloudFormationは、はるかにプロフェッショナルな感覚を提供します。AWSは、クライアントに本番環境グレードのツールを提供できるように常に細心の注意を払っています。 CloudFormationを使用してきたすべての年で、依存関係の管理に関する問題に遭遇したことはありません。

CloudFormationを使用すると、スタックはその出力変数の一部をエクスポートでき、他のスタックで再利用できます。 正直なところ、リージョンごとに複数のスタックをインスタンス化することはできないため、この機能は制限されています。 これは、同じ名前の2つの変数をエクスポートできず、エクスポートされた変数に名前空間がないためです。

Terraformにはそのような機能がないため、あまり望ましくないオプションが残されています。 Terraformを使用すると、別のスタックの状態をインポートできますが、その状態に格納されている多くのシークレットを含む、そのスタック内のすべての情報にアクセスできます。 または、スタックはS3バケットに保存されたJSONファイルの形式で一部の変数をエクスポートできますが、このオプションはさらに面倒です。使用するS3バケットを決定し、適切なアクセス許可を付与して、すべてを書き込む必要があります。ライター側とリーダー側の両方で自分で配管コードを作成します。

Terraformの利点の1つは、データソースがあることです。 したがって、Terraformは、Terraformによって管理されていないリソースを照会できます。 ただし、実際には、一般的なテンプレートを作成する場合、ターゲットアカウントについて何も想定しないため、これはほとんど関係がありません。 CloudFormationでの同等の機能は、テンプレートパラメータを追加することです。これには、繰り返しとエラーの可能性が含まれます。 しかし、私の経験では、これが問題になることはありませんでした。

Terraformの依存関係管理の問題に戻ると、別の例として、ロードバランサーの設定を更新しようとするとエラーが発生し、次のようになります。

 Error: Error deleting Target Group: ResourceInUse: Target group 'arn:aws:elasticloadbalancing:us-east-1:723207552760:targetgroup/strategy-api-default-us-east-1/14a4277881e84797' is currently in use by a listener or a rule status code: 400, request id: 833d8475-f702-4e01-aa3a-d6fa0a141905

予想される動作は、ターゲットグループが削除されていない他のリソースの依存関係であることをTerraformが検出するため、削除を試みてはなりませんが、エラーをスローしてはなりません。

操作

Terraformはコマンドラインツールですが、非常にインタラクティブであるため、人間が実行することを期待していることは明らかです。 バッチモードで(つまり、スクリプトから)実行することは可能ですが、これにはいくつかの追加のコマンドライン引数が必要です。 IaCツールの目的が自動化であることを考えると、Terraformがデフォルトで人間によって実行されるように開発されているという事実は、非常に不可解です。

Terraformはデバッグが困難です。 エラーメッセージは非常に基本的なものであることが多く、何が問題になっているのかを理解できません。その場合、 TF_LOG=debugを指定してTerraformを実行する必要があります。これにより、トロールするための大量の出力が生成されます。 これを複雑にしているのは、TerraformがAWSに対してAPI呼び出しを行って失敗することがありますが、失敗はTerraformの問題ではありません。 対照的に、CloudFormationは、問題がどこにあるかを理解できるように、十分な詳細を含むかなり明確なエラーメッセージを提供します。

Terraformエラーメッセージの例:

 Error: error reading S3 bucket Public Access Block: NoSuchBucket: The specified bucket does not exist status code: 404, request id: 19AAE641F0B4AC7F, host id: rZkgloKqxP2/a2F6BYrrkcJthba/FQM/DaZnj8EQq/5FactUctdREq8L3Xb6DgJmyKcpImipv4s=

上記のエラーメッセージは、根本的な問題(この場合は権限の問題)を実際には反映していない明確なエラーメッセージを示しています。

このエラーメッセージは、Terraformがコーナーに自分自身をペイントする方法も示しています。 たとえば、S3バケットとそのバケットにaws_s3_bucket_public_access_blockリソースを作成し、何らかの理由でそのバケットを破棄するTerraformコードに変更を加えた場合、たとえば、上記の「変更は削除と作成を意味します」の落とし穴などです。 Terraformは、 aws_s3_bucket_public_access_blockを読み込もうとしてスタックしますが、上記のエラーで継続的に失敗します。 Terraformの正しい動作は、必要に応じてaws_s3_bucket_public_access_blockを置き換えるか削除することです。

最後に、TerraformでCloudFormationヘルパースクリプトを使用することはできません。 これは、特にcfn-signalを使用する場合は、煩わしいかもしれません。cfn-signalは、EC2インスタンスがそれ自体の初期化を完了し、リクエストを処理する準備ができていることをCloudFormationに通知します。

構文、コミュニティ、およびロールバック

構文的には、TerraformにはCloudFormationと比較して優れた利点があり、ループをサポートしています。 しかし、私自身の経験では、この機能は少し危険であることが判明する可能性があります。 通常、ループは多数の同一のリソースを作成するために使用されます。 ただし、異なるカウントでスタックを更新する場合は、古いリソースと新しいリソースをリンクする必要がある可能性があります(たとえば、 zipmap()を使用して、2つの配列の値を結合します。一方の配列は古いループサイズのサイズで、もう一方の配列は新しいループサイズのサイズであるため、サイズが異なります。 このような問題はループなしで発生する可能性があることは事実ですが、ループがない場合、問題はスクリプトを作成する人にとってはるかに明白になります。 このような場合にループを使用すると、問題がわかりにくくなります。

Terraformの構文とCloudFormationの構文のどちらが優れているかは、ほとんどの場合、好みの問題です。 CloudFormationは当初JSONのみをサポートしていましたが、JSONテンプレートは非常に読みにくいものです。 幸い、CloudFormationはYAMLもサポートしています。これは、はるかに読みやすく、コメントを許可します。 ただし、CloudFormationの構文は非常に冗長になる傾向があります。

Terraformの構文は、JSON派生物の一種であり、非常に特異なHCLを使用します。 Terraformは、CloudFormationよりも多くの機能を提供し、通常は理解しやすいものです。 したがって、Terraformにはこの点でわずかな利点があると主張することができます。

Terraformのもう1つの利点は、コミュニティが管理するモジュールのセットをすぐに利用できることです。これにより、テンプレートの作成が簡単になります。 1つの問題は、そのようなモジュールが組織の要件に準拠するのに十分な安全性を備えていない可能性があることです。 したがって、高レベルのセキュリティを必要とする組織では、これらのモジュール(および追加のバージョン)を確認する必要がある場合があります。

一般的に、Terraformモジュールは、CloudFormationのネストされたスタックよりもはるかに柔軟性があります。 CloudFormationのネストされたスタックは、その下にあるすべてのものを隠す傾向があります。 ネストされたスタックからの更新操作では、ネストされたスタックが更新されることが示されますが、ネストされたスタック内で何が発生するかは詳細には示されません。

実際には論争になる可能性がある最後のポイントは、CloudFormationが失敗したデプロイメントをロールバックしようとすることです。 これは非常に興味深い機能ですが、残念ながら非常に長くなる可能性があります(たとえば、CloudFormationがElastic Container Serviceへのデプロイが失敗したと判断するのに最大3時間かかる場合があります)。 対照的に、障害が発生した場合、Terraformはどこにいても停止します。 ロールバック機能が良いかどうかは議論の余地がありますが、長い待ち時間が許容できるトレードオフである場合、スタックが可能な限り動作状態に維持されるという事実を理解するようになりました。

Terraform対CloudFormationの防衛において

Terraformには、CloudFormationよりも優れた利点があります。 私の意見では、最も重要なのは、更新を適用するときに、Terraformが、使用しているすべてのモジュールへのドリルダウンを含む、これから行うすべての変更を表示することです。 対照的に、CloudFormationは、ネストされたスタックを使用する場合、ネストされたスタックを更新する必要があることを示すだけで、詳細にドリルダウンする方法を提供しません。 この種の情報は「実行」ボタンを押す前に知っておくことが非常に重要であるため、これはイライラする可能性があります。

CloudFormationとTerraformの両方が拡張機能をサポートしています。 CloudFormationでは、独自に作成したAWS Lambda関数をバックエンドとして使用することで、いわゆる「カスタムリソース」を管理できます。 Terraformの場合、拡張機能はコードの一部を記述して形成するのがはるかに簡単です。 したがって、この場合、Terraformには利点があります。

Terraformは、多くのクラウドベンダーを処理できます。 これにより、Terraformは、複数のクラウドプラットフォーム間で特定の展開を統合できるようになります。 たとえば、AWSとGoogle Cloud Platform(GCP)の間に単一のワークロードが分散しているとします。 通常、ワークロードのAWS部分はCloudFormationを使用してデプロイされ、GCP部分はGCPのCloudDeploymentManagerを使用してデプロイされます。 Terraformを使用すると、代わりに1つのスクリプトを使用して、それぞれのクラウドプラットフォームで両方のスタックをデプロイおよび管理できます。 このように、2つではなく1つのスタックをデプロイするだけで済みます。

TerraformとCloudFormationの非引数

インターネットの周りを循環し続けているかなりの数の非議論があります。 最大の問題は、Terraformがマルチクラウドであるため、どのクラウドプラットフォームで実行されているかに関係なく、1つのツールを使用してすべてのプロジェクトをデプロイできることです。 技術的にはこれは真実ですが、特に一般的なシングルクラウドプロジェクトを管理する場合は、それほど大きな利点ではないように思われます。 現実には、(たとえば)CloudFormationで宣言されたリソースと、Terraformスクリプトで宣言された同じリソースの間にはほぼ1対1の対応があります。 どちらの方法でもクラウド固有のリソースの詳細を知る必要があるため、違いは構文にあります。これは、デプロイメントを管理する上での最大の問題点ではありません。

Terraformを使用することで、ベンダーロックインを回避できると主張する人もいます。 この議論は、Terraformを使用すると、HashiCorp(Terraformの作成者)にロックインされるという意味では成り立ちません。CloudFormationを使用すると、AWSにロックインされるのと同じように、他のクラウドにロックインされます。プラットフォーム。

Terraformモジュールの方が使いやすいという事実は、私にとってそれほど重要ではありません。 まず第一に、AWSは、ユーザーが作成したセキュリティホールやさまざまなコンプライアンスプログラムの違反に対する責任が認識されているため、コミュニティベースのCloudFormationテンプレートの単一のリポジトリをホストすることを意図的に避けたいと考えています。

より個人的なレベルでは、ソフトウェア開発の場合にライブラリを使用することの利点を完全に理解しています。これらのライブラリは、数万行のコードに簡単に遭遇する可能性があるからです。 ただし、IaCの場合、コードのサイズは通常はるかに小さく、そのようなモジュールは通常数十行の長さです。 コピー/貼り付けを使用することは、互換性を維持し、セキュリティを未知の人に委任する際の問題を回避するという意味で、実際にはそれほど悪い考えではありません。

コピー/貼り付けの使用は、多くの開発者やDevOpsエンジニアに嫌われており、これには十分な理由があります。 ただし、私の見解では、コードのスニペットにコピー/貼り付けを使用すると、ニーズに合わせて簡単に調整でき、ライブラリを作成して汎用化するために多くの時間を費やす必要はありません。 たとえば、コードが1ダース以上のテンプレートで複製されない限り、これらのコードスニペットを維持する手間は通常非常に少なくなります。 このような場合、コードを流用してネストされたスタックとして使用することは理にかなっています。自分自身を繰り返さないことの利点は、更新を実行するときにネストされたスタック内で何が更新されるかを確認できないという煩わしさよりもおそらく大きいでしょう。手術。

CloudFormationとTerraformの結論

AWSは、CloudFormationを使用して、常に意図したとおりに機能する堅実なツールを顧客に提供したいと考えています。 もちろん、Terraformのチームもそうしていますが、ツールの重要な側面である依存関係の管理は、残念ながら優先事項ではないようです。

特にマルチクラウドアーキテクチャを使用している場合は、Terraformがプロジェクトに含まれる可能性があります。その場合、Terraformスクリプトは、使用しているさまざまなクラウドベンダー間でリソースの管理を統合する1つの方法です。 ただし、この場合、Terraformを使用して、それぞれのクラウド固有のIaCツールを使用してすでに実装されているスタックを管理するだけで、Terraformの欠点を回避できます。

TerraformとCloudFormationの全体的な感覚は、CloudFormationは不完全ではありますが、よりプロフェッショナルで信頼性が高いということです。特にマルチクラウドではないプロジェクトには絶対にお勧めします。