Microservices 面接の質問
マイクロサービスのインタビューでは、スケーラブルで分散されたシステムを設計・構築する能力が評価されます。多くの場合、分散コンピューティング、API、データ一貫性に関する深い知識を持つシニアエンジニアやアーキテクトを対象としています。アーキテクチャの議論、トレードオフ分析、実践的なコーディングシナリオが混在します。
Microservices 面接で問われる内容
サービス分割と設計
モノリスをサービスに分割する方法、境界付けられたコンテキスト、ドメイン駆動設計の原則。
サービス間通信
同期 vs 非同期パターン、APIゲートウェイ、サービスメッシュ、通信プロトコル。
データ管理と一貫性
分散トランザクションの処理、結果整合性、サーガ、CQRS/イベントソーシング。
デプロイメントと可観測性
コンテナ化、オーケストレーション(Kubernetes)、監視、ロギング、分散システムでのトレーシング。
Microservices 面接の質問例
- 大きなモノリスをマイクロサービスに分割するにはどうすればよいですか?決定プロセスを説明してください。良い回答が押さえる点
- ビジネス能力に基づく境界コンテキストの特定
- ドメイン駆動設計(DDD)の導入による集約とコンテキストマップの作成
- 結合度と凝集度の分析による分割候補の抽出
- 段階的なリファクタリングとストラングラーフィグパターンの適用
- イベントストーミングなどのワークショップでの関係者との合意形成
サンプル回答を見る
モノリスを分割する際は、まずビジネスドメインを理解し、DDDの境界コンテキストを用いてサービス境界を定義します。例えば、ECシステムでは「注文」「在庫」「配送」などが独立したコンテキストになり得ます。次に、各境界コンテキスト間の依存関係を明確にするため、コンテキストマップを作成します。この際、結合度が高く凝集度が低いモジュールは分割候補として優先します。ストラングラーフィグパターンを用いて段階的に切り出すことで、リスクを低減します。また、イベントストーミングを実施してドメインエキスパートも交えた議論が有効です。重要なのは、一度に全てを分割するのではなく、ビジネス価値の高い部分から徐々に移行することです。分割後は、サービス間の通信やデータ整合性の課題(分散トランザクション、サーガなど)に対処する必要があります。
- サーガパターンにおけるオーケストレーションとコレオグラフィの違いを説明し、それぞれを使用する場面を教えてください。良い回答が押さえる点
- オーケストレーションは中央の調整役(オーケストレータ)が各サービスを呼び出す方式
- コレオグラフィは各サービスがイベントを発行・購読し自律的に連携する方式
- オーケストレーションは複雑なワークフローの制御に適している
- コレオグラフィは疎結合でスケーラビリティに優れるが、デバッグが難しい
- 選択基準:トランザクションの複雑性、チームの成熟度、システムのサイズ
サンプル回答を見る
サーガパターンにおけるオーケストレーションは、オーケストレータと呼ばれる中央のサービスが各参加サービスのアクションを順次呼び出し、失敗時には補償トランザクションを発行します。例えば、注文処理では注文サービスがオーケストレータとなり、在庫確保、支払い、配送手配を順に実行します。一方、コレオグラフィは各サービスがイベントを介して非同期に連携します。例えば、在庫サービスが「在庫確保済み」イベントを発行し、支払いサービスがそれを購読して決済を開始します。オーケストレーションは複雑なワークフローや厳格なトランザクション管理が必要な場合に適しており、デバッグが容易です。コレオグラフィはサービス間の結合度が低く、サービス数が多い場合にスケールしやすいですが、複雑なエラーハンドリングやイベントの追跡が難しくなります。選択は、ビジネスロジックの複雑さ、チームの分散度、システム全体の成熟度に依存します。
- お好みの言語でシンプルなサーキットブレーカーの実装を書いてください(擬似コード可)。良い回答が押さえる点
- サーキットブレーカーは3つの状態(CLOSED, OPEN, HALF_OPEN)を持つ
- CLOSEDからOPENへの遷移は失敗閾値を超えた時
- OPEN状態では要求を即座に拒否する
- タイムアウト後にHALF_OPENに遷移し、成功すればCLOSED、失敗ならOPENに戻る
サンプル回答を見る
上記のコードはPythonによるシンプルなサーキットブレーカー実装です。状態管理にはEnumを使用し、CLOSED(正常)、OPEN(遮断)、HALF_OPEN(試験的許可)の3状態を遷移します。外部関数の呼び出しをラップし、失敗が閾値(failure_threshold)に達するとサーキットをOPENにし、以降の呼び出しを遮断します。回復タイムアウト(recovery_timeout)経過後、HALF_OPENとなり次の呼び出しを許可。成功すればCLOSEDに戻り、失敗すれば再びOPENとなります。これにより、下流サービスの障害が上流に伝播するのを防ぎます。実際の運用では、ネットワークエラーやタイムアウトを適切に検出する必要があります。
参考コードpython import time from enum import Enum class CircuitState(Enum): CLOSED = 1 # 正常状態 OPEN = 2 # サーキットオープン(要求遮断) HALF_OPEN = 3 # 半開状態(試験的許可) class CircuitBreaker: def __init__(self, failure_threshold=5, recovery_timeout=10): self.failure_threshold = failure_threshold self.recovery_timeout = recovery_timeout self.state = CircuitState.CLOSED self.failure_count = 0 self.last_failure_time = None def call(self, func, *args, **kwargs): if self.state == CircuitState.OPEN: if time.time() - self.last_failure_time > self.recovery_timeout: self.state = CircuitState.HALF_OPEN print("Circuit half-open: attempting recovery") else: raise Exception("Circuit breaker is OPEN") try: result = func(*args, **kwargs) self.on_success() return result except Exception as e: self.on_failure() raise def on_success(self): if self.state == CircuitState.HALF_OPEN: print("Recovery successful, circuit closed") self.state = CircuitState.CLOSED self.failure_count = 0 elif self.state == CircuitState.CLOSED: self.failure_count = 0 def on_failure(self): self.failure_count += 1 if self.failure_count >= self.failure_threshold: self.state = CircuitState.OPEN self.last_failure_time = time.time() print(f"Circuit opened after {self.failure_count} failures") # 使用例: cb = CircuitBreaker(failure_threshold=3); cb.call(some_function) # 時間計算量: O(1) (呼び出しごとのオーバーヘッドは一定) # 空間計算量: O(1) - 結果整合性を伴う分散トランザクションをどのように処理しますか?具体的な例を挙げてください。良い回答が押さえる点
- 結果整合性は分散システムで一時的にデータが不整合でも最終的に一致することを保証
- Sagaパターン(コレオグラフィまたはオーケストレーション)が一般的
- 補償トランザクションを用いて失敗時のロールバックを実現
- イベントソーシングやCQRSとの組み合わせで整合性を管理
- 例:ECサイトの注文処理(在庫確保→支払い→配送)
サンプル回答を見る
分散トランザクションではACIDの原子性を保証できないため、結果整合性を許容しつつ整合性を担保する必要があります。代表的な手法がSagaパターンです。Sagaは複数のローカルトランザクションの連鎖であり、各ステップが成功したら次のステップへ進み、失敗時には前に進んだステップを補償トランザクションで打ち消します。例えば、ECサイトの注文処理を考えます。まず在庫サービスで在庫を確保し、次に支払いサービスで決済、最後に配送サービスで出荷手配を行います。もし支払いが失敗した場合、在庫確保の補償として在庫予約を解除します。このパターンはオーケストレーション(中央調整)かコレオグラフィ(イベント駆動)で実装されます。また、イベントソーシングとCQRSを組み合わせることで、イベントの永続化と状態の再構築が容易になり、結果整合性を管理しやすくなります。注意点として、補償トランザクションは必ずしも元の状態に戻せるわけではない(例:送金処理)ため、ビジネス上のトレードオフを考慮する必要があります。
- 10以上のマイクロサービスを持つシステムのAPIゲートウェイを設計してください。どのような機能を含めますか?良い回答が押さえる点
- APIゲートウェイはリクエストのルーティングと集約を行う
- 認証・認可、レート制限、ロードバランシングを統一的に提供
- バージョニング、リクエスト変換、キャッシュ、サーキットブレーカー機能
- ログ・トレース・メトリクスの収集ポイントとしての役割
- 各マイクロサービスの通信プロトコルを抽象化(REST, gRPC, GraphQL等)
サンプル回答を見る
10以上のマイクロサービスを抱えるシステムでは、APIゲートウェイが共通の入口として機能し、以下の機能を提供する必要があります。まず、リクエストルーティングとして、クライアントからのパスやヘッダに基づき適切なサービスに転送します。認証・認可は一元化し、各サービスに負荷をかけません。レート制限やサーキットブレーカーで過負荷や障害からシステムを保護します。また、レスポンスの集約(複数サービスへの問い合わせ結果をマージ)やプロトコル変換(内部gRPCを外部にはRESTで提供など)も担います。ロードバランシング、サービスディスカバリ、ログ・トレース・メトリクスの収集も重要です。加えて、APIバージョニングやフォーマット変換(XML→JSONなど)、キャッシュ機能も検討します。拡張性を考慮し、プラグイン可能なアーキテクチャ(例:Kong, Apigee, AWS API Gateway)を採用することが多いです。
- サービス間通信にRESTではなくgRPCを選択するシナリオを説明してください。良い回答が押さえる点
- 高パフォーマンスと低レイテンシが要求される内部通信(サービス間)
- スキーマ厳格なインターフェース定義とコード生成による型安全性の確保
- ストリーミングや双方向通信が必要な場合(gRPCのServer Streaming/Client Streaming)
- ポリグロット環境で共通のプロトコルが必要な場合
- RESTでは不利なケース:頻繁なRTT、ペイロード圧縮の必要性
サンプル回答を見る
gRPCはHTTP/2ベースでバイナリプロトコル(Protocol Buffers)を使用するため、REST/JSONに比べて高速で帯域効率が良いです。特に、同一データセンター内のサービス間通信でレイテンシが重要な場合に適しています。また、サービスインターフェースを.protoファイルで厳格に定義できるため、複数言語間でのコード自動生成が容易で、型安全性が高まります。ストリーミング(サーバーストリーミング、クライアントストリーミング、双方向ストリーミング)をネイティブサポートするため、リアルタイムデータ処理やチャットなどに適しています。例えば、金融系の高速取引システムや、IoTデバイスからの大量データ処理シナリオが挙げられます。一方、外部のWebブラウザやモバイルアプリからの直接呼び出しには向かないため、APIゲートウェイでRESTからgRPCに変換するアーキテクチャが一般的です。RESTが適しているのは、HTTPキャッシュの活用や人間が読みやすいインターフェースが必要な場合などです。
- マイクロサービス全体で分散トレーシングを実装するにはどうすればよいですか?どのようなツールを使用しますか?良い回答が押さえる点
- 分散トレーシングでは一意のTrace IDとSpan IDを各リクエストに割り当てる
- 各サービスでトレースコンテキスト(traceparent, tracestate)を伝搬する
- オープンな標準としてOpenTelemetryが広く使われる
- ツール例:Jaeger, Zipkin, AWS X-Ray, Google Cloud Trace
- 実装手順:計装ライブラリの導入、バックエンドへの送出、UIでの可視化
サンプル回答を見る
分散トレーシングを実装するには、まず各マイクロサービスにトレーシングライブラリ(例:OpenTelemetry SDK)を導入し、リクエストの開始時にTrace IDとSpan IDを生成します。サービス間の通信(HTTP/gRPC/メッセージキュー)では、これらのIDをヘッダなどで伝搬する必要があります。OpenTelemetryはベンダーに依存しない標準仕様であり、多くの言語でサポートされています。トレースデータはバックエンド(Jaeger、Zipkinなど)に送信され、データベースに保存されます。その後、Web UIで分散トレースを可視化し、レイテンシのボトルネックやエラー発生箇所を特定できます。また、トレースデータをメトリクスやログと相関させることで、より深い分析が可能です。クラウド環境ではAWS X-RayやGoogle Cloud Traceなどのマネージドサービスを利用する選択肢もあります。重要なのは、トレーシングのオーバーヘッドを最小限に抑え、サンプリング戦略(例:エラーのみ追跡)を導入することです。
- 下流の依存関係(データベース、キャッシュ、その他のサービスなど)のステータスをレポートするヘルスチェックエンドポイントをコード化してください。良い回答が押さえる点
- ヘルスチェックエンドポイントは/healthなどで公開する
- 各下流依存の状態を個別にチェックし、結果をリストにまとめる
- 全体のステータスはすべての依存がhealthyならhealthy、一つでもunhealthyならunhealthy
- HTTPステータスコードはhealthyで200、unhealthyで503を返す
- 各チェックはタイムアウトを設定し、障害が連鎖しないようにする
サンプル回答を見る
上記のPython/Flaskコードは、データベース(PostgreSQL)、キャッシュ(Redis)、および外部依存サービス(HTTP)のステータスをチェックするヘルスチェックエンドポイントを実装しています。各チェック関数はタイムアウトを設定し、接続エラーを補足して"healthy"または"unhealthy"を返します。エンドポイントは全依存の結果をJSONで返し、全体ステータスがhealthyの場合200、unhealthyの場合503(Service Unavailable)を返します。このようなエンドポイントは、Kubernetesのliveness/readiness probeやロードバランサのヘルスチェックとして使用されます。実際の実装では、依存リストの設定を外部化し、キャッシュやサーキットブレーカーを組み合わせることで、過剰なチェックによる影響を回避します。
参考コードpython from flask import Flask, jsonify import redis import psycopg2 from psycopg2 import OperationalError app = Flask(__name__) # 依存サービスのヘルスチェック関数 def check_database(): try: conn = psycopg2.connect("dbname=test user=admin password=secret host=localhost") conn.close() return "healthy" except OperationalError: return "unhealthy" def check_cache(): try: r = redis.Redis(host='localhost', port=6379, socket_connect_timeout=2) r.ping() return "healthy" except redis.ConnectionError: return "unhealthy" def check_dependency_service(): import requests try: resp = requests.get("http://dependency-service/health", timeout=2) return "healthy" if resp.status_code == 200 else "unhealthy" except requests.RequestException: return "unhealthy" @app.route('/health') def health(): statuses = { "database": check_database(), "cache": check_cache(), "dependency_service": check_dependency_service() } overall = "healthy" if all(v == "healthy" for v in statuses.values()) else "unhealthy" return jsonify({"status": overall, "checks": statuses}), 200 if overall == "healthy" else 503 if __name__ == '__main__': app.run(host='0.0.0.0', port=8080) # 時間計算量: 各依存先への問い合わせ O(1) (タイムアウト依存) # 空間計算量: O(1)
準備方法
- 分散システムの基礎(CAP定理、一貫性モデル、耐障害性)をマスターしましょう。
- ドメイン駆動設計(エンティティ、集約、境界付けられたコンテキスト)を使用したサービス分割を練習しましょう。
- 結果整合性とサーガパターン(補償トランザクションなど)の実装方法を理解しましょう。
- トレードオフについて議論できるように準備しましょう:オーケストレーション vs コレオグラフィ、同期 vs 非同期、ステートフル vs ステートレス。
- システム設計問題に備えましょう:通信、データ、可観測性に焦点を当てたマイクロサービスアーキテクチャのホワイトボーディングを練習しましょう。
よくある質問
マイクロサービスインタビューで最も重要な概念は何ですか?
主要な概念には、サービス分割、サービス間通信(REST、gRPC、メッセージング)、データ一貫性(サーガ、結果整合性)、可観測性(ロギング、メトリクス、トレース)が含まれます。
マイクロサービスインタビューでKubernetesについて知っておくべきですか?
はい、Kubernetesはコンテナをオーケストレーションするためによく使用されます。Pod、サービス、デプロイメント、クラスタ内でマイクロサービスを管理する方法を理解する必要があります。
マイクロサービスのインタビューは、一般的なシステムデザインのインタビューとどう違いますか?
マイクロサービスのインタビューは、特に分散アーキテクチャの課題(サービス境界、データ分散、ネットワーク回復力など)に焦点を当てます。一方、一般的なシステムデザインはモノリスもカバーする場合があります。
マイクロサービスインタビューでどのようなコーディング問題が予想されますか?
サーキットブレーカー、リトライメカニズム、シンプルなサービスヘルスエンドポイントの実装を求められるかもしれません。また、APIの設計やサービス間通信のコードを書く必要があるかもしれません。
本番でマイクロサービスを扱った経験がなくても、経験を示すにはどうすればよいですか?
マイクロサービスのパターンを使った小さなプロジェクトを構築し(Docker、Node.js/Spring Boot、メッセージキューなど)、トレードオフや学んだ教訓について議論しましょう。理論的な知識と個人プロジェクトを組み合わせることで説得力が増します。
Microservices の質問をAIで練習、瞬時にフィードバック
履歴書をアップロードして、パーソナライズされた模擬面接を受け、改善点を確認 — 無料で始められます。