Terraform、Cloud Build、GitOps を使用してインフラストラクチャをコードとして管理する

このチュートリアルでは、一般的な GitOps 手法を使用して、TerraformCloud Build により、コードとしてインフラストラクチャを管理する方法について説明します。GitOps という用語は、最初に Weaveworks によって造られました。その主なコンセプトは、Git リポジトリを使用して必要な環境状態を保存することです。Terraform は、コードを使用してクラウド インフラストラクチャを予想どおりに作成、変更、改善できる HashiCorp オープンソース ツールです。このチュートリアルでは、Google Cloud の継続的インテグレーション サービスである Cloud Build を使用して、Terraform マニフェストを環境に自動的に適用します。

このチュートリアルは、インフラストラクチャを予想どおりに変更するための洗練された戦略を探している開発者とオペレーターを対象としています。この記事は、Google Cloud、Linux、GitHub に精通していることを前提としています。

State of DevOps で、ソフトウェア デリバリーのパフォーマンスを向上させると認められた機能が報告されています。このチュートリアルは、次の機能について説明します。

アーキテクチャ

このチュートリアルが Terraform の実行を管理するための GitOps プラクティスをどのように適用するのかという点について、次のアーキテクチャ図を検討してください。GitHub ブランチ(devprod)を使用して、実際の環境を表していることに留意してください。これらの環境は、Virtual Private Cloud(VPC)ネットワーク(それぞれ devprod)によって 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 コンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。新しい Google Cloud ユーザーは無料トライアルをご利用いただけます。

始める前に

  1. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  2. Cloud Console のプロジェクト セレクタページで、Cloud プロジェクトを選択または作成します。

    プロジェクト セレクタのページに移動

  3. Google Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  4. Cloud Console で、Cloud Shell をアクティブにします。

    Cloud Shell をアクティブにする

    Cloud Console の下部にある Cloud Shell セッションが開始し、コマンドライン プロンプトが表示されます。Cloud Shell はシェル環境です。gcloud コマンドライン ツールなどの Cloud SDK がすでにインストールされており、現在のプロジェクトの値もすでに設定されています。セッションが初期化されるまで数秒かかることがあります。

  5. Cloud Shell で、選択したプロジェクトの ID を取得します。
    gcloud config get-value project
    このコマンドがプロジェクト ID を返さない場合は、プロジェクトを使用するように Cloud Shell を構成します。PROJECT_ID をプロジェクト ID に置き換えます。
    gcloud config set project PROJECT_ID
  6. 必要な API を有効にします。
    gcloud services enable cloudbuild.googleapis.com compute.googleapis.com
    このステップが完了するまでに数分かかる場合があります。
  7. Cloud Shell で Git を初めて使用する場合は、名前とメールアドレスを使用して Git を構成します。
    git config --global user.email "your-email-address"
    git config --global user.name "your-name"
    
    Git はこの情報を使用して、Cloud Shell で作成する commit の作成者を識別します。

このチュートリアルを終了した後、作成したリソースを削除すると、それ以上の請求は発生しません。詳しくは、クリーンアップをご覧ください。

GitHub リポジトリを設定する

このチュートリアルでは、単一の Git リポジトリを使用してクラウド インフラストラクチャを定義します。異なる環境に対応するさまざまなブランチを持つことで、このインフラストラクチャをオーケストレートします。

  • dev ブランチには、開発環境に適用される最新の変更が含まれています。
  • prod ブランチには、本番環境に適用される最新の変更が含まれています。

このインフラストラクチャを使用すると、常にリポジトリを参照してそれぞれの環境で必要とされる構成の内容を把握し、最初に dev 環境にマージすることにより、新たな変更を提案できます。次に dev ブランチを後続の prod ブランチにマージして、変更をプロモートします。

