このチュートリアルでは、一般的な GitOps 手法を使用して、Terraform と Cloud Build により、Infrastructure as Code を管理する方法について説明します。GitOps という用語は、最初に Weaveworks によって造られました。その主なコンセプトは、Git リポジトリを使用して必要な環境状態を保存することです。Terraform は、コードを使用してクラウド インフラストラクチャを予想どおりに作成、変更、改善できる HashiCorp ツールです。このチュートリアルでは、 Google Cloud 継続的インテグレーション サービスである Cloud Build を使用して、Terraform マニフェストを環境に自動的に適用します。
このチュートリアルは、インフラストラクチャを予想どおりに変更するための洗練された戦略を探している開発者とオペレーターを対象としています。この記事は、 Google Cloud、Linux、GitHub に精通していることを前提としています。
State of DevOps で、ソフトウェア デリバリーのパフォーマンスを向上させると認められた機能が報告されています。このチュートリアルでは、次の機能について説明します。
アーキテクチャ
このチュートリアルが Terraform の実行を管理するための GitOps プラクティスをどのように適用するのかという点について、次のアーキテクチャ図を検討してください。GitHub ブランチ(dev と prod)を使用して、実際の環境を表していることに留意してください。これらの環境は、Virtual Private Cloud(VPC)ネットワーク(それぞれ dev と prod)によって Google Cloud  プロジェクトに定義されます。
Terraform コードを dev または prod ブランチに push すると、プロセスが開始されます。このシナリオでは、Cloud Build がトリガーし、Terraform マニフェストを適用して、それぞれの環境で必要な状態を実現します。一方、Terraform コードを他のブランチ(機能ブランチなど)に push すると、Cloud Build が実行され、terraform plan が実行されますが、どの環境にも適用されません。
理想的には、開発者またはオペレーターのいずれかが、保護されていないブランチに対してインフラストラクチャの提案を行い、pull リクエストを通じてそれらを提出する必要があります。このチュートリアルで後述する Cloud Build GitHub アプリは、ビルドジョブを自動的にトリガーし、terraform plan レポートをこれらの pull リクエストにリンクします。このようにして、潜在的な変更を共同編集者と話し合ってレビューし、変更がベースブランチにマージされる前にフォローアップ commit を追加できます。
懸念事項が発生しない場合は、最初に変更を dev ブランチにマージする必要があります。このマージにより、dev 環境へのインフラストラクチャ デプロイメントがトリガーされ、この環境をテストできます。テストを行い、デプロイした内容について確認した後、dev ブランチを prod ブランチにマージして、本番環境へのインフラストラクチャのインストールをトリガーする必要があります。
目標
- GitHub リポジトリを設定します。
- Cloud Storage バケットに状態を保存するように Terraform を構成します。
- Cloud Build サービス アカウントに権限を付与します。
- Cloud Build を GitHub リポジトリに接続します。
- 機能ブランチで環境構成を変更します。
- 開発環境への変更を促進します。
- 本番環境への変更を促進します。
費用
このドキュメントでは、課金対象である次の Google Cloudコンポーネントを使用します。
 
  料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。
  
このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。
前提条件
- Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
- 
    
    
      In the Google Cloud console, on the project selector page, select or create a Google Cloud project. Roles required to select or create a project - Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
