Skip to content
Published on

トスバンク ML Backend Engineer合格ガイド:サーバーアーキテクチャ・MLサービング・学習ロードマップ

Authors

はじめに:MLとバンキングがスケールで交差(こうさ)する場所

本番環境(ほんばんかんきょう)での機械学習(きかいがくしゅう)はJupyterノートブックの話ではありません。サブ10msのレイテンシで毎秒(まいびょう)数百万(すうひゃくまん)の予測(よそく)を提供(ていきょう)し、モデルの精度(せいど)を維持(いじ)し、モデルが失敗(しっぱい)した際のグレースフルデグラデーションを処理(しょり)し、すべての予測が説明可能(せつめいかのう)で監査可能(かんさかのう)な規制環境(きせいかんきょう)で運用(うんよう)することです。

トスバンクのML Service Teamはまさにこの交差点(こうさてん)に位置しています。信用(しんよう)スコアリング、不正検知(ふせいけんち)、レコメンデーションシステム、リアルタイムリスク評価(ひょうか)のためのAIモデルを提供するバックエンドインフラを構築(こうちく)しています。トスバンクがML Backend Engineerを募集(ぼしゅう)する場合、モデルを訓練(くんれん)できる人ではなく、数百万のユーザーにモデルを提供する本番システムを構築・運用できる人を求(もと)めています。

この役割(やくわり)は深いバックエンドエンジニアリングスキル(サーバーアーキテクチャ、分散(ぶんさん)システム、パフォーマンス最適化(さいてきか))と実践的(じっせんてき)なMLサービング知識(ちしき)(モデルデプロイメント、A/Bテストインフラ、特徴量(とくちょうりょう)ストア)の組(く)み合(あ)わせが必要です。

本ガイドではJDの各要件(ようけん)を分析し、具体的(ぐたいてき)な技術(ぎじゅつ)と学習資料(しりょう)にマッピングし、面接(めんせつ)準備(じゅんび)のための6ヶ月プランを提供します。


1. JD徹底分析(てっていぶんせき):トスバンクが本当に求めるもの

1.1 主要責任(しゅようせきにん)

「MLモデルサービングのための堅牢(けんろう)なサーバーアーキテクチャの設計と実装(じっそう)」

これが主要な責任です。推論(すいろん)リクエストを受け付け、適切(てきせつ)なモデルにルーティングし、予測を返し、障害(しょうがい)をグレースフルに処理するシステムを構築します:

  • 同期(REST、gRPC)および非同期(イベント駆動)推論のAPI設計
  • モデルバージョン、メタデータ、デプロイステータスを追跡するモデルレジストリの構築
  • モデルのA/Bテストとカナリアデプロイメントインフラの実装
  • モデルライフサイクル管理:訓練パイプライン統合、モデル検証(けんしょう)、プロモーション、ロールバック
  • 新モデルのシャドーモードデプロイメント(トラフィックを提供するが予測は使用しない)

「高可用性(こうかようせい)と低レイテンシでの大規模トラフィック処理」

トスバンクは数百万のユーザーにサービスを提供しています。MLバックエンドは:

  • モデルエンドポイントあたり毎秒数万リクエストを処理
  • クリティカルな推論パス(不正検知)でp99レイテンシ10ms未満を維持
  • 99.99%の可用性を達成(年間52分未満のダウンタイム)
  • サーキットブレーカー、バルクヘッド、フォールバック戦略(せんりゃく)の実装
  • トラフィックパターンに基づく自動スケーリング

「Kubernetes上でのAIモデルサービングインフラのデプロイと運用」

Kubernetesがデプロイメントプラットフォームです:

  • K8s上でのモデルサービングフレームワーク(TensorFlow Serving、Triton Inference Server、TorchServe)のデプロイ
  • モデルワークロード用GPUスケジューリングとリソースクォータの設定
  • 推論レイテンシとキュー深度に基づくカスタムHPA(Horizontal Pod Autoscaler)メトリクスの実装
  • 永続ボリュームまたはオブジェクトストレージ統合によるモデルアーティファクトの管理
  • IstioまたはArgo Rolloutsを使用したカナリアデプロイメント

「分散トレーシングを備えたMSA(マイクロサービスアーキテクチャ)の構築と保守」

MLプラットフォームはマイクロサービスとして構築されています:

  • 特徴量計算、モデル推論、後処理、ロギングのサービス境界(きょうかい)設計
  • すべてのサービスにわたるOpenTelemetryによる分散トレーシングの実装
  • トラフィック管理、mTLS、オブザーバビリティのためのサービスメッシュ(Istio)
  • ELKまたはLokiスタックによる集中(しゅうちゅう)ロギング

「Python、Kotlin、Goでのコアプラットフォームサービスの開発」

トスバンクはポリグロットな技術スタックを使用します:

  • Python:MLモデルサービング、特徴量エンジニアリング、データパイプライン
  • Kotlin:コアバックエンドサービス、Spring Bootアプリケーション、APIゲートウェイ
  • Go:高性能インフラサービス、CLIツール、オペレーター

3つすべてのエキスパートである必要はありませんが、少なくとも2つに実務的な習熟(しゅうじゅく)と3つ目を学ぶ意欲(いよく)が必要です。

1.2 必須要件(ひっすようけん)

「バックエンドエンジニアリング経験(けいけん)5年以上」

これはミッドからシニアの役割です。コードを書くだけでなく、本番システムを構築・運用した経験が求められます。本番インシデント、プレッシャー下でのパフォーマンス最適化、アーキテクチャ意思決定(いしけってい)の経験が期待(きたい)されます。

「マイクロサービスアーキテクチャの設計と運用経験」

サービス分割戦略、サービス間通信(つうしん)パターン(同期 vs 非同期)、分散トランザクション管理(Sagaパターン、結果整合性)、マイクロサービスの運用オーバーヘッドを議論できる必要があります。

「Python、Kotlin、Goの少なくとも1つに習熟」

1つの言語(げんご)の深い専門知識が、3つすべての浅い知識より価値(かち)があります。ただし、他の2つの言語のコードを読んで理解できる必要があります。

「MLモデルサービングの概念(がいねん)の理解」

モデルを訓練する必要はありませんが、以下を理解する必要があります:

  • モデル形式(SavedModel、ONNX、TorchScript)とそのトレードオフ
  • バッチ推論 vs オンライン推論
  • 特徴量ストアと特徴量計算パイプライン
  • モデル監視(データドリフト、コンセプトドリフト、性能劣化(せいのうれっか))
  • MLモデルのA/Bテスト方法論(ほうほうろん)

「Kubernetes運用経験」

kubectl apply以上の理解が必要です:

  • モデルサービングのためのStatefulSets vs Deployments
  • GPUワークロードのリソースリクエストとリミット
  • ネットワークポリシーとサービスメッシュ設定
  • カスタムリソース定義(CRD)とオペレーター
  • クラスターオートスケーリングとノードプール管理

