シンクロ・フード エンジニアブログ

飲食店ドットコムを運営する、株式会社シンクロ・フードの技術ブログです

CI を Drone から GitHub Actions に移行しました

こんにちは、シンクロ・フードの大前です。

シンクロ・フードではCIにDroneを使用していましたが、2022年の11月ごろにGitHub Actionsへの移行を完了しました。
今回はその経緯やDroneとの比較についてお話します。

なぜ移行したのか

シンクロ・フードでは2018年ごろからDroneを使用していました。
導入時の記事があるので良かったらご覧ください。

tech.synchro-food.co.jp

その後、2020年9月ごろにGitHub Enterprise Server(以下GHES)でもGitHub Actionsが使えるようになりました。
(シンクロ・フードではGHESというオンプレ版を使っています。GitHub Enterprise Cloudと比べて機能追加が少し遅い特徴があります。)

Droneは課題を感じている部分もあり、GitHub Actionsの方が以下のような点で優れていたため移行しました。

GitHub Actionsのワークフロー構文の方が表現力が高い

CIを主に実行しているリポジトリはMonorepoで運用1しているので、各アプリケーションごとに自動テストを実行する必要がありました。
DroneではMatrix Buildsという記法でディレクトリごとに実行するワークフローを制御していましたが、GitHub Actionsではディレクトリごとにトリガーを設定することで読みやすい構造にできる点が魅力でした。

以下にDroneとGitHub Actionsのサンプルコードを記載します。(簡略化のため実際は必要な記述を省略していますがご了承ください)

Droneの場合

この後詳しく解説しますが、まずは全体のコードをご覧ください。

# 1. matrix(並列実行)を定義する
matrix:
  include:
    - SUB_REPOSITORY: ''
      RUBY_VERSION: 2.4.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application1
      RUBY_VERSION: 2.6.0
      APPLY_SEED: true
    - SUB_REPOSITORY: apps/rails-application2
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application3
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false

# 2. あとでYAMLのエイリアスとして使えるようにアンカーを定義する
references:
  if-root-matrix: &if-root-matrix
    matrix:
      SUB_REPOSITORY: ''
  if-app-matrix: &if-app-matrix
    matrix:
      exclude: SUB_REPOSITORY: ''

# 3. CIで実行するジョブを定義する
pipeline:
  run-tests:
    commands:
      - bundle exec rspec
    when:
      <<: *if-rails-app-matrix
  
  lint:
    commands:
      - yarn run lint
    when:
      << *if-root-matrix

  apply-seed-each-app:
    commands:
      - bundle exec rails db:seed
    when:
      matrix:
        APPLY_SEED: true

1. matrix(並列実行)を定義する

以下の部分ではMonorepo内のアプリケーションのディレクトリをSUB_REPOSITORYとして定義しています。
SUB_REPOSITORY: ''はMonorepoのルート階層を意味していて、全アプリケーションに適用する処理はこのときに実行するようにしています。
RUBY_VERSIONAPPLY_SEEDは各アプリケーションごとのCI実行に必要な情報を定義しています。
この場合だと、ルート階層での実行+アプリケーション3つそれぞれで実行ということになります。

matrix:
  include:
    - SUB_REPOSITORY: ''
      RUBY_VERSION: 2.4.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application1
      RUBY_VERSION: 2.6.0
      APPLY_SEED: true
    - SUB_REPOSITORY: apps/rails-application2
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false
    - SUB_REPOSITORY: apps/rails-application3
      RUBY_VERSION: 2.7.1
      APPLY_SEED: false

2. あとでYAMLのエイリアスとして使えるようにアンカーを定義する

以下の部分はGitHub Actionsでいうところのon.*.pathsの設定にあたるもので、Monorepoのルート階層で実行するかアプリケーションごとの階層で実行するかを判別する設定です。これをyamlのアンカーとして定義してテストやlintの処理でエイリアスとして参照します。

references:
  if-root-matrix: &if-root-matrix
    matrix:
      SUB_REPOSITORY: ''
  if-app-matrix: &if-app-matrix
    matrix:
      exclude: SUB_REPOSITORY: ''

3. CIで実行するジョブを定義する

以下は実際にCIで実行するテストやlintの設定です。
lintはMonorepo内の全アプリケーション一律で同じものを適用しているのでルート階層で実行しています。
apply-seed-each-appというのは、特定のアプリケーションのみで実行したい処理の設定で、APPLY_SEEDをtrueにしているアプリケーションでのみ実行されます。

