🚀

Stena Expense のデプロイフロー

に公開

はじめに

こんにちは、ChillStack でエンジニアをしている中川です。
ChillStack では経理部向けに経費申請から不正・不備を自動で検知する「Stena Expense」というサービスを提供しています。
本記事では、Stena Expense の開発チームで実際に採用しているデプロイフローを紹介します。
同じような規模のプロダクトでデプロイの自動化を設計する方の参考になれば幸いです。

今回紹介するデプロイフローの主な特徴は、以下の 4 つです。

  • アプリとインフラのリポジトリ分離
    アプリケーション変更とインフラ変更のライフサイクルを独立させ、それぞれの変更を適切な粒度でレビュー・デプロイできるようにしています。
  • GitOps によるインフラ変更の追跡
    アプリリポジトリから直接デプロイせず、インフラリポジトリに PR を自動作成することで、すべての変更に Git 履歴とレビューを残しています。
  • staging → production の自動プロモーション
    staging で E2E テストを通過すると production リリース PR が自動作成され、production のみ人間の承認を必要とすることで、自動化と安全性を両立しています。
  • release-drafter を用いたリリースノートの自動生成
    PR のマージに応じてリリースノートのドラフトが自動更新され、開発者は GitHub Release を publish するだけで Docker ビルドからデプロイまでの全プロセスが自動で進行します。

これらの仕組みにより、開発者は GitHub Release を publish するだけでリリースプロセス全体が進行し、安全性と開発速度を両立できる構成になっています。

Stena Expense の構成

Stena Expense は、サービスのソースコードをまとめたアプリリポジトリと、インフラ定義をまとめたインフラリポジトリの 2 つで構成されています。
アプリリポジトリは、Go のバックエンド API、Next.js の Web アプリ(顧客向け・社内サポート向け)、Python の不正検知サービスなど、複数のサービスのソースコードを 1 つのリポジトリで管理するモノレポ構成です。
インフラリポジトリには Terragrunt / Kustomize の定義とデプロイワークフローを配置しており、各サービスの Docker イメージは Google Cloud の Artifact Registry に格納されています。
この分離により、アプリケーション変更とインフラ変更のライフサイクルが独立し、それぞれの変更を適切な粒度でレビュー・デプロイできます。

環境構成

Stena Expense では 4 つの環境を運用しており、それぞれ異なるデプロイ方式を採用しています。

環境 用途 デプロイ方式
Dev 開発・ローカルテスト 手動(Makefile)
Staging プレプロダクション検証 自動(Release publish → 自動マージ → apply)
Sandbox 本番相当のテスト環境 半自動(production と同一 PR で更新)
Production 本番 半自動(自動 PR 作成 → 人間がマージ → apply)

デプロイフロー

Stena Expense のデプロイは、GitHub Release の publish を起点としてアプリリポジトリからインフラリポジトリへ PR を自動作成する GitOps ベースの仕組みです。
アプリリポジトリで Docker イメージのビルド & push を行い、その後インフラリポジトリに対してイメージタグの更新 PR を自動作成します。
この設計により、アプリケーションの変更が直接デプロイされることはなく、すべてのデプロイがインフラリポジトリの PR として記録される構成になっています。


Stena Expense のデプロイフロー

リリースの起点は、開発者が GitHub Release のドラフトを publish することです。(①)
GitHub Release のドラフトは release-drafter によって自動生成されるので、開発者はドラフトの内容を確認して publish するだけでリリースを開始できます。
publish によりリリースタグ(例:v3.152.0)が自動作成され、タグ push をトリガーに全サービスの Docker イメージが並列でビルドされ、Artifact Registry に push されます。

全イメージのビルド完了後、インフラリポジトリに staging 向けのリリース PR が自動作成されます。(②)
アプリリポジトリのワークフローが GitHub App トークンを使ってインフラリポジトリにアクセスし、バージョンファイルと Terragrunt / Kustomize のイメージタグを一括更新して release/staging-vX.Y.Z ブランチで PR を作成します。