1.3 優遇要件(ゆうぐうようけん)

「ML推論のためのGPUインフラ経験」

GPUリソース管理は専門的(せんもんてき)なスキルです。NVIDIA GPUスケジューリング、CUDA、Multi-Instance GPU(MIG)パーティショニング、GPU監視(DCGM)の理解が差別化要素となります。

「オープンソースMLインフラプロジェクトへの貢献(こうけん)」

KServe、Seldon Core、MLflow、Kubeflowなどへの貢献は、MLインフラエコシステムを理解していることを示します。

「リアルタイム特徴量サービングの経験」

FeastやTectonなどの特徴量ストアと、サブミリ秒レイテンシでの特徴量サービング能力(のうりょく)は、オンラインML推論で高く評価されます。


2. 技術スタック深掘(ふかぼ)り

2.1 MLサービングのためのサーバーアーキテクチャパターン

同期推論アーキテクチャ

リアルタイムML推論の最も一般的なパターン:

Client -> API Gateway -> Model Router -> Model Server -> Response
                           |
                      Feature Store
                      (Redis/DynamoDB)
// Kotlin - モデルルーターサービス
@RestController
@RequestMapping("/api/v1/predict")
class PredictionController(
    private val modelRouter: ModelRouter,
    private val featureService: FeatureService,
    private val metricsService: MetricsService
) {
    @PostMapping("/{modelName}")
    suspend fun predict(
        @PathVariable modelName: String,
        @RequestBody request: PredictionRequest
    ): ResponseEntity<PredictionResponse> {
        val timer = metricsService.startTimer("prediction_latency")
        try {
            // 1. 特徴量取得
            val features = featureService.getFeatures(
                request.entityId,
                modelName
            )

            // 2. 適切なモデルバージョンにルーティング
            val modelEndpoint = modelRouter.route(
                modelName,
                request.headers
            )

            // 3. モデルサーバー呼び出し
            val prediction = modelEndpoint.predict(features)

            // 4. 監視用に予測をログ
            metricsService.recordPrediction(modelName, prediction)

            return ResponseEntity.ok(prediction)
        } catch (e: ModelUnavailableException) {
            // デフォルトモデルまたはルールベースシステムにフォールバック
            return ResponseEntity.ok(fallbackPrediction(modelName, request))
        } finally {
            timer.stop()
        }
    }
}

非同期推論アーキテクチャ

非リアルタイムワークロード(バッチスコアリング、事前計算)向け:

Producer -> Kafka Topic -> Flink/Consumer -> Model Server -> Result Topic
                                                |
                                           Feature Store
# Python - 非同期推論ワーカー
from confluent_kafka import Consumer, Producer
import tritonclient.grpc as grpc_client

class InferenceWorker:
    def __init__(self, config):
        self.consumer = Consumer(config.kafka_consumer_config)
        self.producer = Producer(config.kafka_producer_config)
        self.triton = grpc_client.InferenceServerClient(
            url=config.triton_url
        )

    def process_batch(self, messages):
        # 効率的なGPU利用のために複数リクエストをバッチ化
        inputs = self._prepare_batch_inputs(messages)

        # Triton Inference Server呼び出し
        result = self.triton.infer(
            model_name="fraud_detection_v3",
            inputs=inputs,
            outputs=[grpc_client.InferRequestedOutput("predictions")]
        )

        predictions = result.as_numpy("predictions")

        # 結果を公開
        for msg, pred in zip(messages, predictions):
            self.producer.produce(
                topic="prediction-results",
                key=msg.key(),
                value=self._serialize_prediction(pred)
            )

        self.producer.flush()

2.2 MLワークロードのためのKubernetes

Kubernetes上のGPUスケジューリング

# GPU対応モデルサービングデプロイメント
apiVersion: apps/v1
kind: Deployment
metadata:
  name: fraud-model-serving
  labels:
    app: fraud-model
    version: v3
spec:
  replicas: 3
  selector:
    matchLabels:
      app: fraud-model
  template:
    metadata:
      labels:
        app: fraud-model
        version: v3
      annotations:
        prometheus.io/scrape: 'true'
        prometheus.io/port: '8002'
    spec:
      containers:
        - name: triton
          image: nvcr.io/nvidia/tritonserver:24.01-py3
          ports:
            - containerPort: 8000
              name: http
            - containerPort: 8001
              name: grpc
            - containerPort: 8002
              name: metrics
          resources:
            requests:
              cpu: '4'
              memory: '16Gi'
              nvidia.com/gpu: '1'
            limits:
              cpu: '8'
              memory: '32Gi'
              nvidia.com/gpu: '1'
          volumeMounts:
            - name: model-store
              mountPath: /models
          readinessProbe:
            httpGet:
              path: /v2/health/ready
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /v2/health/live
              port: 8000
            initialDelaySeconds: 30
            periodSeconds: 15
      volumes:
        - name: model-store
          persistentVolumeClaim:
            claimName: model-artifacts-pvc
      nodeSelector:
        gpu-type: nvidia-a100
      tolerations:
        - key: nvidia.com/gpu
          operator: Exists
          effect: NoSchedule

MLワークロード用カスタムHPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: fraud-model-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: fraud-model-serving
  minReplicas: 3
  maxReplicas: 20
  metrics:
    - type: Pods
      pods:
        metric:
          name: inference_request_queue_depth
        target:
          type: AverageValue
          averageValue: '10'
    - type: Pods
      pods:
        metric:
          name: inference_latency_p99_ms
        target:
          type: AverageValue
          averageValue: '8'
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 30
      policies:
        - type: Percent
          value: 50
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 120

2.3 モデルサービングフレームワーク

Triton Inference Server

Triton(NVIDIA製)は複数のフレームワークをサポートし、高度な機能を提供します:

# Triton用モデル設定
# モデルリポジトリ内のconfig.pbtxt
"""
name: "fraud_detection"
platform: "onnxruntime_onnx"
max_batch_size: 64
input [
  {
    name: "features"
    data_type: TYPE_FP32
    dims: [ 128 ]
  }
]
output [
  {
    name: "probability"
    data_type: TYPE_FP32
    dims: [ 1 ]
  }
]
instance_group [
  {
    count: 2
    kind: KIND_GPU
  }
]
dynamic_batching {
  preferred_batch_size: [ 16, 32, 64 ]
  max_queue_delay_microseconds: 100
}
"""

KServe on Kubernetes

KServe(旧KFServing)はK8s上で標準化(ひょうじゅんか)されたMLサービングレイヤーを提供します:

apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
  name: fraud-detection
spec:
  predictor:
    model:
      modelFormat:
        name: onnx
      storageUri: 's3://models/fraud-detection/v3'
      resources:
        requests:
          cpu: '2'
          memory: '8Gi'
          nvidia.com/gpu: '1'
    minReplicas: 2
    maxReplicas: 10
  transformer:
    containers:
      - name: feature-transformer
        image: registry.tossbank.com/feature-transformer:v1
        resources:
          requests:
            cpu: '1'
            memory: '2Gi'

