Slide 1

Slide 1 text

更な 可用性を求めて、 5年間運用した Kotlin のアプリケーションを Go でリプレイスす 話 エムスリー株式会社 田口 健介 © M3, Inc. 2025 1

Slide 2

Slide 2 text

00 自己紹介 / サービス紹介 01 5年運用して残った課題 Index 02 Go にして何が変わったか 03 AI と進めた言語リプレイス 04 結果 © M3, Inc. 2025 2

Slide 3

Slide 3 text

自己紹介 エムスリー株式会社 デジスマ診療 開発チーム 田口 健介 Kensuke Taguchi ● 2022年にエムスリー入社。「デジスマ診療」の 開発チームにジョイン ● [ プロフィール画像 ] 2024年か チームリーダーとして、開発と チームビルディングに従事 ● アイコンは実家の柴犬 © M3, Inc. 2025 3

Slide 4

Slide 4 text

とは 必要な機能を集約・ワンストップ化した 業務支援クラウドサービス。 受付業務をサポートし、待ち時間を減ら すことで患者にシームレスな診療体験を 提供できます。 再来促進、患者フォ ローアップ機能搭載で再診率・治療継続 率向上に寄与します。

Slide 5

Slide 5 text

拡大す デジスマ診療の利用ユーザー数 約5年で非線形に増加した利用ユーザー数 ● 利用増に耐え スケーラビリティ ● 医療機関のオペレーションを止めない可用性 出所: 2027年3月期 第1四半期決算発表資料 https://corporate.m3.com/ir/library/ © M3, Inc. 2025 5

Slide 6

Slide 6 text

デジスマのアーキテクチャ概略 多層に走 サービス間の呼び出し ● (元々)Kotlin + Spring Boot のマイクロサービスを Kubernetes (EKS) で運用 ● 業務 API どうしも呼び合うため、1リクエストの裏で呼び出しが多層に走 © M3, Inc. 2025 6

Slide 7

Slide 7 text

01 5年運用して見えた課題 2つの課題と対策の代償 © M3, Inc. 2025 7

Slide 8

Slide 8 text

課題① 起動直後のパフォーマンス 暖機が終わ までの性能低下 ● 起動処理と、実行しなが のコード最適化で CPU を 使うため、起動直後は本来の性能が出ない ● 起動直後は 20〜30 rps 程度しか捌けない ● 割 当て 起動か 7〜8分は定常の3〜5倍 CPU を増やせば改善す が、暖機後は過剰な 割 当てにな というジレンマ 本番で新しく起動した1台の CPU 使用量(1分平均) © M3, Inc. 2025 8

Slide 9

Slide 9 text

1日のトラフィック傾向 診療時間に沿った山型のトラフィック 施設画面向け API 患者アプリ向け API 診療が始ま 時刻に、 階段状に立ち上が 午前9時/10時に診療が始ま 医療機関が多く、昼休みを挟んだ午前・午後の二部構成にな グラフは平日1日・30分ごと。施設画面向け API のピークを 100 として正規化(両系列とも同一スケール) © M3, Inc. 2025 9

Slide 10

Slide 10 text

現状の対策と代償 暖機を前提にした設定の積み上が 積み上げた設定 何のためか Istio warmupDurationSecs 起動した Pod に流すリクエストを 少しずつ増やす 代償 Pod を増やしてもすぐには効かない minReadySeconds 準備完了後もしば く古い Pod を残し、 全 Pod の入 替えに時間がかか 。 maxSurge / maxUnavailable 入 替えは一度に少しずつ 切 戻しにも同じだけかか 時間スケジュールに 混み合う前に、あ かじめ台数を増やす 最適な台数のチューニングが必要 スケーリング ● 起動直後にリクエストが集中しない仕組みが必要 ● サービスごとに、暖機の時間と起動台数を調整し続け ことにな ○ © M3, Inc. 2025 対策のたびに別のとこ の設定を触 ことにな 10

Slide 11

Slide 11 text

課題② キャンセルの伝搬 上流が諦めても走 続け 処理 応答を待つ 呼び出し元 処理中 呼ば た側 0s ● © M3, Inc. 2025 誰も待っていない処理が続く 10s 30s 60s 上流がエラーを返した後も、下流は処理を続け ○ ● 10秒でタイムアウト → エラーを返す その間ずっと、リクエストを処理す スレッドと DB コネクションを掴んでい この結果、リクエストが詰ま 、最悪の場合 Pod がダウンす 11

