大量のPush通知をCloud Functions経由で送信する

実験の様子

こんにちは。ふぁぼ通知が好きなうなすけです。

モバイルアプリでは、Push通知によってユーザーに情報を伝えたり、行動を促したりすることが日常的に行われています。

その通知を送る対象のユーザーが数十人程度の規模なら、愚直に一通一通送信すればいいでしょうが、一気に万単位のユーザーにPush通知を送信したい場合、愚直に送信すると完了までに数時間かかることも往々にしてあります。

CASHでの大量Push通知事例

pull req

以前、ある施策のために42万件のPush通知を送る必要がありました。CASHではFirebase Cloud Messaging(以下FCM)をPush通知の送信に使用しているのですが、このとき、通知1件ごとにAPIを叩いていく方式だと送信完了に1日かかってしまうという事態になりました。

FCM Topic Messaging

そのような場合に使用できるのが、Topicを用いた通知の送信です。Topicを使用すると、そのTopicを購読している端末に一度にPush通知を送ることができます。実際、42万件のPush通知は最終的にTopicによって送信しました。

Topicの利点と欠点

Topicを使用する場合、あらかじめユーザー(というよりはデバイストークン)をTopicに対して登録させなければなりません。Firebaseのドキュメントに記載されている「荒天警報」のような、事前に通知の対象がわかっている場合は使い勝手が良いです。 しかし弊社の場合は、急に「こういうユーザーに対してこういう通知を送ってみたい!」という 実験 が提案され、対象となる大量のユーザーIDが渡される、ということがままあります。そのような場合、Topicを新規に作成しなければならず、それに時間がかかること、Topic自体も作成から使用可能までに最大で1日の遅れが生じることから、「即・大量に!」というユースケースには使い勝手があまりよくありませんでした。

既存の実装で使えるものはないか?

まずは、インターネット上に大量にPush通知を送信するためのプロダクトがないかどうか調べることにしました。すると、Mercariさんでの事例がOSSとして公開されていることを見つけました。

tech.mercari.com

github.com

「これじゃん!」と思い導入しようと、まずソースコードを読んでみました。すると、GaurunはiOSはAPNs、AndroidGoogle Cloud MessagingもしくはFirebase Cloud Messagingを使用するような実装になっていました。 CASHでは、iOSAndroidもFCMを使用してPush通知を送信しています。Gaurunを、両OSでFCMを使用するように変更することは不可能ではありませんでしたが、既存のAPIを変更せずに、ということが結構難しそうだった1ので、何か別のやりかたを探すことにしました。

FaaSを用いたPush通知

そこで、Faasを使うことを思い付きました。もともとPush通知というタスクは、複雑な処理をしない小さなものであり、単発のタスクなので、FaaSに乗せるにはもってこいの題材でしょう。

また、ちょうどいいタイミングでGoogle Cloud FunctionsがNode.js 8 Runtimeをサポートし始めたので、async/awaitも使えるしやってみようということになりました。2

The Node.js 8 Runtime  |  Cloud Functions Documentation  |  Google Cloud

Promiseを用いた並列実行

Cloud Functionsを使うことにしたところで、Push通知1件に対して1 function callでは、以前の問題を解決できているとは言えません。

なので、対象となるデバイストークンを一気に投げて、その大量のトークンを分割したものを自分自身に送信する、という再帰的な処理を行なうことで大量のデバイスに対してのPush通知システムを作成しました。

github.com

結果

幸か不幸か、数万規模の通知を送ることが直近は無さそうした。なので社内の有志を募り、その人の端末に向けて大量の通知を送ってみることにしました。下記のスクリーンショットはそのときの様子です。

このときは14端末に向けてそれぞれに500通、合計7000件のPush通知を送信しました。送信完了までにかかった時間は約1分間でした。

実験の様子

これが高速なのかそうでないのかは、他の通知基盤がどのようなスペックかわからないので何も言えません。しかし手動で実行したrequest自体は1回で、そのresponseは一瞬で返ってきていることを考えると、これまでの手間が大幅に削減されたことは明白です。

問題点

呼び出し上限に達してしまう

そりゃそうだろ、という感じではあるのですが、自身を再帰的に呼び出すので一瞬にして実行数が跳ね上がり、quota limitに引っ掛かります。

Quotas  |  Cloud Functions Documentation  |  Google Cloud

一応、Function invocations per second は申請によって上限を緩和させることができます。しかしなるべくなら初期上限のままで運用できるような仕組みにしたいです。案としては再帰実行前にジッタを挿入して呼び出しタイミングをずらすというものがあります。

他には、API limitation の存在しないFaaSを使用するという手もあります。いや、そんなものがあるのか?となりましたが、Knativeというものがありますね。これは確かに無制限に呼び出せますが……

その辺りの話を今度弊社が開催するイベントで話す予定なので、皆さん是非参加してください。

bank.connpass.com

Push通知の全てのパラメータに対応していない

ひとまずCASHで使用するパラメータに絞って実装したために、必要最小限のパラメータにしか対応していません。

まとめ

今回、FaaSによるPush通知基盤という、ほぼ実験的な手法を試してみました。結果、これまでの作業時間を大幅に短縮することに成功しましたが、API呼び出し回数制限などの新な課題も発見しました。

BANKでは、このような実験的なアプローチを、ビジネスモデルだけでなく、技術的な部分にも実践していく企業です。興味があれば、是非お声掛けください!