2.4 特徴量ストアアーキテクチャ

特徴量ストアは訓練とサービングのための一貫した特徴量計算を提供します:

# Feastによる特徴量定義
from feast import Entity, Feature, FeatureView, FileSource
from feast.types import Float32, Int64

# エンティティ定義
user = Entity(
    name="user_id",
    join_keys=["user_id"],
    value_type=Int64,
)

# 特徴量ビュー定義
user_transaction_features = FeatureView(
    name="user_transaction_features",
    entities=[user],
    ttl=timedelta(hours=24),
    schema=[
        Feature(name="transaction_count_7d", dtype=Float32),
        Feature(name="avg_transaction_amount_30d", dtype=Float32),
        Feature(name="max_transaction_amount_7d", dtype=Float32),
        Feature(name="unique_merchants_30d", dtype=Int64),
        Feature(name="late_night_transaction_ratio", dtype=Float32),
    ],
    online=True,
    source=FileSource(
        path="s3://features/user_transactions.parquet",
        timestamp_field="event_timestamp",
    ),
)
// Go - 高性能特徴量サービングエンドポイント
package main

import (
    "context"
    "encoding/json"
    "net/http"
    "time"

    "github.com/redis/go-redis/v9"
)

type FeatureServer struct {
    redis *redis.Client
}

type FeatureRequest struct {
    EntityID   string   `json:"entity_id"`
    Features   []string `json:"features"`
}

type FeatureResponse struct {
    Features map[string]float64 `json:"features"`
    Latency  int64              `json:"latency_us"`
}