Slide 12

Slide 12 text

02 Go にして変わったこと 暖機の消滅とキャンセルの伝搬 © M3, Inc. 2025 12

Slide 13

Slide 13 text

Go へのリプレイスという決断 既定でそうな 言語への移行 Kotlin でも実現でき Go だと既定でそうな ● あ かじめコンパイルしておく仕組みを入 ● ネイティブビルドなので、暖機が要 ない ○ ● ctx を引数で受け取 、渡すのが習慣 ● ただしビルドに時間がかか うに ○ 応答を待つ間ブロックしない う、処理経路を 全て書き換え ○ API クライアントのライブラリも対応した ● 実装す ときに自然と意識でき AI エージェントで移植を進め ○ 「や な 今」と判断できた理由のひとつ ものへ乗 換え ● どち も「でき 」。ただし、ずっと気をつけ続 け ことにな © M3, Inc. 2025 13

Slide 14

Slide 14 text

変化① 暖機の消滅 起動直後を特別扱いしない運用へ ● ビルドの時点でコンパイルが終わってい ので、起動時に最適化処理が走 ない ● 暖気に関す 設定を削除 ○ © M3, Inc. 2025 設定がシンプルになったため、サービス毎の微調整も不要に 14

Slide 15

Slide 15 text

変化② キャンセルの伝搬 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

Slide 16

Slide 16 text

変化③ ビルドとベースの透明性 ビルド時間の短縮とベース部分の見通し CI ジョブの所要時間(中央値) Kotlin Go テスト 406 秒 112 秒 イメージビルド 311 秒(Buildpacks) 204 秒(Dockerfile + BuildKit) ● ● ベース部分の透明性が上がった ○ フレームワークが暗黙にやってく ○ 便利さと引き換えの「内部で何をしてい か分か ない」は減った 成果物が単一バイナリにな 、イメージも起動も単純になった ○ © M3, Inc. 2025 範囲が狭く、リクエストが通 道筋をコードで追え Buildpacks の うにビルド側の作 込みを気にしなくて い 16

Slide 17

Slide 17 text

03 AI と進めた言語リプレイス 振 舞いを変えないための仕掛け © M3, Inc. 2025 17

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

移植の進め方 リクエストミラーリングに 本番検証 ● 本番のリクエストを Istio で複製して Go 版へ流す レスポンスは捨て ためユーザー影響はゼロ ● 両者のレスポンスをログに落とし、あとか 構造を比較す ● 全てのケースを網羅でき わけではない © M3, Inc. 2025 差分が出たとこ だけを直す 19

Slide 20

Slide 20 text

04 リプレイスの結果 2つの課題への答え © M3, Inc. 2025 20

Slide 21

Slide 21 text

結果① 立ち上が と入 替え 起動直後のパフォーマンスの改善 ● 再起動直後の1台に 100 rps を3分。成功率 0% → 100% ● 起動直後の1発目のレスポンスが 数十 ms ○ ● 全台のローリングアップデートが 約11分 → 1分未満 ○ ● Kotlin はレプリカ3〜4台で約11分。Go は1分粒度のメトリクスでは観測できない 短縮の効用は「速くなった」 ○ © M3, Inc. 2025 暖機を待たずに応答でき 「や 直しが効く うになった」 緊急の切 戻しも同じだけ速い。デプロイのたびに開くリスクの窓が縮んだ 21

Slide 22

Slide 22 text

結果② キャンセルの伝搬 期限どお に切 応答を待つ 呼び出し元 0s © M3, Inc. 2025 10秒でタイムアウト 処理中 呼ば た側 ● うになった処理時間 課題②ではここまで走 続けていた 10s 10秒でタイムアウトした場合は下流の処理もキャンセルさ 30s 60s うに 22

Slide 23

Slide 23 text

まとめ まとめ ● 可用性を上げ ために何を直すかを洗い出した ○ ● Kotlin でもでき 。ただし気をつけ続け ことにな ○ ● © M3, Inc. 2025 立ち上が の遅さと、キャンセルの伝わ なさ Go は既定でそうなってい ので、意識す 観点が減 結果として課題は解決、可用性の向上に繋がった 24

Slide 24

Slide 24 text

本日はあ がとうございました © M3, Inc. 2025 © M3, Inc. 2025 25