モバイルアプリの配布基盤を、サードパーティのコードを大量に抱え込む「実行環境」として運用している方に向けた記事です。WeChat(中国の大手メッセンジャーアプリ)やAlipay(大手決済アプリ)が、数万規模のミニプログラム(アプリ内で動く小規模アプリ)をどう安全に動かしているか、その仕組みをSREの視点で整理します。
「WeChatはメッセンジャーであり銀行でありUberでもある」という説明はよく聞きますが、これは表層の話です。技術的に重要なのは、サードパーティのコードをストア審査なしで配布し続けながら、どうやってシステム全体の可用性を守っているかという点です。これは単なるモバイル開発の話ではなく、マルチテナント基盤の障害設計そのものと言えます。
なぜこの仕組みが可用性設計の教材になるのか
iOSにはGuideline 2.5.2という審査基準があります。アプリはそれ自体で完結していなければならず、審査後に機能を追加・変更するコードをダウンロードして実行してはいけない、という規定です。
この規定の唯一の抜け穴が、OS標準のWebKitやJavaScriptCoreといったエンジン上で動くスクリプト実行でした。Dart(Flutterが使うプログラミング言語)をAOT(事前コンパイル)した実行ファイルはネイティブコードなので、この抜け穴に該当しません。
つまりWeChatやAlipayは、FlutterやReact Nativeのようなネイティブコンパイル型フレームワークを使えず、独自のJavaScriptベースの実行エンジンを自前で構築するしかありませんでした。この制約こそが、後述する二層アーキテクチャを生んだ理由です。
技術的な仕組み: 二層分離とプロセス隔離
WeChatのミニプログラム基盤(MINAフレームワーク)は、レンダリング層とロジック層を明確に分離しています。
レンダリング層はWebView(HTMLを表示するコンポーネント)内で動き、WXML/WXSSというマークアップで画面を描画します。一方、開発者が書くビジネスロジックはJSCoreという純粋なJavaScriptエンジンの別スレッドで動き、DOMやwindowオブジェクトに直接アクセスできません。
この分離には2つの狙いがあります。ひとつはセキュリティで、サードパーティのコードがUIを直接操作できないようにする隔離です。もうひとつは性能で、ビジネスロジックの処理待ちでUIがフリーズしない設計です。
AlipayのAPPXフレームワークはさらに踏み込み、レンダリング用スクリプト(index.js)とロジック用スクリプト(index.worker.js)を完全に別のV8(Googleが開発するJavaScriptエンジン)コンテキストで動かします。WebViewとV8エンジンを並列に初期化することで起動を速くし、フレームワーク自体のJSと開発者のJSも別コンテキストに分けています。
Douyin(TikTokの中国版)のミニプログラム基盤も同じ思想を踏襲しており、JS CoreとWebViewの二層構造、TTML/TTSSという独自マークアップを採用しています。運営元のByteDanceは、さらに高度な二スレッド構成を持つLynxというフレームワークもオープンソース化しています。
SREの視点で見る比較: マイクロサービスの障害封じ込めと同じ発想
この二層分離は、マイクロサービスアーキテクチャにおけるプロセス隔離やサーキットブレーカーの発想と重なります。
マイクロサービスでは、1つのサービスの障害が他のサービスに連鎖しないよう、プロセスやコンテナで境界を切ります。ミニプログラム基盤では、1つのサードパーティコードの不具合や無限ループが、アプリ全体やほかのミニプログラムに波及しないよう、レンダリングスレッドとロジックスレッドを分離しています。
いわば「テナントごとのブラストラディウス(障害が及ぶ範囲)を最小化する設計」です。Kubernetesでネームスペースやリソースクォータを使ってテナントを隔離するのと、狙いは同じです。
違いは実行単位の粒度です。マイクロサービスはプロセスやコンテナ単位で隔離しますが、ミニプログラム基盤はアプリ内のスレッドやJSエンジンのコンテキスト単位で隔離します。モバイル端末という限られたリソースの中で、数万のミニプログラムを都度ロード・アンロードする前提だからこそ、この粒度が選ばれています。
読者への影響: 自社のプラグイン・拡張基盤は同じリスクを抱えていないか
サードパーティのコードを実行環境に組み込むプロダクトを運営しているなら、この設計思想は他人事ではありません。ブラウザ拡張、Slackアプリ、CMSのプラグイン機構、SaaSのWebhook実行基盤なども構造は同じです。
以下の観点で、自社の拡張実行基盤を点検してみる価値があります。
- サードパーティコードの実行スレッド・プロセスは、コア機能と分離されているか
- 1つの拡張機能の異常終了やタイムアウトが、他のテナントの可用性に影響しないか
- 拡張機能ごとのCPU・メモリ上限(クォータ)を設定し、監視ダッシュボードで可視化しているか
- 拡張機能の更新配布パイプラインに、カナリアリリースやロールバックの仕組みがあるか
特に3点目のクォータ設定は、オブザーバビリティ(システム内部の状態を外部から観測できる設計)の基本です。実行時間・メモリ使用量・エラー率をテナント単位でメトリクス化しておかないと、障害発生時にどのミニプログラムが原因か切り分けられません。
Prometheusのようなメトリクス基盤を使っているなら、ラベルにテナントID(この文脈ではミニプログラムのIDに相当)を付与し、テナント単位のSLO(サービスレベル目標、可用性や応答速度の達成目標)を設定できるか確認してみてください。全体のSLOだけでは、特定テナントの劣化がマスクされてしまいます。
配布パイプラインについても、iOSのストア審査を経ずに更新できる仕組みは、裏を返せば「審査という安全弁がない」ということです。社内のCI/CDでカナリアリリースやオートロールバックを組んでいなければ、不具合のあるミニプログラムがそのまま全ユーザーに配信されるリスクがあります。IaC(Infrastructure as Code、インフラをコードで管理する手法)でこの配布基盤を構築しているなら、Terraformのモジュール単位でテナントごとのリソース定義を分離し、変更差分をレビューできる構成にしておくと安全です。
まとめ
WeChatやAlipayのミニプログラム基盤は、iOSの審査ルールという制約から生まれた、独自JSエンジンによる二層分離アーキテクチャです。
レンダリング層とロジック層を別スレッド・別コンテキストで動かす設計は、マイクロサービスにおけるプロセス隔離やブラストラディウスの最小化と本質的に同じ発想です。
自社でプラグインや拡張機能を実行する基盤を持っているなら、まずはテナント単位のメトリクスとSLOが設定されているか、拡張機能ごとのリソースクォータが効いているかを確認してみてください。
そのうえで、配布パイプラインにカナリアリリースやロールバックの仕組みがあるかどうかも、次に見直すポイントになります。