func (s *FeatureServer) GetFeatures(
    w http.ResponseWriter, r *http.Request,
) {
    start := time.Now()

    var req FeatureRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    // Redisからバッチフェッチ
    ctx := context.Background()
    pipe := s.redis.Pipeline()

    cmds := make([]*redis.StringCmd, len(req.Features))
    for i, feature := range req.Features {
        key := req.EntityID + ":" + feature
        cmds[i] = pipe.Get(ctx, key)
    }

    _, err := pipe.Exec(ctx)
    if err != nil && err != redis.Nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    features := make(map[string]float64)
    for i, cmd := range cmds {
        val, err := cmd.Float64()
        if err == nil {
            features[req.Features[i]] = val
        }
    }

    resp := FeatureResponse{
        Features: features,
        Latency:  time.Since(start).Microseconds(),
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(resp)
}

2.5 OpenTelemetryによる分散トレーシング

MLマイクロサービス間のトレーシングはレイテンシ問題のデバッグに不可欠(ふかけつ)です:

// Kotlin - OpenTelemetryインストルメンテーション
import io.opentelemetry.api.GlobalOpenTelemetry
import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.api.trace.StatusCode

class TracedModelInference(
    private val modelClient: ModelClient
) {
    private val tracer = GlobalOpenTelemetry.getTracer("ml-inference")

    suspend fun infer(
        modelName: String,
        features: Map<String, Float>
    ): PredictionResult {
        val span = tracer.spanBuilder("model-inference")
            .setSpanKind(SpanKind.CLIENT)
            .setAttribute("model.name", modelName)
            .setAttribute("model.features.count", features.size.toLong())
            .startSpan()

        return try {
            span.makeCurrent().use {
                val result = modelClient.predict(modelName, features)

                span.setAttribute("model.version", result.modelVersion)
                span.setAttribute(
                    "model.latency_ms",
                    result.inferenceLatencyMs.toDouble()
                )
                span.setStatus(StatusCode.OK)

                result
            }
        } catch (e: Exception) {
            span.setStatus(StatusCode.ERROR, e.message ?: "Unknown error")
            span.recordException(e)
            throw e
        } finally {
            span.end()
        }
    }
}

2.6 モデルサービングのためのサーキットブレーカーパターン

モデルサーバーが不健全(ふけんぜん)になった場合、サーキットブレーカーがカスケード障害を防止(ぼうし)します:

// Kotlin - Resilience4jサーキットブレーカー
import io.github.resilience4j.circuitbreaker.CircuitBreaker
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig
import java.time.Duration

val circuitBreakerConfig = CircuitBreakerConfig.custom()
    .failureRateThreshold(50f)
    .slowCallRateThreshold(80f)
    .slowCallDurationThreshold(Duration.ofMillis(200))
    .waitDurationInOpenState(Duration.ofSeconds(30))
    .slidingWindowSize(100)
    .minimumNumberOfCalls(20)
    .build()

val modelCircuitBreaker = CircuitBreaker.of(
    "fraud-model", circuitBreakerConfig
)

// フォールバック付き使用例
fun predictWithFallback(
    features: Map<String, Float>
): PredictionResult {
    return try {
        modelCircuitBreaker.executeSupplier {
            modelClient.predict("fraud_detection_v3", features)
        }
    } catch (e: Exception) {
        // モデル不可用時はルールベースシステムにフォールバック
        ruleBasedFraudDetection(features)
    }
}

2.7 モデル監視とオブザーバビリティ

# Python - カスタムメトリクスによるモデル監視
from prometheus_client import Counter, Histogram, Gauge
import numpy as np

# メトリクス
prediction_counter = Counter(
    "model_predictions_total",
    "Total predictions",
    ["model_name", "model_version"]
)

prediction_latency = Histogram(
    "model_prediction_latency_seconds",
    "Prediction latency",
    ["model_name"],
    buckets=[0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5]
)

feature_drift_gauge = Gauge(
    "model_feature_drift_score",
    "Feature drift score (PSI)",
    ["model_name", "feature_name"]
)

class ModelMonitor:
    def __init__(self, model_name, reference_data):
        self.model_name = model_name
        self.reference_distributions = self._compute_distributions(
            reference_data
        )

    def record_prediction(self, features, prediction, latency):
        prediction_counter.labels(
            model_name=self.model_name,
            model_version="v3"
        ).inc()

        prediction_latency.labels(
            model_name=self.model_name
        ).observe(latency)

    def check_feature_drift(self, recent_features):
        """ドリフト検出のためのPSI(Population Stability Index)計算"""
        for feature_name, values in recent_features.items():
            psi = self._calculate_psi(
                self.reference_distributions[feature_name],
                values
            )
            feature_drift_gauge.labels(
                model_name=self.model_name,
                feature_name=feature_name
            ).set(psi)

            if psi > 0.2:
                self._alert_drift(feature_name, psi)

    def _calculate_psi(self, expected, actual, bins=10):
        expected_hist, edges = np.histogram(expected, bins=bins)
        actual_hist, _ = np.histogram(actual, bins=edges)

        expected_pct = (expected_hist + 1) / (sum(expected_hist) + bins)
        actual_pct = (actual_hist + 1) / (sum(actual_hist) + bins)

        psi = sum(
            (actual_pct - expected_pct) * np.log(actual_pct / expected_pct)
        )
        return psi

3. 面接対策(めんせつたいさく):30問

3.1 バックエンドアーキテクチャ(問題1-10)

Q1. 毎秒5万リクエストをp99レイテンシ10ms未満で処理するモデルサービングシステムをどのように設計しますか?

マルチティアアーキテクチャから始めます:レート制限とルーティング用APIゲートウェイ(KongまたはEnvoy)、リクエストを正しいモデルバージョンに誘導(ゆうどう)するモデルルーターサービス、TritonまたはTF Servingを実行するモデルサーバーポッド。サービス間通信にはRESTの代わりにgRPCを使用してシリアライゼーションオーバーヘッドを削減。モデルをGPUメモリにプリロードし、ダイナミックバッチングでGPU使用率を最大化。Redisでサブミリ秒アクセスの頻繁に使用される特徴量をキャッシュ。

Q2. オンライン推論、バッチ推論、ニアリアルタイム推論の違いと使い分けを説明してください。

オンライン推論はリクエストごとに同期的に予測を提供し、不正検知などのリアルタイム意思決定に使用(レイテンシ要件:10ms未満)。バッチ推論は大規模データセットを定期的に処理し、事前計算されたレコメンデーションや信用スコア更新に使用(レイテンシ:分から時間)。ニアリアルタイム推論はストリーミングデータを小さな遅延で処理し、わずかな遅延が許容(きょよう)されるイベント駆動スコアリングに使用(レイテンシ:100msから秒)。

Q3. モデルサーバーが障害した場合のグレースフルデグラデーションの実装方法は?

レイヤー1:障害しきい値後にオープンするモデルエンドポイントごとのサーキットブレーカー。レイヤー2:プライマリモデルが利用不可の場合、よりシンプルなモデル(軽量モデルまたはルールベースシステム)にフォールバック。レイヤー3:頻繁に見られる入力に対するキャッシュされた予測。レイヤー4:クリティカルパスのデフォルト安全値(自動承認ではなく手動レビューにフラグ)。フォールバック率を監視し、しきい値を超えた場合にアラート。

Q4. Sagaパターンと、複数サービスに書き込むMLパイプラインへの適用方法を説明してください。

Sagaパターンは分散トランザクションを一連のローカルトランザクション(それぞれに補償(ほしょう)アクションを持つ)に分解して管理します。MLパイプラインでは:ステップ1が予測をデータベースに書き込み(補償:削除)、ステップ2が予測イベントをKafkaに公開(補償:ロールバックイベント公開)、ステップ3が新データポイントで特徴量ストアを更新(補償:特徴量を元に戻す)。いずれかのステップが失敗した場合、補償アクションが逆順で実行されます。

Q5. 新しいモデルバージョンのゼロダウンタイムデプロイメントの方法は?

カナリアデプロイメントを使用:新モデルバージョンを現行のものと並行してデプロイ。少量(1-5%)のトラフィックを新バージョンにルーティング。カナリアの予測品質メトリクス(精度、レイテンシ、エラー率)を監視。メトリクスが健全であればトラフィックを段階的に増加。Argo RolloutsまたはIstioトラフィック分割を使用。古いモデルバージョンを実行したまま即座にロールバックできる能力を維持。

Q6. モデルサービングの水平スケーリングと垂直(すいちょく)スケーリングの違い、それぞれの選択基準は?

水平スケーリングはより多くのモデルサーバーレプリカ(ポッド)を追加します。CPUバウンドモデルや、より多くの同時リクエスト処理が必要な場合に推奨。垂直スケーリングは既存インスタンスにより多くのリソース(GPU、メモリ)を追加します。単一GPUに収まらない大規模モデル(モデル並列性)や、水平スケーリングが遅いモデルロード時間の場合に推奨。実践では、ほとんどのワークロードに水平スケーリング、非常に大きなモデル(LLM)に垂直スケーリングを使用。

Q7. 複数マイクロサービスにまたがるML推論パイプラインでのリクエストレベルのトレーシング実装方法は?

計装(けいそう)標準としてOpenTelemetryを使用。APIゲートウェイでトレースコンテキスト(トレースID、スパンID)を注入。各サービスがコンテキストを抽出(ちゅうしゅつ)し、子スパンを作成し、下流呼び出しにコンテキストを伝搬(でんぱ)。主要操作(さぎょう)をインストルメント:特徴量フェッチ、モデル推論、後処理。トレースをJaegerまたはTempoに送信。スパンにカスタム属性を追加:モデル名、モデルバージョン、特徴量数、予測値。

Q8. 訓練とオンライン推論の両方にサービスする特徴量ストアのCAP定理トレードオフを説明してください。

特徴量ストアは一貫性(訓練とサービングが同じ特徴量を使用)、可用性(オンライン推論は待てない)、分断耐性が必要です。オンラインサービングでは:結果整合性を持つAPを優先 — Redisからベストエフォートの鮮度で提供。訓練では:CPを優先 — 厳密なポイントインタイム正確性を持つバッチストア(S3、BigQuery)を使用。デュアルライトアーキテクチャで橋渡し:特徴量をオンラインストア(Redis)とオフラインストア(S3)の両方に書き込み、リコンシリエーションチェックを実施。

Q9. マイクロサービスアーキテクチャでのカスケード障害の防止方法は?

障害ドメインを分離するバルクヘッドの実装(下流サービスごとの独立したスレッドプール)。設定可能なしきい値を持つサーキットブレーカーの使用。すべての外部呼び出しにタイムアウトを実装(無制限の待機なし)。指数バックオフとジッターによるリトライ。バウンドバッファによるリクエストキューの制限。下流サービスがプローブできるヘルスチェックエンドポイント。システムがオーバーロード時のAPIゲートウェイでのロードシェディング。

Q10. MLモデルエンドポイントのAPIバージョニングへのアプローチは?

破壊的変更にはURLベースバージョニング(例:/v1/predict、/v2/predict)を使用。マイナー変更にはヘッダーベースバージョニングを使用。モデルルーターがAPIバージョンをモデルバージョンにマッピング。後方互換性を維持:v2デプロイ時にv1呼び出し元が引き続き動作すべき。廃止(はいし)ポリシー:APIバージョン削除の3ヶ月前に告知。移行期間中は両バージョンを同時に実行。

3.2 MLサービング(問題11-15)

Q11. TensorFlow Serving、Triton Inference Server、TorchServeを比較してください。それぞれいつ使いますか?

TensorFlow ServingはTFモデルに最適化され、成熟したgRPC APIを持ち、モデルバージョニングを標準でサポート。TFのみの環境で使用。Triton Inference Serverは複数フレームワーク(TF、PyTorch、ONNX、TensorRT)をサポートし、ダイナミックバッチングを提供し、GPU使用率が最高。マルチフレームワーク環境やGPU効率が最重要な場合に使用。TorchServeはPyTorchモデルのネイティブサービングソリューションで、TorchScriptと統合。よりシンプルな要件のPyTorchのみのワークロードに使用。

Q12. ダイナミックバッチングとは何か、GPUベース推論で重要な理由は?

ダイナミックバッチングは複数の個別推論リクエストをGPUに送信する前に単一バッチにグループ化します。GPUは並列計算に最適化されており、32のバッチを単一入力とほぼ同じ速度で処理します。バッチングなしではGPU使用率が5-10%まで低下する可能性があります。ダイナミックバッチングはより多くのリクエストを待つためにわずかな遅延(通常100-500マイクロ秒)を追加します。トレードオフ:リクエストあたりのレイテンシがわずかに高くなりますが、スループットは劇的(げきてき)に向上します。

Q13. ONNX形式とその使用場面を説明してください。

ONNX(Open Neural Network Exchange)はクロスフレームワークのモデル形式です。PyTorchで訓練されたモデルをONNXにエクスポートし、TF Serving、Triton、またはONNX Runtimeで提供できます。メリット:フレームワークに依存しないサービング、ONNX Runtimeによる最適化(グラフ最適化、量子化(りょうしか))、デプロイの柔軟性。訓練フレームワークの選択をサービングインフラから分離したい場合、またはONNX Runtimeがネイティブサービングより良い性能を提供する場合に使用。

Q14. 本番環境でのMLモデルのA/Bテストの実装方法は?

インフラレベル:両モデルバージョンを別々のエンドポイントとしてデプロイ。モデルルーターがユーザーIDのハッシュに基づいてトラフィックを分割(ユーザーごとの一貫した処理を保証)。モデルバージョン、ユーザーID、タイムスタンプとともに予測をログ。分析側:成功メトリクスを定義(クリック率、コンバージョン、不正検知率)、統計的有意性(ゆういせい)を計算し、ガードレールメトリクス(レイテンシ、エラー率)を監視。

Q15. モデルウォームアップとは何か、なぜ重要か?

モデルウォームアップは、新しくデプロイされたモデルに実際のトラフィックをルーティングする前にダミー推論リクエストを送信するプロセスです。ウォームアップなしでは、最初の実際のリクエストが高レイテンシを経験します:モデルがディスクからメモリ(またはGPU)にロードされる必要がある、TensorFlowとPyTorchが遅延初期化を行う(最初の呼び出し時にコンパイル)、キャッシュ(CPUキャッシュ、GPUキャッシュ)がコールド状態。ウォームアップはこのコールドスタートペナルティを排除します。

3.3 Kubernetesとインフラ(問題16-20)

Q16. Kubernetes上でモデルサービングのGPUリソースをどのように管理しますか?

K8s用NVIDIAデバイスプラグインをインストール。ポッドスペックでGPUリソースのリクエストとリミットを定義。ノードセレクターとトレレーションでGPUワークロードをGPUノードにスケジュール。MIG(Multi-Instance GPU)でA100を複数ポッド間で共有。DCGM-exporterとPrometheusでGPU使用率、メモリ、温度を監視。保留中のGPUポッドリクエストに基づいてスケールするGPUノードプール付きクラスターオートスケーラーを設定。

Q17. モデルサービングのStatefulSetとDeploymentを比較してください。それぞれいつ使いますか?

ステートレスモデルサーバー(任意のレプリカが任意のリクエストを処理可能)にはDeploymentsを使用(最も一般的)。モデルがローカルストレージの永続キャッシュを必要とする場合、モデルアンサンブルに安定したネットワークアイデンティティが必要な場合、またはモデルシャーディングに決定論的なポッドからシャードへのマッピングが必要な場合にStatefulSetsを使用。実践では、ほとんどのモデルサービングワークロードは外部ストレージ(S3、PVC)付きDeploymentsを使用。

Q18. Kubernetes上でモデルのカナリアデプロイメントを実装する方法は?

オプション1 — Istioトラフィック分割:新モデルバージョンを別デプロイメントとしてデプロイ。パーセンテージでトラフィックを分割するIstio VirtualServiceルールを作成。安定版からカナリアにトラフィックを段階的にシフト。オプション2 — Argo Rollouts:カナリア戦略を持つRolloutリソースを定義。モデルメトリクスをチェックする分析テンプレートを設定。分析結果に基づいて自動プロモートまたはロールバック。両オプションとも自動意思決定のためにPrometheusメトリクスと統合。

Q19. Kubernetes上でのモデルアーティファクトのストレージとバージョニングの方法は?

モデルアーティファクトの信頼できるソースとしてオブジェクトストア(S3、GCS、MinIO)を使用。各モデルバージョンはユニークなパスに保存:s3://models/model-name/version/。モデルレジストリ(MLflow Model Registry、カスタム)でメタデータを追跡:バージョン、メトリクス、訓練ランID、承認ステータス。K8sでは:ポッド起動時にオブジェクトストレージからモデルをダウンロードするinitコンテナを使用、またはCSIドライバーでオブジェクトストレージを直接マウント。

Q20. MLマイクロサービスのためのサービスメッシュのセットアップ方法は?

サイドカーインジェクション付きでIstioをインストール。すべてのサービス間通信にmTLSを設定(ゼロトラストセキュリティ)。トラフィックルーティング(カナリア、A/Bテスト)にVirtualServiceを使用。サーキットブレーキングと外れ値検出にDestinationRuleを使用。リクエストレベルの可視性のためにEnvoyアクセスロギングを有効化。サービスメッシュ可視化のためにKialiを設定。

3.4 分散システム(問題21-25)

Q21. 複数サービスにまたがるML予測パイプラインでのExactly-Once処理の保証方法は?

サービス間の真のExactly-Onceは非常に困難です。代わりに、各サービスをべき等(とう)にすることでEffectively-Onceを目指します。ゲートウェイで一意のリクエストIDを生成し、すべてのサービスに伝搬。各サービスがこのリクエストIDを既に処理したかチェック(Redisまたはデータベースのべき等キー)。Kafkaベースパイプラインではkafkaトランザクションを使用。データベース書き込みにはリクエストIDをユニーク制約としたupsertを使用。

Q22. リアルタイムとバッチの両消費者にサービスする特徴量計算パイプラインの設計方法は?

LambdaまたはKappaアーキテクチャを使用。Lambda:速度レイヤー(Flink)がストリーミングデータからリアルタイム特徴量を計算し、バッチレイヤー(Spark)が定期的に特徴量を再計算して精度を確保。Kappa:再処理能力を持つFlinkのみを使用。いずれの場合も、特徴量をデュアルストアに書き込み:オンラインサービング用Redis、オフライン訓練用S3/BigQuery。

Q23. マイクロサービスアーキテクチャにおける分散ロギングの課題と解決策は?

課題:ログ量(毎分数百万行)、サービス間の相関(そうかん)、ログ形式の不一致、ストレージコスト。解決策:必須フィールド(トレースID、サービス名、タイムスタンプ、重要度)を持つJSON形式の構造化ロギング。Fluent Bitサイドカー経由でログをKafkaに送信し、OpenSearchまたはLokiに転送。冗長(じょうちょう)ログのサンプリングを実装(デバッグレベルリクエストの10%をログ)。ログベースのアラートでエラーパターンを検出。

Q24. サービス間分散トレーシングでのクロックスキューの処理方法は?

すべてのノードで厳密なドリフトしきい値(1ms未満)のNTP同期を使用。トレース収集では、Jaeger/Tempoエージェント側のタイムスタンプ調整を使用。クロックが不一致の場合の因果関係(いんがかんけい)順序には、Lamportタイムスタンプまたはベクタークロックを使用。実践では、すべてのK8sノードが同じNTPサーバープールを使用し、クロックドリフトが5msを超えた場合にアラートを設定。

Q25. ML予測APIのレートリミッターの設計方法は?

トークンバケットまたはスライディングウィンドウアルゴリズムを使用。2つのレベルで実装:共有ステートのためにRedisを使用したAPIゲートウェイでのクライアントごとのレート制限と、単一モデルがすべてのリソースを消費するのを防ぐモデルルーターでのモデルごとのレート制限。設定:バーストトラフィックを許可(バケット容量)、クライアントごとの持続レートを設定、クリティカルリクエストの優先キューを実装(不正検知はレコメンデーションより高い優先度)。

3.5 Python、Kotlin、Go(問題26-30)

Q26. PythonのAsyncio、Kotlinコルーチン、GoのGoroutineを並行MLサービングで比較してください。

Python asyncioは協調的マルチタスキングのシングルスレッドです。I/Oバウンド作業(モデルサーバー応答待ち)には良いですが、GILによりCPUバウンド作業は制限。uvicorn/FastAPIで非同期推論エンドポイントに使用。Kotlinコルーチンは構造化並行性を持つJVM上の軽量スレッド。複数の下流サービスに同時呼び出しが必要なSpring Bootサービスに適切。Goのgoroutineはプリエンプティブスケジューリングの軽量グリーンスレッド。最小オーバーヘッドの高並行インフラサービス(特徴量サーバー、APIゲートウェイ)に最適。

Q27. KotlinでgRPCモデルサーバー呼び出し用のコネクションプールの実装方法は?

モデルサーバーへの複数gRPCチャネルを維持するチャネルプールを使用。各チャネルは複数リクエストを多重化できます:

class GrpcChannelPool(
    private val endpoint: String,
    private val poolSize: Int = 10
) {
    private val channels = ConcurrentLinkedQueue<ManagedChannel>()

    init {
        repeat(poolSize) {
            channels.offer(
                ManagedChannelBuilder
                    .forTarget(endpoint)
                    .usePlaintext()
                    .keepAliveTime(30, TimeUnit.SECONDS)
                    .maxInboundMessageSize(16 * 1024 * 1024)
                    .build()
            )
        }
    }

    fun getChannel(): ManagedChannel {
        return channels.poll() ?: createNewChannel()
    }

    fun returnChannel(channel: ManagedChannel) {
        if (channel.getState(false) != ConnectivityState.SHUTDOWN) {
            channels.offer(channel)
        }
    }
}

Q28. ML APIのリクエストロギングとレイテンシ追跡を実装するGoミドルウェアを書いてください。

func MetricsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        traceID := r.Header.Get("X-Trace-ID")
        if traceID == "" {
            traceID = uuid.New().String()
        }

        // ステータスコードをキャプチャするためにレスポンスライターをラップ
        wrapped := &statusResponseWriter{ResponseWriter: w, status: 200}

        next.ServeHTTP(wrapped, r)

        duration := time.Since(start)

        // メトリクス記録
        httpRequestsTotal.WithLabelValues(
            r.Method, r.URL.Path,
            strconv.Itoa(wrapped.status),
        ).Inc()

        httpRequestDuration.WithLabelValues(
            r.Method, r.URL.Path,
        ).Observe(duration.Seconds())

        // 構造化ログ
        log.Info().
            Str("trace_id", traceID).
            Str("method", r.Method).
            Str("path", r.URL.Path).
            Int("status", wrapped.status).
            Dur("latency", duration).
            Msg("request completed")
    })
}

