キーポイント
- Monzoは金融サービス業界においてクラウド技術とKubernetesをいちはやく採用し技術的な課題やサービス障害と戦ってきた。Kubernetesとマイクロサービスへの早期投資は後に非常に大きなリターンとなった。
- 組織が最新の革新的なテクノロジーに必要な投資やサポートを提供する意思や能力がないのであれば、わかりやすい技術を採用して成功率を高める方がよいだろう。
- インフラとアプリの間の橋渡しに常に注意を払うこと。よく定義された使いやすいインターフェースはインフラの複雑さや負担を隠蔽してエンジニアのプロセスを改善するのである。
- 可能な限り統一性を持たせて誰もが貢献できるスタックを構築することで高レベルな抽象化とイテレーションの短縮につなげる。
- システムは孤立して構築・運用されるものではない。組織内の行動は技術的アーキテクチャの成功に大きな役割を果たし、インシデントや障害は分析と内省のためのユニークな機会を提供する。
この記事では英国に本拠を置くオンライン銀行Monzoのアーキテクチャを構築する際に学んだ実践的な教訓を共有する。成功した取り組みと失敗した取り組みの両方を掘り下げる。
また、システムのスケールアップや適切なツールの開発など、エンジニアが顧客の求める機能を提供することに集中できるような複雑な仕組みについて説明したい。
※ 訳注: 本記事はQcon in Londonでの同題講演を書きおこした記事である。
Monzoが考える目標
Monzoの目的は金融サービスへのアクセスを民主化することである。700万人の顧客基盤を持つ私たちは、プロセスの合理化の重要性を理解しており、そのために決済システムの統合を推進している。これらの一部はまだFTPファイル転送に依存しており、それぞれに独自のルール、基準、標準規格を持っている状態である。私たちはシステムを継続的に改良し、根底にある複雑さを感じさせたり製品提供を制限することなく、顧客に新機能を展開できるようにした。2022年9月には、英国での直接の引き落としとクレジット決済を可能にするBacsスキームの直接参加者となった。2017年からMonzoはBacsに統合されていたが、私たちの代わりに処理していたパートナーを介した参加であった。
昨年、私たちはSWIFTネットワークを直接利用して統合を実現した。その結果、顧客に対して何の混乱もなく展開できたのである。このシームレスな統合の例は、本記事全体でもふれることとなる。
Monzoの技術スタック
すべてのインフラとサービスをAWS上に構築することにしたのは重要な決定だった。これは当時の金融サービス業界で前例のないことだった。金融庁がクラウドコンピューティングとアウトソーシングに関する初期段階のガイダンスを発行している頃、私たちはクラウドにデプロイする企業になっていた。決済スキームの統合のためにデータセンターを運用しているが、私たちのコアプラットフォームは、メッセージインターフェイスのための最小限の計算でAWS上に構築したサービス上で動作している。
AWSを利用することで銀行を運営するための必要なインフラを手に入れたが、あわせていわゆるモダンなソフトウェアも必要だった。既成のソリューションのほとんどはオンプレミスのシステムだった。Monzoはレガシーなテクノロジーに縛られることなくクラウド上で動作するように設計されたモダンな銀行を目指していた。
マイクロサービスの採用
早期にマイクロサービスを用いるという決定が下された。バンキングテクノロジーの信頼性向上のためには、頼りになる勘定システムが必要だった。まず最初に銀行の台帳、サインアップ、アカウント、認証、認可を処理するサービスが作られた。これらのサービスはコンテキストに縛られて自身のデータを管理する。サービス間のデータを整理するために静的コード生成を使用することで、エンティティ間や振る舞いにおける堅牢なAPIとセマンティックなコントラクトを確立することを容易にした。
このアプローチを用いると異なるデータベース間におけるエンティティの分離も容易になる。例えばトランザクションモデルには一意のアカウント・エンティティが存在するが、その他の情報はすべてアカウントサービス内に存在する。アカウントサービスはリモートプロシージャコール(RPC)を使用して呼び出され、詳細なアカウント情報を取得する。
サービスメッシュの登場前であるMonzoの初期の頃はRabbitMQを介したRPCが採用された。これはリクエストキューとリプライキューを伴うメッセージの負荷分散と配信可用性を担う技術である。
図1:Monzoの初期に登場したRabbit MQ
現在のMonzoはHTTPリクエストを採用している。顧客がカードで支払いをすると複数のサービスがリアルタイムに動作して、支払いを受け入れるべきか拒否すべきかを決定する。これらのサービスは決済チーム、金融犯罪領域チーム、台帳チームなど、さまざまなチームから提供されているのである。
図2:お客様がカードで商品代金を支払う様子
Monzoは決済スキームごとのアカウントや台帳サービスの抽象化は避けたいと考えている。また多くのサービスと抽象化は実現不可能であり、決済サービスの統合には各サービスは独立して拡張できる状態が必要と考えている。
コアデータベースとしてのCassandra
初期の段階で主要なデータベースアーキテクチャとしてCassandraの採用を決定した。Cassandraでは各サービスが独自のキースペースで動作し、キースペース間が完全に隔離されている場合は他のサービスから直接データを読み取ることはできないのだ。
図3: MonzoにおけるCassandra
CassandraはオープンソースのNoSQLデータベースで、パーティショニングとレプリケーションに基づいてデータを複数のノードに分散させ、クラスタの動的な拡大と縮小を可能にする。タイムスタンプとクォーラムベースの読み取りを用いることで強い一貫性があり、最終書き込み優先のセマンティックスによって結果的に一貫性のあるシステムとなる。
Monzoはアカウントのキースペースにレプリケーションファクター係数として「3」を設定し、データを保持している3つのノードにアクセスし、ノードの過半数がデータに同意したときに戻ってくるローカルクォーラムのクエリを定義した。このアプローチにより問題が少なく一貫性が高いよりパワフルでスケーラブルなデータベースになった。
ノード間でデータを均等に分散してホットパーティションを防ぐためには、パーティショニングキーが大切である。しかし、高速アクセスとテーブル間のデータ重複回避のバランスを取りながら、適切なパーティショニングキーを見つけることは難しいものだ。Cassandraはこのタスクに適しており効率的かつ安価なデータ書き込みを可能にするのである。
Cassandraで全データセットを反復処理するのはコストがかかり、トランザクションも不足する。これらの制限を回避するために、エンジニアはデータを異なる形でモデル化し、正規表とインデックス表というパターンを採用する必要がある。データはこれらの表に逆順で書き込まれ、まずインデックス表に、次に正規表に書き込まれ、書き込みが完全に完了していることを確認する。
例えばホテルにポイントを追加する場合、データはまずpois_by_hotel
テーブルに書き込まれ、次にhotels_by_poi
テーブルに、最後に正規表であるhotels
テーブルに書き込まれる、といった具合である。
図4:ホテルの例、読みにくいポイントオブインタレストテーブルを使用した場合
Kubernetesへの移行
スケーラビリティは有益だが、システムに複雑さをもたらすため、私たちは信頼性の高いデータの書き方を学ぶことになる。これを軽減するために私たちはエンジニアに抽象化されたコードと自動生成コードを提供している。可用性の高いサービスとデータストレージを確保するために、2016年からKubernetesを利用している。当時のKubernetesはまだ初期の段階だったが、アプリケーション開発と運用のためのオープンソースのオーケストレーターとしての可能性が見えていた。当時、マネージドサービスや包括的なドキュメントがなかったため、Kubernetesの操作に習熟しなければならなかったが、ここで得たKubernetesの専門知識に後の私たちは大いに助けられたのである。
2016年半ばには、HTTPに切り替え、サービスの発見とルーティングにLinkerdを採用した。これにより低速なサービスインスタンスや、信頼性の低いサービスインスタンスが発生した場合の負荷分散とレジリエンシー特性が改善された。
しかしながら2017年に経験した障害のように、Kubernetesとetcdの間の相互作用によってサービスディスカバリーが失敗し、正常なエンドポイントが残らないという問題があった。これは新興の技術や成熟した技術で発生する歯がゆい問題の典型例といえる。Kubernetesを大規模に運用しているチームにとって価値あるk8s.afには、同様の問題の話が数多く掲載されている。私たちはこういった経験をKubernetesを避ける理由とするのではなく、学習の機会として捉えるべきと考えている。
当初は小規模なチーム向けの技術的な選択だったが、後に300人のエンジニア、2500のマイクロサービス、そして毎日数百のデプロイメントにスケールアップしている。それを管理するためにサービスやデータの境界を分けプラットフォームチームがコアな抽象化に埋め込まれたインフラとベストプラクティスを提供し、エンジニアはビジネスロジックに集中できるようにしている。
図5:共有コアライブラリレイヤー
データマーシャリング、HTTPサーバー、メトリクスに統一されたテンプレートと共有ライブラリを使用し、デフォルトでロギングとトレースを提供する。
オブザーバビリティスタック
Monzoは、Prometheus、Grafana、OpenTelemetry、Elasticsearchなど、オブザーバビリティスタックに様々なオープンソースツールを使用している。私たちは、サービスやインフラからテレメトリーデータを収集することに多大な投資をしており、2500万以上のメトリクスサンプルと数十万Spanを常にスクレイピングしている。新しいサービスがオンラインになるとすぐに何千ものメトリクスが生成され、エンジニアはテンプレート化されたダッシュボードでそれを見ることができる。これらのダッシュボードは自動化されたアラートにも反映され適切なチームにルーティングされる。
例えば私たちはテレメトリーデータを使用して、新しい顧客向けの一機能である「Get Paid Early」のパフォーマンスを最適化している。この新しい機能によって負荷が急増したときサービスの依存関係がホットパスの一部となり、負荷に対応するためのプロビジョニングが行われないという問題が発生した。この情報は絶えず変化するため、静的にエンコードできず、オートスケーリングも信頼できなかった。その代わりにPrometheusとトレースデータを使ってホットパスに関与するサービスを動的に分析し、適切にスケーリングした。テレメトリーデータを利用することで、ヒューマンエラー率を減らし、機能を自己完結できるようにしたのである。
プラットフォームインフラを抽象化する
私たちはプラットフォームインフラを抽象化することで、エンジニアのやり取りを簡素化することを目指している。その理由は2つあり、1つ目はエンジニアがKubernetesを深く理解する必要がないこと、2つ目は私たちが積極的にサポートし、強く把握している明確な指針を持つ機能群を提供したいことである。
Kubernetesには膨大な機能性があるため、様々な方法による実装が可能だ。私たちの目標はアプリケーション・エンジニアリング・チームの作業負担を軽減し、プラットフォームの運用にかかる人件費を最小限に抑えられる高度な抽象化である。エンジニアはKubernetesのYAMLファイルを書く必要はないのだ。
エンジニアが変更を実施する場合は変更の正確性をチェックし、クリーンな環境でDockerイメージを構築し、すべてのKubernetesマニフェストを生成してデプロイするツールを提供している。
図6:エンジニアによる変更のデプロイ
私たちは現在、KubernetesインフラをセルフホストプラットフォームからAmazon EKSに移行する大きなプロジェクトを進めているが、この移行も私たちのデプロイパイプラインによってシームレスに行われている。
私たちのデプロイアプローチ、コード生成、サービスカタログについてもっと知りたい方は、QCon London 2022で講演を参照されたい。この講演では私たちが開発したツールや、開発者体験に対する私たちの哲学についてふれている。
分散型システムの故障モードへの対応
分散システムは故障しやすいものでありそれを認めて受け入れることが重要であると、私たちのチームは認識している。書き込み操作の場合、問題が発生し、データが正常に書き込まれたかどうかが不明な場合があるのだ。
図7: Cassandraでの失敗の処理
異なるノードからデータを読み込む際に不整合が発生することがあり、一貫性が求められるバンキング・サービスでは問題になり得る。この問題に対処するため、チームはバックグラウンドで継続的に稼働する別のサービスを使用して、矛盾しているデータの検出と解決にあたっている。このサービスでは問題にフラグを立ててさらに調査したり修正プロセスを自動化したりできる。またユーザーからのリクエスト時に検証チェックを実行できるが、この場合は遅延が発生する可能性に気づいたのである。
図8: KafkaとCoherenceサービス
Coherenceサービスはインフラと銀行業務のサービス間の通信に有益である。MonzoはKafkaクラスタとSarama-basedライブラリを使用してKafkaとやり取りをしている。これらのライブラリやSaramaのアップデートの信頼性を確保するために、Coherenceサービスはステージング環境と本番環境の両方で継続的に実行されている。Coherenceサービスは他のマイクロサービスと同様にライブラリを利用し、ライブラリやKafkaの構成に偶然の変更があった場合に発生する問題を、本番システムに影響を与える前に特定できる。
組織的側面
エンジニアが効率的にシステムを開発・運用するためには、システムやツールへの投資が必要である。「均一性」と「標準化された道筋」というコンセプトの元に一貫性とわかりやすさに心がけ、統一感のないサービス設計や、保守性の低いサービスの開発を防ぐのである。
Monzoでは新人エンジニアに参画初日からコーディングからデプロイまでのプロセスを説明するドキュメントや質問をするためのサポート体制を提供しており、これによって彼らを早期に「標準化された道筋」に乗せることに注力している。また、悪い習慣を後から変えるのは難しいため、オンボーディングプロセスは、長期的な行動やアイデア、コンセプトを確立するために定義している。Monzoはオンボーディングに継続的に投資しており、新規サービスで避けるべきパターンを明確にするため、「レガシーパターン」というセクションを設けている。
小さな変更には自動コード修正ツールを使用するが、大きな変更には新しいパターンに適合させるために人手による大幅なリファクタリングが必要になる場合があり、これを複数のサービス間で実施するには時間を要する。Monzoでは望ましくないパターンや動作を防ぐために静的解析チェックを導入してリリースの前に問題を検出している。エンジニアが自分の修正とは関係のないチェックに失敗してつまづくことがないよう、これらのチェックの適用前に、既存のコードベースがクリーンアップされていることを確認している。このアプローチはエンジニアがチェックを無視することを許容するのではなく、品質を向上するシグナルとして機能することを目指している。チェックを回避するためのハードルは高いが、正しい動作をするシステムこそが、もっとも近道となるように意図的に設けられているのだ。
インシデントへの対応
2018年4月、英国の大手銀行TSBは、顧客を新しいバンキングプラットフォームにマイグレーションする難易度の高いプロジェクトを実施した。その結果、顧客は長期にわたって資金にアクセスできなくなり、TSBは多額の罰金を受け、顧客に対する約3300万ポンド(訳注:約57億円)の補償、風評被害が発生した。この事件に関するFCA報告書では、過度に野心的な計画スケジュール、不十分なテスト、開発スピードと品質を天秤にかけるという課題など、技術的側面、組織的側面の双方から検証している。問題の原因を技術だけに求めがちだが、この報告書では、サービス停止の原因となった組織的な原因を検証することの重要性が強調されているのだ。
業務改善において過去の事件の振り返りは非常に有益だ。Monzoは2019年7月にインシデントを経験した。スケールアップ作業中にCassandraの設定エラーが発生し、クラスタへの書き込みと読み込みがすべて停止したのである。このでき事をきっかけに、その後数年続くデータベースの運用能力を高める改善施策が始まった。以降Monzoはオブザーバビリティに投資しCassandraやその他の本番システムの理解を深め業務手順書や本番練習を通じて、すべての運用事項に自信を持つようになったのである。
正しい選択をする
私は最初にMonzoが行った初期の技術選定について触れたが、この7年間私たちは多くの困難を乗り越えて、実験、構築、トラブルシューティングを行わなければならなかった。そして、そのプロセスは今も続いている。組織が複雑なシステムに不可欠な投資やサポートを提供する意思や能力がない場合は、アーキテクチャや技術選定の際に、そのことも考慮しなければならない。十分な投資をせずに最新技術やバズワードで選ぶと、失敗する可能性が高い。それならばもっとシンプルで成功確率が高い技術を選んだ方がいい。退屈なアプローチだと思うかもしれないが、最終的にはそれが一番安全で信頼性の高い選択肢になるのである。
結論
チームは常にツールを改良し、抽象度を高めている。技術的な選択肢を少数に絞り、これらのツールや抽象度を継続的に改善することで、エンジニアはインフラではなく、ビジネス課題に集中できる。システムが標準化の枠から外れないようアンテナを張ることが重要なのだ。
インフラストラクチャ・アズ・コード、オブザーバビリティ、オートメーション、Terraformなど、組織におけるインフラへの注目度は高いが、見落としがちなテーマとして、インフラとソフトウェアエンジニアとの橋渡しが挙げられる。エンジニアはすべての専門家である必要はない。コアパターンは明確に定義され、テストされ、文書化され、オーダーメイドのインターフェースの背後に抽象化して実装できる。このアプローチは、時間の節約、標準化の推進、組織のベストプラクティスを生み出すのである。
この発表ではインシデントのさまざまな例を紹介しながら内省の重要性を強調した。多くの場合、技術的な根本原因があるかもしれないが、深く掘り下げて原因となりうる組織の問題を特定することが不可欠である。残念なことに多くのポストモーテムは技術的な詳細に重点を置き、組織的な原因を無視する傾向にある。
技術アーキテクチャの成否を考えるときはぜひとも組織の行動やインセンティブが与える影響を考慮してもらいたい。システムは孤立状態で成り立つものではなく、運用するソフトウェアの安定性、スピード、セキュリティ、信頼性に答えることが成功に不可欠なのだから。