GKE や Open Match、Agones を使用したマルチプレーヤー型 Google Doodle ゲームの構築
Google Cloud Japan Team
Google は、Kubernetes、マネージド ゲームサービス、オープンソース ソフトウェアを使用して、検索ホームページ用に Google 独自のゲームを制作しています。
※この投稿は米国時間 2023 年 4 月 4 日に、Google Cloud blog に投稿されたものの抄訳です。
はじめに
Google 検索ホームページを訪問した人は誰でも、検索バーの上に表示されるロゴが、独創的で楽しくて目を引くようなものに変わることがあると気づいたはずです。それは「Google Doodle」と呼ばれています。
近年は、対話型だけでなく、マルチプレーヤー型も見かけるようになりました。Google ホームページを閲覧した人は誰でも、同じページを閲覧している他の 1 人以上のユーザーとゲームを楽しむことができます。しかし、こんな疑問が湧いてきます。「世界中にいる Google ユーザーに楽しまれているこれらのゲームを動かすために、どんな技術が背後では使われているのだろう?」「同様のエクスペリエンスを作りたいと考えている読者も、これらの技術を使用できるのだろうか?」
今回は Jacob Howcroft にインタビューしたいと思います。Jacob は、7 年前から Google Doodle チームでソフトウェア エンジニアを務めており、Google Doodle チームがマルチプレーヤー型 Google Doodle エクスペリエンスに挑戦する取り組みをリードしてきました。
Google Doodle とは何ですか?
Jacob: Google「Doodle」は、地域の文化や世界の文化に影響を与えたりそれらを形成したりした、さまざまな祝日、個人、場所、モノなどをお祝いするために変更された Google のロゴです。当初の Doodle は静止画イラストでしたが、今ではゲームやその他の対話型エクスペリエンスも制作するようになりました。
マルチプレーヤー型 Google Doodle とは何ですか?
Jacob: プレーヤー同士をつなげる対話型エクスペリエンスです。これによって、友人やオンライン上の無作為のユーザーとつながることにより、一緒にプレイすることができます。ブラウザやモバイル デバイスで google.com にアクセスして、Google ロゴが「再生」ボタン付きのものに変わっているのを見つけた場合、それをクリックするとマルチプレーヤー型ゲームに移動します。
最近リリースされたマルチプレーヤー型 Google Doodle とはどのようなものですか?また、その後どうなりましたか?
Jacob: 最初のマルチプレーヤー型 Doodle は、2018 年のハロウィンにリリースされた The Great Ghoul Duel でした。これまでで最も人気のある Doodle の一つになったので、2022 年のハロウィンに The Great Ghoul Duel 2 をリリースしました。また、2022 年 7 月に Celebrating Pétanque もリリースしました。ペタンクというフランスのスポーツを祝うマルチプレーヤー型ゲームです。
大勢の人がペタンク Doodle とハロウィン Doodle をプレイしました。特にハロウィンは、プレーヤーの多くが何ラウンドもプレイしてくれました。「The Great Ghoul Duel」は、Twitter 上と Discord 上にファン コミュニティがあり、2018 年から活動を続けています。もちろん、彼らは続編を見てとても喜んでいました。
Google ホームページ用のマルチプレーヤー型ゲームをリリースするうえでの課題は何ですか?
Jacob: 最大の課題はスケーリングに備えることです。Google 上ではユーザーによって毎日何十億件もの検索が行われています。そのようなユーザーにゲームを提供しているため、リリース直後に膨大な数のプレーヤーを処理する必要があります。おまけに、これらのゲームは数時間のうちに世界中にリリースされるため、大急ぎでスケールアップする必要があります。Doodle はサプライズですから、ベータリリースでテストすることはできません。つまり、QA、負荷テスト、内部テストプレイを非常に厳密に行う必要があるということです。
マルチプレーヤー型 Google Doodle にはどれくらいの数のプレーヤーが参加しますか?
Jacob: Doodle がホームページ上にある間に、ユーザーは何百万回もプレイします。ホームページ上で Doodle が動作した後で google.com/doodles アーカイブを見ると、毎日何千ものゲームがプレイされています。
ブラウザベースのゲームと他の種類のマルチプレーヤー型ゲームにはどんな違いがありますか?
Jacob: モバイルゲーム、コンソール ゲーム、PC ゲームは通常 UDP を使用するのに対して、JavaScript や TypeScript で作成されたブラウザベースのゲームでは WebSocket プロトコルを使用することが主な違いです。WebSocket プロトコルを使用すると、GameServer ごとに HTTPS / Web Socket Secure(WSS)用の証明書が必要になるのですが、これが課題になります。ゲームは HTTPS ページ上で動作します。ということは、すべての WebSocket 接続もセキュリティで保護されている必要があるのです。これを行うには、従来の UDP マルチプレーヤー型ゲームの接続のときのように IP やポートに直接接続するのではなく、ゲームサーバー用の DNS アドレスも必要です。
これらのマルチプレーヤー型 Doodle ゲームをどのように作ったのかを教えてもらえますか?
さまざまな Google Cloud プロダクトを使用したり、無料で入手可能なゲーム インフラストラクチャや分散システムのオープンソース プロジェクトを利用したりしています。ゲーム自体には、何年にもわたって Doodle 用に作成してきた数多くの TypeScript ツールやライブラリだけでなく、PIXI などの一部のオープンソース エンジンも使用しています。
どんな Google Cloud プロダクトを使用しましたか?
Jacob: Google Kubernetes Engine(GKE)がほとんどすべてを実行しています。
マッチメーカーとゲームサーバー クラスタのグローバルなロード バランシングを処理するために外部 HTTP(S) 負荷分散の Ingress を利用しています。
Redis Memorystore は、ゲームサーバーのルーティングを構成し、マッチメーカーのマッチメイキング チケットを保存するために使用されています。
Google マネージド証明書を使用することで、マッチメーカーとゲームサーバーの SSL 証明書の自動作成とローテーションが可能になっています。これは手動での証明書の処理をなくしてくれたため、非常に助かりました。
Cloud Logging、Cloud Monitoring、ログベースの指標を使用することによって、ゲームサーバーのステータスやマッチメイキング チケットの数などの指標を調査して本番環境を監視するダッシュボード一式が手に入りました。
Cloud Run と Cloud Endpoints がマルチクラスタ ゲームサーバー選択システムを動かしています。このシステムは、GKE クラスタのセットの中からプレーヤーに適切なゲームサーバー インスタンスを選択し、それらのゲームサーバーを「割り当て済み」としてマークします。基本的に、これらのゲームサーバー上でプレーヤーがプレイするため、中断は許されません。詳細については後述しますが、マルチクラスタ アロケータ システムがスケーリングの大部分を支援してくれました。
バックエンドにオープンソース プロジェクトを使用することにした理由は何ですか?
Jacob: 私たちは主にフロントエンド チームで、ウェブ用のコンテンツやフロントエンド サーバーを構築しています。Google Cloud や Kubernetes などのより深いバックエンド テクノロジーのこととなると、専門外です。ただし、入手可能なオープンソース システムがこのスキルギャップを埋めてくれるため、専門領域外の作業も可能になり、より複雑なエクスペリエンスを構築することができます。
どのオープンソース プロジェクトを利用しましたか?
Jacob: 2018 年の最初のマルチプレーヤー型 Doodle ではゲームのプレーヤーを仲介するために Open Match を利用しました。これが非常にうまく機能したため、新しいマルチプレーヤー型ゲームスタックでもその使用が継続されました。
Agones は、GKE クラスタ上にインストールされ、ゲームサーバーをホストしてスケールし、プレーヤーのプレイ中に中断が起きないようにしています。Agones は、GKE クラスタ内で実行中のゲームサーバー プロセスのタイプと数を宣言するのに使用できる、GameServer と Fleet のカスタム リソース定義を提供してくれます。また先ほど述べたように、ゲームサーバーを選択して割り当て、それらのプレーヤー接続の準備が整ったことについてマークできるのも、Agones のおかげです。私たちは、世界中で複数の GKE クラスタと Agones クラスタを実行することによって、ほぼすべての潜在的プレーヤー(Google.com のユーザー全員)のためのゲームサーバーを確保しています。
また、これらがすばらしいソリューションであるだけでなく、Google Cloud が Open Match と Agones の両方を設立し、それぞれのコミュニティの支援を得てこれらのプロジェクトを維持し続けているということから、自分たちは恵まれた環境にいて、助けを求めることができる相手が身近にいることがわかりましたし、実際に助けてもらいました。
また、マッチメイクされたプレーヤーによる、GKE クラスタにホストされた特定の Agones GameServer Pod への WebSocket 接続を調整するために Traefik Proxy を使用して、プレーヤーたちが一緒にゲームをプレイできるようにしています。
開発ツールと運用ツールについて言えば、インフラストラクチャのプロビジョニングに Terraform を使用し、すべての GKE クラスタへのオープンソース ツールとカスタム コンポーネントのデプロイの調整に Helm と Helmfile を使用しています。
最終的に作成したカスタムコードの量はどれくらいですか?
Jacob: 皆さんが思うほど多くはありません。
Open Match では、マッチ関数用のいくつかのカスタム コンポーネントを作成し、Agones などの外部システムと統合する必要がありますが、gRPC の相互運用性のおかげで、Open Match が Go で作成されていても、そのすべてを Node.js で作成することができました。WebSocket ベースのゲームサーバーも Node.js で作成しました。これには、クライアントとサーバー間でコードを共有し、開発時間を短縮できるというメリットがあります。
Traefik ルートを構成するためのカスタム Kubernetes コントローラも作成しました。Traefik は、特定のキー形式に作成することによって、Redis からの構成を検出するようにセットアップできます。作成されたコントローラは、Agones GameServer イベントを監視し、イベントが発生したときにそのイベントの名前、Pod IP、ポートを Memorystore に書き込みました。Traefik はそのルーティングを動的に更新するので、wss://<project subdomain>.cloud.doodles.goog/<ゲームサーバー名> を使用して GameServer にアクセスできます。
マッチメイキングと、プレーヤーのグループに対するゲームサーバーの割り当てに関するアーキテクチャを順を追って説明してもらえますか?
Jacob: まず、プレーヤーがグローバル マルチクラスタ Ingress HTTP(S) ロードバランサに接続します。そこでプレーヤーは、世界中に分散した GKE クラスタにホストされているマッチメーカー フロントエンドのうち、最も距離が近いものにルーティングされます。通常は、このようなクラスタを 1 つの大陸に 1 つか 2 つ置いています。
マッチメーカー フロントエンドを通して、チケットが Open Match 内で作成され、他のプレーヤーとマッチされます。
Agones Fleet が新しい GameServer を開始すると、カスタム Agones-Traefik コントローラがその Pod IP を Memorystore Redis インスタンスに書き込みます。Traefik Proxy がそのインスタンスから読み取って、先ほど述べたとおり、クラスタ内でルートをセットアップします。こうして、プレーヤーは “wss://cluster.game.cloud.doodles.goog/foo” などの URL で GameServer “foo” に接続します。
Node.js で作成された Open Match Director コンポーネントは、マッチを受け取り、Cloud Run を活用した Agones マルチクラスタ割り当てサービスを使用して GameServer を割り当てます。各マッチメーカー クラスタは 1 つ以上の GameServer クラスタから割り当てられます。
私たちは負荷テストの実施中に、最適な GameServer Fleet(GameServer のグループ分け)サイズは主に Kubernetes のコントロール プレーンによって制限されるということに気づきました。そのため、処理可能なサイズは、配分率、ゲームサーバー アクティビティの量、およびコントロール プレーン サイズによって決まります。通常、Google のゲーム セッションは非常に短いため、そのターンオーバーごとに Kubernetes API に大きな負荷がかかります。
もともとは、クラスタごとに Open Match マッチメーカーのインスタンスと Agones GameServer Fleet が存在していました。ところが、GameServer クラスタの最適なサイズが約 6,000 台の Agones GameServer であることがわかったため、マッチメーカー クラスタと GameServer クラスタ用の 1 対多のアーキテクチャによってスケールアップが可能になりました。これは、クラスタの数が多いほど、全体のスループットが上がるためです。
GameServer の割り当てに成功したら、マッチメーカーが GameServer の URL を使用してプレーヤーに返信します。プレーヤーは別の Ingress を介してそれに接続し、Traefik Proxy がその要求を適切な GameServer に転送します。
ゲーム セッションの終了時点でゲームサーバーをシャットダウンせずに、Agones SDK を介してゲームサーバーをリセットして「準備完了」状態に戻すことによって、新しいゲームに割り当てることができます。これにより、GameServer のターンオーバーが減少し、Kubernetes API と Traefik Proxy の負荷が軽減されます。
通常は、グローバル プレーヤー ベースをサポートするのに、1 つの Open Match インスタンスが推奨されています。リージョンごとにインスタンスを用意した理由は何ですか?
Jacob: Open Match がグローバル プレーヤー ベースを処理できることは知っていましたが、リージョンごとに別々の Open Match インスタンスを使用しました。これは、トラフィックを最も近いマッチメーカーに転送するのにロード バランシングを利用できるようにするためです。本当に大きなプレーヤーのプールがあったために、プレーヤー ベースのシャーディングを気にする必要がありませんでした。マッチメーカーごとに十分な量が確保されていたのです。
リリース前にベータ版をテストするという選択肢はなかったので、世界中で Open Match のインスタンスを増やすことで、リリース日のリスクを軽減し、必要に応じて Open Match インスタンスとその背後の Agones クラスタを水平方向にスケールすることができました。
WebSocket ベースの通信について詳しく説明してもらえますか?なかなか扱いづらそうですよね。
Jacob: WebSocket ゲームには、Web Socket Secure(WSS)が必要です。これは、すべての GameServer を URL でアドレス指定し、すべての GameServer に SSL 証明書を設定する必要があることを意味します。何千台もの GameServer の動的 Fleet に対してこれを実現するには、これまでにない解決策が必要でした。
Halloween 2018 Doodle では、サブドメインごとに GameServer にルーティングして何千もの DNS エントリを静的に生成していました。ただしこのアプローチでは、Agones と GKE の動的でスケーラブルな機能のすべてを利用することはできませんでした。
そのため、新しいセットアップでは、Traefik Proxy を導入しました。これを選択した理由は、さまざまなプロバイダを使用してルートを動的に構成できるからです。そのうちの一つが Redis です。そして、GameServer の監視や、Redis への構成の書き込みを行う Kubernetes コントローラを作成しました。プロキシがプレーヤーの ping にどう影響するかが懸念されましたが、負荷テストを重ねた結果、目立った影響がないことがわかりました。このセットアップによって Fleet を動的にスケールできるようになりました。新しい GameServer のスピンアップができ次第、DNS エントリを操作しなくても、すぐに接続で使用できるようにもなりました。
また、クライアント上の Google Closure WebSockets とサーバー上の ws を使用して、WebSocket ライブラリを作成しました。ゲームごとに、プロトコル バッファ(クライアントからサーバーへのメッセージとサーバーからクライアントへのメッセージ)を使用して API を定義します。通常はこのメッセージに、ゲーム内のさまざまなアクションごとに異なるサブ メッセージ タイプのための oneof フィールドが含まれています。ライブラリは TypeScript ジェネリスクを使用して、ゲームコードが必ずこの API を通してメッセージを送受信するようにします。
ゲームを間違いなく面白いものにすることはすべてのゲームにとって欠かせないことですが、これをどのようにテストしましたか?
Jacob: 面白いかどうかをテストする方法はいくつかあります。1 つ目の方法は、週 1 回のチーム テストプレイです。Doodle チーム全員が一堂に会して最新のデモをプレイし、フィードバックやアイデアを共有します。2 つ目の方法は、内部ユーザーテスト、いわゆる、「カフェテスト」です。数人の Google 社員をランダムに選んだり、ミニキッチンにスタンドを設置したりして、リアルタイムでフィードバックを収集します。Google 社員の多くがとても率直に意見をくれるため、この方法で良好な結果が得られます。最後に、バックグラウンドやゲームに対する馴染み方が異なる外部ユーザーによるロケーションテストをセットアップします。このロケーションテストによって、最終製品に向けて非常に貴重で多様な視点が得られます。
Google のフロントページでは、その種のトラフィックの負荷テストをどのように行いますか?
Jacob: 初期段階で最初にやることは、ヘッドレス ゲーム クライアントを作成することです。これは UI を操作することなく、スクリプトによってゲームをプレイできることを意味します。次に、ユーザーをシミュレートする、ノードベースの負荷テスト用クライアントを作成します。
Kubernetes Job を使用して、何千ものこれらの偽ユーザーを GKE にデプロイし、クラスタに指定します。少人数のユーザーから始め、クラスタがそれを適切に処理したら人数を増やします。なんらかの問題が発生するまでこのプロセスを繰り返します。
その後、問題をデバッグして修正し、もう一度やり直します。このプロセスを通して、Agones クラスタに最適な Fleet サイズを見つけることができました。それが、約 6,000 台の GameServer でした。この数値は Kubernetes プロバイダ、マッチのサイズ、ゲームの持続時間、その他の要因によって異なります。すべてのケースに当てはまるわけではないのでご注意ください。
負荷テストのトラフィック量はどのように見積もりましたか?
Jacob: 過去のゲームの指標を調査し、それを使用してこのゲームのトラフィックを推定しました。ただし、さまざまな要因で大きく変化する可能性があるため、目標をできるだけ高くする必要があります。それでも、ピーク時のトラフィックが非常に高かったため、リリース中に追加のクラスタをデプロイすることになりました。
本番環境にデプロイする準備が整ったのはいつでしたか?それはどのように実現しましたか?
負荷テストおよび過去の Doodle パフォーマンスの分析を通して、おおよそのスケールを把握してから、Fleet 全体を Terraform と Helmfile で定義し、デプロイしました。Doodle は世界各地の現地時間に従ってロールアウトしていくため、トラフィックは約 24 時間で急速に増えると予想し、計画を立てました。
すべてのリソースを自動スケールしたのですか、それとも、静的なインフラストラクチャを維持しただけですか?
Jacob: 運用保守を簡単にするため、スケールダウンを避けたい場合は別にして、GKE ノードプール上でノードの自動スケーリングを有効にしました。WebSocket の接続時間が長かったため、ノードのターンオーバー時に移動する Pod の量を減らす必要がありました。Kubernetes デプロイに自動スケーリングを使用することはあまりありませんでした。短時間に大量のトラフィックを受信することがわかっていたため、事前に高レベルまでスケールしておいて、短いリリース期間に手動で調整することにしました。全体のゲームリリースに対して 100% の容量で稼働したため、Agones Fleet 自動スケーリングも使用しませんでしたが、kubectl scale fleet halloween --replicas=N を実行することによって、Agones Fleet のサイズを簡単に変更できるという機能は活用しました。ノードの自動スケーリングを使用すると、クラスタが自動的に Fleet に合わせてサイズを変更します。
用意した GKE クラスタは何個で、それらをどのように管理しましたか?
Jacob: 最新のゲーム(Halloween 2022)では、30 個のクラスタを用意しました。平均で、マッチメーカー クラスタあたり 3~5 個の GameServer クラスタを用意しました。これらのクラスタのすべてを管理するには、Terraform と Helm を使用して、それらのループ コンストラクトを利用する必要がありました。これにより、何百にも及ぶはずだった構成ファイルの行を、数十行にまで減らすことができました。
Cloud Monitoring を使用したということでしたが、積極的にモニタリングしたものは何ですか?
Jacob: システム全体のさまざまな指標をモニタリングしましたが、その中でも特に重要なものを紹介します。
状態ごとの GameServer の数を示す Agones Fleet ステータス用のダッシュボードを用意しました。特に「準備完了」と「割り当て済み」の状態はチェックしていました。これにより、Fleet が最大容量に達する時期を知ることができました。リリース期間が約 48 時間しかなかったため、アラートをセットアップせずに、ずっと待機していることにしました。ただし、google.com/doodles アーカイブ上で動くゲームについては、エラー率に基づいたアラートをセットアップしました。
ゲームサーバーとマッチメーカーの接続もモニタリングしました。特にマッチメーカーに関してモニタリングしたのは、マッチの成功率です。マッチの成功率はユーザー エクスペリエンスにおける第一の指標だったので、モニタリングは欠かせませんでした。マッチの失敗は、ユーザーが一番がっかりする出来事です。もう一つの重要な指標は、マッチに参加するプレーヤーの数が十分になるまでユーザーが待機している時間です。幸い、トラフィックが非常に高かったため、この点を心配する必要はありませんでした。
独自のカスタム「ゲームオーバー」イベントを追跡しました。終了したゲームの累計の調査はとてもワクワクするものでした。この調査を行うにあたり、マッチメーカー接続数、作成した Open Match コンポーネントでの成功数と失敗数、ゲームサーバー接続数などの、カスタムのログベースの指標を合計するグラフを作成しました。
最後に、Agones と Open Match のすべての組み込み指標を利用して、プラットフォーム全体の健全性を概観しました。
やり直せるとしたら、別の方法で取り組みたい点はありますか?
Jacob: 教訓の一つは、想定したトラフィックを処理するために割り当てを倍以上用意する必要があったことです。システムの設計は、リリース中に新しいクラスタをオンラインにすることがとても簡単にできるようになっています。実際に使ってみて、厳しい時間の制限がある中でのスケールアップで大変重宝しました。ただし、一部のリージョンで割り当てが十分ではなかったため、Blue/Green デプロイ(古いバージョンから切り替える前に新しいバージョンのデプロイ全体を実行すること)を完了できませんでした。つまり、リリース中にインフラストラクチャを変更する場合はダウンタイムを設ける必要がありました。これは、割り当てに十分な空きを作ることによって回避できます。したがって、何かをリリースする際は、Google Cloud 顧客アカウント担当者と連携して、割り当て量の確認も含めたフル リリース レビューをリリース日より十分前もって行うことをおすすめします。
さらに、特定のリージョンで Agones GameServer を増やすことができない場合に、マッチメイキングおよびゲームサーバー オーケストレーション システム全体を別のリージョンに適切にスピルオーバーさせることも簡単ではありませんでした。つまり、リリース中は、予想されるピーク時の負荷を考慮し、各 Open Match インスタンスに十分な数の GameServer のバックアップを用意する必要があったのです。リリース中のカスケード障害は回避しましたが、その代わり、最もビジーなクラスタを手動でスケールアップする必要がありました。
順を追って説明すると、ユーザーがマッチメイキング フロントエンドに接続して、マッチへの参加を要求します。ユーザーは十分な数のプレーヤーが揃うまで待機します。それから割り当て要求が Agones に送信されます。ただし、すべての Agones クラスタがいっぱいの場合は、エラーが返されます。この時点で、すでに、何千人ものプレーヤーがマッチへの参加を試みているかもしれません。しかしそれらの行き場がないため、すべてのマッチメイキング要求が失敗します(すでに 100% の容量で稼働しているのです)。さらにそこへ新しいユーザーもマッチメイキング システムに接続してくると、そのすべての要求も失敗し、マッチメイキング クラスタのカスケード障害につながります。Fleet が割り当て量の一定のしきい値を超えたということがわかったら、早めにマッチメイキング システムによる新しいマッチメイキング要求の受け付けが停止するのが理想です。
今後、このような状況により適切に対応するためには、各 Open Match インスタンスの背後にある Fleet に関する最新データを把握するためのフロントエンドが必要です。これにより、クライアントが過負荷になる前に別のインスタンスを使用するように指示することができます。Fleet が上限に達したことをアロケータ サービスから報告された時点では、すでに問題が発生している可能性があります。
マルチ クラスタ ゲートウェイへの切り替えも検討しています。これにより、レート制限を構成できるようになります。そうすれば、クラスタの最大容量に基づいて最大 QPS を計算し、それに応じてレート制限を設定して、GCP にスピルオーバーを処理させることができます。このソリューションはやや動的さに欠けますが、実装は非常にシンプルなものになります。
まとめ
Google Doodle チームがマルチプレーヤー型 Google Doodle ゲームの開発、ホスティング、スケーリング、オーケストレーションを行った方法について詳しく説明してくれた Jacob に感謝します。
Google Doodle チームは Google Cloud(特に、Google Kubernetes Engine)とオープンソース Google Cloud Gaming のソリューションを組み合わせることにより、歴代のマルチプレーヤー型 Doodle を Google 検索と同じ規模で運用することに成功してきました。これは本当にすばらしいことです。
また、Google Cloud のマルチプレーヤー型ゲーム向けのオープンソース ソリューションを利用しながら、比較的わずかなカスタムコードでこれほどの包括的なマルチプレーヤー型ゲーム プラットフォームを構築できたことは、まさに驚くべきことです。
上述のソリューションのいずれかにご興味をお持ちの場合は、以下にアクセスして詳細をご覧ください。
これまで Google が何十年にもわたって世界中でリリースしてきた 5,000 を超える Google Doodle を見てみたくありませんか?ぜひ google.com/doodles にアクセスしてお楽しみください。
- Google Doodle、シニア ソフトウェア エンジニア Jacob Howcroft
- Google Cloud、デベロッパー アドボケイト Mark Mandel