Q29. テスタビリティのためのPython MLサービングアプリケーションの構造化方法は?

関心事をレイヤーに分離:APIレイヤー(FastAPIルート)、サービスレイヤー(ビジネスロジック、特徴量組立)、モデルレイヤー(モデルロード、推論)。すべての外部依存(モデルクライアント、特徴量ストア、データベース)に依存性注入を使用。モデル推論のインターフェース(PythonのProtocolクラス)を作成し、テストでモック可能に。各レイヤーを独立してテスト:ビジネスロジックのユニットテスト、テストモデルサーバーとの統合テスト、フルパイプラインのエンドツーエンドテスト。

Q30. MLプラットフォームサービスでPython vs Kotlin vs Goをいつ使い分けますか?

Python:モデルサービングラッパー、特徴量エンジニアリングパイプライン、データ検証、MLライブラリ(scikit-learn、pandas、TensorFlow)との緊密な統合が必要なサービス。Kotlin:APIゲートウェイ、オーケストレーションサービス、JVMエコシステム(Spring Boot、Kafkaクライアント、JDBC)の恩恵を受けるもの、既存Javaインフラと統合するサービス。Go:高性能ステートレスサービス(特徴量サーバー、リクエストルーター)、CLIツール、Kubernetesオペレーター、起動時間とメモリフットプリントが重要なサービス。原則(げんそく):最も得意な言語ではなく、仕事に適した言語を使用。