開始するには、solutions-terraform-cloudbuild-gitops リポジトリをフォークします。

  1. GitHub で https://github.com/GoogleCloudPlatform/solutions-terraform-cloudbuild-gitops.git に移動します。
  2. ページの右上隅にある [Fork] をクリックします。

    リポジトリをフォークする。

    これで、ソースファイルを格納する solutions-terraform-cloudbuild-gitops リポジトリのコピーが作成されます。

  3. 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/ フォルダには、devprod などの環境を表すサブフォルダが含まれています。これらはそれぞれ、成熟、開発、および運用のさまざまな段階でワークロードを論理的に分離します。これらの環境はできる限り類似した構成にすることをおすすめしますが、各サブフォルダには独自の Terraform 構成があり、必要に応じて一意の設定ができるようになっています。

  • modules/ フォルダには、インライン Terraform モジュールが含まれています。これらのモジュールは、関連リソースの論理グループを表し、異なる環境でコードを共有するために使用されます。

  • cloudbuild.yaml ファイルは、一連の手順に基づいてタスクを実行する方法など、Cloud Build に関する手順を含むビルド構成ファイルです。このファイルは、Cloud Build がコードを取得するブランチに応じて、条件付き実行を指定します。たとえば、

    • devprod ブランチの場合は、次の手順が実行されます。

      1. terraform init
      2. terraform plan
      3. terraform apply
    • その他のブランチの場合は、次の手順が実行されます。

      1. terraform init(すべての environments サブフォルダ)
      2. terraform plan(すべての environments サブフォルダ)

terraform initterraform plan は、提案されている変更がすべての単一環境に適用されることを確認するため、すべての environments サブフォルダで実行されます。この方法により、pull リクエストをマージする前に、たとえば、承認されていないエンティティにアクセス権が付与されていないかという点について計画を確認できます。

Cloud Storage バケットに状態を保存するように Terraform を構成する

デフォルトでは、Terraform はローカルの terraform.tfstate という名前のファイルに状態を保存します。多くのユーザーが Terraform を同時に実行していて、各マシンが現在のインフラストラクチャを独自に理解している場合は特に、このデフォルト構成が原因でチームでの Terraform の使用が難しくなる場合があります。

このような問題を回避するために、このセクションでは、Cloud Storage バケットを指すリモート状態を構成します。リモート状態はバックエンドの機能であり、このチュートリアルでは、backend.tf ファイルで構成されます。次に例を示します。

# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

terraform {
  backend "gcs" {
    bucket = "PROJECT_ID-tfstate"
    prefix = "env/dev"
  }
}

次の手順では、Cloud Storage バケットを作成し、新しいバケットと Google Cloud プロジェクトを指すようにいくつかのファイルを変更します。

  1. Cloud Shell で、Cloud Storage バケットを作成します。

    PROJECT_ID=$(gcloud config get-value project)
    gsutil mb gs://${PROJECT_ID}-tfstate
    
  2. オブジェクトのバージョニングを有効にして、デプロイの履歴を保持します。

    gsutil versioning set on gs://${PROJECT_ID}-tfstate
    

    オブジェクトのバージョニングを有効にすると、ストレージ コストが増加します。これは、古い状態のバージョンを削除するようにオブジェクトのライフサイクル管理を構成することで軽減できます。

  3. 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
    
  4. すべてのファイルが更新されたかどうかを確認します。

    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")
    
  5. 変更を 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 セキュリティに関するベスト プラクティスに従う必要があります。

  1. Cloud Shell で、プロジェクトの Cloud Build サービス アカウントのメールを取得します。

    CLOUDBUILD_SA="$(gcloud projects describe $PROJECT_ID \
        --format 'value(projectNumber)')@cloudbuild.gserviceaccount.com"
    
  2. 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 リポジトリにのみアプリをインストールする手順を示していますが、より多くのリポジトリまたはすべてのリポジトリにアプリをインストールすることもできます。

  1. Cloud Build アプリの GitHub Marketplace ページにアクセスします。

    Cloud Build アプリページを開く

  2. GitHub でアプリを初めて構成する場合は、[Setup with Google Cloud Build] をクリックします。それ以外の場合は、[Edit your plan] をクリックして、お支払い情報を選択し、[Edit your plan] ページで [grant this app access] をクリックします。

  3. [Google Cloud Build のインストール] ページで、[Only select repositories] を選択し、「your-user/solutions-terraform-cloudbuild-gitops」と入力して、フォークされたリポジトリに接続します。

  4. [インストール] をクリックします。

  5. Google Cloud にログインします。

    [承認] ページが表示されます。Cloud Build GitHub アプリに対して Google Cloud への接続を承認するよう求められます。

    Google Cloud にログインする

  6. [Authorize Google Cloud Build by GoogleCloudBuild] をクリックします。

    Cloud Console にリダイレクトされます。

  7. 作業中の Google Cloud プロジェクトを選択します。

  8. 利用規約に同意する場合は、チェックボックスをオンにして、[次へ] をクリックします。

  9. リポジトリの選択手順で、your-user/solutions-terraform-cloudbuild-gitops を選択して Google Cloud プロジェクトに接続し、[接続] をクリックします。

  10. [完了]、[接続] の順にクリックします。

