🛠️

TerraformとGithub Actionsで快適デプロイ

2023/03/28に公開

IaC

IaCって言葉に聞き馴染みはありますか?
Infrastructure as Codeの略で、インフラの設定値をコード管理しようという仕組みです。
X.1ではIaCとしてTerraformを採用しています。
Terraformは特定のクラウドプラットフォームに依存しないサードパーティ製のIaCツールです。
X.1では現在ほとんどのサービスがAWSで動作していますが、時代の変化によってはGPCやAzureが主となっていく可能性も多分にあります。
こういった状況においてはサードパーティ製であることがメリットとなります。
しかし、サードパーティ製とはいえかなり更新頻度は高く、AWSの新しいリソース等も業務上使える状態だと判断される頃にはTerraformも対応済みであることがほとんどです。

CI/CD

横文字が続きますが、CI/CDについても触れておきます。
Continuous Integration / Continuous Deliveryの略で、開発においては自動テスト、自動デプロイのことを指します。
長らくデプロイはエンジニアのひとつの作業タスクでしたが、イベント駆動型のクラウドコンピューティングサービスによってこれらの作業は自動化されていくことが多くなりました。
自動化することで、作業者が異なっても同一環境で一定のデプロイが行える点や、作業担当者のヒューマンエラーも排除できることから基本的にメリットしかないと思っています。
旧システムへの導入はなかなかハードルが高いものがありますが、新規システムであれば必須で導入すべき仕組みのひとつですね。

本題

X.1ではタイトルにも書いてある通り、Github Actions上でTerraformを実行することで自動デプロイの仕組みを導入しています。
必要な環境変数等もGithub Actions上で埋め込むことで、クレデンシャルな情報のハードコーディングを避ける他、個々人のローカル環境に依存しないデプロイが実現できます。

実際に使用しているワークフロー

以下にX.1で実際に使用しているワークフローの一例を紹介します。

CI

  • PRをドラフトからレビュー待ちに変更したことをトリガーに起動
  • Lambda関数の単体テストをPytestで実行
  • Terraform planを実行して構文誤りの検出と今回の変更が期待通りかを確認

CD

  • PRがマージされたとき(pushイベント)に起動
  • Terraform applyを実行してリソースをAWSに反映
name: CI/CD
on:
  push:
    branches:
      - 'master'
      - 'staging'
      - 'production'
  pull_request:
    types:
      - "ready_for_review"
    branches:
      - 'master'
      - 'staging'
      - 'production'
jobs:
  pytest:
    name: Execute pytest for lambda function
    if: ${{ github.event_name == 'pull_request' }}
    runs-on: ubuntu-latest
    steps:
      - name: Checkout branch
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.9"

      - name: Install dependencies
        run: |
          cd ./src
          python -m pip install --upgrade pip
          pip install pipenv
          pipenv lock
          pipenv sync --dev

      # mockサーバを動かすために必要
      - name: Set up Node
        uses: actions/setup-node@v3
        with:
          node-version: 18

      - name: Install dependencies for node
        run: |
          yarn install --frozen-lockfile

      - name: Exec pytest
        run: |
          cd ./src
          pipenv run test

      - name: Pytest coverage comment
        if: always()
        uses: MishaKav/pytest-coverage-comment@main
        with:
          pytest-coverage-path: ./src/pytest-coverage.txt
          junitxml-path: ./src/pytest.xml

  terraform:
    name: Confirm terraform plan
    if: ${{ ! failure() }}
    needs: pytest
    runs-on: ubuntu-latest
    steps:
      - name: Checkout branch
        uses: actions/checkout@v3

      - name: Set up lambda function
        run: |
          cd src
          ./setup.sh

      - name: Set env to dev
        if: ${{ github.base_ref == 'master' || github.ref == 'refs/heads/master' }}
        run: |
          echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_DEV }}" >> $GITHUB_ENV
          echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_DEV }}" >> $GITHUB_ENV
          echo "TERRAFORM_DIR=./terraform/envs/dev" >> $GITHUB_ENV

      - name: Set env to stg
        if: ${{ github.base_ref == 'staging' || github.ref == 'refs/heads/staging' }}
        run: |
          echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_STG }}" >> $GITHUB_ENV
          echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_STG }}" >> $GITHUB_ENV
          echo "TERRAFORM_DIR=./terraform/envs/stg" >> $GITHUB_ENV

      - name: Set env to prd
        if: ${{ github.base_ref == 'production' || github.ref == 'refs/heads/production' }}
        run: |
          echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID_PRD }}" >> $GITHUB_ENV
          echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY_PRD }}" >> $GITHUB_ENV
          echo "TERRAFORM_DIR=./terraform/envs/prd" >> $GITHUB_ENV

      - name: configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ env.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ env.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: terraform setup
        uses: hashicorp/setup-terraform@v2

      - name: setup tfcmt
        if: ${{ github.event_name == 'pull_request' }}
        env:
          TFCMT_VERSION: v3.4.1
        run: |
          wget "https://github.com/suzuki-shunsuke/tfcmt/releases/download/${TFCMT_VERSION}/tfcmt_linux_amd64.tar.gz" -O /tmp/tfcmt.tar.gz
          tar xzf /tmp/tfcmt.tar.gz -C /tmp
          mv /tmp/tfcmt /usr/local/bin
          tfcmt --version

      - name: terraform init
        run: |
          cd ${{ env.TERRAFORM_DIR }}
          terraform init

      - name: terraform plan
        if: ${{ github.event_name == 'pull_request' }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          cd ${{ env.TERRAFORM_DIR }}
          tfcmt plan -patch -- terraform plan -no-color -input=false

      - name: terraform apply
        if: ${{ github.event_name == 'push' }}
        run: |
          cd ${{ env.TERRAFORM_DIR }}
          terraform apply -auto-approve

おわり

今後も導入すべきものはスピード感をもって導入していきます❗

エックスポイントワン技術ブログ

Discussion