4. 6ヶ月学習ロードマップ

第1ヶ月:バックエンド基礎(きそ)

第1-2週:サーバーアーキテクチャ

  • クリーンアーキテクチャとヘキサゴナルアーキテクチャパターンを学習
  • KotlinとSpring BootでREST APIを構築(CRUD、バリデーション、エラーハンドリング)
  • GinまたはEchoフレームワークでGoで同じAPIを実装
  • k6で負荷テストを使用してパフォーマンス特性(とくせい)を比較

第3-4週:並行性(へいこうせい)とパフォーマンス

  • 公式ガイドとKotlin in ActionでKotlinコルーチンを学習
  • GoroutineとChannelを使用したGoの並行HTTPクライアントを実装
  • FastAPIとasyncioでPython非同期APIを構築
  • 3つの実装すべてをプロファイル:メモリ使用量、負荷時レイテンシ、CPU使用率

第2ヶ月:Kubernetes深掘り

第1-2週:コアK8sコンセプト

  • CKA(Certified Kubernetes Administrator)カリキュラムを完了
  • Minikubeまたはkindでマルチサービスアプリケーションをデプロイ
  • Deployments、Services、ConfigMaps、Secrets、PVCsを実装
  • リソースリクエスト、リミット、QoSクラスを練習

第3-4週:ML向け高度なK8s

  • NVIDIAデバイスプラグインでGPUスケジューリングを設定
  • PrometheusからのカスタムメトリクスでHPAを設定
  • Argo Rolloutsでカナリアデプロイメントを実装
  • トラフィック分割付きIstioサービスメッシュを設定