staging 向けのリリース PR は自動でマージされ、staging 環境へのデプロイが実行されます。(③)
k8s マニフェストの適用(kubectl apply)と Cloud Run サービス・ジョブの更新(terragrunt apply)が並列で実行された後、Cloud Run Job による DB マイグレーションが実行されます。
デプロイ完了後に Playwright による E2E テストが自動実行されます。

E2E テスト通過後、production 向けのリリース PR が自動作成されます。(④)
production と sandbox の両環境のバージョンファイルとイメージタグを更新し、release/production-vX.Y.Z ブランチで PR を作成します。
production デプロイだけは人間による承認を必須としており、開発者は staging 環境で動作を検証し、問題がないことを確認してからマージします。
production 向けのリリース PR がマージされると、staging と同様の手順で production へのデプロイが実行されます。

リリースノート

リリースノートは release-drafter を使って PR ラベルから自動生成しています。

https://github.com/release-drafter/release-drafter

release-drafter は、main ブランチにマージされた PR のタイトル・作成者・ラベルを集約して、次回リリースに含まれる変更内容をドラフトとして自動で書き溜めてくれる GitHub Action です。
PR がマージされるたびにドラフトが更新されていくため、リリース時には内容を確認して publish するだけでリリースノートが完成します。
ラベルによるカテゴリ分けやセマンティックバージョニングのルール(major / minor / patch)に基づく次バージョン番号の自動解決にも対応しており、リリース作業を大きく省力化できます。

以下は Stena Expense で実際に採用しているワークフローと設定ファイルの一部抜粋です。

.github/workflows/release_draft.yaml
name: Release Draft

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  update-release-draft:
    permissions:
      contents: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - uses: release-drafter/release-drafter@v6
        with:
          commitish: main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
.github/release-drafter.yml
name-template: "🚀 v$RESOLVED_VERSION"
tag-template: "v$RESOLVED_VERSION"

categories:
  - title: "🎮 Controller"
    labels:
      - "controller"
  - title: "🛢️ DB Migration"
    labels:
      - "controller-migrations"
  - title: "💻 Manager"
    labels:
      - "manager"

change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
change-title-escapes: '\<*_&'

version-resolver:
  major:
    labels:
      - "major"
  minor:
    labels:
      - "minor"
      - "controller-migrations"
  patch:
    labels:
      - "patch"
  default: patch

template: |
  ## What's Changed

  $CHANGES

  ## 🌱 Full Change Log
  https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION

autolabeler:
  - label: controller
    files:
      - "controller/**"
  - label: controller-migrations
    files:
      - "controller/migrations/**"
  - label: manager
    files:
      - "manager/**"

PR のラベルは変更ファイルパスから autolabeler で自動付与されるため、開発者が手動でラベルを管理する必要はありません。
サービスごとにカテゴリ分けされたチェンジログが自動で生成されます。


自動生成されたリリースノート

最後に

プロダクト開発において、デプロイフローの整備は開発速度とリリースの安全性を両立させるための重要な基盤です。
複数の技術スタックやサービスで構成されるプロダクトでは、デプロイフローの設計次第で開発体験とリリース安定性が大きく変わります。
特に近年は、AI コーディングアシスタントの普及により、人間を遥かに超えるスピードで変更が生み出されるようになり、その重要性はこれまで以上に高まっているといえます。
今後もリリースサイクルの高速化と安全性の両立を目指した改善を続けていきたいと思います。


ChillStack ではプロダクトを一緒に育ててくれる仲間を募集しています。
「今は転職する気はないけど、ちょっと興味を持ったので話を聞いてみたい」という方も大歓迎ですので、ご興味ある方お待ちしてます。

会社概要
https://chillstack.com/

採用ページ
https://chillstack.com/career

公式 Note
https://note.com/chillstack

株式会社ChillStack Tech Blog

Discussion