はじめに
こんにちは、enechainでeScanという電力取引向けのリスク管理システム(ETRM)を開発している西村です。
本記事では、機能の早期フィードバックを得るための一時的なテスト環境(いわゆるFeature環境)を、kustomizeとArgo CDを活用してどのように構築できるかをご紹介します。
従来の開発フローでは、mainブランチ(またはdevelopブランチ)にマージされるまで機能を確認できないことが多く、PdMやQAエンジニアなどの非エンジニア職の方々からのフィードバックが遅れがちでした。その結果、手戻りのコストが増え、開発スピードや品質にも影響を及ぼしていました。
そこで、Pull Requestにラベルを付けると自動的に独立した環境が立ち上がる仕組みを整備しました。これにより、まだmainブランチにマージする前の段階で動作を確認し、素早くフィードバックを得られるようになりました。
本記事では、この仕組みの設計プロセスと、その導入によって得られた成果について詳しくご紹介します。
解決したい課題
抱えていた問題点
まず、私たちが当時抱えていたのは以下のような問題でした。
PdM・QAエンジニアなど非エンジニア職との機能確認がしづらい環境
エンジニア同士のコードレビューはGitHub上でできても、実際のアプリケーション画面を触れる環境がないと、UI/UXまわりの細かなフィードバックが難しいという状況でした。
不具合の発見や仕様のすれ違いの発覚が開発後期になりがちな問題
リリース前の段階で機能を十分に試せないため、不具合や仕様のすれ違いがリリース後や大きく開発が進んだ後で判明するケースがありました。これにより、開発全体に修正コストがかかり、スケジュールにも大きな影響を及ぼすリスクがありました。
開発の反復サイクルが遅くなることによる生産性低下
機能実装後にマージして初めて動作確認ができる環境では、mainブランチ(またはdevelopブランチ)へマージ → テスト環境リリース → 不具合発見 → 再度修正…というフローが必要になります。
このように開発とフィードバックのサイクルが長くなると、修正にかかる工数が増えるだけでなく、リリースサイクルもどんどん後ろ倒しになる傾向がありました。
sequenceDiagram participant MB as mainブランチ participant DB as developブランチ participant FB as featureブランチ participant DE as Develop環境 DB->>FB: featureブランチ作成 Note over FB: 開発開始 Note over FB: コードレビューのみ可能<br/>PdM/QAエンジニアは確認不可 FB->>DB: マージ DB->>DE: Develop環境作成 Note over DE: PdM/QAエンジニアによる<br/>機能確認 alt 問題なし DB->>MB: mainブランチにマージ else 問題あり Note over FB: 新規PRで修正 end
これらを一言でまとめると、開発の早い段階で非エンジニアの視点で十分な検証ができず、手戻りリスクが高いという課題でした。
理想的な開発フロー
こうした問題点を解消するために、以下のような理想的な開発フローを目指しました。
featureブランチごとに独立した環境を自動で立ち上げる
1つのブランチが1つの実行環境を持ち、そこにPull Requestレビュー担当者やPdM/QAエンジニアがいつでもアクセスできます。
mainブランチへマージ(またはクローズ)後には自動で環境を削除
リソースを無駄に消費しないようクリーンアップが自動化されており、管理者の負担を減らします。
sequenceDiagram participant MB as mainブランチ participant FB as featureブランチ participant FE as Feature環境 MB->>FB: featureブランチ作成 Note over FB: 開発開始 FB->>FE: Feature環境自動作成 Note over FE: PdM/QAエンジニアが<br/>リアルタイムで確認可能 alt 問題なし FB->>MB: mainブランチにマージ Note over FE: Feature環境自動削除 else 問題あり Note over FB: 修正コミット FB->>FE: Feature環境自動更新 end
このようなブランチ単位で Feature環境を作り、不要になれば消すという流れができれば、最小限の手間で誰もが開発途中の機能を確認できるようになり、フィードバックの速度を大幅に向上させることができます。
前提条件
kustomize を使った環境構築
私たちのプロジェクトでは、開発 (Develop)、ステージング (Staging)、本番 (Production) といった複数環境を、 kustomize を使って一元管理しています。
kustomizeの特徴として、ベースのマニフェストと環境ごとの差分をレイヤーとして扱える点が挙げられます。これにより、共通化できる設定は同じマニフェストを使い、環境固有の設定(例:レプリカ数やドメイン名など)だけを分離して管理ができます。
Argo CD による GitOps 運用
kustomize で生成されたマニフェストを、Argo CD によって自動デプロイしている点が、この運用の大きな特徴です。
Argo CD はリポジトリの状態と Kubernetes 環境を常に同期し、差分があれば自動的に反映することで、以下のようなメリットをもたらします。
手動デプロイの手間・ミスを削減
Gitにプッシュされたタイミングをフックに、Argo CDが最新のマニフェストを読み取り、Kubernetesクラスタを自動更新します。手動コマンドによる人為的ミスを減らすだけでなく、デプロイ速度の向上も期待できます。
構成の可視化や一元管理
「Gitで管理している設定が、現在のクラスタに正しく適用されているか」をリアルタイムで可視化できます。クラスタの状態とリポジトリの状態に差分があれば即座に検知し、どこを修正すべきかを把握しやすくなります。
GitOpsの実現による運用効率化
Argo CDによってGit を単一の信頼できる情報源 (Single Source of Truth) とする GitOpsのプラクティスを取り入れられるため、開発から運用までのフローをシンプルに保てます。マニフェストの変更履歴やバージョン管理が明確なため、トラブル発生時のロールバックも容易です。
リポジトリ構成の分離
また、アプリケーションのソースコードリポジトリとkustomize用のマニフェストリポジトリを分離していることも大きな特徴です。アプリケーションコードとインフラ設定を同じリポジトリで管理すると、変更の追跡や責任範囲の切り分けが複雑になる可能性があります。
そのため 「アプリケーションコードの変更(機能追加)」 と 「インフラ設定の変更(環境構築やリソース設定)」 を明確に切り分ける運用をしています。
設計方針
Feature 環境で実現する内容
今回の設計では、featureブランチごとに独立した環境を立ち上げることを第一のゴールとしています。具体的には、ブランチ名をドメインに含めたURL(例: https://<branch-name>.dev.enechain.com
)でアクセス可能な環境が自動的に作られるイメージです。
こうすることで、ブランチに紐づくPull Requestがオープンになっている間は、そのPRに対応したテスト環境が常に存在し、いつでも確認・テスト・レビューができるようになります。
Feature 環境の動的リソース作成方法
kustomize の標準機能での課題
kustomize自体にはPrefixSuffixTransformer
などの機能が用意されているものの、ブランチ名の特殊文字の処理や、環境変数展開の柔軟性に課題がありました。
例えば、ブランチ名にスラッシュ (/
) やピリオド (.
) が含まれる場合など、そのままでは正しいドメインやリソース名に置き換えられないケースがありました。
独自ツールを活用
そこで、ksubst-rs
というツールを実装して導入しています。
ksubst-rs は、kustomizeのビルドを行う前にYAMLテンプレート内のプレースホルダ(例: ${FEATURE_NAME}
)を
環境変数に応じて柔軟に差し替えることが可能です。これにより、例えばブランチ名に含まれる特殊文字を自動的にエスケープしてリソース名やドメインに落とし込む、といった処理を簡単に実装できます。
> cat kustomize.yaml apiVersion: networking.gke.io/v1 kind: ManagedCertificate metadata: name: ${FEATURE_NAME-}escan-mc spec: domains: - ${FEATURE_NAME.}dev.enechain.com > cat kustomize.yaml | FEATURE_NAME=feature1 ksubst apiVersion: networking.gke.io/v1 kind: ManagedCertificate metadata: name: feature1-escan-mc spec: domains: - feature1.dev.enechain.com
kustomize build の出力先ディレクトリ構成
これまではArgo CDによる運用で、アプリケーションごとに kustomize のディレクトリを直接参照 する構成を採用していました。
しかし、Feature環境をdevelopと同じnamespaceで管理する必要性が出てきたため、それぞれのマニフェストをまとめて一元管理できる output/
ディレクトリ 方式に切り替えました。具体的には、CI(GitHub Actionsなど)で ksubst-rs
を用いてブランチ名の特殊文字などを適切にエスケープし、環境変数 ${FEATURE_NAME}
などをYAMLテンプレート内で動的に置き換えます。
その後、kustomize build
を実行して生成された YAML を output/
配下にコミットします。すると、Argo CD はこの output/
を GitOps の監視対象として自動的に差分を検知し、Kubernetesクラスタに反映してくれます。
output/ ├── escan-dev/ │ ├── main.yaml ## Develop環境向けベース │ ├── feature1.yaml ## feature1 ブランチ用 │ └── feature2.yaml ## feature2 ブランチ用 ├── escan-dev-job/ │ ├── main.yaml │ ├── feature1.yaml │ └── feature2.yaml ..
このように ブランチごとのマニフェストを output/
に集約することで、featureブランチの環境が増えても、ディレクトリ内部のファイルを追加するだけで済むようになります。
また、ksubst-rs
で 同一 namespace 内でも各ブランチごとにユニークなリソース名を生成できるため、developブランチと干渉せずに並行開発を進められる点が大きなメリットです。開発規模が拡大しても運用が複雑化しにくい、柔軟かつシンプルなワークフローになりました。
Feature 環境用ディレクトリ構成
As-Is(現状構成)
従来は、Develop
・Staging
・Production
のように環境別ディレクトリが並んでおり、Feature環境のような "都度生成・削除" されるディレクトリ構成までは想定していませんでした。
k8s ├── base └── overlays ├── develop │ ├── base │ ├── application-dev │ └── job-dev ├── staging │ ├── base │ ├── application-stg │ └── job-stg └── production ├── base ├── application-prod └── job-prod
To-Be(新構成)
そこで新たに develop-template
ディレクトリを作り、これをコピー・編集することでFeature用ディレクトリを自動生成するように変更しました。
k8s ├── base └── overlays ├── develop │ ├── develop-template │ ├── feature1 │ ├── feature2 │ └── ... ├── staging │ ├── base │ ├── application-stg │ └── job-stg └── production ├── base ├── application-prod └── job-prod
develop-template
Develop環境に相当する設定のベースをまとめておき、ブランチ名やリソース名・ドメイン名に相当する部分だけプレースホルダにしておきます。
feature1 / feature2
CIでfeatureブランチごとに自動生成され、
ksubst-rs
を使ってブランチ名やイメージ名を挿入していきます。
GitHub Flow ベースの開発フロー
ここからは実際のフローを、シンプルなGitHub Flowと合わせて説明します。
feature ブランチの作成
新機能を開発する際は、mainブランチ(またはdevelopブランチ)から
feature/xxx
の形式でブランチを作成します。Pull Request 作成と Feature環境の構築
Pull Requestを作成し、ラベルなどで
feature
を指定すると、CIが起動します。- ブランチ名を環境変数に渡しつつ
develop-template
を複製して、feature/xxx
用のマニフェストを生成 - 生成したマニフェストを
output/
ディレクトリに配置 - Argo CDがその変更を検知し、Kubernetes上に新しいFeature環境がデプロイされる
- ブランチ名を環境変数に渡しつつ
開発中の Feature 環境更新
featureブランチへのコミットごとにCIが動き、Dockerイメージをビルドした後にプッシュ、マニフェストを再生成して
output/
にコミットします。Argo CDは差分を検知すると即座に再デプロイを行うため、Pull Request のステータスに応じてリアルタイムで機能が更新 されるイメージです。レビューとマージ ⇒ Feature 環境の自動削除
Pull Requestが承認・マージされたら、そのブランチは不要になるため、対応するFeatureディレクトリをCIが自動で削除します。すると
output/
からもそのマニフェストが消えるため、Argo CDは "不要なリソースが消えた" と認識し、実際のKubernetesリソースを削除します。
sequenceDiagram participant PR as Pull Request participant AR as Artifact Registry participant KR as Kustomize Repo participant AC as Argo CD participant K8s as Kubernetes Cluster %% 作成フロー Note over PR: featureラベル付与 PR->>AR: Dockerイメージ作成&Push PR->>KR: develop-templateからFeatureディレクトリ作成 PR->>KR: kustomize editでイメージ名設定 KR->>KR: ksubstで環境変数挿入 KR->>KR: kustomize build実行 KR->>KR: output/ディレクトリに配置&コミット AC->>KR: 変更検知 AC->>K8s: 自動デプロイ %% 削除フロー Note over PR: featureラベル削除/Close/Merge PR->>KR: Featureディレクトリ削除 KR->>KR: kustomize build実行 KR->>KR: output/ディレクトリ更新&コミット AC->>KR: 削除検知 AC->>K8s: リソース削除
このように、ブランチのライフサイクル(作成〜マージ〜削除)に対応して、環境が自動で作られ、自動で消える仕組みを整えておけば、エンジニアと非エンジニアの双方にとって動作確認が常に手軽に行える環境を維持できます。
運用で得られたTips
最後に、Feature環境の運用を通じて得られた重要なポイントと、具体的な問題と解決策を紹介します。
1. Cloud リソースの再利用方針
問題
ksubst-rs
を使えば、Feature環境ごとにユニークなリソース名を振り分けられます。しかし、Cloud SQL や BigQuery のような重いリソースをFeature環境のたびに新規作成するのは費用面や運用面でもオーバースペックだと考えました。
解決策
Google Cloudのリソース(Cloud SQLやBigQueryなど)は、Develop環境と同じものを流用する運用に設計しました。Feature環境はあくまでKubernetes上のアプリケーションやConfigMapなどをユニークに作るだけに留め、データベースやデータレイクは共通化することでコストを抑制しています。
2. DNS/SSL 設定の自動化
問題
当初、Feature環境を構築するにあたり、「各Feature環境ごとにドメインをどのように用意し、SSL証明書の発行やロードバランサーのHTTPS通信をどう確立すればいいのか」が不明確で、運用負荷が高くなることを懸念していました。
解決策
実際には、私たちが管理するkubernetesクラスタにexternal-dns
のデプロイを管理する仕組みがインストールされており、特定のドメインへのDNSレコードは自動同期される状態でした。そのため、私たちが行ったのは、ManagedCertificate リソースをマニフェストで作成するだけです。これだけでGoogle CloudがSSL証明書を自動取得し、Ingressと連携してHTTPS通信を確立してくれるため、当初の心配は杞憂に終わりました。
結果として、ドメインとSSL証明書を含むFeature環境の構築は、ほぼ自動化された状態で運用できています。
3. RDBマイグレーションの衝突
問題
Feature環境とDevelop環境が同一のCloud SQLデータベースを参照しているため、スキーマ変更のマイグレーションで競合するケースがありました。
先にFeature環境のマイグレーションが走ってテーブル定義を変更すると、次にDevelop環境で同マイグレーションが動く際、差分が期待と一致しないため、エラー発生してSync停止の事象がありました。
解決策
DBスキーマに変更がある場合は先にmainブランチへマージする という運用ルールをチーム全体で徹底し、大きな変更も含めて常にDevelop環境と同一のスキーマを参照することで、マイグレーション競合を回避しました。
4. BigQuery スキーママイグレーションの競合
問題
全てのFeature環境はdevelop-template
のマニフェストをコピーしているだけだったため、Argo CDのPreSync
が同じタイミングで一斉に実行されました。その結果、BigQueryのスキーママイグレーションが衝突して以下のようなレートリミットエラーが発生しました。
getTable error: Error: Exceeded rate limits: too many api requests per user per method for this user_method (TableService.getTable).
解決策
argocd.argoproj.io/sync-wave
を活用し、各Feature環境のBigQueryマイグレーション実行タイミングを制御しました。CI(ksubst-rs
実行時)でBQ_MIGRATION_SYNC_WAVE
を自動インクリメントして環境ごとにマイグレーションをずらして実行することで、BigQuery APIのレートリミットエラーを回避しています。
kind: Job metadata: name: ${FEATURE_NAME-}bq-schema-migration annotations: argocd.argoproj.io/hook: PreSync argocd.argoproj.io/sync-wave: "${BQ_MIGRATION_SYNC_WAVE}"
なお、Feature環境増加に伴い、実行直列化およびマイグレーション時間延長の問題があるものの、現状では問題なく運用できています。
おわりに
導入効果
今回ご紹介したように、kustomizeとArgo CDを活用したFeature環境の構築手法を導入することで、以下の効果が得られました。
レビュー時に実際の画面・機能を確認
コードを読むだけでなく、UI/UXを含めたフィードバックを早期に得られ、手戻りを大幅に削減できるようになりました。
非エンジニアの方々とのコミュニケーション改善
リンクを渡すだけで "実際の動く環境" を試してもらえるため、仕様の意思疎通や動作テストがスムーズになりました。
環境構築・削除の自動化による開発者負担の軽減
従来は手動での環境作成やクリーンアップに時間を要していたところ、CI/CDへ任せられるようになり、開発スピードが向上しました。
さらに、FourKeysを用いてリリース頻度やリードタイムをモニタリングしています。詳しくは、同じチームのYamatoさんが執筆したFour Keys導入からはじめる開発プロセスの改善を参照してください。非エンジニアとエンジニア間の手戻りが減り、機能単位の開発スパンが短くなることでリリース頻度が上がったことを定量的に確認できました。
今後の展望
リソース使用量の最適化
Feature環境が大量に作成される場合、不要なリソースをいかに効率よくクリーンアップするかが課題になります。
デプロイ高速化
CI/CDがボトルネックになる場合、コンテナイメージのキャッシュ戦略やテストの並列化なども検討していく必要があります。
モニタリングやアラート体制の強化
Feature環境といえども、何らかの不具合が発生した際にすぐ気づける仕組みを整えると、開発効率がさらに高まります。
なお、私たちのkustomizeを活用したFeature環境の構築手法が、必ずしもベストプラクティスとは限りません。しかし、まだ同様の事例が少ないのも事実です。本記事が、Feature環境を活用した効率的な開発フローを模索する上で、1つのヒントとなれば幸いです。
enechainでは、事業拡大のために共に技術力で道を切り拓いていく仲間を募集しています。