TerraformとGithub Actionsで快適デプロイ
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