Cloud Build GitHub アプリが構成され、GitHub リポジトリが Google Cloud プロジェクトにリンクされました。今後、GitHub リポジトリの変更により Cloud Build の実行がトリガーされ、GitHub Checks を使用して結果が GitHub に報告されます。

新機能ブランチで環境構成を変更する

これでほとんどの環境が構成されました。次に開発環境でコードを変更してみましょう。

  1. GitHub で、フォークされたリポジトリのメインページに移動します。
  2. dev ブランチ内にいることを確認します。

    dev ブランチにいることを確認する。

  3. ファイルを編集用に開くには、modules/firewall/main.tf ファイルに移動し、鉛筆アイコンをクリックします。

  4. 15 行目の target_tags フィールドで "http-server**2**" のタイプミスを修正します。

    値は "http-server" を指定してください。

  5. 「http ファイアウォール ターゲットの修正」などの commit メッセージをページの下部に追加し、[Create a new branch for this commit] を選択します。

  6. [Propose file change] をクリックします。

  7. 次のページで、[Create pull request] をクリックして、変更した新しい pull リクエストを開きます。

    pull リクエストが開くと、Cloud Build ジョブが自動的に開始されます。

  8. [Show all checks] をクリックし、チェックが緑色になるまで待ちます。

    pull リクエストのすべてのチェックを表示する。

  9. [Details] をクリックして、[View more details on Google Cloud Build] リンクで terraform plan の出力などの詳細情報を表示します。

Cloud Build ジョブが cloudbuild.yaml ファイルで定義されたパイプラインを実行したことに注意してください。前述のように、このパイプラインは取得されるブランチに応じて異なる動作をします。ビルドは、$BRANCH_NAME 変数が環境フォルダと一致するかどうかを確認します。一致する場合、Cloud Build はその環境の terraform plan を実行します。それ以外の場合、Cloud Build はすべての環境に対して terraform plan を実行し、提案された変更がすべての環境に適用されることを確認します。これらの計画のいずれかの実行が失敗すると、ビルドも失敗します。

