CloudNative Days Winter 2025で行われたセッション「Dev ContainersとSkaffoldで実現するクラウドネイティブ開発環境 ~ローカルのみという制約に挑む~」では、株式会社ビットキーの酒井敦氏が、ローカル環境のみという制約の中で、本番に近いクラウドネイティブ開発環境をいかに構築してきたかを紹介した。Docker ComposeからDev ContainersとSkaffoldへの移行を軸に、現場で直面した課題とその解決策、そして本番との差分を最小化するための実践的な設計思想を語った。
ローカル環境のみで本番に近い開発体験が求められた理由
CloudNativeな開発において、本番環境に近い構成をいかに早い段階から再現できるかは、品質と開発効率を大きく左右する。セッションで酒井氏が取り上げたのは、その理想と現実のギャップであった。前提条件は明確で、開発環境はローカルに限定され、Google Cloud上で稼働する本番環境と同等の振る舞いを手元で再現する必要があった。
酒井氏は冒頭で「現在も開発中である開発環境について赤裸々にご紹介することで、少しでも皆さんの今後の開発の参考になればと思っています」と語り、本セッションが完成形の紹介ではなく、試行錯誤の過程を共有するものであることを示した。
背景にはビットキーが提供するプロダクトの特性がある。同社のサービスは、顔認証デバイスやスマートロックなど複数のハードウェアとソフトウェアが密接に連携して成立しており、機能追加のたびにE2Eテストが必要となる。QAチームが実機を用意して行う検証は負荷が高く、酒井氏は「たくさんのハードウェアとソフトウェアが連携しているので、E2Eのコストが非常に高いです」と当時の課題を振り返る。
この問題を解消するために開発されたのが、社内向けのテストシステムである。実デバイスを自動制御し、入室や退出といったシナリオを再現することでテストの自動化を目指した。しかしこのテストシステムは、KubernetesやPub/Sub、Cloud StorageといったGoogle Cloudのリソースを前提としており、開発環境と本番環境の乖離は看過できない課題であった。
一方で、開発環境専用にクラウドプロジェクトを用意するほどのコストはかけられない。酒井氏は「開発環境はローカルに用意する必要がありました」と言い、制約条件を率直に示した。セッションは、このローカル縛りという前提のもとで、いかに本番に近いクラウドネイティブ開発環境を実現していったのか、その道筋を明らかにしていく。
Docker Composeでは対応できなかったJobs開発の限界
テストシステムの初期開発では、ローカル環境で複数コンポーネントを扱いやすいDocker Composeが採用された。Web、API、Executor、Pub/Subをコンテナとして立ち上げる構成は、当初は本番環境を意識した開発を進めるうえで十分な柔軟性を備えていた。
Pub/Subについては、公式のエミュレーターを利用することでローカルでも実運用に近い挙動を再現できていた。環境変数を設定するだけでアプリケーション側のライブラリが自動的に接続先を切り替える仕組みは扱いやすく、depends_onやヘルスチェックを組み合わせることで、起動順序や安定性も確保されていた。
開発効率の面では、Go製のAPIやExecutorにairを導入し、コード変更時のホットリロードを実現していた。酒井氏は当時を振り返り「コードを直すたびにイメージを作り直すのが大変だったので、airを使っていました」と語っている。Web側についてもTypeScript向けのウォッチツールを併用し、Docker Composeを軸とした開発体験は一定の完成度に達していた。
しかし、この構成が転機を迎えたのは、Kubernetes Jobsを前提とした処理の開発が必要になったタイミングである。テストシステムでは、UIテスト自動化ツールをCLI経由で実行する必要があったが、このCLIは大量のメモリを消費する特性を持っていた。その結果、複数処理を同一コンテナ内で動かすとOOMが発生しやすくなった。
この問題に対し酒井氏は、「一つのCLI実行につき、一つのPodとして分離する必要がありました」と語っている。処理単位でリソースを確実に隔離できるKubernetes Jobsのモデルは、本番環境では不可欠であったが、Docker Composeでは同じ前提での開発が難しかった。こうして開発環境と本番環境の乖離は無視できないものとなり、よりKubernetesに近い開発環境へ移行する必然性が明確になったのである。
Dev ContainersとSkaffoldで実現したKubernetes前提の開発環境
Docker Composeの限界が明確になったことで、酒井氏はローカル環境でもKubernetesを前提とした開発が可能な構成へと舵を切った。その中核を担ったのが、Dev ContainersとSkaffoldの組み合わせである。
Dev Containersの採用は、開発環境そのものをコードとして定義し、開発者間の差異を最小化することが狙いであった。必要なツール群やKubernetesクライアント、Skaffoldなどをdevcontainer.jsonに宣言的に記述することで、誰が環境を立ち上げても同じ状態から開発を始められる。酒井氏はこの点について「Dev Containersに移行したことで、開発環境のセットアップがかなり楽になりました」と語っている。
ローカルKubernetes環境としては、当初Kindも検討されたが、最終的にMinikubeが選択された。クラスタの作成と破棄を頻繁に繰り返す開発スタイルにおいて、安定して動作することが最優先されたためである。酒井氏は「作って壊すを繰り返す中で、Minikubeが一番安定していました」と振り返り、実運用を通じた判断であったことを明かしている。
この構成において、開発体験の要となったのがSkaffoldである。Skaffoldは、Kubernetesリソースのデプロイ、各Podのログ出力、コード変更に伴うイメージの再ビルドといった処理を一手に引き受ける。これにより、Docker Compose時代に利用していたairのようなホットリロードツールは不要となり、Kubernetesを前提とした開発フローに一本化された。ログも一画面に集約され、複数リソースが連携する挙動を把握しやすくなったという。
一方でローカル環境ならではの課題も少なくなかった。Skaffoldはリソースの起動順序を保証しないため、Pub/Subエミュレーターの起動タイミングによっては、TopicやSubscriptionが存在しない状態でアプリケーションが起動してしまう問題が発生した。これに対しては、Dev ContainersのpostCreateCommandで事前にリソースを作成する仕組みを導入し、安定動作を確保している。
またCloud Storageについてはfake-gcs-serverを用いたエミュレーションを行い、スクリーンショット保存などの機能をローカルで再現した。ライブラリのバージョン不整合といったトラブルもあったが、実際にプルリクエストを送り修正を取り込むことで対応しており、現実的な運用知見が蓄積されていった。
こうして構築された開発環境は、ローカルでありながらKubernetes前提の設計と実装を可能にし、本番環境との差分を意識した開発を支える基盤となったのである。
制約下でも本番に寄り添う開発環境を育て続けるという選択
セッションで示された取り組みは、ローカル環境のみという制約の中で、いかに本番に寄り添ったCloudNative開発体験を実現するかという問いへの一つの答えだ。ビットキーのテストシステムは、当初Docker Composeによる構成から始まり、Kubernetes Jobsの開発が必要になったことを契機に、Dev ContainersとSkaffoldを軸とした開発環境へと進化してきた。
Dev Containersの導入によって開発環境のセットアップは大幅に簡素化され、開発者間の環境差異も最小化された。Skaffoldは、Kubernetesリソースのデプロイ、ログの集約、コード変更に伴うイメージ再作成を一手に担い、従来利用していたホットリロードツールを不要なものとした。Dockerfileのキャッシュ設定を工夫することでビルド時間も短縮され、開発体験の維持と改善が両立されている。
また本番環境と開発環境のKubernetes設定差分はKustomizeによって吸収され、Pub/SubやCloud Storageについてはエミュレーターを活用することで、クラウドと同等の体験をローカルで再現している。これらの工夫は、ツールを増やすこと自体が目的ではなく、必要十分な構成を選び取った結果である点に特徴がある。
酒井氏は今後の展望について、次のように語っている。「各テスト対象のデバイスにエージェントをインストールして、このテストシステムに登録されるような仕組みを作っています。認証周りを実装しているところですが、その開発にもこの環境を使っていくので、今から耐えられるかどうかが楽しみです」と語り、改善を前提にした開発姿勢を示した。
- この記事のキーワード
