コネヒト開発者ブログ

コネヒト開発者ブログ

ecschedule用のYAMLファイル専用リポジトリを解体して、各アプリケーションのリポジトリへ移管した話

プラットフォームグループでインフラエンジニアをしている @laughk です。 今回は、当社の ECS Scheduled Task の管理リポジトリを解体し、各アプリケーションのリポジトリへ移管した話を紹介します。

背景

コネヒトでは、ECS Scheduled Task の管理を一元管理するリポジトリがありました。このリポジトリでは ecschedule というツールを使って EventBridge のルールを YAML で管理、あわせて ECS のタスク定義もJSONで管理し、GitHub Actions 経由で EventBridge ルールの適用を ecschedule apply で行うというものでした。ecschedule については、以下の記事で導入の話を書いています。

github.com

tech.connehito.com

このリポジトリでは、3つのプロダクトの EventBridge のルールが一元管理されていました。

これは導入当初は各プロダクトのデプロイツールで ecs-deploy を使っていた関係で ECS サービスやタスク定義などのリソースをコード管理するという習慣自体が無かったことに由来します。ECS 関連のリソースの管理される場所も定まっておらず、ecschedule で必要とされる定期実行バッチの EventBridge のルールを管理する YAML ファイルを含め適切な管理場所も無かったため、独立したリポジトリを用意した経緯があります。

なお、定期実行バッチで利用する ECS タスク定義の JSON も同様の状況でしたが、後述するように ecspresso の導入によって Web アプリケーションのデプロイとまとめて管理されるようになりました。

なぜ一元管理しているリポジトリを解体したのか

ecschedule で管理していたリポジトリを解体した理由は、主に以下の2点です。

  • ECS 関連のデプロイ・リソースの管理状況が変わった
  • ecschedule の運用が安定し、プロダクトのリポジトリから独立している状況が煩わしくなった

結果として、 ecschedule で管理していたリポジトリを解体し、各プロダクトのリポジトリに ecschedule 用の YAML ファイルの管理と CI/CD などの自動化された仕組みを移管しました。

ECS関連のデプロイ・リソースの管理状況が変わった

ecs-deploy から ecspresso に移行したことで、タスク定義やデプロイ周りのコード化、デプロイの CI/CD 化が進みました。 特にそれまで Web アプリケーション用の ECS タスク定義と定期実行バッチ用の ECS タスク定義が別々に管理されていたのが、ecspresso によってデプロイも含めて共通化されたことが大きいです。

ecspresso 導入後に Web アプリケーションと定期実行バッチ用の ECS タスク定義更新の共通化については、以下のスライドにまとめているので興味のある方はご覧ください。

www.docswell.com

結果、定期実行バッチの EventBridge のルールだけが別のリポジトリに分かれている状況がかえって手間になりました。

ecschedule の運用の安定し、プロダクトのリポジトリから独立している状況が煩わしくなった

ecschedule 導入当初はツールに扱いなれていないメンバーも多く、ちょっとした記載方法のミスや設定漏れが発生するケースもあり、インフラエンジニアが中央集権的に管理する意義はありました。

しかし、問題が発生するごとにチェックスクリプトを用意して CI/CD でチェックする対応を繰り返していくうちにトラブルが発生するケースはなくなり、結果としてインフラエンジニアが介入する必要はほとんどなくなりました。

むしろ定期実行バッチの追加・変更・削除については関連する機能を実装した開発メンバー・チームのほうが状況を適切に把握しているケースがほとんどのため、インフラエンジニアが管理しているリポジトリに対して別で Pull Request を出す運用がそもそも実態に見合っていないものになっている状況でした。

解体から移管の概要

ecschedule 用の YAML ファイルを一元管理していたリポジトリの解体と各アプリケーションのリポジトリへの移管の概要は下図のイメージです。リポジトリのディレクトリごとに分かれていたものを、対応するアプリケーションのリポジトリに移管しました。

解体から移管の際に意識・工夫したこと

リポジトリの解体から設定ファイルの移管の際に意識・工夫したことは以下の通りです。

  • git log は git filter-repo を使って git log を保持した状態で移管
  • コードだけ移管するのではなく CI/CD による自動化もセット。人やチームに移管するのではなく、ツール・仕組みに移管する
  • デプロイで扱う CLI ツールは aqua で管理