- id: 'tf plan'
  name: 'hashicorp/terraform:0.11.14'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      if [ -d "environments/$BRANCH_NAME/" ]; then
        cd environments/$BRANCH_NAME
        terraform plan
      else
        for dir in environments/*/
        do
          cd ${dir}
          env=${dir%*/}
          env=${env#*/}
          echo ""
          echo "*************** TERRAFOM PLAN ******************"
          echo "******* At environment: ${env} ********"
          echo "*************************************************"
          terraform plan || exit 1
          cd ../../
        done
      fi 

同様に、terraform apply コマンドは環境ブランチに対して実行されますが、それ以外の場合は完全に無視されます。このセクションでは、新しいブランチにコード変更を送信したため、Google Cloud プロジェクトにインフラストラクチャの展開は適用されませんでした。

- id: 'tf apply'
  name: 'hashicorp/terraform:0.11.14'
  entrypoint: 'sh'
  args:
  - '-c'
  - |
      if [ -d "environments/$BRANCH_NAME/" ]; then
        cd environments/$BRANCH_NAME
        terraform apply -auto-approve
      else
        echo "***************************** SKIPPING APPLYING *******************************"
        echo "Branch '$BRANCH_NAME' does not represent an oficial environment."
        echo "*******************************************************************************"
      fi

ブランチをマージする前に Cloud Build の実行成功を必須とする

それぞれの Cloud Build の実行が成功した場合にのみマージを適用できるようにするには、次の手順に進みます。

  1. GitHub で、フォークされたリポジトリのメインページに移動します。
  2. リポジトリ名の下にある [Settings] をクリックします。
  3. 左側のメニューで [Branches] をクリックします。
  4. [Branch protection rules] で、[Add rule] をクリックします。
  5. [Branch name pattern] で、[dev] を選択します。
  6. [Rule settings] で、[Require status checks to pass before merging] を選択し、[Status checks found in the last week for this repository] で [Build] をクリックします。
  7. [Create] をクリックします。
  8. 手順 5~8 を繰り返し、[Branch name pattern] を [prod] に設定します。

この構成は、dev ブランチと prod ブランチの両方を保護するうえで重要です。つまり、commit を最初に別のブランチに push する必要があり、その後でのみ保護されたブランチにマージできます。このチュートリアルの保護では、マージを許可するために Cloud Build の実行が成功する必要があります。

開発環境の変更を促進する

マージされるのを待っている pull リクエストがあります。そこで、必要な状態を dev 環境に適用します。

  1. GitHub で、フォークされたリポジトリのメインページに移動します。
  2. リポジトリ名の下で、[Pull requests] をクリックします。
  3. 作成した pull リクエストをクリックします。
  4. [Merge pull request]、[Confirm merge] の順にクリックします。

    統合を確定する。

  5. 新しい Cloud Build がトリガーされたことを確認します。

    Cloud Build ページに移動する

  6. ビルドを開き、ログを確認します。

    ビルドが完了すると、次のような内容が表示されます。

    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
    
  7. external-ip-value をコピーし、ウェブブラウザでアドレスを開きます。

    このプロビジョニングでは、VM を起動してファイアウォール ルールを伝達するのに数秒かかる場合がありますが、最後に、ウェブブラウザに Environment: dev が表示されます。

本番環境の変更の促進

開発環境をすべてテストしたため、インフラストラクチャ コードを本番環境にプロモートさせることができます。

  1. GitHub で、フォークされたリポジトリのメインページに移動します。
  2. [New pull request] をクリックします。
  3. ベース リポジトリの場合は、フォークされたリポジトリを選択します。
  4. ベースには prod を選択し、比較には dev を選択します。
  5. [Create pull request] をクリックします。
  6. タイトルには「Promoting networking changes」などのタイトルを入力し、[Create pull request] をクリックします。
  7. terraform plan の詳細を含む Cloud Build から提案された変更を確認し、[Merge pull request] をクリックします。
  8. [Confirm merge] をクリックします。
  9. Cloud Console で [ビルド履歴] ページを開き、本番環境に適用されている変更を確認します。

    Cloud Build ページに移動する

  10. ビルドが完了するのを待ってから、ログを確認します。

    ログの最後に、次のような内容が表示されます。

    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
    
  11. external-ip-value をコピーし、ウェブブラウザでアドレスを開きます。

    このプロビジョニングでは、VM を起動してファイアウォール ルールを伝達するのに数秒かかる場合がありますが、最後に、ウェブブラウザに Environment: prod が表示されます。

Cloud Build でサーバーレスのコード パイプラインとしてのインフラストラクチャの構成が正常に終了しました。今後、次のことをお試しください。

  • 個別のユースケースのデプロイメントを追加します。
  • ニーズを反映する追加の環境を作成します。
  • 環境ごとの VPC ではなく、環境ごとのプロジェクトを使用します。

クリーンアップ

チュートリアルが終了したら、Google Cloud で作成したリソースをクリーンアップして今後料金が発生しないようにします。

プロジェクトの削除

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] ページに移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