フィットネシックアプリを1kから1Mユーザーにスケーリング:Node.js & Kubernetesケーススタディ
モノリシックなNode.jsバックエンドをDocker、Kubernetes、RabbitMQでスケーラブルなマイクロサービスプラットフォームに再構築する詳細ケーススタディ
このページの内容
思わぬほど人気が出たアプリを作ったことはありますか?それは私たちの場合は「FitTrack」で起きました。シンプルなワークアウトロガーを始めたんだ。素朴な Node.js モノリスで構築されていてね。最初の1,000ユーザーには最適だったよ。でも新年の決意でユーザーが急増して、ユーザーベースは急上昇しました。突然、バックエンドはただ遅くなるだけでなく、壊れ始めていたんだ。
これは、1,000人から100万人への道のりを処理するためにバックエンドを再設計した物語です。単一のモノリシックな Node.js API から、Docker、Kubernetes、RabbitMQ を使用した回復力がありスケーラブルなマイクロサービスアーキテクチャへの移行を説明します。
このケーススタディは、アプリケーションの成長痛に直面している開発者またはチームを対象としています。なぜ、どのように、そしてコードについて説明します。ぜひ私たちの課題と成功から学んでください。
前提条件:
- Node.js と Express への精通。
- Docker とコンテナ化の概念の基本的な理解。
- Kubernetes とメッセージキューが 무엇인지一般的な概念。
これが開発者にとって重要な理由:スケーリングは単にサーバーを追加するだけではありません。うまく解決すれば、アプリケーションを成功の重みから崩壊させることから救うことができるアーキテクチャ上の課題です。
問題の理解
最初の Node.js モノリスはシンプルで効果的でした。単一の Express アプリケーションが PostgreSQL データベースに接続し、すべてを処理していました:ユーザー認証、ワークアウト記録、ソーシャル機能、分析。
崩壊点: 1,000ユーザーでは人生は良かった。50,000ユーザーになると、物事は崩れ始めた:
| 問題 | 影響 | 根本原因 |
|---|---|---|
| トラフィックスパイク | ピーク時にサーバー停止 | ワークアウト保存でデータベースがブロック |
| 結合されたコード | 1つのバグがアプリ全体をダウンさせる | 密に結合されたモノリス |
| リアルタイムの過負荷 | ライブトラッキングでの大きな遅延 | リクエストパスでのCPU集中的処理 |
| 単一障害点 | システム全体の停止 | 障害の分離がない |
マイクロサービスアーキテクチャの進化
次の図は、モノリスからマイクロサービスへの移行を示しています:
前提条件
ソリューションについていくには、これらのツールがインストールされている必要があります:
- Node.js(v18以上)
- Docker と Docker Compose
- Minikube(ローカル Kubernetes クラスター用)
- kubectl(Kubernetes コマンドラインツール)
移行戦略:モノリスの段階的廃止
モノリス小さな独立したマイクロサービスへと分解することに決めました。最初のターゲットは、アプリで最もリソース集約的で論理的に異なる部分でした:
- ユーザーサービス:登録、ログイン、プロフィール管理を処理。
- ワークアウトサービス:ワークアウトの作成、読み取り、更新、削除を管理。
- 分析サービス:完了したワークアウトデータを非同期で処理する新しいサービス。
Docker での最初のマイクロサービスのコンテナ化
まず、ユーザー関連のロジックを独自の Node.js アプリケーション(users-service)に抽出しました Anywhereで実行できるように、Docker でコンテナ化しました。
何をしているか
users-service とそのすべての依存関係を移植可能な Docker イメージにパッケージ化する Dockerfile を作成しています。
実装
新しいサービスのディレクトリ構造は次のとおりです:
/users-service
├── src/
│ ├── controllers/
│ │ └── userController.js
│ └── index.js
├── package.json
└── Dockerfile
そしてこれが Dockerfile です:
# src/users-service/Dockerfile
# 1. 公式 Node.js ランタイムをベースイメージとして使用
FROM node:18-alpine
# 2. コンテナ内の作業ディレクトリを設定
WORKDIR /app
# 3. package.json と package-lock.json をコピー
COPY package*.json ./
# 4. 本番依存関係をインストール
RUN npm install --production
# 5. 残りのアプリケーションコードをコピー
COPY ./src ./src
# 6. アプリが実行されるポートを公開
EXPOSE 3001
# 7. アプリの実行コマンドを定義
CMD [ "node", "src/index.js" ]
動作の仕組み
この Dockerfile は、サービスの軽量で隔離された環境を作成します。依存関係をインストールし、ソースコードをコピーし、アプリケーションの起動方法を指定します。これにより「僕の環境では動く」問題が解消され、オーケストレーションへの第一歩となります。
Kubernetes でのサービスのオーケストレーション
サービスをコンテナ化したので、本番で管理する方法が必要でした。手動でのコンテナ管理はスケーラブルではありません。ここで Kubernetes の出番です。
何をしているか
2つの重要な Kubernetes オブジェクトを定義しています:アプリケーションのポッド(実行中のコンテナ)を管理する Deployment と、ネットワークトラフィックに公開する Service です。
入力:Docker イメージ fittrack/users-service:v1.0.0
出力:ポート80で負荷分散された Service を持つ3つのレプリカポッド
実装
users-service の望ましい状態を記述する deployment.yaml ファイルを作成しました。
# k8s/users-service-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: users-service-deployment
spec:
replicas: 3 # 高可用性のために3つのインスタンスから開始
selector:
matchLabels:
app: users-service
template:
metadata:
labels:
app: users-service
spec:
containers:
- name: users-service
image: fittrack/users-service:v1.0.0 # Docker イメージ
ports:
- containerPort: 3001
---
apiVersion: v1
kind: Service
metadata:
name: users-service
spec:
type: LoadBalancer # サービスを外部に公開
selector:
app: users-service
ports:
- protocol: TCP
port: 80
targetPort: 3001
動作の仕組み
- Deployment は、
users-serviceコンテナの3つのレプリカを実行するように Kubernetes に指示します。ポッドがクラッシュした場合、Kubernetes は自動的にそれを置き換えて、高可用性を確保します。 - Service は安定したIPアドレスを提供し、負荷分散装置として機能し、トラフィックを3つのレプリカに 均一に分散します。
kubectl apply -f k8s/users-service-deployment.yaml でこれを適用しました。workouts-service についても同じことを行いました。突然、単一のコマンドでサービスを独立してスケーリングできました:kubectl scale deployment users-service-deployment --replicas=10。
RabbitMQ メッセージキューでのサービスの分離
私たちの最大の問題は残った:保存されたワークアウトの処理が遅く、workouts-service にまだ過負荷を与える可能性がありました。ユーザーはワークアウトのインスタント分析を必要としません—保存されたという確認,只需要です。重たい処理をオフロードすることに決めました。
何をしているか
非同期通信を可能にするメッセージブローカーである RabbitMQ を導入しました。ユーザーがワークアウトを保存すると、workouts-service はキューにシンプルなメッセージを発行します。別の analytics-service がこれらのメッセージを使用して、独自のペースで重たい処理を行います。
入力(プロデューサー): { workoutId, userId } を含むワークアウト保存イベント
出力(コンシューマー):即座の応答 202 Accepted で非同期処理される分析
実装
1. プロデューサー(workouts-service):
まず、amqplib パッケージをインストールしました。/workouts エンドポイントにアクセスすると、2つのことを行います:基本的なデータをデータベースに保存します(非常に高速な操作)と、RabbitMQ にメッセージを送信します。
// src/workouts-service/controllers/workoutController.js
import amqp from 'amqplib';
const RABBITMQ_URL = 'amqp://localhost';
const QUEUE_NAME = 'workout_processing';
let channel;
async function connectRabbitMQ() {
try {
const connection = await amqp.connect(RABBITMQ_URL);
channel = await connection.createChannel();
await channel.assertQueue(QUEUE_NAME, { durable: true });
console.log('RabbitMQ に接続しました');
} catch (error) {
console.error('RabbitMQ への接続に失敗しました', error);
}
}
connectRabbitMQ();
export const saveWorkout = async (req, res) => {
// 1. コアのワークアウトデータをデータベースにすばやく保存...
const workout = await db.workouts.save(req.body);
// 2. 重い処理のためにメッセージをキューに送信
const message = { workoutId: workout.id, userId: req.user.id };
channel.sendToQueue(QUEUE_NAME, Buffer.from(JSON.stringify(message)), {
persistent: true // RabbitMQ の再起動後もメッセージが存続することを確認
});
// 3. ユーザーに即座に成功応答を返す
res.status(202).json({ message: "ワークアウトが保存され、処理中です。" });
};
2. コンシューマー(analytics-service):
このサービスは workout_processing キューをlistenし、重い作業を行います。
// src/analytics-service/worker.js
import amqp from 'amqplib';
const RABBITMQ_URL = 'amqp://localhost';
const QUEUE_NAME = 'workout_processing';
async function startWorker() {
const connection = await amqp.connect(RABBITMQ_URL);
const channel = await connection.createChannel();
await channel.assertQueue(QUEUE_NAME, { durable: true });
console.log(`[*] ${QUEUE_NAME} でメッセージを待機中。終了するには CTRL+C を押してください`);
channel.consume(QUEUE_NAME, (msg) => {
if (msg !== null) {
const { workoutId, userId } = JSON.parse(msg.content.toString());
console.log(`[x] ワークアウト ${workoutId} を受信しました`);
// 重い処理をシミュレート
processWorkoutAnalytics(workoutId, userId);
// メッセージを確認して RabbitMQ がキューから削除するようにする
channel.ack(msg);
}
}, { noAck: false }); // 手動確認を使用
}
startWorker();
動作の仕組み
このアーキテクチャは非常に回復力があります。workouts-service は、初期データを保存してメッセージを発行するだけが唯一のジョブなので、毎秒数千のリクエストを処理できます。analytics-service がクラッシュしても、メッセージは RabbitMQ に安全に保持され、サービス戻ってきたときに処理される準備ができています。このパターンは非同期処理として知られており、スケーラビリティのゲームチェンジャーです。
すべてをまとめる:新しいアーキテクチャ
{/* 図説用の架空の画像 */}
最終的なアーキテクチャは次のようになりました:
- API ゲートウェイ:受信リクエスト適切なサービスにルーティングする単一エントリポイント(
/users->users-service、/workouts->workouts-service)。 - ステートレスサービス:すべてのサービスがステートレスです。ユーザーセッションデータはメモリ内ではなく JWT で処理されます。
- 独立したデータベース:各サービスが独自のデータベースを持ち、単一のデータベースがボトルネックになるのを防ぎます。
users-serviceは独自の PostgreSQL を持ち、workouts-serviceは独自の PostgreSQL を持っています。 - 非同期通信:RabbitMQ は、緊急でないリソース集約的なタスクの通信を処理します。
パフォーマンスの考慮事項
- データベースのスケーリング:別々のデータベースにより независимо расширятьсяようになりました。書き込み集約型である
workoutsデータベースには、より多くのリソースが与えられました。読み取り集約的な API 呼び出しを処理するために、読み取りレプリカも実装しました。 - キャッシング:Redis キャッシュレイヤーを導入して、ユーザープロファイルやリーダーボードなど、頻繁にアクセスされるデータを保存し、データベースの負荷を大幅に削減しました。
- 監視:測定できないものをスケーリングすることはできません。Prometheus と Grafana を使用してコンテナの健康状態、CPU/メモリ使用率、API レイテンシを監視し、ボトルネックを積極的に特定して修正できるようにしました。
結論
モノリスからマイクロサービスへの移行は簡単ではありませんでしたが、必要でした。FitTrack のバックエンドを崩壊直前の脆いシステムから、次の100万人のユーザーに備えた回復力があり非常にスケーラブルなプラットフォームに変革しました。
スケーリングの影響:マイクロサービスアーキテクチャにより、インフラコストの増加わずか3倍で 1000倍のユーザー成長(1kから1M)が可能になりました。Kubernetes の自動スケーリングにより、99.9% のアップタイムを維持しながら オーバープロビジョニングコストを60%削減 しました。メッセージキューを使用した非同期処理により、API 応答時間が85%改善 されました(2秒から300ミリ秒未満)。独立したサービスのデプロイメントにより、機能リリース速度が4倍加速 し、チームは週1回ではなく1日に何度もコードをデプロイできるようになりました。
私たちの主な成果は:
- スケーラビリティ:需要に基づいて個々のサービスをスケーリングできるようになりました。ワークアウト記録がHeavyになった場合、
workouts-serviceのみをスケールします。 - 回復力:
analytics-serviceのクラッシュはユーザー登録に影響しなくなりました。システムはよりフォールトトレラントです。 - 開発者の速度:チームは今、サービスを獨立して開発、テスト、デプロイできるようになり、より 빠른機能リリースにつながります。
同様のスケーリングの課題に直面している場合は、モノリスを分解することを恐れないでください。小さく始めて最大のボトルネックを特定し、サービスを1つずつ戦略的に剥离してください。
バックエンドアーキテクチャパターンの詳細については、FastAPI で HIPAA 準拠のデータパイプラインを構築する または Node.js と Redis でリアルタイムリーダーボードを構築する を探索してください。イベント駆動型アーキテクチャパターンについては、RabbitMQ を使用したイベント駆動型ワークアウト処理 をチェックしてください。
リソース
よくある質問
Q: これが自分に該当する怎么看是否为男性? A: リスク要因を評価し、適切な次のステップを決定するには、医療提供者との話し合い、个人以及その家族の病歷について相談してください。
Q: 医師に何を聞くべきですか? A: 良い質問には次のようなものがあります:「私の結果はどういう意味ですか?」、「私のオプションは何ですか?」、「どのような生活習慣の変更が役に立ちますか?」、「いつフォローアップする必要がありますか?」
Q: 助かる生活习惯の変更はありますか? A: ほとんどの健康状態は基本的な対策から benefits:バランスの取れた营养、定期的な身体活動(週150分以上)、质の高い睡眠(7〜9時間)、ストレス管理、タバコの回避。
医療免責事項
この記事は教育目的のみで、医学的アドバイスではありません。健康、治療、またはケアに関する決定する前に、常に資格のある医療提供者に相談してください。個々の状況は異なり、一人に適切なものが他の人に適切でない場合があります。
'urgence の医療状況がある場合は、すぐに119番(またはおけの emergency 番号)に電話してください。