bank.co.jp


  1. 独自Forkを自社で運用することもできますが、せっかくなら上流に還元したいし、メンテはしたくないので……

  2. 結局 async/awaitは使いませんでしたが。

Identity-Aware Proxy を GKE で利用するコツ

こんにちは,BANK でサーバサイドエンジニア兼インフラエンジニアをやっております,
高橋 (@takutakahashi) といいます.
技術ブログの技術記事第一弾として,管理画面をいい感じにした話をしようと思います!

Identity-Aware Proxy?

さて,GCP では,GAE や Google 提供のHTTPS Loadbalancer を経由する web サービスに対し,
超簡単に Google 認証を付与できる Identity-Aware Proxy (以下 IAP) が提供されています.
Google アカウントによる認証,認可を設定することが可能です.
詳細は以下を参照ください.

https://cloud.google.com/iap/docs/concepts-overview?hl=ja

CASH 管理画面の課題

CASH では,定常運用のために管理画面を持っています.
ActiveAdmin で実装し,アプリケーションのバックエンド API の傍らで動作しています.

この管理画面,もちろん組織の人間でない第三者からのアクセスを防ぐ必要があります.
以前は,アクセス防御のために Basic 認証を利用し,1つのユーザー/パスワードで運用していました.
この状態だと,

  • ユーザーとパスワードがわかっていれば誰でもアクセスできる
  • インターン生や退職者など,元関係者からのアクセスを防ぐことができない

という課題がありました.
なんとかうまい具合に認証できないか?ということで,
Cloud Identity-Aware Proxy を GKE と組み合わせて使ってみて,そこから得た教訓を共有する.
というのが今回の記事の趣旨です.

GKE と HTTPS Loadbalancer

以下は,kubernetes の用語が多く出てきます.ここでは解説しませんので調べてください.

CASH のバックエンドオーケストレーションには,Google Kubernetes Engine (以下 GKE) を利用しています.
GKE では,アプリケーションを外部公開するための Ingress リソースを作成した際に,
Google HTTPS LoadBalancer と連携しロードバランサを作成してくれる機能があります .

IAP + GKE

IAP は,HTTPS Loadbalancer に適用できる認証システムです.
GKE で Loadbalancer は自動作成されるため,GKE がどのように Loadbalancer を作成するのかが理解できれば,
IAP と GKE をどのように組み合わせて利用するべきか,理解することができます.

IAP は Service に適用できる

IAP を効果的に使うためには,LoadBalancer と Kubernetes リソースがどうマッピングされているかを知る必要があります.
以下は,機能のマッピング表です.

役割 Cloud LoadBalancer Kubernetes
IP や HostHeader によるルーティング Frontend Ingress
実際のワークロードへのアクセス Backend Service
アプリケーション健全性確認 HealthCheck Pod (Liveness Probe)

IAP は,HTTP(S) LoadBalancer の Backend に適用することができます.
つまり,Kubernetes の文脈では Service に適用することとなります.

IAP 適用で気をつけること

CASH 管理画面 の IAP 化でも発生した問題ですが,API と管理画面で同じ Service を経由している場合,
IAP を有効にすると API にも認証がかかってしまうことになります.
これを回避するために,Service はアプリケーションポートごとではなく役割ごとに作成しておくことが望ましいです.

以前の CASH のリソース図はこの様になっており,
f:id:takutakahashi:20180810100238p:plain

この構成を以下のように変更し,
f:id:takutakahashi:20180810100355p:plain

管理画面のアクセス先 svc を分離することでこの問題を解決できました.
ミソは,同一 deployment を向く svc を定義することです. 今後アプリケーションを構築する際も,このように役割分離をしておくことで,
単一役割となりリソース編集コストが減る,マイクロサービス化した際に楽,など,色々とメリットがあります.

ちなみに

現在 IAP を有効にするには,GUI で設定するか,gcloud を利用して CUI で設定するかの方法を取る必要があります.
できれば ingress-gce にやってほしいなあ...という気持ちがあるので調べたら,以下の issue で議論されていました.

github.com

issue の進みがあまり良くないので,機能が実装されるまで結構時間がかかりそうですね...

終わりに

アプリケーション事業を運営する上で欠かせない管理画面.
ユーザー認証など,ビジネスロジックに影響しない部分はインフラレイヤーで解決し,
節約したリソースで爆速開発していくことが望ましいですね.

宣伝

cloud.withgoogle.com

9/19, 20 で開催される Google Cloud Next '18 in Tokyo にて,BANK インフラストラクチャ事例紹介のセッションを実施いたします! このブログを読んで,詳しい構成が気になった方は,ぜひお越しいただき,弊社のセッションをご覧ください!

BANKの技術ブログ、はじめます

f:id:bankinc:20180625145752j:plain

こんにちは、株式会社バンクのサーバーサイドエンジニアのうなすけです。

さて、この度社内の技術情報発信のために技術ブログを始めることにしました。

弊社の代表的サービスであるCASH について、それを支える技術的な内容は、これまで極めて限定的な場でしか語られてきませんでした。

cash.jp

今後は、バンク内部で使用されている技術について、その選定の過程、社内に蓄積されている知見やつまづきなどを発信していきたいと思っています。

まだまだ急成長中で、手がまわらず更新も不定期になるとは思いますが、今後よろしくお願いします。