Terraformによるインフラ構成の開発フロー

こんにちは。zoo(YOUTRUST/X)です。

豆苗を初めて買いました。一度刈り取った後、水につけて再度育てているのですが、トトロのワンシーンを彷彿とさせる成長を見ることができ、とても楽しいです。豆苗にハマりそうです。いえ、既にハマっています。

今回は、Terraformによるインフラ構成変更の本番反映までの開発フローを紹介します。

YOUTRUSTのインフラの概要やTerraformにおけるディレクトリ構成の詳細に関しては、過去の記事をご覧ください。

tech.youtrust.co.jp

tech.youtrust.co.jp

ブランチ戦略

ブランチにはmainsandboxfeature/xxxがあります。

  • main
    • Production環境に対応するブランチ。Production環境の状態とmainブランチの状態は一致します。
  • sandbox
    • Sandbox環境に対応するブランチ。Sandbox環境の状態とsandboxブランチの状態は一致します。
  • feature/xxx
    • 作業用のブランチ。このブランチに対応する環境はありません。

なお、Production環境とSandbox環境は以下の用途で利用している環境になります。

  • Production環境
    • サービス提供に利用しています。いわゆる本番環境です。
  • Sandbox環境
    • インフラの動作確認や脆弱性診断に利用する本番と同じ構成の環境です。ステージング環境と呼ばれることもあります。

本番に構成を反映するまでのフローは、以下の通りです。

  1. 変更作業をfeature/xxxブランチにて実施しGitHubにプッシュ
  2. feature/xxxブランチからsandboxブランチに向けてプルリクエストを作成
  3. レビューに通過したらsandboxブランチにマージしてSandbox環境に構成を反映(GitHub Actionsにて反映)
  4. Sandbox環境にて挙動の確認
  5. sandboxブランチからmainブランチに向けてプルリクエストを作成(GitHub Actionsにて作成)
  6. 差分を確認した後、マージしてTerraformの構成をProduction環境に反映(GitHub Actionsにて反映)

GitFlowを簡略化したようなイメージです。

このフローでは、sandboxブランチにマージしないと挙動確認ができない、という悩みポイントがあります。ですが、現在のYOUTRUSTでTerraformを触るのがほぼ1人という状況のため、問題は起きていません。feature/xxxブランチをSandbox環境に反映して挙動確認できるようにすることで、もしかしたらよりよいフローを作れるかもしれません。

上記のフローでは、GitHub Actionsを用いて自動化している部分や変更のチェックをしている部分がありますので、以下で説明していきます。

feature/xxxブランチからsandboxブランチへのプルリクエスト作成時

このタイミングでは、以下の4つを行なっています。

  • Terraformファイルのフォーマットのチェック
  • Linterによるエラーの発見や非推奨の書式の警告
  • 静的解析によるセキュリティスキャン
  • Sandbox環境およびProduction環境に対するterraform planの実行

Terraformファイルのフォーマットのチェック

terraform fmt -check -recursiveを用いて、Terraformファイルのフォーマットのチェックを行っています。 それにより、terraform fmtが実行されていない場合に気づけるようにしています。

対応するworkflowの設定は以下になります。

name: tffmt

on:
  push:

jobs:
  tffmt:
    name: tffmt
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.2.7
      - name: Terraform fmt
        run: terraform fmt -check -recursive

Linterによるエラーの発見や非推奨の書式の警告

TerraformのLinterであるTFLintを用いて、実行時にエラーとなる可能性がある箇所の検出や非推奨の書式に対する警告などを出力するようにしています。

workflowにおいては、Setup TFLint Actionを用いてTFLintを利用できるようにしています。 設定方法の詳細は、Setup TFLint ActionのREADMEをご覧ください。

対応するworkflowの設定は以下になります。 なお、一つのyamlファイル内でjobをSandbox環境用とProduction環境用に分けているのですが、ファイルを別にしなかった理由は覚えていません...

name: tflint

on:
  pull_request:

permissions:
  id-token: write
  contents: read

jobs:
  tflint-for-sandbox:
    name: tflint-for-sandbox
    runs-on: ubuntu-latest
    defaults:
      run:
        shell: bash
        working-directory: environments/sandbox
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::xxxxxxxxxxxx:role/sample
          aws-region: ap-northeast-1

      - name: Setup terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.2.7

      - name: Init terraform
        run: terraform init -upgrade

      - uses: terraform-linters/setup-tflint@v2
        name: Setup tflint
        with:
          tflint_version: v0.39.2
          github_token: ${{ secrets.GITHUB_TOKEN }}

      - name: Init tflint
        run: tflint --init
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Run tflint
        run: tflint

静的解析によるセキュリティスキャン

Terraformコードの静的解析によるセキュリティスキャナーであるtfsecを用いてセキュリティのチェックを行っています。

workflowにおいては、tfsec-pr-commenter-actionを用いてtfsecを実行しています。 オプションのsoft_fail_commentertrueにしているため、結果がFailになることはありません。 そのため、扱いとしては参考情報、ということになります。

対応するworkflowの設定は以下になります。

name: tfsec
on:
  pull_request:
    paths:
      - '**.tf'
jobs:
  tfsec:
    name: tfsec-pr-commenter
    runs-on: ubuntu-latest
    strategy:
      matrix:
        working_directory: [
          environments/sandbox,
          environments/prod
        ]

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: tfsec
        uses: aquasecurity/tfsec-pr-commenter-action@v1.2.0
        with:
          github_token: ${{ github.token }}
          working_directory: ${{ matrix.working_directory }}
          soft_fail_commenter: true                                 

Sandbox環境およびProduction環境に対するterraform planの実行

sandboxブランチにプルリクエストを作成したタイミングで、Sandbox環境へのplanの結果とProduction環境へのplanの結果を確認します。

tfcmtおよびactions-setup-tfcmtを用いて、planの結果がプルリクエストにコメントされるようにしています。 また、最新の結果だけをコメント上に表示するために、github-commentおよびactions-setup-github-commentを用いて、最新ではない結果のコメントを非表示にするようにしています。

Sandbox環境へのplanに対応するworkflowの設定は以下になります。

name: Plan Sandbox

on:
  pull_request:
    branches:
      - sandbox

jobs:
  plan:
    name: Plan
    runs-on: ubuntu-latest
    strategy:
      matrix:
        dir: [environments/sandbox]
    permissions:
      contents: read
      id-token: write
      pull-requests: write
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::xxxxxxxxxxxx:role/sample
          aws-region: ap-northeast-1

      - name: Setup terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.2.7

      - name: Setup tfcmt
        uses: shmokmt/actions-setup-tfcmt@v2

      - name: Setup github-comment
        uses: shmokmt/actions-setup-github-comment@v2

      - name: Check format
        id: fmt
        run: terraform fmt -check -recursive
        continue-on-error: true

      - name: Initialize
        id: init
        run: terraform init -upgrade
        working-directory: ${{ matrix.dir }}

      - name: Validate
        id: validate
        run: terraform validate -no-color
        working-directory: ${{ matrix.dir }}

      - name: Print PR number
        id: pr-number
        run: |
          PR_NUMBER=$(echo $GITHUB_REF | sed -e 's/[^0-9]//g')
          echo "number=${PR_NUMBER}" >> $GITHUB_ENV

      - name: Plan
        id: plan
        run: |
          tfcmt -owner team-youtrust -repo infra-aws -pr ${PR_NUMBER} -var target:sandbox plan -- terraform plan -no-color
        continue-on-error: true
        working-directory: ${{ matrix.dir }}
        env:
          PR_NUMBER: ${{ env.number }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Hide old plan comments
        run: |
          github-comment hide -org team-youtrust -repo infra-aws -pr ${PR_NUMBER} -token ${GITHUB_TOKEN} \
          -condition 'Comment.HasMeta && (Comment.Meta.SHA1 != Commit.SHA1)'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ env.number }}