pipeline:
  run-tests:
    commands:
      - bundle exec rspec
    when:
      <<: *if-rails-app-matrix
  
  lint:
    commands:
      - yarn run lint
    when:
      << *if-root-matrix

  apply-seed-each-app:
    commands:
      - bundle exec rails db:seed
    when:
      matrix:
        APPLY_SEED: true

Matrix Buildsは各アプリケーションごとに何を実行するのかが一見しただけではわからないですし、アプリケーションごとに独自の処理が増えるとより複雑になってしまうという点がつらかったです。

GitHub Actionsの場合

今度はGitHub Actionsのサンプルコードです。
Monorepoのルート階層で実行する処理はroot.ymlに、各アプリケーションごとに実行する処理は個別にymlファイルを用意します。今回はrails-application1の設定ファイルを例としてrails-application1.ymlに記載します。

GitHub Actionsのコードについては詳しい説明は割愛しますが、ひと目見ただけでも各アプリケーションで実行する内容がなんとなく読み取れるのではないでしょうか。

name: CI

on: push

jobs:
  lint:
    steps:
      - name: Run Lint
        run: yarn run lint
name: rails-application1

on:
  push:
    paths:
      - 'apps/rails-application1'
      
jobs:
  run-tests:
    steps:
      - name: RSpec
        run: bundle exec rspec
  apply-seed:
    steps:
      - name: Apply seed
        run: bundle exec rails db:seed

コミュニティが大きくドキュメントや知見が豊富

他にもGitHub Actionsの方が公式のドキュメントが豊富で、知見がインターネット上に多いという点が魅力でした。
また自作のワークフローが多く公開されているという点も良いです。

GitHubとの連携機能が充実している

GitHubが提供しているだけあり、GitHubとの連携機能が豊富です。

  • GitHub上のPull RequestからCIの実行結果やログをスムーズに確認できる。
  • ワークフローのトリガーが豊富。
  • GitHubのトークンを発行する手間なく参照できる。

移行方法

GHESではCIが実行されるインスタンスを自前でホスティングする必要があることからはじめにインフラ構築をしました。
インフラ周りのお話についてはまた別途記事を公開する予定のためここでは割愛させていただきます。

その後、Droneで実行しているRuboCopやRSpecなどをGitHub Actionsに移行しました。
DroneからGitHub Actionsへの書き換えは技術的に難しいところはありませんが、GitHub Actionsの構文に慣れていなかったので勉強しながら書き換えていきました。

2020年10月ごろにActionsを導入し、Droneからの移行が完全に完了したのは2022年の11月ごろでした。2

所感

GitHub Actionsに移行してしばらく経ちましたが、やはりDroneよりも資料が豊富で開発しやすいと感じています。
また、GitHub Actionsでは複数のリポジトリでワークフローを再利用3しやすい点も便利だと思いました。
Droneでは共通化したい処理をDockerイメージにすることで共通化していたため、より手軽になったと思います。

一方でGitHub Actionsそのものというわけではないですが、GHESによる課題も少しありました。
というのもGHESはGitHub Enterprise Cloudと比べて機能追加が少し遅いです。当時はbundle installyarn installの結果をキャッシュするactions/cacheがGHESでは使えなかったため自作する必要がありました。(現在はGHESでも使えるようになっています)

まとめ

DroneからGitHub Actionsへの移行について紹介しました。
GitHub Actionsへの移行によって、よりCIの開発がしやすい環境になったと言えます。

シンクロ・フードではCIチームというCIに関する開発をするチームを設置していて、各開発者が所属するメインのチームと兼任する形で所属しています。
CIチームのメンバーも徐々に増えてきているため、今後はよりCI環境の改善を加速させていきたいと思います。


最後に、エンジニア募集のお知らせです! シンクロ・フードでは、まだまだ技術的な課題やチャレンジが残っています。 ぜひ、一緒に改善していきましょう!

また、飲食店ドットコム、求人飲食店ドットコムを始めとしたサービスの開発・改善も、どんどん進めております。 ご興味のある方は、以下の採用サイトからご連絡ください!

www.synchro-food.co.jp


  1. Monorepoへの移行についてエンジニアブログのこちらの記事にまとめています。こちらも良ければぜひご覧ください。
  2. 2年ほどかかっていますがCIの開発に割り当てられる時間があまり多くないというところによるものです。また、その期間は移行だけでなく並行してCIの速度改善などの取り組みをしていました。
  3. Composite Action