失敗することを前提とした GitHub Actions のワークフロー実装
はじめに
ビットキーで bitkey platform を開発している @otakakot が担当します。
私はビットキーに2022年11月にジョイン[1]して、GitHub Actions にてリリース作業効率化のためにワークフローを実装してきました。
ビットキーにジョインしてから GitHub Actions を本格的に触り始めたので記事執筆時点での経験は約2年ほどです。
まだまだ知らないことも多いですがアドベントカレンダーという場所をお借りして、私が GitHub Actions でワークフローを実装するときに気をつけていることをこの機会に振り返り、みなさんに共有したいと思います。
動作確認環境を持つ
GitHub Actions は実際に動かしてみないとわからないというケースが多々あります。
そういうときのために動作確認のためのリポジトリを持っておくと便利です。
私は以下のようにプレイグラウンドを用意してそこで試してます。
新たに GitHub Actions を使ってワークフローを構築するときに PR に「この環境でやってみました」とリンクを共有して実際の動きを確認してもらったりもします。
ローカルで GitHub Actions を動かす OSS もありますが、Public リポジトリなら無料で使えるので実際に動かした方が手っ取り早いと思いローカルでの動作確認などは行なっていないです。
ワークフローをリトライ可能単位で分ける
ワークフロー実行時にはネットワークトラブルなど思わぬ形で失敗することがあります。
すべてのジョブおよびステップが、1からリトライしても問題がない処理であれば工夫は不要です。
しかし、すべての処理がそうであるとは限りません。
ビットキーではコスト削減を目的として本番ミラー環境をゼロから構築しています。
そんななかでフロントエンドとバックエンドの構築を一連の流れで行います。
お互いの構築は疎な関係であるためそれぞれのワークフローは独立して実行が可能です。
そのようなとき、私は repository_dispatch
としてリトライ可能な処理単位として切り出します。
こうしておけばフロントエンドの構築は成功したけどバックエンドの構築は失敗したというときにバックエンドの構築だけリトライすればよいという状況が生み出せます。
repository_dispatch
は Webhook によるイベントトリガーなので cURL などでも呼び出すことが可能です。
この状態にしておけばワークフローに失敗しても リトライとして簡単に再開ができます。
また、切り出したワークフローは repository_dispatch
だけでなく workflow_dispatch
として手動実行できるようにしておくのがおすすめです。
その際は repository_dispatch
と workflow_dispatch
どちらで呼び出されても実行できるようにしておきます。
外部から値を渡したいときには repository_dispatch
の client_payload
と workflow_dispatch
の inputs
をそれぞれ参照する必要があります。
name: rerun
run-name: ${{ github.workflow }} (${{ github.ref_name }} by ${{ github.actor }})
concurrency: ${{ github.workflow }}
on:
workflow_dispatch:
inputs:
value:
required: true
type: string
repository_dispatch:
types:
- rerun
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
value: ${{ steps.value.outputs.value }}
steps:
- name: Value
id: value
run: |
if [[ -z "${{ inputs.value }}" ]]; then
echo "value=${{ github.event.client_payload.value }} >> $GITHUB_OUTPUT
else
echo "value=${{ inputs.value }}" >> $GITHUB_OUTPUT
fi
echo:
runs-on: ubuntu-latest
needs: prepare
steps:
- name: Echo
run: |
echo "${{ needs.prepare.outputs.value }}"
シェルスクリプトの活用
GitHub Actions にはワークフローを効率的に実装するためのマーケットプレイスが用意されています。
巨人の肩に乗っかることができるので非常に便利なのですが、ローカル環境で実行するには実装を読む必要がありいざという時に労力がかかります。
その問題を解決するためにあえて、具体的にどういった処理をしているか把握・表現するためにも泥臭く自分でスクリプトを書くのもひとつの手だと思っています。
またこうしておくことで他にもメリットがあります。
「GitHub の障害によりワークフローを起動できない」なんてことも発生します。
「GitHub が障害だからリリースできません」ではなくそんなときにもリリース作業を行えるようにしておきます。
そのためにも私はなるべくシェルスクリプトで泥臭くコマンドを活用してワークフローを構築しています。
失敗通知の共通化
ワークフロー処理に失敗した場合、Slack などに通知したいユースケースがあるかと思います。
ワークフローごとに if: failure()
を記述してもよいですがそれぞれ処理を実装するのは億劫になります。
そこで便利になってくるのが workflow_run
によるイベントトリガーです。
on.workflow_run.workflows:
で失敗した時に通知を飛ばしたい workflow の名前を指定します。
*
を指定することによりプリフィックスやサフィックスで対象ワークフローを指定することができます。
実装例を以下に記載します。
name: sample_workflow
run-name: ${{ github.ref_name }} by @${{ github.actor }} at ${{ github.workflow }}
on:
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
failure:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Failure
run: exit 1
name: workflow_run
run-name: ${{ github.ref_name }} by @${{ github.actor }} at ${{ github.workflow }}
on:
workflow_run:
workflows:
- "sample_workflow"
- "prefix_*"
- "*_suffix"
types:
- completed
defaults:
run:
shell: bash
jobs:
workflow_run:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
timeout-minutes: 5
steps:
- name: Echo
run: |
echo ${{ github.event.workflow_run.id }}
おわりに
最初は(今も...) なんとなく動けばいいかで実装していましたがハマることも多く GitHub Actions は動作確認が難しくなかなか試行錯誤もできないので構築が難しいです。
そんな GitHub Actions を触り始めたころに出会いたかった書籍 No.1 も共有しておきます。
最初から書籍を読むのはハードルが高いと言う方は以下の記事をおすすめします。
書籍にはたくさんのプラクティスが詰まっていますが、記事にはほんとに簡単に適応できるプラクティスが凝縮されて記載されているので読んでいますぐ実践できます。
この記事が誰かのお役に立てれば幸いです。
20日目の 株式会社ビットキー Advent Calendar 2024 は @takuuuuuuu777 が担当します。
20日目の GitHub Actions Advent Calendar 2024 は @qqpann さんです。
Discussion