git log は git filter-repo を使って保持した状態で移管

ecschedule 向けの設定ファイルを単純に移管することは簡単です。 しかし、それなりの時間運用していたものであるため、過去の経緯はできる限り追跡可能にしておきたいです。

そこで git filter-repo を使って、過去のコミット履歴を保持したままリポジトリを移管しました。 1 git 本体には含まれていないため、追加で brew や scoop でインストールする必要があります。 2

github.com

cluster1/prd-event-rules.yamlcluster1/dev-event-rules.yaml というファイルを batches というリポジトリからリポジトリ service1 の scheduled_batch_rules/ 配下に git log を保った状態で移動する例を示します。

CLIオペレーションとしては、おおよそ次の流れになります。一度移行元で scheduled_batch_rules/*.yaml だけの構成を作った後で、移行先リポジトリに強制的にマージするイメージです。

## ローカルにクローンしている batches リポジトリに移動
$ cd /path/to/batches

## 移行したいファイル以外、すべてをgit log含めて削除する
$ git filter-repo --path cluster1/dev-events-rules.yaml --path cluster1/prd-events-rules.yaml --force

## cluster1/ を scheduled_batch_rules/ に git log を保ちつつリネーム
$ git filter-repo --path-rename cluster1:scheduled_batch_rules --force

## 移行先のクローンしてあるリポジトリに移動
$ cd /path/to/cluster1

## 移行元のリポジトリをリモートに追加
$ git remote add batches /path/to/batches

## 移行元のリポジトリのリモートブランチを取得
$ git fetch batches

## 移行元のリポジトリのブランチを移行先のリポジトリにマージ
$ git merge batches/main --allow-unrelated-histories

注意点

  • /path/to は適宜自分の環境のものに読み替えてください
  • git fileter-repo は一度実行すると rebase などでも元に戻すことはできなくなるため、移行元となるリポジトリは移行用に別途クローンしておくことをおすすめします。
  • 移行元を移行先で取り込む際の git merge--allow-unrelated-histories オプションがないとエラーとなりマージできません。

コードだけ移管するのではなく CI/CD による自動化もセット

単純に設定ファイルの管理を別リポジトリに移すだけではこれまで利用してきた CI/CD による自動化された仕組みも失われてしまいます。 そうなると当然ながら CI/CD も含めて移管することになりますが、これまでの定期実行バッチの EventBridge のルールを管理するためだけのリポジトリからアプリケーションの開発が行われているリポジトリへの移管となるため、既存の CI/CD の影響を加味しつつ行いました。

移管した仕組みは次の通りです

  1. Pull Request 作成時に行うジョブ
    • ecschedule diff (差分チェック)
    • ecschedule 用の YAML の最低限のチェックツールの実行
  2. main ブランチマージの際に行うジョブ/tag push ( GitHub Release 作成)の際に行うジョブ 3
    • ecschedule apply (dev 環境への適用)
    • ecschedule apply (prd 環境への適用)

コネヒトでは全社的に GitHub Flow が採用されているためこのような流れになっています。

ecschedule の適用については既に ecspresso によるデプロイ(ECSサービス更新とバッチ用のECSタスク定義更新)が同様の流れで行われるようにしていたため、基本的にはデプロイが成功した後にジョブが走るようにしています。

それぞれについてもう少し詳しく説明していきます。

Pull Request 作成時に行うジョブ

まずは ecschedule diff によるYAMLと実際の EventBrige の EventBridge のルールの差分チェックを行うシンプルなジョブです。 GitHub Actions の YAML は次の通りです。

name: diff-check
on:
  push:
    paths:
      - "scheduled_batch_rules/*"
      - ".github/workflows/scheduled-batch-yaml-check.yaml"

permissions:
  id-token: write
  contents: read

jobs:
  diff-check:
    strategy:
      matrix:
        target:
          # diff check の対象となるファイルパスを指定
          - scheduled_batch_rules/dev-events-rules.yaml
          - scheduled_batch_rules/prd-events-rules.yaml
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: setup aqua
        uses: aquaproj/aqua-installer@v3.0.1
        with:
          aqua_version: v2.36.0

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: arn:aws:iam::123456789012:role/assume-role-for-ecs
          aws-region: ap-northeast-1
          role-session-name: GitHubActions-${{ github.run_id }}

      - name: check diff
        run: |
          ecschedule diff -conf ${{ matrix.target }} -all

工夫しているポイントとしては次のものがあります

  • トリガーの push.paths に ecschedule で利用する設定ファイルとGitHub Actions の YAML ファイルを指定し、定期実行バッチを変更しない場合は CI/CD ジョブが走らないようにする
  • matrix を使って dev 環境と prd 環境の差分チェックを並列して行う

次は、ecschedule 用の YAML の最低限のチェックツールの実行を行うジョブです。

check_rules_yaml.sh という独自に用意したシェルスクリプトを使って、YAML の構文チェックを行っています。内容は本エントリの趣旨から外れてしまうため詳細は割愛しますが、 EventBridge のルール名の重複チェックと overrides.*.command に余分なスペースが入っていいかのチェックを行います。

name: scheduled batch yaml check
on:
  push:
    paths:
      - "scheduled_batch_rules/*"
      - ".github/workflows/scheduled-batch-yaml-check.yaml"

jobs:
  scheduled_batch_yaml_check:
    strategy:
      matrix:
        target:
          # syntax check の対象となるファイルパスを指定
          - scheduled_batch_rules/dev-events-rules.yaml
          - scheduled_batch_rules/prd-events-rules.yaml

    runs-on: ubuntu-latest
    steps:
      - name: Check out code
        uses: actions/checkout@v4
        with:
          path: service1

      - name: Check out platform-toolbox
        uses: actions/checkout@v4
        with:
          repository: Connehito/platform-toolbox
          token: ${{ secrets.MACHINE_USER_GITHUB_ACCESS_TOKEN }}
          sparse-checkout: ecs_scheduled_task
          path: platform-toolbox

      - name: Run Syntax Check
        run:
          /bin/bash platform-toolbox/ecs_scheduled_task/check_rules_yaml.sh service1/${{ matrix.target }}

工夫しているポイントは次のとおりです。

  • 独自のチェックスクリプトはインフラツール管理用のリポジトリに移管し、CI/CD 上でマルチリポジトリクローンで対応
  • ecschedule diff 同様に matrix を使って dev 環境と prd 環境の構文チェックを並列して行う

特に、前者のチェックスクリプト check_rules_yaml.sh の扱いは複数のリポジトリで共有して利用できてかつ多重管理になることを避けたかった背景があります。

独自に作成したスクリプトなので最悪コピペで ecschedule の設定ファイル同様に移管することもできますが、それでは管理が散り散りになり今後の更新・修正が煩雑になります。当初は Draft Releaseデプロイフローを作成してみました - コネヒト開発者ブログ でも紹介されているカスタムアクションを作って共有化することも検討しましたが、その場合シェルスクリプトでチェックアウトしたリポジトリのファイル内容を読み込むのに難があったため、今回は actions/checkoutrepository オプションを使って別リポジトリのクローンを行うことで対処しました。4

Connehito/platform-toolbox というリポジトリは、元々コネヒトの主にインフラエンジニアが業務で利用する共通のスクリプトやツールを置いているプライベートリポジトリです。今回の check_rules_yaml.sh の置き場所としても適切であったため、このリポジトリに移管しました。

今回のシェルスクリプト check_rules_yaml.sh はコネヒト独自のものであるものの、CI/CD 上で利用するちょっとしたスクリプトがある場合でも同様に共通化することができます。

mainブランチマージ/tag push時に行う適用のジョブ

ecschedule apply が伴うジョブは次の通りです。

# ---- (中略) ----

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:

        # --- ( ecspresso によるデプロイジョブ ) ---

  scheduled_batch_rules:
    runs-on: ubuntu-latest
    needs: [ deploy ]
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: setup aqua # aqua で管理しているパッケージのインストール
        uses: aquaproj/aqua-installer@v3.0.1
        with:
          aqua_version: v2.36.0

      # 以下、定期実行バッチルールの適用
      # dev環境向けのバッチルールが変更された場合のみに、適用する
      - uses: dorny/paths-filter@v3
        id: scheduled-batch-rules-changes
        with:
          filters: |
            src:
              - 'scheduled_batch_rules/dev-events-rules.yaml'

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      - name: ecschedule apply to dev
        if: steps.scheduled-batch-rules-changes.outputs.src == 'true'
        run: |
          ecschedule apply -conf scheduled_batch_rules/dev-events-rules.yaml -all

ecschedule apply については ecspresso によるデプロイが成功した後にシンプルに ecschedule の適用ジョブが実行されるように追加しています。この時、 main ブランチマージの時のみ dorny/paths-filter で定期実行バッチ用の EventBridge のルールファイルを指定し、定期実行バッチを変更しない場合は極力 CI/CD ジョブが走らないようにしています。

本当は tag push の際も同様にしたいところですが paths-filter の仕様上 main ブランチマージ直後に作成された tag の場合、正しく差分を検知できません。そのため、本番環境への適用のジョブでは paths-filter を使わず、冪等であることを加味して常に ecschedule apply するようにしています。

デプロイで扱うCLIツールは aqua で管理

ecschedule の設定ファイルの移管は CI/CD の自動化も含めて行いましたが、 ecschedule そのもののも aqua を使って管理するようにしました。

github.com

aqua は Go 製の宣言型 CLI パッケージマネージャで、リポジトリトップに aqua.yaml というファイルを置くことで、リポジトリ内で利用するCLIツールとそれらのバージョン管理ができます。 aqua を導入した背景としては、主に次の課題を解決するためです。

  1. 各個人で ecschedule, ecspresso などを brew, scoop, mise などの管理に任せていると、いざローカルで利用する必要がある際にバージョンが合わずに想定外の動作をするケースがある
  2. ローカルで利用する CLI ツールのバージョンと GitHub Actions で利用する CLI ツールのバージョンをそろえたい
  3. CLI ツールの継続的なバージョンアップ

これらの課題は一度 CI/CD でワークフローが出来上がってしまえば、その後あまり意識されることが無くなってしまうものであります。 しかし、長期的な視点で考えるといずれも放置されるような状況になると「ある日突然 CI/CD が動かなくなる」という事態にもなりかねません。 そのため、ecschedule 導入のタイミングで同時に仕組みでつぶしておきたい課題でありました。

aqua を導入することで、リポジトリ内で利用する CLI ツールのバージョン管理を一元化し、 GitHub Actions での利用も含めてバージョンを一元管理できるようになりました。また、 renovate にも対応しているため、継続的なアップデートも人の手によるものではなく仕組みで行えるようにできます。

バッチ専用リポジトリを解体してどうなっているか

リポジトリの解体から移管が完了してからしばらく経ちますが、当初の目論見通りの改善ができたように感じています。

具体的には、バッチ追加の Pull Request がアプリケーション実装と EventBridge のルール追加・変更を一つのリポジトリで一気通貫して行えるようになり、インフラエンジニアによる形骸化した Pull Request レビューも必要なくなりました。

一見些細な事ではありますが、開発者が普段コミットしているリポジトリで完結するようになったことで、複数のリポジトリに合わせる余計な環境構築の必要はなくなり、別々に Pull Request を出してそれぞれのデプロイのタイミングを伺う必要も無くなりました。より必要な開発に集中できる環境を用意することに寄与できたように思えます。

ecschedule のような「直接アプリケーションには関わらない、けれどもシステムの運用上アプリケーションの開発者が直接触れたほうがよいインフラリソース管理」において、コネヒトでは今回のようなアプリケーションリポジトリに統合するという形に落ち着きました。もしも同様にアプリケーションと距離の近いインフラリソースの管理に課題を感じているケースの参考になれば幸いです。


  1. git log を保持したまま別のリポジトリにファイルを移す方法として git filter-branch を使う事例もあります。しかし git filter-branch は git 2.46.1 時点で公式でも非推奨となっており、git filter-repo を使うことが推奨されています。参考: Git - git-filter-branch Documentation
  2. https://github.com/newren/git-filter-repo/blob/main/INSTALL.md#installation-via-package-manager
  3. コネヒトでは一般的なステージング環境を dev 、本番環境を prd と呼んでいます。
  4. actions/checkout: Action for checking out a repo - Checkout multiple repos (private): https://github.com/actions/checkout?tab=readme-ov-file#checkout-multiple-repos-private