- 
      Create a project: To create a project, you need the Project Creator
      (roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
 
- 
  
    Verify that billing is enabled for your Google Cloud project. 
- 
    
    
      In the Google Cloud console, on the project selector page, select or create a Google Cloud project. Roles required to select or create a project - Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
- 
      Create a project: To create a project, you need the Project Creator
      (roles/resourcemanager.projectCreator), which contains theresourcemanager.projects.createpermission. Learn how to grant roles.
 
- 
  
    Verify that billing is enabled for your Google Cloud project. 
- 
    
    In the Google Cloud console, activate Cloud Shell. At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize. 
- Cloud Shell で、選択したプロジェクトの ID を取得します。gcloud config get-value project PROJECT_IDは、実際のプロジェクト ID に置き換えます。gcloud config set project PROJECT_ID 
- 必要な API を有効にします。gcloud services enable cloudbuild.googleapis.com compute.googleapis.com 
- Cloud Shell で Git を初めて使用する場合は、名前とメールアドレスを使用して Git を構成します。git config --global user.email "YOUR_EMAIL_ADDRESS" git config --global user.name "YOUR_NAME" 
- devブランチには、開発環境に適用される最新の変更が含まれています。
- prodブランチには、本番環境に適用される最新の変更が含まれています。
- GitHub で https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops.git に移動します。
- ページの右上隅にある [Fork] をクリックします。   - これで、ソースファイルを格納する - solutions-terraform-cloudbuild-gitopsリポジトリのコピーが作成されます。
- Cloud Shell で、このフォークされたリポジトリのクローンを作成し、 - YOUR_GITHUB_USERNAMEを GitHub ユーザー名に置き換えます。- cd ~ git clone https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops.git cd ~/solutions-terraform-cloudbuild-gitops 
- environments/フォルダには、- devや- prodなどの環境を表すサブフォルダが含まれています。これらはそれぞれ、成熟、開発、および運用のさまざまな段階でワークロードを論理的に分離します。これらの環境はできる限り類似した構成にすることをおすすめしますが、各サブフォルダには独自の Terraform 構成があり、必要に応じて一意の設定ができるようになっています。
- modules/フォルダには、インライン Terraform モジュールが含まれています。これらのモジュールは、関連リソースの論理グループを表し、異なる環境でコードを共有するために使用されます。
- cloudbuild.yamlファイルは、一連の手順に基づいてタスクを実行する方法など、Cloud Build に関する手順を含むビルド構成ファイルです。このファイルは、Cloud Build がコードを取得するブランチに応じて、条件付き実行を指定します。たとえば、- devと- prodブランチの場合は、次の手順が実行されます。- terraform init
- terraform plan
- terraform apply
 
- その他のブランチの場合は、次の手順が実行されます。 - terraform init(すべての- environmentsサブフォルダ)
- terraform plan(すべての- environmentsサブフォルダ)
 
 
- Cloud Shell で、Cloud Storage バケットを作成します。 - PROJECT_ID=$(gcloud config get-value project) gcloud storage buckets create gs://${PROJECT_ID}-tfstate- ```sh gcloud storage buckets update gs://${PROJECT_ID}-tfstate --versioning ``` Enabling Object Versioning increases [storage costs](/storage/pricing){: track-type="tutorial" track-name="internalLink" track-metadata-position="body" }, which you can mitigate by configuring [Object Lifecycle Management](/storage/docs/lifecycle){: track-type="tutorial" track-name="internalLink" track-metadata-position="body" } to delete old state versions.- terraform.tfvarsファイルと- backend.tfファイルの両方で- PROJECT_IDプレースホルダをプロジェクト ID に置き換えます。- cd ~/solutions-terraform-cloudbuild-gitops sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars sed -i s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf - OS X/MacOS では、次のように - sed -iの後に 2 つの引用符(- "")の追加が必要な場合があります。- cd ~/solutions-terraform-cloudbuild-gitops sed -i "" s/PROJECT_ID/$PROJECT_ID/g environments/*/terraform.tfvars sed -i "" s/PROJECT_ID/$PROJECT_ID/g environments/*/backend.tf 
- すべてのファイルが更新されたかどうかを確認します。 - git status- 出力は次のようになります。 - On branch dev Your branch is up-to-date with 'origin/dev'. Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: environments/dev/backend.tf modified: environments/dev/terraform.tfvars modified: environments/prod/backend.tf modified: environments/prod/terraform.tfvars no changes added to commit (use "git add" and/or "git commit -a")
- 変更を commit して push します。 - git add --all git commit -m "Update project IDs and buckets" git push origin dev- GitHub の構成に応じて、前述の変更を push するために認証する必要があります。 
 - Cloud Build サービス アカウントに権限を付与する- Cloud Build サービス アカウントが Google Cloud リソースを管理する目的で Terraform スクリプトを実行できるようにするには、プロジェクトへの適切なアクセス権を付与する必要があります。説明をわかりやすくするため、このチュートリアルではプロジェクト エディタへのアクセス権が付与されています。ただし、プロジェクト編集者のロールに広範囲の権限がある場合、本番環境では、通常は最小限のアクセス権を付与する、会社の IT セキュリティに関するベスト プラクティスに従う必要があります。セキュリティのベスト プラクティスについては、すべてのアクセス試行を明示的に検証するをご覧ください。 - Cloud Shell で、プロジェクトの Cloud Build サービス アカウントのメールを取得します。 - CLOUDBUILD_SA="$(gcloud projects describe $PROJECT_ID \ --format 'value(projectNumber)')@cloudbuild.gserviceaccount.com"
- Cloud Build サービス アカウントに必要なアクセス権を付与します。 - gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$CLOUDBUILD_SA --role roles/editor
 - Cloud Build を GitHub リポジトリに直接接続する- このセクションでは、Cloud Build GitHub アプリのインストール方法について説明します。このインストールでは、GitHub リポジトリをGoogle Cloud プロジェクトに接続できるため、新しいブランチを作成するか、コードを GitHub に push するたびに Cloud Build が Terraform マニフェストを自動的に適用できます。 - 次の手順では、 - solutions-terraform-cloudbuild-gitopsリポジトリにのみアプリをインストールする手順を示していますが、より多くのリポジトリまたはすべてのリポジトリにアプリをインストールすることもできます。- Cloud Build アプリの GitHub Marketplace ページにアクセスします。 - GitHub でアプリを初めて構成する場合は、ページの下部にある [Setup with Google Cloud Build] をクリックして、[Grant this app access to your GitHub account] をクリックします。
- GitHub でアプリを構成するのが初めてではない場合は、[Configure access] をクリックします。個人アカウントの [Applications] ページが開きます。
 
- Cloud Build の行で [Configure] をクリックします。 
- [Only select repositories] を選択し、[ - solutions-terraform-cloudbuild-gitops] を選択してリポジトリに接続します。
- [Save] または [Install] をクリックします。ボタンのラベルはワークフローによって異なります。インストールを続行するために Google Cloud にリダイレクトされます。 
- ご自身の Google Cloud アカウントを使ってログインします。プロンプトが表示されたら、Cloud Build と GitHub の統合を承認します。 
- Cloud Build ページでプロジェクトを選択します。ウィザードが表示されます。 
- [リポジトリを選択] セクションで、GitHub アカウントと - solutions-terraform-cloudbuild-gitopsリポジトリを選択します。
- 利用規約に同意する場合は、チェックボックスをオンにして、[接続] をクリックします。 
- [トリガーを作成] セクションで、[トリガーを作成] をクリックします。 - トリガー名を追加します(push-to-branchなど)。このトリガー名は、後で必要になるためメモしておきます。
- [イベント] セクションで、[ブランチに push する] を選択します。
- [ソース] セクションの [ブランチ] フィールドで .*を選択します。
- [作成] をクリックします。
 
- トリガー名を追加します(
 - Cloud Build GitHub アプリが構成され、GitHub リポジトリが Google Cloud プロジェクトにリンクされました。今後、GitHub リポジトリの変更により Cloud Build の実行がトリガーされ、GitHub Checks を使用して結果が GitHub に報告されます。 - 新機能ブランチで環境構成を変更する- これでほとんどの環境が構成されました。次に開発環境でコードを変更してみましょう。 - GitHub で、フォークされたリポジトリのメインページに移動します。 - https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops 
- devブランチ内にいることを確認します。
- ファイルを編集用に開くには、 - modules/firewall/main.tfファイルに移動し、鉛筆アイコンをクリックします。
- 30 行目の [ - target_tags] フィールドで- "http-server2"のタイプミスを修正します。- 値には - "http-server"を指定してください。
- 「Fixing http firewall target」などの commit メッセージをページの下部に追加し、[Create a new branch for this commit and start a pull request] を選択します。 
- [Propose changes] をクリックします。 
- 次のページで、[Create pull request] をクリックして、変更した新しい pull リクエストを開きます。 - pull リクエストが開くと、Cloud Build ジョブが自動的に開始されます。 
- [Show all checks] をクリックし、チェックが緑色になるまで待ちます。   
- [Details] をクリックして、[View more details on Google Cloud Build] リンクで - terraform planの出力などの詳細情報を表示します。
 - pull リクエストはまだ統合しないでください。 - Cloud Build ジョブが - cloudbuild.yamlファイルで定義されたパイプラインを実行したことに注意してください。前述のように、このパイプラインは取得されるブランチに応じて異なる動作をします。ビルドは、- $BRANCH_NAME変数が環境フォルダと一致するかどうかを確認します。一致する場合、Cloud Build はその環境の- terraform planを実行します。それ以外の場合、Cloud Build はすべての環境に対して- terraform planを実行し、提案された変更がすべての環境に適していることを確認します。これらの計画のいずれかが失敗すると、ビルドが失敗します。- 同様に、 - terraform applyコマンドは環境ブランチに対して実行されますが、それ以外の場合は完全に無視されます。このセクションでは、新しいブランチにコード変更を送信したため、 Google Cloud プロジェクトにインフラストラクチャの展開は適用されませんでした。- ブランチをマージする前に Cloud Build の実行成功を必須とする- それぞれの Cloud Build の実行が成功した場合にのみマージを適用できるようにするには、次の手順に進みます。 - GitHub で、フォークされたリポジトリのメインページに移動します。 - https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops 
- リポジトリ名の下にある [Settings] をクリックします。 
- 左側のメニューで [Branches] をクリックします。 
- [Branch protection rules] で、[Add rule] をクリックします。 
- [Branch name pattern] に「 - dev」と入力します。
- [Protect matching branches] セクションで、[Require status checks to pass before merging] を選択します。 
- 以前に作成した Cloud Build トリガー名を検索します。 
- [作成] をクリックします。 
- 手順 3~7 を繰り返し、[Branch name pattern] を - prodに設定します。
 - この構成は、 - devブランチと- prodブランチの両方を保護するうえで重要です。つまり、commit を最初に別のブランチに push する必要があり、その後でのみ保護されたブランチにマージできます。このチュートリアルの保護では、マージを許可するために Cloud Build の実行が成功する必要があります。- 開発環境の変更を促進する- マージされるのを待っている pull リクエストがあります。そこで、必要な状態を - dev環境に適用します。- GitHub で、フォークされたリポジトリのメインページに移動します。 - https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops 
- リポジトリ名の下で、[Pull requests] をクリックします。 
- 作成した pull リクエストをクリックします。 
- [Merge pull request]、[Confirm merge] の順にクリックします。   
- 新しい Cloud Build がトリガーされたことを確認します。 
- ビルドを開き、ログを確認します。 - ビルドが完了すると、次のような内容が表示されます。 - Step #3 - "tf apply": external_ip = EXTERNAL_IP_VALUE Step #3 - "tf apply": firewall_rule = dev-allow-http Step #3 - "tf apply": instance_name = dev-apache2-instance Step #3 - "tf apply": network = dev Step #3 - "tf apply": subnet = dev-subnet-01 
- EXTERNAL_IP_VALUEをコピーし、ウェブブラウザでアドレスを開きます。- http://EXTERNAL_IP_VALUE - このプロビジョニングで、VM を起動してファイアウォール ルールが伝播されるまでに数秒かかる場合があります。最終的には、ウェブブラウザに「Environment: dev」が表示されます。 
- Cloud Storage バケット内の Terraform 状態ファイルに移動します。 - https://storage.cloud.google.com/PROJECT_ID-tfstate/env/dev/default.tfstate 
 - 本番環境の変更をプロモートする- 開発環境をすべてテストしたため、インフラストラクチャ コードを本番環境にプロモートさせることができます。 - GitHub で、フォークされたリポジトリのメインページに移動します。 - https://github.com/YOUR_GITHUB_USERNAME/solutions-terraform-cloudbuild-gitops 
- リポジトリ名の下で、[Pull requests] をクリックします。 
- [New pull request] をクリックします。 
- ベース リポジトリの場合は、フォークされたリポジトリを選択します。 
- [base] に、独自のベース リポジトリから - prodを選択します。[compare] に- devを選択します。  
- [Create pull request] をクリックします。 
- [title] には「 - Promoting networking changes」などのタイトルを入力し、[Create pull request] をクリックします。
- terraform planの詳細を含む Cloud Build から提案された変更を確認し、[Merge pull request] をクリックします。
- [Confirm merge] をクリックします。 
- Google Cloud コンソールで [ビルド履歴] ページを開き、本番環境に適用されている変更を確認します。 
- ビルドが完了するのを待ってから、ログを確認します。 - ログの最後に、次のような内容が表示されます。 - Step #3 - "tf apply": external_ip = EXTERNAL_IP_VALUE Step #3 - "tf apply": firewall_rule = prod-allow-http Step #3 - "tf apply": instance_name = prod-apache2-instance Step #3 - "tf apply": network = prod Step #3 - "tf apply": subnet = prod-subnet-01 
- EXTERNAL_IP_VALUEをコピーし、ウェブブラウザでアドレスを開きます。- http://EXTERNAL_IP_VALUE - このプロビジョニングで、VM を起動してファイアウォール ルールが伝播されるまでに数秒かかる場合があります。最終的には、ウェブブラウザに「Environment: prod」が表示されます。 
- Cloud Storage バケット内の Terraform 状態ファイルに移動します。 - https://storage.cloud.google.com/PROJECT_ID-tfstate/env/prod/default.tfstate 
 - Cloud Build でサーバーレスのコード パイプラインとしてのインフラストラクチャの構成が正常に終了しました。今後、次のことをお試しください。 - 個別のユースケースのデプロイメントを追加します。
- ニーズを反映する追加の環境を作成します。
- 環境ごとの VPC ではなく、環境ごとのプロジェクトを使用します。
 - クリーンアップ- チュートリアルが終了したら、Google Cloud で作成したリソースについて料金が発生しないようにクリーンアップします。 - プロジェクトの削除- In the Google Cloud console, go to the Manage resources page.
- In the project list, select the project that you want to delete, and then click Delete.
- In the dialog, type the project ID, and then click Shut down to delete the project.
 - GitHub リポジトリを削除する- GitHub リポジトリで新しい pull リクエストがブロックされないように、ブランチの保護ルールを削除できます。 - GitHub で、フォークされたリポジトリのメインページに移動します。
- リポジトリ名の下にある [Settings] をクリックします。
- 左側のメニューで [Branches] をクリックします。
- [Branch protection rules] セクションで、devとprodの両方の行の [Delete] ボタンをクリックします。
 - 必要に応じて、Cloud Build アプリは、GitHub から完全にアンインストールできます。 - GitHub の [Applications] 設定に移動します。 
- [Installed GitHub Apps] タブで、[Cloud Build] 行の [Configure] をクリックします。次に、[Danger zone] セクションで、[Uninstall Google Cloud Builder] 行の [Uninstall] ボタンをクリックします。 - ページの上部に「You're all set.A job has been queued to uninstall Google Cloud Build.」と表示されます。 
- [Authorized GitHub Apps] タブで、[Google Cloud Build] 行の [Revoke] ボタンをクリックし、ポップアップで [I understand, revoke access] をクリックします。 
 - GitHub リポジトリを保持しない場合は、次のようにします。 - GitHub で、フォークされたリポジトリのメインページに移動します。
- リポジトリ名の下にある [Settings] をクリックします。
- [Danger Zone] までスクロールします。
- [Delete this repository] をクリックし、確認手順に沿って操作します。
 - 次のステップ- Cloud Foundation Toolkit テンプレートを使用して、再現可能なエンタープライズ対応の基盤をGoogle Cloudで迅速に構築することを検討してください。
- このチュートリアルで説明されている GitOps ワークフローを確認する。Cloud Build Infra-As-Code パイプラインによる再現可能な大規模な Google Cloud 環境(Cloud Next '19)をご覧ください。
- Cloud Build を使用した GitOps スタイルの継続的デリバリーのチュートリアルに進む。
- Cloud Build のより高度な機能を確認する。ビルドステップの順序の構成、アーティファクトのビルド、テスト、デプロイ、カスタム ビルドステップの作成をご覧ください。
- Cloud Build による Terraform デプロイメントのスケーリングとコンプライアンスの確保に関するブログを確認する。
- DevOps に関するリソースを読む。
- このチュートリアルに関連する DevOps 機能の詳細については、以下をご覧ください。
- DevOps のクイック チェックで、業界における立ち位置を把握する。
 
GitHub リポジトリを設定する
このチュートリアルでは、単一の Git リポジトリを使用してクラウド インフラストラクチャを定義します。異なる環境に対応するさまざまなブランチを持つことで、このインフラストラクチャをオーケストレートします。
このインフラストラクチャを使用すると、常にリポジトリを参照してそれぞれの環境で必要とされる構成の内容を把握し、最初に dev 環境にマージすることにより、新たな変更を提案できます。次に dev ブランチを後続の prod ブランチにマージして、変更をプロモートします。
開始するには、solutions-terraform-cloudbuild-gitops リポジトリをフォークします。
このリポジトリのコードは次のように構成されています。
提案された変更がすべての環境に適していることを確認するために、terraform init と terraform plan はすべての environments サブフォルダで実行されます。たとえば、pull リクエストをマージする前にプランを確認することで、承認されていないエンティティにアクセス権が付与されていないことを保証できます。
Cloud Storage バケットに状態を保存するように Terraform を構成する
デフォルトでは、Terraform はローカルの terraform.tfstate という名前のファイルに状態を保存します。多くのユーザーが Terraform を同時に実行していて、各マシンが現在のインフラストラクチャを独自に理解している場合は特に、このデフォルト構成が原因でチームでの Terraform の使用が難しくなる場合があります。
このような問題を回避するために、このセクションでは、Cloud Storage バケットを指すリモート状態を構成します。リモート状態はバックエンドの機能であり、このチュートリアルでは、backend.tf ファイルで構成されます。次に例を示します。
次の手順では、Cloud Storage バケットを作成し、新しいバケットと Google Cloud プロジェクトを指すようにいくつかのファイルを変更します。