この記事は、Speee Advent Calendar5日目の記事です。 昨日の記事はこちら!
はじめに
DX事業本部 開発基盤グループの秋吉です。
Speee では GitHub Actions を積極的に活用しており、開発する上で必要な様々な作業を自動化しています。
一方で、リポジトリや環境数が増加するにつれて同じような GitHub Actions のワークフローファイルが増加していました。 したがって、ワークフローに対して共通の変更を加える場合であっても、全リポジトリ・全環境のワークフローファイルをそれぞれ手動で編集する必要がありました。 GitHub Actions の活用が進みワークフロー数が増加してきた結果、これが開発の妨げとなる技術的負債になっていました。
そこで、本記事では GitHub Actions の Reusable workflow という機能を使用し、それらを共通化したことについて説明します。
背景
まず、GitHub Actions を利用して実現しているこれまでの状況について説明します。
DX事業本部ではインフラのプラットフォームとして主に AWS を利用しており、各インフラを Terraform で管理しています。*1*2 また、Terraform の各種操作を GitHub Actions 上で実行しており、GitHub への操作をトリガーとして Terraform の Plan や Apply といった操作を自動的に実行しています。
インフラリポジトリの構成として、DX事業本部で提供している各サービスごとにリポジトリが分かれています。 そして、各リポジトリでは、主に以下の3種類の GitHub Actions ワークフローを定義しています。
- Test
terraform plan
コマンドを実行するためのワークフロー- Pull Request (PR) への Push (PR作成時を含む)をトリガーにワークフローを開始する
- Plan の結果を PR にコメントする
- Test cron
terraform plan
を定期的(1日に4回)に実行するためのワークフロー- 差分を検知した場合に Slack に通知する
- Deploy
terraform apply
を実行するためのワークフローstaging
ブランチもしくはproduction
ブランチに PR がマージされたときにワークフローを開始する
これらを図にすると以下のようになります。
GitHub Actions で Terraform の操作を自動化することで、 GitHub への操作のみでインフラを管理することを実現しています。
課題
Terraform の操作は自動化されており、効率的な開発はできていました。 しかしながら、ここで問題となっていたのは GitHub Actions のワークフローを定義する YAML ファイル自体の運用でした。
DX事業本部では、サービスごとにインフラ管理用リポジトリを分割しており、社内のインフラリポジトリ数は10以上存在しています。 さらに、各リポジトリでは production 環境と staging 環境で構成(tfstate) を分けており、それぞれの環境で Terraform の各種コマンドを実行するための GitHub Actions ワークフローが存在しています。
すなわち、 .github/workflows
ディレクトリの最小構成は以下のような状態になっていました。
.github/workflows ├── prod-deploy.yml # Production 環境デプロイ用ワークフロー ├── prod-test-cron.yml # Production 環境 Plan 定期実行用ワークフロー ├── prod-test.yml # Production 環境 Plan 用ワークフロー ├── stg-deploy.yml # Staging 環境デプロイ用ワークフロー ├── stg-test-cron.yml # Staging 環境 Plan 定期実行用ワークフロー └── stg-test.yml # Staging 環境 Plan 用ワークフロー
これがサービス(インフラ管理用リポジトリ)ごとに存在しているというわけです。
上記の構成が最小構成、かつ、インフラリポジトリ数が10以上あるため、GitHub Actions のワークフローの数は Terraform に関するものだけでも約80ファイルも存在していました。
問題は、Test, Test cron, Deploy それぞれのワークフローが、リポジトリや環境が異なったとしてもほとんどが同じ構成だという点です。 つまり、サービスや環境が増えるごとに、似たような GitHub Actions ワークフローの定義ファイルがコピペによって増えていくという非効率的な運用を行っていました。
したがって、ワークフローの定義ファイルに対して共通の変更を加えようとした場合に、小さな変更であったとしてもそれらのファイル全てに変更を加えなければなりませんでした。 当然ながら、複数リポジトリにまたがる約80個のファイルに対して手動で変更を加えるのは非常に手間がかかります。 結果として、初期設定が済んだあとは塩漬けにし、本当に必要な場合以外はほとんどメンテナンスを行うことができませんでした。
ワークフローを再利用する
上述した問題を解決するために、 Reusable workflows という機能を利用することにしました。
こちらの機能は2021年11月にGAになったばかりの機能です。 github.blog
Reusable workflows という機能は、その名の通りワークフローファイルを再利用して複数のワークフローを作成することを可能にします。
使用方法
呼び出される側のワークフローを called workflow 、呼び出す側のワークフローを caller workflowと言います。
called workflow では、ワークフローの開始条件である on
の値に workflow_call
を指定する必要があります。
また、workflow_call
の値にinputs
および secrets
を指定することで、引数と秘匿値を呼び出す側の caller workflow から受け取ります。
# called.yml (called workflow) on: workflow_call: inputs: name: required: true type: string secrets: my-secret: required: true jobs: hello: # (省略)
caller workflow では、jobs.*.uses
の値に呼び出される側の called workflow を指定します。
また、引数と秘匿値はそれぞれ with
と secrets
の値に渡します。
# caller.yml (caller workflow) on: # (省略) jobs: caller: uses: my-org/my-repo/.github/workflows/called.yml@main with: name: bob secrets: my-secret: ${{ secrets.bobs-secret }}
さらに詳細な機能については GitHub のドキュメントをご参照ください。
https://docs.github.com/ja/actions/learn-github-actions/reusing-workflowsdocs.github.com
使用例
今回は例として staging 環境の Test (terraform plan
) を実行する GitHub Actions ワークフローを再利用可能な状態に変更してみます。
共通化前のワークフローファイル
まず、共通化前のワークフローファイルは以下のような内容でした。 実際に使用していたワークフローファイルから一部の記述を省略・簡略化して記載します。
# test-stg.yml name: test-stg on: pull_request: branches: - staging paths: - infra/** jobs: terraform: name: Terraform runs-on: ubuntu-latest defaults: run: shell: bash working-directory: infra/staging/main steps: - uses: actions/checkout@v2 - uses: aws-actions/configure-aws-credentials@v1 with: aws-region: ap-northeast-1 aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - run: echo "##[set-output name=text;]$(cat $GITHUB_WORKSPACE/infra/.terraform-version | tr -d '\n')" id: terraform_version - uses: hashicorp/setup-terraform@v1 with: terraform_version: ${{ steps.terraform_version.outputs.text }} - run: terraform fmt -check -recursive - run: terraform init - run: terraform validate - run: terraform plan -input=false id: plan - name: Comment to PR env: PLAN_RESULT: ${{ steps.plan.outputs.stdout }} uses: actions/github-script@5.0.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | // Slack にコメント
また、同じディレクトリには上記とほぼ同じ内容の production 環境用のファイルも存在しています。
共通化後のワークフローファイル
再利用可能なワークフロー(called workflow)ファイルは以下のようになります。
# _terraform-plan.yml (called workflow) name: Terraform Plan on: workflow_call: inputs: working-directory: description: ワーキングディレクトリ type: string required: true aws-region: description: AWS のリージョン名 type: string default: ap-northeast-1 required: false secrets: aws-access-key-id: description: AWSアクセスキーID required: true aws-secret-access-key: description: AWSシークレットアクセスキー required: true github-token: description: GitHubのトークン required: true slack-webhook-url: description: SlackのWebhook URL required: false jobs: terraform: name: Terraform runs-on: ubuntu-latest defaults: run: shell: bash working-directory: ${{ inputs.working-directory }} steps: - uses: actions/checkout@v2 - uses: aws-actions/configure-aws-credentials@v1 with: aws-region: ap-northeast-1 aws-access-key-id: ${{ secrets.aws-access-key-id }} aws-secret-access-key: ${{ secrets.aws-secret-access-key }} - run: echo "##[set-output name=text;]$(cat $GITHUB_WORKSPACE/infra/.terraform-version | tr -d '\n')" # Terraform のバージョンを取得する id: terraform_version - uses: hashicorp/setup-terraform@v1 with: terraform_version: ${{ steps.terraform_version.outputs.text }} - run: terraform fmt -check -recursive - run: terraform init - run: terraform validate - run: terraform plan -input=false id: plan - name: Comment to PR env: PLAN_RESULT: ${{ steps.plan.outputs.stdout }} uses: actions/github-script@5.0.0 with: github-token: ${{ secrets.github-token }} script: | // Slack にコメント
caller workflow では以下のように記述し、上記のワークフローを呼び出します。
# test-stg.yml (caller workflow) name: test-stg on: pull_request: branches: - staging paths: - infra/** jobs: plan: uses: my-org/my-infra-repo/.github/workflows/_terraform-plan.yml@staging with: working-directory: infra/staging/main secrets: aws-access-key-id: ${{ secrets.STG_TF_AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.STG_TF_AWS_SECRET_ACCESS_KEY }} github-token: ${{ secrets.GITHUB_TOKEN }}
非常にシンプルですね。
このように、Reusable workflow の機能を使用してワークフローファイルを共通化することで、関数のように呼び出すことができるようになりました。 また、環境数が増加したとしても記述を最小限に抑えることができます。
共通化後のディレクトリ構成
上記の例と同様の方法で Test cron および Deploy のワークフローも再利用可能なワークフロー(_terraform-*.yml
)に共通化した結果、ディレクトリ構成は以下のようになりました。
.github/workflows ├── _terraform-plan.yml ├── _terraform-plan-cron.yml ├── _terraform-apply.yml ├── prod-deploy.yml ├── prod-test-cron.yml ├── prod-test.yml ├── stg-deploy.yml ├── stg-test-cron.yml └── stg-test.yml
ファイル数は増えているものの、呼び出す側(caller workflow) の記述量は大幅に削減されました。 さらに、ワークフローに対して共通の変更を加える際も、呼び出される側(called workflow) のみを編集するだけで全環境に反映されるようになりました。
今後の課題
Reusable Workflow の制約として、プライベートリポジトリのワークフローは同じリポジトリのワークフローからしか呼び出すことができません。 そのため、現段階ではリポジトリ内でのワークフローの共通化は実現しましたが、複数リポジトリに渡っての共通化はできていません。 そこで、次はより汎用的な Reusable workflow を作成し、それをパブリックリポジトリに公開する予定です。
DX事業本部の全インフラリポジトリからその Reusable workflow を参照することで、より効率的に GitHub Actions のワークフローを管理できるようになることを目指しています。
結論
GitHub Actions ワークフローのファイル共通化することで冗長な記述を削減することができました。 それにより、これまでは困難だった機能追加や修正などの運用を積極的に行うことができるようになりました。
DX事業本部 開発基盤グループでは引き続き業務の効率化に取り組み、開発効率の最大化を目指して行こうと思います!
さいごに
Speeeでは一緒にサービス開発を推進してくれる仲間を大募集しています! もしSpeeeに興味を持っていただいた方は以下で社内メンバーのカジュアル面談を公開しているので、お気軽にご連絡ください💁
エンジニアだけでなく、様々なポジションで募集中なので、「どんなポジションがあるの?」と気になってくれてた方は、こちらチェックしてみてください!もちろんオープンポジション的に上記に限らず積極採用中です!!!