駅メモ!開発基盤チームの id:xztaityozx です!今回は CI/CD のお話です。
現在、駅メモ!チームでは Jenkins を使った CI/CD が構築されています。今回ここに GitHub Actions を加えることとなりました。チームでは段階的に GitHub Actions に移行していく計画です。 GitHub Actions を採用した理由としては、技術スタックの変化による需要の増加と Jenkins で抱えていた問題を解決するためという 2 点が主です。この記事では後者について書こうと思います。
現在の Jenkins 構成で困っていたこと
現在の Jenkins 構成では、起動できるサーバのスペックを柔軟に切り替えたり、追加できるようにしたいというリクエストに答えづらい状態でした。仕組み上の制約などから、追加の作業はそこそこ時間のかかる作業となっていたからです。
以前 CI でカバレッジ計測をしたいというリクエストがありましたが、提供まで 3 ヶ月の時間を要しました。
他のタスクに対応しつつだったので、かかりっきりというわけではなかったのですが、カバレッジ計測に合った別パイプラインの追加などが必要で、その作業すべてが手作業だったため、時間を取られてしまい提供が遅れてしまったのです。
このように、新しいワークフローを追加する際に、気軽さと柔軟性の欠如が問題となっていました。似たような事例でも直列に繋いだり、git hook 使用して対応したりといった対策が必要だった状況です。
もっと気軽に、自由に自動チェックやテストを追加したいですね…。
philips-labs/terraform-aws-github-runner
の Multi runner モジュールを使っていろんな設定の Runner を使えるようにする
この問題に対する解決策として、GitHub Actions と philips-labs/terraform-aws-github-runner の Multi runnerモジュールを活用する方法を紹介します。philips-labs/terraform-aws-github-runner
はオートスケールする GitHub Actions の Self-Hosted Runner を AWS 上に構築してくれる Terraform モジュールです。id:Eadaeda が以前にもこのテックブログで紹介していますので、興味があれば参照してみてください。
さて、このMulti runner
モジュールの README には以下のように書かれています。
This module replaces the top-level module to make it easy to create with one deployment multiple type of runners.
This module creates many runners with a single GitHub app. The module utilizes the internal modules and deploys parts of the stack for each runner defined.
どうやらこのモジュール、1 つの GitHub App に様々な設定の Runner を複数個構築できるというもののようです。コミット履歴やプルリクエストを辿ってみると、実装のきっかけになった議論を見つけることができました。
ざっくりとした内容は runs-on
に与えた値で起動するインスタンスのインスタンスタイプや OS を制御できると良いよねということです。これの具体的な実現方法としては、単純に Runner の仕組みを複数個作るというもので、図にすると以下のような感じでしょうか。
Multi runner モジュールを使ってみる
早速試してみましょう。今回の場合はt3.small
やt3a.small
を起動できるsmall
という Runner の設定がほしいというリクエストが来たとして、Multi runner モジュールで Runner を作ってみます!
module "multi_runner" { source = "philips-labs/github-runner/aws//modules/multi-runner" # 実装当時のLatest version = "3.6.0" prefix = "hoge-multi-runner" aws_region = data.aws_region.current.name vpc_id = data.aws_vpc.vpc.id subnet_ids = data.aws_subnets.subnets.ids github_app = { id = "ここにAppID" key_base64 = "ここに秘密鍵" webhook_secret = random_id.random.hex } webhook_lambda_zip = "./download-lambda/webhook.zip" runners_lambda_zip = "./download-lambda/runners.zip" runner_binaries_syncer_lambda_zip = "./download-lambda/runner-binaries-syncer.zip" multi_runner_config = { "small" = { # t3.smallかt3a.smallなRunner matcherConfig = { # このRunnerを呼び出すのに使う runs-on の値 labelMatchers = [["self-hosted", "linux", "x64", "ubuntu-20.04", "small"]] exactMatch = true } runner_extra_labels = "ubuntu-20.04,small" runner_name_prefix = "linux-x64-small_" # エファメラルランナーの場合は0でよい # https://github.com/philips-labs/terraform-aws-github-runner#ephemeral-runners delay_webhook_event = 0 runner_config = { runner_os = "linux" runner_architecture = "x64" # エファメラルランナー設定を有効にしてRunnerは1つのJobを実行したら終了するように enable_ephemeral_runners = true ami_filter = { name = ["hoge-github-runner-*"] state = ["available"] } ami_owners = [data.aws_caller_identity.current.account_id] # このRunner設定はt3.smallかt3a.smallを起動する! instance_types = ["t3.small", "t3a.small"] # 起動速度を考えて事前ビルドしたAMIから上げることにしたので、Syncerでactions/runnerのバイナリをSyncする機能はOFFにした enable_runner_binaries_syncer = false } } } }
あとはterraform plan
の出力を読んでterraform apply
、GitHub App 側の設定を行って終了です。更に Runner の設定を増やしたい場合は、"small"
と同じように増やしていけば良いだけです!簡単!
module "multi_runner" { source = "philips-labs/github-runner/aws//modules/multi-runner" version = "3.6.0" ... multi_runner_config = { "small" = { ... } "medium" = { ... } "large" = { ... } } ... }
Perl::Critic を動かしてみる
テストの一部として組み込まれている Perl::Critic による静的解析を GitHub Actions で動かしてみます。small
ぐらいのスペックがあれば十分なので、最初の移行としてぴったりな題材です。
# https://json.schemastore.org/github-workflow.json name: Perl Critic on: pull_request: types: [opened, synchronize, reopened] paths: - "**.pl" - "lib/**.pm" - "t/**.t" - "t/**.pm" - ".github/workflows/critic.yaml" concurrency: group: critic-${{github.ref}} cancel-in-progress: true jobs: critic: # 動かすRunnerの指定をする部分 runs-on: [self-hosted, linux, x64, ubuntu-20.04, small] steps: - uses: actions/checkout@v3 - name: Get delta id: changed-files run: | echo diffs="$(gh pr diff --name-only ${{ github.event.number }} | grep -Pe '^(.*\.(pl|pm)|t/.*\.t)$' | tr '\n' ' ')" >> $GITHUB_OUTPUT env: # 認証に secrets.GITHUB_TOKEN が使えるのが本当に便利 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Run Check if: ${{ steps.changed-files.outputs.diffs != '' }} run: | /usr/local/bin/perlcritic --profile config/dev/perlcriticrc \ ${{steps.changed-files.outputs.diffs}}
Perl ファイルの差分だけを検査するような簡単なワークフローです。ポイントは runs-on
に Runner のlabelMatchers
設定の値が書かれているところです。
注意点として、runs-on
のラベルは部分一致するもののうちのどれかが選ばれるという GitHub の仕様があります。例えば以下のように複数の Runner の設定があるとき
[self-hosted, linux, x64, ubuntu-20.04, small]
[self-hosted, linux, x64, ubuntu-20.04, large]
[self-hosted, linux, x64, ubuntu-20.04, xlarge]
runs-on
に[self-hosted, linux, x64, ubuntu-20.04]
と設定するだけではsmall
,large
,xlarge
のどれにもマッチするため、どの Runner がジョブを拾うかはわからないのです。このことはMulti runner
モジュールの README にも書いてあるので、詳しくはそちらをお読みください。
すこしruns-on
を間違えただけで意図しないコスト増加がありえるので、ワークフローのコードレビューでは必ず見ておきたい項目になりそうです。
まとめ
- Self-Hosted Runner な GitHub Actions を構築することになった
- 起動できる Runner のスペックを柔軟に切り替えたり、追加できるようにしたかった
philips-labs/terraform-aws-github-runner
の Multi runner できそうだったのでやってみた- うまく動いたのでよかった
今後、利用が活発になると見える問題点や、JIT Runnerも試してみたいなと思っているので、また知見が溜まったらこのブログで記事を書きたいと思います。以上です。