i Cubed Systems Engineering blog

株式会社アイキューブドシステムズの製品開発メンバーが、日頃のCLOMO開発の様子などを紹介します。

Android Enterprise Recommended 2020 の一環として EMM 通知受信に対応した話

はじめまして。 サーバーサイドエンジニアの kentaroh-momiyama です。 CLOMO や CLOMO SECURED APPs MANAGER の機能追加や改善を行っています。

2020年は Android Enterprise Recommended (以下、AER と呼称)の対応に明け暮れましたので、その対応の中でもとくに感慨深かった、EMM 通知の受信機能について書いてみようと思います。

AER とは? AER 2020 とは?

いきなり聞き慣れない用語が飛び出してきましたので、本題の前に少し説明します。

AER とは、Google が定める一定の要件に対応した EMM ソリューションに対して、Google が与える認定のことです。「法人が導入しやすい水準にある EMM ソリューションとして Google からお墨付きをもらっている」というわけです。ここでは CLOMO が EMM ソリューションに相当します。

以前から CLOMO は Android の MDM に対応していましたが、「あらためてAERの認定が欲しい」と言って Google に名乗りを上げたわけです。そうすると Google から AER 認定を獲得するために必要な要件が送られてきますので、コツコツと対応を進めます。Google のレビューを通過すれば、晴れて AER 認定をもらえます。

また、AER 認定は年次で取得しなければなりません。毎年要件が更新されるので、都度対応していく必要があります。弊社も 2018 年から毎年この認定を受けており、2020 年も認定を受けるべく対応を進めました。今年取得した認定が AER 2020 というわけです。

EMM 通知とは?

次に「EMM」なのですが、これは Enterprise Mobility Management の略称であり、一般的には MDM を包括するより上位の概念だと説明されています。 Google は EMM を実現するために専用の API (便宜上、以下では EMM API と呼称) を提供しています。

ここで、従来の MDM 型のデバイス制御と EMM API を利用したそれとを比較してみましょう。

  • MDM 型のデバイス制御
    • Android デバイスへの直接作用によるデバイス制御
      • デバイスの初期化やロックなどがこれに相当します
  • EMM API を利用したデバイス制御
    • API アクセスによるデバイス制御
      • デバイスにアプリケーションをリモートインストールしたり、インストールされているアプリに対して設定群を適用したり、といった制御がこれに相当します
      • MDM による制御と比較すると、デバイスに対して間接的です

後者 の「デバイスに対して間接的」という特徴に着目してください。その制御を実現しただけでは、実際にデバイスに意図した制御が適用されたのかどうか、 EMM プロバイダー(ここでは CLOMO のこと)は知ることができません。

f:id:momiyama-i3:20201203163721p:plain

その不便さを埋めて EMM プロバイダーに通知してくれる解決策が EMM 通知なのです。

EMM 通知 は、

  • EMM API によるデバイス制御の結果
  • Google Play 上におけるアプリケーション状況の変更
    • 承認された/アプリが更新された/削除された など

のような EMM プロバイダー側では知り得ない情報を、Google が通知してくれる仕組みです。Google Cloud Pub/Sub を使って実現されています。

EMM API を利用した EMM 通知の取得

まずは実際に EMM 通知の取得を実験しなければなりません。 さまざまな試行錯誤や紆余曲折がありましたが、ここではその結果得られた知見だけをご紹介します。公式ドキュメントだけでは読み取れない事項も記載します。

Pub/Sub 用のクライアントライブラリが必要かと思っていたが、不要だった。

これにはかなり驚かされました。 CLOMOでは EMM API を使って EMM 制御を実現している関係で、次の API が使えました。

これらのAPIを呼ぶことで、Pub/Sub を意識しなくて済みました。当然、トピックやサブスクリプションといった、Pub/Sub を使う上で必須のオブジェクトも作成していません。Pub/Sub を利用する際のインフラ面のコストからも解放されました。 現在は、新規に EMM API を使った 制御に参画することができなくなっています。新たに EMM 制御に参画したい法人さま向けには別の API が用意されています。

これらのAPIは、エンタープライズごとに呼ぶ必要がある

EMM 制御を利用したいお客様はあらかじめ、CLOMO画面上でエンロール操作というものを実施して、「エンタープライズ」というエンティティを Google上に作らなければなりません。 そのようにして作られたエンタープライズごとに、これらの API を呼ばないといけません。例えば、pullNotificationSet が受信用の API ですが、レスポンスボディに異なるエンタープライズの通知が混ざることはない、ということです。

同じ通知を何度も受け取らないようにするには

pullNotificationSet にて EMM 通知を受信したら、20 秒以内に acknowledgeNotificationSet を POST します。そうすることで、同じ通知を再び受信することがなくなります(上の pullNotificationSet のページに明記あり)。

トピック名は不要だが、取得できてはいる

sendTestPushNotification を POST することで内容がほぼ空虚なテスト用通知を起こすことができます。このとき、レスポンスにトピック名が含まれています。我々は直接には Google Pub/Sub を使っていないので、無視していいと思われます。

pullNotificationSet の挙動

pullNotificationSet は、requestMode というパラメーターを取ります。「waitForNotifications」と「returnImmediately」という値を指定できます。このパラメーターを指定しなかった場合、「waitForNotifications」を指定したのと同じ挙動になります。ここまでは公式ドキュメントに記載されている情報です。