第3ヶ月:MLサービング基礎

第1-2週:モデルサービングフレームワーク

  • サンプルモデルでTensorFlow Servingをデプロイ
  • Triton Inference ServerをデプロイしONNXモデルでテスト
  • 2つのレイテンシとスループットを比較
  • ダイナミックバッチングを実装しスループット向上を測定

第3-4週:KServeとモデル管理

  • KubernetesクラスターにKServeをインストール
  • InferenceServiceでモデルをデプロイ
  • モデルバージョンアップグレードのカナリアデプロイメントを実装
  • Prometheusメトリクスでモデル監視を設定

第4ヶ月:分散システム

第1-2週:理論(りろん)とパターン

  • 「Designing Data-Intensive Applications」第5、8、9章を読む
  • マイクロサービスパターン学習:サーキットブレーカー、バルクヘッド、Saga、CQRS
  • Resilience4jを使用してKotlinでサーキットブレーカーを実装
  • 分散システム設計問題を練習

第3-4週:オブザーバビリティ

  • マルチサービスアプリケーションにOpenTelemetryインストルメンテーションを設定
  • 分散トレーシング用にJaegerまたはTempoをデプロイ
  • メトリクス収集とダッシュボード用にPrometheus + Grafanaを設定
  • 相関IDを使用したサービス間の構造化ロギングを実装

第5ヶ月:特徴量ストアとデータパイプライン

第1-2週:特徴量ストア

  • Feastをローカルにデプロイし特徴量ビューを定義
  • Redisバックエンドでオンライン特徴量サービングを実装
  • Pythonで特徴量計算パイプラインを構築
  • 訓練データのポイントインタイム正確性をテスト

第3-4週:エンドツーエンドMLパイプライン

  • 完全なMLサービングパイプライン構築:特徴量ストア→モデルサーバー→API
  • トラフィック分割によるA/Bテストインフラを実装
  • モデル監視を追加(データドリフト、予測分布)
  • 自動モデル再訓練トリガーを設定

第6ヶ月:統合と面接準備

第1-2週:ポートフォリオプロジェクト

  • すべてのコンポーネントを備えた本番品質のMLサービングプラットフォームを構築
  • ダイアグラムと決定記録でアーキテクチャを文書化
  • 主要MLメトリクスのモニタリングダッシュボードを作成
  • SLA要件を検証する負荷テストを作成

第3-4週:面接準備

  • 30問の面接質問をパートナーと練習
  • MLインフラに焦点を当てた5回のシステム設計模擬面接を実施
  • 定量的メトリクスで履歴書をレビュー・改善
  • ポートフォリオプロジェクトの5分間アーキテクチャウォークスルーを準備
  • 文化(ぶんか)と技術コンテキストのためにToss Techブログ投稿を学習

5. 履歴書(りれきしょ)戦略

トスバンクが見たいもの

履歴書はMLインフラを大規模に構築・運用できることを証明すべきです。すべてを影響(えいきょう)の観点からフレーミングしましょう。

スケールメトリクスをリードに

  • 「毎秒10万予測をp99レイテンシ8ms未満で処理するMLサービングインフラの設計・運用」
  • 「KServeベースCI/CDパイプライン構築により、モデルデプロイ時間を2日から15分に短縮(たんしゅく)」
  • 「Redisクラスターを使用した日次5000万特徴量をサブミリ秒レイテンシで提供する特徴量ストアの構築」

ポリグロット経験を示す

  • 深さ指標付きで主要言語を最初にリスト(「Python 5年、本番MLシステム」)
  • コンテキスト付きで副言語を含める(「Spring Bootマイクロサービス用Kotlin」、「高性能ツール用Go」)
  • 言語選択の決定を強調:「低レイテンシ要件のため特徴量サーバーにGoを選択、p99を5msから0.8msに削減」

運用成熟度(せいじゅくど)を示す

  • MLシステムのオンコール経験(モデル障害、データドリフトインシデント)
  • コスト最適化:GPU使用率改善、モデルサービングインフラの適正化
  • ゼロダウンタイムデプロイメントと移行ストーリー
  • ML固有メトリクスの監視とアラート設定

履歴書フォーマット

  • 最大2ページ
  • JDキーワードに直接マッピングする「テクニカルスキル」セクション
  • 成果のXYZ形式:「Yを実装してXを達成し、Zの結果を得た」
  • 関連するオープンソース貢献や公開講演(こうえん)へのリンク
  • 少なくとも1つの関連プロジェクトを持つGitHubプロファイル

6. ポートフォリオプロジェクトアイデア

プロジェクト1:フルスタックMLサービングプラットフォーム

JDの全要件を示す完全なMLサービングプラットフォームを構築:

  • モデルサーバー:GPUサポート付きKubernetes上のTriton Inference Server
  • APIゲートウェイ:gRPCモデルクライアント付きKotlin Spring Boot
  • 特徴量ストア:Python特徴量パイプライン付きRedisバック特徴量サービング
  • 監視:モデル固有メトリクス付きPrometheus + Grafanaダッシュボード
  • カナリアデプロイメント:自動分析付きArgo Rollouts
  • 分散トレーシング:全サービスにわたるOpenTelemetry

プロジェクト2:リアルタイム不正検知システム

エンドツーエンドの不正検知システムを構築:

  • トランザクションイベントを受け付けるKotlin API
  • リアルタイム特徴量を計算するGo特徴量サーバー
  • モデルアンサンブルによるPythonモデルサービング
  • モデル比較のためのA/Bテストインフラ
  • ルールベース検知へのフォールバック付きサーキットブレーカー
  • 検知率、偽陽性(ぎようせい)率、レイテンシを表示するダッシュボード

プロジェクト3:モデルデプロイメントパイプライン

MLモデルのCI/CDパイプラインを構築:

  • モデル追跡とレジストリ用MLflow
  • カナリアサポート付きKServeによるモデルサービング
  • 自動モデル検証(スキーマチェック、パフォーマンスベンチマーク)
  • 新モデルのシャドーモードデプロイメント
  • メトリクス劣化時の自動ロールバック
  • デプロイメントイベントのSlack/Webhook通知

プロジェクト4:MLワークロード用Kubernetesオペレーター

Goでカスタムk8sオペレーターを構築:

  • MLModelリソース用カスタムリソース定義
  • モデルサービングデプロイメントを管理するコントローラー
  • 推論メトリクスに基づく自動スケーリング
  • S3からのモデルアーティファクト同期
  • モデルポッドのヘルスチェックと自動復旧(ふっきゅう)
  • メトリクス用Prometheus統合

7. クイズ:知識をテスト

Q1:MLサービングシステムが毎秒1万リクエストを処理中。新モデルバージョンをデプロイし推論レイテンシが5msから50msに増加。システムに何が起こり、どう対応しますか?

