回答:
私はまた、既存のAWSインフラストラクチャをTerraformに移行する状態にあります。開発中に回答を更新することを目指します。
私は、Terraformの公式の例と、不確かな領域を具体化するための複数の試行錯誤に大きく依存しています。
.tfstate
ファイル
Terraform configを使用して、さまざまなインフラストラクチャに多くのボックスをプロビジョニングできます。複数のユーザーが実行することもできるため、この状態は中央の場所(S3など)にある必要がありますが、 gitではありません。
これはTerraformを確認すると確認できます.gitignore
。
開発者による制御
私たちの目的は、完全な監査(gitログ)を維持しながら、インフラストラクチャのより多くの制御を開発者に提供し、変更を正常性チェックする機能(プルリクエスト)を提供することです。そのことを念頭に置いて、私が目指している新しいインフラストラクチャワークフローは次のとおりです。
編集1-現在の状態で更新
この回答を始めてから、私は多くのTFコードを記述し、私たちの状況でより快適に感じています。途中でバグや制限に遭遇しましたが、これは、急速に変化する新しいソフトウェアを使用する特徴であることを認めます。
レイアウト
それぞれに複数のサブネットを持つ複数のVPCを持つ複雑なAWSインフラストラクチャがあります。これを簡単に管理するための鍵は、インフラストラクチャコード(テラフォームとパペットの両方)を整理するために使用できる地域、環境、サービス、所有者を含む柔軟な分類法を定義することでした。
モジュール
次のステップは、Terraformモジュールを格納する単一のgitリポジトリを作成することでした。モジュールの最上位のdir構造は次のようになります。
tree -L 1 .
結果:
├── README.md
├── aws-asg
├── aws-ec2
├── aws-elb
├── aws-rds
├── aws-sg
├── aws-vpc
└── templates
それぞれがいくつかの健全なデフォルトを設定しますが、それらを「接着剤」で上書きできる変数として公開します。
接着剤
glue
上記のモジュールを利用する2番目のリポジトリがあります。これは、分類法ドキュメントに沿ってレイアウトされています。
.
├── README.md
├── clientA
│ ├── eu-west-1
│ │ └── dev
│ └── us-east-1
│ └── dev
├── clientB
│ ├── eu-west-1
│ │ ├── dev
│ │ ├── ec2-keys.tf
│ │ ├── prod
│ │ └── terraform.tfstate
│ ├── iam.tf
│ ├── terraform.tfstate
│ └── terraform.tfstate.backup
└── clientC
├── eu-west-1
│ ├── aws.tf
│ ├── dev
│ ├── iam-roles.tf
│ ├── ec2-keys.tf
│ ├── prod
│ ├── stg
│ └── terraform.tfstate
└── iam.tf
クライアントレベルの内部には、.tf
グローバルリソース(IAMロールなど)をプロビジョニングするAWSアカウント固有のファイルがあります。次は、EC2 SSH公開鍵を使用したリージョンレベルです。最後に、私たちの環境(でdev
、stg
、prod
など)私たちのVPCのセットアップ、インスタンスの作成とピアリングの接続などが格納されています。
補足:ご覧のとおり、私terraform.tfstate
はgitを維持することよりも自分のアドバイスに反対しています。これは私がS3に移行するまでの一時的な措置ですが、私が現在唯一の開発者なので、私に適しています。
次のステップ
これはまだ手動プロセスであり、まだJenkinsではありませんが、かなり大規模で複雑なインフラストラクチャを移植しており、これまでのところ非常に優れています。私が言ったように、いくつかのバグがうまくいきます!
編集2-変更
この最初の回答を書いてからほぼ1年が経ち、Terraformと私自身の状況は大きく変化しました。現在、Terraformを使用してAzureクラスターを管理する新しいポジションにいますが、Terraformは今v0.10.7
です。
状態
人々は繰り返し私に、状態はGitに入れるべきではないと言ってきました-そしてそれらは正しいです。私たちはこれを、開発者のコミュニケーションと規律に依存する2人のチームとの暫定措置として使用しました。より大規模な分散型チームにより、S3のリモート状態を完全に活用し、DynamoDBが提供するロックを使用しています。理想的には、これは領事に移行され、クロスクラウドプロバイダーを削減するのはv1.0です。
モジュール
以前は、内部モジュールを作成して使用していました。これはまだ事実ですが、Terraformレジストリの出現と成長に伴い、これらを少なくともベースとして使用するようにしています。
ファイル構造
新しいポジションには、2つのinfx環境のみが含まれる、より単純な分類法がdev
ありprod
ます。それぞれに独自の変数と出力があり、上記で作成したモジュールを再利用します。remote_state
また、プロバイダは、環境間で作成されたリソースの出力を共有するのに役立ちます。私たちのシナリオは、グローバルに管理されているTLDに対するさまざまなAzureリソースグループのサブドメインです。
├── main.tf
├── dev
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
└── prod
├── main.tf
├── output.tf
└── variables.tf
企画
ここでも、分散チームの追加の課題があるため、常にterraform plan
コマンドの出力を保存します。plan
とapply
ステージの間で変更を加えることなく、何が実行されるかを検査して知ることができます(ただし、ロックはこれに役立ちます)。プレーンテキストの「シークレット」変数が含まれている可能性があるため、このプランファイルは必ず削除してください。
全体的に、Terraformに非常に満足しており、新しい機能が追加されて学習と改善が続けられています。
Terraformを多用しており、推奨セットアップは次のとおりです。
各環境(stage、prod、qaなど)のTerraformコードを個別のテンプレートセット(したがって個別の.tfstate
ファイル)に保存することを強くお勧めします。これは重要です。変更を行う間、個別の環境は実際には互いに分離されています。そうしないと、ステージングでコードをいじりながら、prodで何かを爆破するのは簡単すぎます。Terraform、VPC、およびなぜ環境ごとのtfstateファイルが必要なのか、理由についてのカラフルな説明を参照してください。
したがって、一般的なファイルレイアウトは次のようになります。
stage
└ main.tf
└ vars.tf
└ outputs.tf
prod
└ main.tf
└ vars.tf
└ outputs.tf
global
└ main.tf
└ vars.tf
└ outputs.tf
ステージVPCのすべてのTerraformコードがstage
フォルダーに入れられ、製品VPCのすべてのコードがprod
フォルダーに入れられ、VPCの外部にあるすべてのコード(IAMユーザー、SNSトピック、S3バケットなど)がglobal
フォルダーに入れられます。
慣例により、通常、Terraformコードを3つのファイルに分割します。
vars.tf
:入力変数。outputs.tf
:出力変数。main.tf
:実際のリソース。通常、インフラストラクチャは2つのフォルダで定義します。
infrastructure-modules
:このフォルダーには、小さく、再利用可能な、バージョン管理されたモジュールが含まれています。各モジュールは、VPCやデータベースなどのインフラストラクチャの単一部分を作成する方法の青写真と考えてください。infrastructure-live
:このフォルダには、実際に稼働している実行中のインフラストラクチャが含まれています。このインフラストラクチャは、のモジュールを組み合わせて作成しますinfrastructure-modules
。このフォルダー内のコードは、設計図から構築した実際の家と考えてください。テラフォームモジュールは、フォルダ内のテラフォームテンプレートのただのセットです。たとえば、単一のVPCのすべてのルートテーブル、サブネット、ゲートウェイ、ACLなどを定義すると呼ばれるフォルダーがあるとvpc
しますinfrastructure-modules
。
infrastructure-modules
└ vpc
└ main.tf
└ vars.tf
└ outputs.tf
次に、そのモジュールをinfrastructure-live/stage
とinfrastructure-live/prod
で使用して、ステージVPCと製品VPCを作成します。たとえば、次のinfrastructure-live/stage/main.tf
ようになります。
module "stage_vpc" {
source = "git::git@github.com:gruntwork-io/module-vpc.git//modules/vpc-app?ref=v0.0.4"
vpc_name = "stage"
aws_region = "us-east-1"
num_nat_gateways = 3
cidr_block = "10.2.0.0/18"
}
モジュールを使用するには、module
リソースを使用して、そのsource
フィールドにハードドライブのローカルパス(例source = "../infrastructure-modules/vpc"
:)、または上記の例のようにGit URL(モジュールソースを参照)を指定します。Git URLの利点は、特定のgit sha1またはタグ(ref=v0.0.4
)をです。これで、インフラストラクチャを小さなモジュールの集まりとして定義するだけでなく、それらのモジュールにバージョンを付け、必要に応じて慎重に更新またはロールバックすることができます。
再利用、テスト、および文書化された多数のインフラストラクチャパッケージを作成しましたVPC、Dockerクラスター、データベースなどを作成するためにを作成しました。内部では、それらのほとんどがバージョン付きのTerraformモジュールです。
Terraformを使用してリソース(EC2インスタンス、データベース、VPCなど)を作成すると、作成したものに関する情報が.tfstate
ファイルに記録されます。これらのリソースを変更するには、チームの全員.tfstate
が同じファイルにアクセスする必要がありますが、Gitにチェックインしないでください(理由の説明については、こちらを参照してください)。
代わりに、Terraform Remote Stateを.tfstate
有効にしてS3にファイルを保存することをお勧めします。これにより、Terraformを実行するたびに最新のファイルが自動的にプッシュ/プルされます。S3バケットでバージョニングを有効にして.tfstate
、最新バージョンが破損した場合に古いファイルにロールバックできるようにしてください。ただし、重要な注意:Terraformはロックを提供しません。そのため、2人のチームメンバーがterraform apply
同じ.tfstate
ファイルで同時に実行すると、お互いの変更が上書きされる可能性があります。
この問題を解決するために、Terraruntと呼ばれるオープンソースツールを作成しました。これは、Amazon DynamoDBを使用してロックを提供するTerraformのシンラッパーです(ほとんどのチームにとって完全に無料です)。詳細については、Terragruntを使用してTerraformに自動リモート状態ロックと構成を追加するをご覧ください。
実世界でTerraformを使用するために学んだすべてのベストプラクティスを詳細に説明する一連のブログ投稿「A Terreformの包括的なガイド」を開始しました。
更新:Terraformブログの投稿シリーズの包括的なガイドが非常に人気になり、Terraform:Up&Runningと呼ばれる本に拡大しました!
remote config
Terraform構成をチェックアウトしたばかりの場合、または以前のリモート構成を変更したい場合は、実行する必要があります。Terraform 0.9では、の概念が導入されbackends
、これにより多くのことが簡素化されます。詳細については、このPRを参照してください。
remote config
prod状態を指すようにコマンドを再実行する必要があります。環境ごとに異なる状態を想定しています。そうですか?v0.9を楽しみにしています。
.tf
ファイルのセットを2つの異なる環境にデプロイする場合は、はい、remote config
切り替えるたびに実行する必要があります。これは明らかに非常にエラーが発生しやすいので、この手法を実際に使用することはお勧めしません。代わりに、チェックアウトこのブログの記事で推奨テラフォームファイルのレイアウトを一緒にこのブログの記事でテラフォームモジュールを使用する方法。
以前remote config
はこれを許可していましたが、現在は " バックエンド "に置き換えられているため、Teraformリモートは使用できなくなりました。
terraform remote config -backend-config="bucket=<s3_bucket_to_store_tfstate>" -backend-config="key=terraform.tfstate" -backend=s3
terraform remote pull
terraform apply
terraform remote push
詳細はドキュメントを参照してください。
私はここに多くの答えがあることを知っていますが、私のアプローチはかなり異なります。
⁃ Modules
⁃ Environment management
⁃ Separation of duties
モジュール
環境管理
IaCにより、SDLCプロセスがインフラストラクチャ管理に関連するようになりました。開発アプリケーション環境だけでなく開発インフラストラクチャも期待できません。
職務の分離
小規模な組織や個人のインフラストラクチャを実行している場合、これは実際には当てはまりませんが、運用の管理に役立ちます。
一部のリソースはめったに変更されず、他のリソースは常に変更されるため、これはリリースの問題にも役立ちます。分離はリスクと複雑さを取り除きます。
この戦略は、AWSのマルチアカウント戦略と類似しています。詳細については、読んでください。
CI / CD
これはそれ自体のトピックですが、Terraformは優れたパイプライン内で非常にうまく機能します。ここで最も一般的なエラーは、CIを特効薬として扱うことです。技術的には、Terraformは、アセンブリパイプラインの段階でのみインフラストラクチャをプロビジョニングする必要があります。これは、通常はテンプレートを検証およびテストするCIステージで発生することとは別のものです。
注意:モバイルで書かれているため、エラーを許しません。
回答が非常に堅固で有益になる前に、ここに2セントを追加してみます
少数のリソースで作業する方が簡単で高速です。
terraform plan
とterraform
適用の両方が、リソースのステータスを確認するためにクラウドAPI呼び出しを行います。ブラスト半径は、リソースが少ないほど小さくなります。
リモート状態を使用してプロジェクトを開始します。
tfstate
gitでファイルを管理することは悪夢です。一貫した構造と命名規則を実践してみてください。
リソースモジュールをできるだけわかりやすくします。
変数として渡したり、データソースを使用して検出したりできる値をハードコーディングしないでください。
data
ソースを使用し、terraform_remote_state
特にコンポジション内のインフラストラクチャモジュール間の接着剤として使用します。
(参照記事: https : //www.terraform-best-practices.com/code-structure)
例:
少数のリソースで作業する方が簡単で高速なので、以下に推奨コードレイアウトを示します。
注:各プロジェクトには固有の特性があるため、厳密には従わないように参照してください
.
├── 1_tf-backend #remote AWS S3 + Dynamo Lock tfstate
│ ├── main.tf
│ ├── ...
├── 2_secrets
│ ├── main.tf
│ ├── ...
├── 3_identities
│ ├── account.tf
│ ├── roles.tf
│ ├── group.tf
│ ├── users.tf
│ ├── ...
├── 4_security
│ ├── awscloudtrail.tf
│ ├── awsconfig.tf
│ ├── awsinspector.tf
│ ├── awsguarduty.tf
│ ├── awswaf.tf
│ └── ...
├── 5_network
│ ├── account.tf
│ ├── dns_remote_zone_auth.tf
│ ├── dns.tf
│ ├── network.tf
│ ├── network_vpc_peering_dev.tf
│ ├── ...
├── 6_notifications
│ ├── ...
├── 7_containers
│ ├── account.tf
│ ├── container_registry.tf
│ ├── ...
├── config
│ ├── backend.config
│ └── main.config
└── readme.md
インフラストラクチャを調整するためにterraformを使用する際に従う必要があるベストプラクティスはほとんどないと思います
- 同じコードを再度記述しないでください(再利用性)
- 簡単に保守できるように、環境構成を分けてください。
- リモートバックエンドs3(暗号化)とダイナモDBを使用して同時ロックを処理する
- モジュールを作成し、メインインフラストラクチャでそのモジュールを複数回使用します。これは、異なるパラメーターを渡すことで複数回呼び出すことができる再利用可能な関数のようなものです。
複数の環境に対応
ほとんどの場合、推奨される方法はterraformの「ワークスペース」を使用して複数の環境を処理することですが、ワークスペースの使用法は組織の作業方法によって異なる可能性があると思います。その他は、環境の状態を分離するために、各環境(ステージ、製品、QAなど)のTerraformコードを格納します。ただし、この場合、多くの場所で同じコードをコピーしているだけです。
├── main.tf
├── dev
│ ├── main.tf
│ ├── output.tf
│ └── variables.tf
└── prod
├── main.tf
├── output.tf
└── variables.tf
ほとんどの場合、すべての環境は90%同一であると信じているので、いくつかの異なるアプローチに従って、各環境フォルダーに保持することで、同じterraformコードの重複を処理および回避しました。
├── deployment
│ ├── 01-network.tf
│ ├── 02-ecs_cluster.tf
│ ├── 03-ecs_service.tf
│ ├── 04-eks_infra.tf
│ ├── 05-db_infra.tf
│ ├── 06-codebuild-k8s.tf
│ ├── 07-aws-secret.tf
│ ├── backend.tf
│ ├── provider.tf
│ └── variables.tf
├── env
│ ├── dev
│ │ ├── dev.backend.tfvar
│ │ └── dev.variables.tfvar
│ └── prod
│ ├── prod.backend.tfvar
│ └── prod.variables.tfvar
├── modules
│ └── aws
│ ├── compute
│ │ ├── alb_loadbalancer
│ │ ├── alb_target_grp
│ │ ├── ecs_cluster
│ │ ├── ecs_service
│ │ └── launch_configuration
│ ├── database
│ │ ├── db_main
│ │ ├── db_option_group
│ │ ├── db_parameter_group
│ │ └── db_subnet_group
│ ├── developertools
│ ├── network
│ │ ├── internet_gateway
│ │ ├── nat_gateway
│ │ ├── route_table
│ │ ├── security_group
│ │ ├── subnet
│ │ ├── vpc
│ └── security
│ ├── iam_role
│ └── secret-manager
└── templates
環境に関連する構成
環境関連の構成とパラメーターを変数ファイルに分けて保持し、その値を渡してインフラストラクチャを構成します。例として
dev.backend.tfvar
region = "ap-southeast-2"
bucket = "dev-samplebackendterraform"
key = "dev/state.tfstate"
dynamo_db_lock = "dev-terraform-state-lock"
dev.variable.tfvar
environment = "dev"
vpc_name = "demo"
vpc_cidr_block = "10.20.0.0/19"
private_subnet_1a_cidr_block = "10.20.0.0/21"
private_subnet_1b_cidr_block = "10.20.8.0/21"
public_subnet_1a_cidr_block = "10.20.16.0/21"
public_subnet_1b_cidr_block = "10.20.24.0/21"
インフラストラクチャ部分の条件付きスキップ
env固有の変数ファイルに構成を作成し、その変数に基づいて、その部分を作成するかスキップするかを決定します。このようにして、必要に応じて、インフラストラクチャの特定の部分をスキップできます。
variable vpc_create {
default = "true"
}
module "vpc" {
source = "../modules/aws/network/vpc"
enable = "${var.vpc_create}"
vpc_cidr_block = "${var.vpc_cidr_block}"
name = "${var.vpc_name}"
}
resource "aws_vpc" "vpc" {
count = "${var.enable == "true" ? 1 : 0}"
cidr_block = "${var.vpc_cidr_block}"
enable_dns_support = "true"
enable_dns_hostnames = "true"
}
以下のコマンドは、各環境のインフラ変更を初期化して実行するために必要です。必要な環境フォルダーに移動します。
terraform init -var-file=dev.variables.tfvar -backend-config=dev.backend.tfvar ../../deployment/
terraform apply -var-file=dev.variables.tfvar ../../deployment
サブフォルダーの考え方は好きではありません。これは、環境ごとにソースが異なり、ドリフトする傾向があるためです。
より良いアプローチは、すべての環境(dev、preprod、prodなど)に単一のスタックを用意することです。単一の環境で作業するには、を使用しますterraform workspace
。
terraform workspace new dev
これにより、新しいワークスペースが作成されます。これには、専用の状態ファイルと、terraform.workspace
コードで使用できる変数が含まれます。
resource "aws_s3_bucket" "bucket" {
bucket = "my-tf-test-bucket-${terraform.workspace}"
}
このようにして、バケットが呼び出されます
上記のワークスペースに適用した後(terraform workspace select <WORKSPACE>
環境を変更するために使用)。コードをマルチリージョンに対応させるには、次のようにします。
data "aws_region" "current" {}
resource "aws_s3_bucket" "bucket" {
bucket = "my-tf-test-bucket-${data.aws_region.current.name}-${terraform.workspace}"
}
取得する(us-east-1リージョンの場合)
従うべきいくつかのTerraformベストプラクティス:
ハードコーディングを避ける:開発者が手動で直接リソースを作成する場合があります。これらのリソースをマークし、terraformインポートを使用してコードに含める必要があります。サンプル:
account_number =“ 123456789012” account_alias = "mycompany"
DockerコンテナーからTerraformを実行する:Terraformは、実行できるバージョンを簡単に制御できる公式のDockerコンテナーをリリースします。
CI / CDパイプラインでビルドジョブを設定するときは、Terraform Dockerコンテナーを実行することをお勧めします。
TERRAFORM_IMAGE=hashicorp/terraform:0.11.7
TERRAFORM_CMD="docker run -ti --rm -w /app -v ${HOME}/.aws:/root/.aws -v ${HOME}/.ssh:/root/.ssh -v `pwd`:/app $TERRAFORM_IMAGE"
詳細については、私のブログを参照してください:https : //medium.com/tech-darwinbox/how-darwinbox-manages-infrastructure-at-scale-with-terraform-371e2c5f04d3
このスレッドに貢献したいと思います。
一部の特殊なケースでは、Terraform状態ファイルへの手動アクセスが必要になります。リファクタリング、変更の破棄、障害の修正などには、運用担当者がTerraform状態の操作を実行する必要があります。そのような場合は、要塞ホスト、VPNなどを使用して、Terraform状態への特別に制御されたアクセスを計画してください。
CI / CDパイプラインのガイドラインを含む詳細をカバーする、より長いベストプラクティスブログを確認してください。
それでも優れたソリューションを探している場合は、ワークスペース固有の変数を持つことができるさまざまな環境のフォルダー構造を維持する代わりに使用できるワークスペースを見てください。
エフゲニー・ブリックマンとして 言及したことは、モジュール構造を持っている方が良いでしょう。