フロントエンドでNext.jsやRemixのモダンな構成を採用していても、裏側のバックエンドが巨大な単一アプリケーション(モノリス)のままという状況は珍しくありません。
APIのレスポンスが遅い、デプロイのたびに全画面が止まる、特定の機能を直すだけなのに全体をビルドし直す必要がある。こうした症状に心当たりがあるエンジニア・SRE・PMの方に向けて、モノリスをDocker(アプリケーションとその依存関係をコンテナという単位にまとめる技術)でモジュール分割する際の考え方を整理します。
巨大な一枚岩のコードベースを段階的に小さなサービス群へ分解していく「モダナイゼーション」という取り組みが、なぜ今も多くの現場で課題になり続けているのか、技術的な仕組みから見ていきます。
モノリスの何が問題で、分割すると何が変わるのか
モノリスとは、フロントエンド・バックエンド・バッチ処理など複数の機能が1つのコードベース・1つのデプロイ単位にまとまっているアーキテクチャを指します。
開発初期はシンプルで扱いやすい反面、チームやコードが育つにつれて「1箇所の変更が他の機能に影響する」「ビルド時間が伸びる」「言語やフレームワークを途中で変えられない」という制約が顕在化します。
これに対してマイクロサービスアーキテクチャは、機能ごとに独立したサービスへ分割し、それぞれを別々にデプロイ・更新できるようにする設計です。
たとえば認証機能、決済機能、商品検索機能をそれぞれ独立したサービスに分ければ、検索のアルゴリズムを変更してもデプロイするのは検索サービスだけで済みます。これにより障害の影響範囲も狭まり、言語選定もサービスごとに最適なものを選べるようになります。
Dockerコンテナ化が分割を現実的にする仕組み
サービスを分割しただけでは、実際の運用は成立しません。各サービスが異なるライブラリバージョンや実行環境を要求すると、サーバー間で「自分の環境では動く」問題が頻発します。
ここで使われるのがDockerです。Dockerは、アプリケーションのコード・依存ライブラリ・実行環境をまとめて「イメージ」という単位にパッケージし、どの環境でも同じ挙動で動かせるようにする仕組みです。
具体的なDockerfile(イメージのビルド手順を記述するファイル)の例を見てみましょう。
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]このように、ベースイメージの指定・依存関係のインストール・起動コマンドの定義までを1ファイルにまとめることで、どの開発者のマシンでも、どのサーバーでも同じ環境を再現できます。
複数のサービスを連携させる場合はdocker-compose.ymlで定義します。
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
- db
environment:
- DATABASE_URL=postgres://user:password@db:5432/database
db:
image: postgres
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=databaseアプリケーションサービス(web)とデータベース(db)を別々のコンテナとして定義し、depends_onで起動順序を制御しています。サービスごとにポートや環境変数を独立管理できる点が、モノリスとの大きな違いです。
関連技術との比較で見える位置づけ
Dockerによるコンテナ化は、仮想マシン(VM)による環境分離と比較されることがよくあります。VMはOS全体を仮想化するため起動が重く、1台のサーバーで動かせる台数も限られます。
DockerはホストOSのカーネルを共有しつつプロセスを隔離するため、起動が数秒程度と速く、同じマシンでより多くのコンテナを稼働できます。この軽量さが、サービスを細かく分割するマイクロサービス化と相性が良い理由です。
また、本番環境でコンテナを大量に運用する段階になると、Docker単体ではなくKubernetesのようなオーケストレーションツール(コンテナの起動・スケール・障害時の再起動を自動管理する仕組み)が必要になります。Docker ComposeはKubernetesに移行する前の、開発環境やシンプルな構成での利用に向いています。
フロントエンド領域で例えるなら、モノリスからマイクロサービスへの移行は、1つの巨大なReactアプリをマイクロフロントエンド(画面単位・機能単位でアプリを分割し、それぞれ独立してビルド・デプロイする手法)に分割する発想と近い構造を持っています。バックエンドのサービス境界と、フロントエンドのマイクロフロントエンド境界を揃えて設計すると、チーム間の依存が減りやすくなります。
自分のプロジェクトが該当するか確認する
実際に着手する前に、今のコードベースが分割に向いているかを確認しておくと手戻りが減ります。
- デプロイ頻度: 1つの機能修正のために毎回アプリ全体をデプロイしているか
- ビルド時間: CIのビルド・テストが10分を超えて開発速度を落としていないか
- チーム構成: 複数チームが同じリポジトリの同じファイルを触って競合が頻発していないか
- スケーリング要件: 特定機能(検索・画像処理など)だけ負荷が偏っているか
- 言語・フレームワークの制約: 新機能を既存の技術スタックに無理やり合わせていないか
これらに2つ以上当てはまる場合は、全面刷新ではなく、負荷が集中している機能から1つずつ切り出す段階的なアプローチが現実的です。
今日から確認・試せること
いきなり本番環境を分割する前に、ローカルで小さく検証することをおすすめします。
まず手元のDocker・Docker Composeのバージョンを確認します。
docker --version
docker compose version次に、モノリスの中で比較的独立性の高い1機能(バッチ処理や画像変換など)を選び、先ほどのDockerfileのような形で単体コンテナ化してみます。既存のアプリと同じ挙動で動くかをローカルで確認できれば、分割の第一歩として十分です。
CI/CDパイプライン(コードの変更を自動でテスト・ビルド・デプロイする仕組み)については、GitHub ActionsやGitLab CI/CDの公式ドキュメントにあるDocker向けワークフローのサンプルを確認し、既存のテストスイートがコンテナ内でも同じ結果を返すかをまず検証するとよいでしょう。
まとめ
モノリスのモダナイゼーションは、機能分割・Dockerによるコンテナ化・CI/CDの自動化という3つの要素が組み合わさって初めて効果を発揮します。
分割だけ進めてCI/CDが追いつかないと、むしろデプロイ作業が増えて逆効果になることもあります。着手前に、デプロイ頻度やビルド時間など上記の判断基準で現状を確認してみてください。
最初の一歩としては、全体のKubernetes化を目指すよりも、独立性の高い1機能をDockerfileでコンテナ化し、ローカルで動作確認するところから始めるのが現実的です。