「returnImmediately」を指定した場合、リクエスト時に返すべき EMM 通知があればそれを返しますし、なければ 204 ステータスを返します。こちらは明らかに、cron による定期取得を想定したパラメーターだと考えられます。

逆に「waitForNotifications」を指定して実行すると、返すべき EMM 通知が起きるまで最大 20 秒間、レスポンスを待ちます。つまり、20 秒間の間に EMM 通知が起きればその内容を返します。20 秒待って何も起きなければ 204 ステータスを返します。cron による定期取得でも利用できますし、デーモンによる取得でも使えます。

CLOMO ではどう実現したか?

当初の想定

開発チームとしては、EMM 通知が起きたタイミングでなるべくリアルタイムで取得したい、という思いがあり、当初はデーモンでの実装を想定していました。 あるポリシーが意図通りに適用されたかどうかの結果を EMM 通知してもらうとして、そのポリシーのすべての設定可能項目について一度に通知されるのかどうか不透明だったことが大きいです。

具体的には、デーモンにし、エンタープライズごとにワーカープロセスを立ち上げる想定でした。ワーカープロセスごとに各エンタープライズの EMM 通知を監視させる、というわけです。各ワーカープロセスは、最大で 20 秒に一回、pullNotificationSet を呼ぶ、イメージです。 その場合、考えなければいけないことが4点、あります。それぞれどんなふうに解消していったのでしょうか。

1. デーモンはグレースフルにシャットダウンできるか?

CLOMO は Kubernetes を使った Blue-Green Deployment を採用しています。シャットダウンのシグナルを送ったときに、「waitForNotifications」にてレスポンス待機中のスレッドを落とすようなことがあってはなりません。また、落ちようとしているデーモンと立ち上がろうとしているデーモンとで、同じ EMM 通知を受け取ってもいけません。何らかの排他制御が必要です。 一方で、EMM 通知を取得しそこねることについては、acknowledgeNotificationSet があるためそれほど心配しませんでした。

2. 新たに EMM 制御の利用を開始したお客様に関する EMM 通知の監視をどうやって開始するか?

ワーカープロセスとは別に、EMM 通知を監視すべきエンタープライズの数をチェックするプロセスを立てました。

3. EMM API のクォータに抵触しないのか?

EMM API にはクォータによるリクエスト数の制限が存在しますが、クォータはエンタープライズごとに設定されており、今回は大丈夫そうです。

4. メモリー消費について

目下、マルチプロセスにてデーモン実装を進めていますが、エンタープライズの数だけのプロセスが立ち上がることになるので、懸念はしていました。検証プロセス後、ヤバそうとなった場合の善後策を、弊社プラットフォームメンバーを交えつつ協議していました。最悪デーモンではなく cron での実装に切り替えるが、その切替えはいつでもできると考え、マルチプロセスデーモンのまま実装を進めました。

以上のような検討を進め、検証フェーズに突入したわけです。ところが。

検証

検証フェーズに入ってまもなく、デーモン用Podのメモリ消費量が AKS 全体を圧迫する状況に陥りました。

予想外だったのは、デーモンを運転しているうちにメモリ消費量が増えていく、という現象を想定していたのですが、どうもデーモン用Podを起動しただけで逼迫する、という表れ方だったことです。前者であれば、メモリ消費量の大きいワーカープロセスを再起動する、という対策を考えていたのですが、検証環境のようなエンタープライズの少ない環境でさえ起動直後に逼迫するとは想定しておらず、完全に虚を突かれました。

関係者で集まって議論し、次のような案を検討しました。

  • モジュール読み込みとプロセス分岐のタイミングを変更
  • マルチプロセスではなくマルチスレッド化
  • ワーカープロセス数を固定し、各プロセス内でマルチスレッド化

しかし結局、プロセスであれスレッドであれ、ワーカーがエンタープライズごととなるようなアーキテクチャーがそもそも良くない、という結論に至りました。言うまでもなく、cron への切替えに舵を切ったということです。せっかく上で検討した内容が運用までもっていけなかったのは残念ですが、改修内容がハッキリしておりかつメモリ問題が確実に改善されるので、背に腹はかえられません。

こうして、EMM 通知受信機能は日の目をみた

こうした紆余曲折を経てようやく、EMM 通知受信を利用したアプリ管理機能がリリースされました。 https://support.clomo.com/?page_id=37301

今では以下のように運用しています。

  • なるべくリアルタイムに通知が受け取れるよう、cronジョブを行うPod数を調整しています。
  • アプリのアップデートをキャッチしたりGooglePlay上の承認行為をキャッチしたりするのに利用しています。

なお、EMM 通知のリアルタイム性を Google は保証していません。ご利用にあたってそこだけは注意したほうが良さそうです。

振り返って

Android Enterprise Recommended に認定されるために取り組んでいる内容についてご紹介しました。

AERの対応は、「Google に与えられた要件に逐次対応する」という地味な作業にも解釈できてしまいそうですが、我々としては「やらされ仕事」に終始するのではなく、CLOMO がお客様に使っていただきたい機能として再解釈してご提供するよう努めています。今後もお客様の笑顔を追求してまいります。