上記で出力されるコメントは、以下のようになります。

tfcmtによるコメントへの結果の出力
tfcmtによるコメントへの結果の出力

sandboxブランチへのプルリクエストのマージ時

このタイミングでは、以下の2つを行なっています。

  • Sandbox環境へのterraform apply
  • sandbox ブランチからmainブランチへのプルリクエストの作成

Sandbox環境へのterraform apply

sandboxブランチにマージされたタイミングで、Sandbox環境に適用しています。

対応するworkflowの設定は以下になります。

name: Apply Sandbox

on:
  push:
    branches:
      - sandbox

jobs:
  plan:
    name: Apply
    runs-on: ubuntu-latest
    strategy:
      matrix:
        dir: [environments/sandbox]
    permissions:
      id-token: write
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Configure aws credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::xxxxxxxxxxxx:role/sample
          aws-region: ap-northeast-1

      - name: Setup terraform
        uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.2.7

      - name: Apply
        run: |
          terraform init -upgrade
          terraform apply -auto-approve
        working-directory: ${{ matrix.dir }}

sandbox ブランチからmainブランチへのプルリクエストの作成

毎回手動でプルリクエストを作るのが面倒なため、workflowでプルリクエストを作成しています。

作成されるプルリクエストの内容は必要最低限になっています。 どのfeature/xxxブランチが含まれるのか分かるようにするなど、より良くできる余地はありますが、関わっているメンバーが限られているため、現時点では必要十分と考えています。

対応するworkflowの設定は以下になります。

name: Create Pull Requests to Apply to Production Environments

on:
  pull_request:
    types:
      - closed
    branches:
      - sandbox

jobs:
  create_pull_requests:
    if: github.event.pull_request.merged == true
    name: Create pull requests to apply to production environments
    runs-on: ubuntu-latest
    env:
      GH_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN_OF_GITHUB }}
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Set current date
        id: set_current_date
        env:
          TZ: 'Asia/Tokyo'
        run: |
          echo "current_date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV

      - name: Check if pull request to main exists or not
        id: check_pull_request_to_main
        run: |
          echo "count=$(gh pr list --base 'main' | wc -l)" >> $GITHUB_ENV

      - name: Create pull request to main from sandbox
        if: ${{ env.count == 0 }}
        run: |
          gh pr create --base "main" --title "apply to production on ${{ env.current_date }}" --body ""

sandboxブランチからmainブランチへのプルリクエスト作成時

このプルリクエストは自動で作成され、このタイミングでは、以下の4つを行なっています。

  • Terraformファイルのフォーマットのチェック
  • Linterによるエラーの発見や非推奨の書式の警告
  • 静的解析によるセキュリティスキャン
  • Production環境に対するterraform planの実行

terraform plan以外は、sandboxブランチへのプルリクエスト作成時にも実行しているので、実行しなくても良いですが、多大なコストがかかるわけではないので、このタイミングでも実行しています。

具体的なworkflowの設定は、sandboxブランチへのプルリクエストと同様のため、そちらを参考にしてください。

mainブランチへのプルリクエストのマージ時

このタイミングでは、以下を行なっています。

  • Production環境へのterraform apply

具体的なworkflowの設定は、sandboxブランチへのプルリクエストと同様のため、そちらを参考にしてください。

まとめ

今回は、Terraformによるインフラ構成変更の本番反映までの開発フローを紹介しました。

現在、インフラの構成変更に携わるメンバーは流動的で、同タイミングで作業をするメンバーは多くて2人です。 この状況において、困ることは発生しておらず、また問題も発生しておりません。 同タイミングで作業するメンバーが少し増えても、おそらく問題は発生しないだろうと考えています。

ぜひ参考にしていただければと思います。

YOUTRUSTでは、積極的にエンジニアを採用しています。 カジュアル面談も大歓迎ですので、興味がある方はぜひお声がけください!

herp.careers