10K RPSと50msのレイテンシで、システムには500の同時接続が必要(50から増加)。これによりスレッドプール、コネクション制限、下流キャパシティが枯渇し、カスケードタイムアウトを引き起こす可能性があります。即時対応:カナリアデプロイメントを古いモデルバージョンにロールバック。その後調査:モデルが大きい(より多くのGPUメモリが必要)?最適化されていない(TensorRT最適化や量子化が必要)?追加のネットワーク呼び出しを行っている?レイテンシの問題を修正してから再デプロイ。常に100%トラフィックに到達する前にレイテンシ退行をキャッチする自動カナリア分析を用意。

Q2:不正検知モデルの精度が2週間で95%から80%に低下しましたが、モデル自体は変更されていません。何が起きていますか?

データドリフトまたはコンセプトドリフトです。考えられる原因:(1) 入力特徴量分布がシフト — 新しいトランザクションパターン、季節性、ビジネス変更(新加盟店カテゴリ、プロモーションイベント)。(2) 上流データパイプラインのバグで不正確な特徴量値を生成。(3) 特徴量ストアの古いデータ(TTLが長すぎ、パイプライン障害)。診断:特徴量ドリフトメトリクス(各特徴量のPSI)をチェック、最近のデータと訓練データの分布を比較、上流パイプラインの健全性を検証。解決:最新データで再訓練、自動ドリフト検出アラートを実装、定期的な再訓練スケジュールを設定。

Q3:LLM(30億パラメータ、6GBモデル)をKubernetes上で提供する必要があります。モデルロードに30秒かかります。スケーリングとデプロイメントの方法は?

各ポッド起動時のダウンロードを避けるため、PVCバックモデルストレージ付きStatefulSetsまたはDeploymentsを使用。オフピーク時にモデルイメージをプリプル。ベースライントラフィックを処理するのに十分な最小レプリカを設定(ゼロからのスケーリングを回避)。HPAを積極的にスケールアップ(プリエンプティブ)し、ゆっくりスケールダウン(10分以上の安定化ウィンドウ)するよう設定してコールドスタートレイテンシを回避。Pod Disruption Budgetでローリングアップデートがすべてのレプリカをオフラインにしないことを確保。テスト推論を提供した後にのみパスするヘルスチェックによるモデルウォームアップを検討。

Q4:Kubernetesクラスターに5つのMLモデルサービングチームが共有する8つのA100 GPUがあります。GPUリソースを公平に管理する方法は?

ネームスペースごとのリソースクォータを実装(各チームにネームスペースを割り当て)。GPU制限を設定:例えばチームAが2GPU、チームBが2GPU、2つは共有。フルGPUが不要なモデル用にNVIDIA MIGでA100を小さなインスタンスに分割。優先クラスを実装:本番モデルが開発ワークロードより高い優先度。需要がキャパシティを超えた場合にGPUノードを追加するクラスターオートスケーラー。チームごとのGPU使用率を監視し、四半期ごとに未使用割り当てを回収。GrafanaでGPUスケジューリングダッシュボードを設定。

Q5:特徴量ストアとモデルサーバー間のインターフェースを設計しています。どのプロトコルと形式を選び、その理由は?

Protocol BuffersとgRPCを使用。gRPCはHTTP/2多重化とバイナリシリアライゼーションによりRESTより低いレイテンシを提供。Protocol Buffersは強い型付けにより、特徴量ストアとモデルサーバー間のスキーマ不一致を防止。スキーマ定義がチーム間のコントラクトとして機能。特徴量テンソル形式には、デシリアライゼーションオーバーヘッドを回避するためにフラットバッファまたはnumpy互換バイナリ形式を使用。頻繁にリクエストされる特徴量セットをモデルサーバーのローカルメモリにキャッシュ(TTL付きLRUキャッシュ)。バッチリクエストにはストリーミングgRPCをサポートして大きな単一メッセージのオーバーヘッドを回避。


8. 参考資料(さんこうしりょう)とリソース

書籍(しょせき)

  1. Martin Kleppmann — Designing Data-Intensive Applications, O'Reilly, 2017
  2. Sam Newman — Building Microservices, 第2版, O'Reilly, 2021
  3. Chip Huyen — Designing Machine Learning Systems, O'Reilly, 2022
  4. Chris Richardson — Microservices Patterns, Manning, 2018

公式ドキュメント

  1. Kubernetes Documentation — https://kubernetes.io/docs/
  2. NVIDIA Triton Inference Server — https://github.com/triton-inference-server/server
  3. KServe Documentation — https://kserve.github.io/website/
  4. Feast Feature Store — https://feast.dev/
  5. OpenTelemetry Documentation — https://opentelemetry.io/docs/

コースとチュートリアル

  1. Made With ML — MLOpsコース — https://madewithml.com/
  2. Full Stack Deep Learning — https://fullstackdeeplearning.com/
  3. Google ML Engineering Best Practices — https://developers.google.com/machine-learning/guides/rules-of-ml
  4. CKA試験準備 — https://kubernetes.io/docs/reference/kubectl/

コミュニティと講演(こうえん)

  1. MLOps Community — https://mlops.community/
  2. KubeCon + CloudNativeCon録画 — https://www.cncf.io/kubecon/
  3. Chip Huyenのブログ — https://huyenchip.com/blog/
  4. Toss Tech Blog — https://toss.tech/
  5. Netflix Tech Blog — https://netflixtechblog.com/

まとめ

トスバンクのML Backend Engineerポジションは、強力なバックエンドエンジニアリングの基礎、実践的なMLサービング専門知識、Kubernetes運用スキルという稀(まれ)な組み合わせを要求します。モデルを訓練するために雇(やと)われるのではなく、数百万の銀行顧客にモデルを提供する信頼性が高く、スケーラブルで、観測可能なシステムを構築するために雇われるのです。

6ヶ月ロードマップはまずバックエンドアーキテクチャの深さを構築し、その後ML固有のインフラ知識を重ねることに焦点を当てています。この順序は重要です:MLサービングを学ぶ堅実なバックエンドエンジニアは、分散システムに苦戦するMLスペシャリストより優れた成果を出します。

ポートフォリオは3つのことを示すべきです:複数の言語で高性能システムを構築できること、Kubernetes上でMLモデルをデプロイ・運用できること、本番環境でMLを実行する運用の現実(監視、スケーリング、インシデント対応)を理解していること。

金融サービス企業がコア業務(ぎょうむ)により多くのAIを統合するにつれ、ML Backend Engineerの需要は増加(ぞうか)し続けます。トスバンクは韓国においてこのトレンドの最前線(さいぜんせん)にいます。徹底的(てっていてき)に準備し、実際のプロジェクトを構築し、バックエンドエンジニアリングと機械学習の交差点で活躍できることを示しましょう。

構築を始めましょう。モデルたちはサービングされるのを待っています。