Upgrade to Pro — share decks privately, control downloads, hide ads and more …

更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話

 更なる可用性を求めて、5年間運用したKotlinのアプリケーションをGoでリプレイスする話

Avatar for Kensuke Taguchi

Kensuke Taguchi

September 15, 2026

Other Decks in Programming

Transcript

  1. 自己紹介 エムスリー株式会社 デジスマ診療 開発チーム 田口 健介 Kensuke Taguchi • 2022年にエムスリー入社。「デジスマ診療」の

    開発チームにジョイン • [ プロフィール画像 ] 2024年か チームリーダーとして、開発と チームビルディングに従事 • アイコンは実家の柴犬 © M3, Inc. 2025 3
  2. デジスマのアーキテクチャ概略 多層に走 サービス間の呼び出し • (元々)Kotlin + Spring Boot のマイクロサービスを Kubernetes

    (EKS) で運用 • 業務 API どうしも呼び合うため、1リクエストの裏で呼び出しが多層に走 © M3, Inc. 2025 6
  3. 課題① 起動直後のパフォーマンス 暖機が終わ までの性能低下 • 起動処理と、実行しなが のコード最適化で CPU を 使うため、起動直後は本来の性能が出ない

    • 起動直後は 20〜30 rps 程度しか捌けない • 割 当て 起動か 7〜8分は定常の3〜5倍 CPU を増やせば改善す が、暖機後は過剰な 割 当てにな というジレンマ 本番で新しく起動した1台の CPU 使用量(1分平均) © M3, Inc. 2025 8
  4. 1日のトラフィック傾向 診療時間に沿った山型のトラフィック 施設画面向け API 患者アプリ向け API 診療が始ま 時刻に、 階段状に立ち上が 午前9時/10時に診療が始ま

    医療機関が多く、昼休みを挟んだ午前・午後の二部構成にな グラフは平日1日・30分ごと。施設画面向け API のピークを 100 として正規化(両系列とも同一スケール) © M3, Inc. 2025 9
  5. 現状の対策と代償 暖機を前提にした設定の積み上が 積み上げた設定 何のためか Istio warmupDurationSecs 起動した Pod に流すリクエストを 少しずつ増やす

    代償 Pod を増やしてもすぐには効かない minReadySeconds 準備完了後もしば く古い Pod を残し、 全 Pod の入 替えに時間がかか 。 maxSurge / maxUnavailable 入 替えは一度に少しずつ 切 戻しにも同じだけかか 時間スケジュールに 混み合う前に、あ かじめ台数を増やす 最適な台数のチューニングが必要 スケーリング • 起動直後にリクエストが集中しない仕組みが必要 • サービスごとに、暖機の時間と起動台数を調整し続け ことにな ◦ © M3, Inc. 2025 対策のたびに別のとこ の設定を触 ことにな 10
  6. 課題② キャンセルの伝搬 上流が諦めても走 続け 処理 応答を待つ 呼び出し元 処理中 呼ば た側

    0s • © M3, Inc. 2025 誰も待っていない処理が続く 10s 30s 60s 上流がエラーを返した後も、下流は処理を続け ◦ • 10秒でタイムアウト → エラーを返す その間ずっと、リクエストを処理す スレッドと DB コネクションを掴んでい この結果、リクエストが詰ま 、最悪の場合 Pod がダウンす 11
  7. Go へのリプレイスという決断 既定でそうな 言語への移行 Kotlin でも実現でき Go だと既定でそうな • あ

    かじめコンパイルしておく仕組みを入 • ネイティブビルドなので、暖機が要 ない ◦ • ctx を引数で受け取 、渡すのが習慣 • ただしビルドに時間がかか うに ◦ 応答を待つ間ブロックしない う、処理経路を 全て書き換え ◦ API クライアントのライブラリも対応した • 実装す ときに自然と意識でき AI エージェントで移植を進め ◦ 「や な 今」と判断できた理由のひとつ ものへ乗 換え • どち も「でき 」。ただし、ずっと気をつけ続 け ことにな © M3, Inc. 2025 13
  8. 変化② キャンセルの伝搬 context で下流まで伝わ キャンセル const queryTimeout = 10 *

    time.Second func (h *Handler) Get(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // 上流が切れれば ctx も終了する ctx, cancel := context.WithTimeout(ctx, queryTimeout) defer cancel() rows, err := h.db.QueryContext(ctx, query) // DB クエリも止まる resp, err := h.client.Do(req.WithContext(ctx)) // 呼び出し先も止まる } • 受け取った ctx を渡していくだけで、DB クエリも、別の API の呼び出しも一緒に止ま • 渡すのが習慣になってい ため、多くのライブラリで自然に実装でき © M3, Inc. 2025 15
  9. 変化③ ビルドとベースの透明性 ビルド時間の短縮とベース部分の見通し CI ジョブの所要時間(中央値) Kotlin Go テスト 406 秒

    112 秒 イメージビルド 311 秒(Buildpacks) 204 秒(Dockerfile + BuildKit) • • ベース部分の透明性が上がった ◦ フレームワークが暗黙にやってく ◦ 便利さと引き換えの「内部で何をしてい か分か ない」は減った 成果物が単一バイナリにな 、イメージも起動も単純になった ◦ © M3, Inc. 2025 範囲が狭く、リクエストが通 道筋をコードで追え Buildpacks の うにビルド側の作 込みを気にしなくて い 16
  10. 同一性の担保と AI の使いどこ 振 舞いの同一性を担保す 仕組み 振 舞いを固定す 仕掛け AI

    の使いどこ • • ogen ◦ API 定義か サーバ・クライアントを生成 ◦ 定義は Kotlin 版と共通なので I/F の一致が ビルド時に担保さ • • 詳細は後述 仕様を決め 工程がなく、正解 (Kotlin 版の振 舞い)が既にあ • 「ついでの改善をしない」方針 ◦ • 速度を優先し、素直に再実装す 人がや べきは「どこを揃えないか」の判断 Golden Test と SQL の構文比較 ◦ © M3, Inc. 2025 ◦ 本番リクエストのミラーリング ◦ 言語リプレイスは AI と相性が い Kotlin のレスポンスを正解とし、比較 18
  11. 移植の進め方 リクエストミラーリングに 本番検証 • 本番のリクエストを Istio で複製して Go 版へ流す レスポンスは捨て

    ためユーザー影響はゼロ • 両者のレスポンスをログに落とし、あとか 構造を比較す • 全てのケースを網羅でき わけではない © M3, Inc. 2025 差分が出たとこ だけを直す 19
  12. 結果① 立ち上が と入 替え 起動直後のパフォーマンスの改善 • 再起動直後の1台に 100 rps を3分。成功率

    0% → 100% • 起動直後の1発目のレスポンスが 数十 ms ◦ • 全台のローリングアップデートが 約11分 → 1分未満 ◦ • Kotlin はレプリカ3〜4台で約11分。Go は1分粒度のメトリクスでは観測できない 短縮の効用は「速くなった」 ◦ © M3, Inc. 2025 暖機を待たずに応答でき 「や 直しが効く うになった」 緊急の切 戻しも同じだけ速い。デプロイのたびに開くリスクの窓が縮んだ 21
  13. 結果② キャンセルの伝搬 期限どお に切 応答を待つ 呼び出し元 0s © M3, Inc.

    2025 10秒でタイムアウト 処理中 呼ば た側 • うになった処理時間 課題②ではここまで走 続けていた 10s 10秒でタイムアウトした場合は下流の処理もキャンセルさ 30s 60s うに 22
  14. まとめ まとめ • 可用性を上げ ために何を直すかを洗い出した ◦ • Kotlin でもでき 。ただし気をつけ続け

    ことにな ◦ • © M3, Inc. 2025 立ち上が の遅さと、キャンセルの伝わ なさ Go は既定でそうなってい ので、意識す 観点が減 結果として課題は解決、可用性の向上に繋がった 24