Mercari Engineering Blog

We're the software engineers behind Mercari. Check out our blog to see the tech that powers our marketplace.

リモートワークも支えるWIASの半年間の技術的進化

f:id:bobchan1915:20200227060056p:plain
WIASのシステムの様子

こんにちは、メルペイSREチームのkekeです。新型コロナウイルスの感染拡大を懸念し、先週から株式会社メルカリ(以下、メルカリ)では原則として在宅勤務(リモートワーク)が導入されています。そのため、私も含めて多くの社員が自宅で業務を行っています。

以前、社内で使われているWi-Fi勤怠打刻サービスWIASについて紹介させていただきました。その投稿ではサーバーレスアーキテクチャの採用や保守・運用の話を中心にしました。

本記事では、今回のリモートワーク化でのWIASの活躍とこの半年間のWIASの技術的進化を取り上げたいと思います。

以前の紹介記事はこちらです。

tech.mercari.com

WIASのおさらい

きっと前回の投稿を読まれてない方もいらっしゃっると思うので、WIASが何なのか軽くおさらいをします。

WIASとはWi-Fiの接続情報を使って、勤怠管理システムであるKing of Time(KOT)へ打刻を行う社内サービスです。これによって社員は勤怠打刻機やWeb UIを通して勤怠を記録をする必要がなくなります。

f:id:bobchan1915:20200227055715p:plain
WIASの大まかな仕組み

このサービスはCloud FunctionsやCloud Runなどのサーバーレスを中心に構築していて、メルカリ社員なら誰でも使うことができます。

リモートワークでも使われるWIAS

WIASはもとからリモートワークに対応していました。営業の方やオンコール対応のエンジニアなどを代表に、オフィスに来ないけど業務を行っている方のためにSlackコマンドを用意していました。Slackで/wias hello/wias byeを打つとそれぞれ出勤、退勤打刻をすることができます。詳しくは前の投稿を御覧ください。

このSlackコマンドは、Wi-Fi打刻とインターフェースは違えどバックエンドのシステムは共通になっています。

f:id:bobchan1915:20200227055840p:plain
SlackコマンドとWi-Fi打刻のシステム

今回、リモートワークを原則化したことによって社内で「どのように勤怠をつけるか」が問題として上がっていました。そして、WIASのこの機能が勤怠打刻方法として採用されました。今では東京オフィスのほとんどの人が使っている打刻方法になっています。

色んな人が毎日WIASを使っているのを見かけると開発者としては非常に嬉しいです。しかしそれ以上に嬉しいことは、私がこのサービスを少しずつ進化を重ねてきたものを多くの人に使ってもらえることができる点です。ユーザーには目には見えない、些細な地道な進化を何度も重ねてきました。

ここから、その技術的進化を紹介したいと思います。

半年の技術的進化

2019年7月に正式にリリースをして、今回のリモートワークでは勤怠打刻方法として広く採用されたWIASですが、リリースから何も変わっていないわけではありません。

いくつかの技術的進化したポイントを紹介させていただきたいと思います。

1. コストの大幅削減

まず、初めにコスト削減に取り組みました。

お金が多くかかってしまうのならせっかく自前のサービスを作っても費用対効果が疑われるようになります。お金を払ってでもでサービスを契約したり、アウトソーシングをして作ってもらった方が得策の場合もあります。そのため、WIASはコストにもこだわっており、低コスト化を目指して3つの施策を行ったので紹介します。

1.1 デバイスのブラックリスト化

これまではWi-Fiへアクセスをする全てのデバイスの情報をCloud PubSubのメッセージとしてNode.jsのアプリケーションがPublishをして、バックエンドが打刻の処理をしていました。WIASへ登録されていないデバイスの情報も送っていたのです。昨年10月ごろにあった社内ネットワークの変更によって、会議室内にあるiPadなどの常駐端末の情報もPublishされるようになってしまいました。昼夜関わらず、膨大な数のデバイスが定期的にアクセスしていたのです。

サーバーレスはリソースの起動時間だけでなく呼び出し回数によっても課金が発生するので、課金額はメッセージ数に大きく依存することになります。そのため、アクセスログを分析して、常駐端末はブラックリストに入れて打刻のために処理しないようにしました。

f:id:bobchan1915:20200227060005p:plain

結果としてメッセージ数が大幅に減って、恐ろしいことに80%強のコストを削減することができました。最初からやっておいたほうが良かったのではないかと思っていらっしゃる方もいると思いますが、社内のネットワーク構成が変更されるまでは常駐端末が接続をしてくることはありませんでした。また、アクセスログにはMACアドレスとアクセスポイントぐらいしか情報がないので、常駐端末であるかどうかも慎重に判断しなければならず、その点が難しかったです。

しかし、この施策によってメッセージ数はアクセスポイントにアクセスする常駐端末以外の端末分だけになりました。

1.2 未登録デバイスのフィルタリング

f:id:bobchan1915:20200227060056p:plain

先程の取り組みで、常駐端末をブラックリストを追加することによってCloud PubSubのメッセージ数を減らすことができて、大幅にコストを削減することができました。しかし、メッセージを送っているものの中にWIASへ登録していないユーザーが含まれています。それをフィルターするためにオンプレミスサーバーへCloud SQL Proxyを導入して、WIASのユーザー登録情報を参照して、登録デバイスでなければメッセージをPublishしないようにしました。これによって登録ユーザーの数のみメッセージはPublishされるようになりました。しかし、さきほど取り上げたブラックリストの実装は無駄だったのでしょうか。

いいえ、そんなことはありません。 仮に、ブラックリストがなければ常駐端末もデータベースへ登録ユーザーか問い合わせていたことでしょう。挙動としては同じような出力になりますが、常駐端末についてデータベースに問い合わせることがなくなるため、Cloud SQLインスタンスのコンピュータリソース削減になっていて、運用しやすくもできています。

ここまででメッセージ数は常駐端末でない、かつ、WIAS登録ユーザーの数になりました。

1.3 LRUキャッシュによるメッセージ数の削減

WIAS登録ユーザーしかメッセージはPublishされなくなったのですが、それでも約10秒ごとにアクセスイベントが発生してメッセージはPublishされていました。

そのため、LRUキャッシュを実装して、多くても1分間に1回しかPublishされないようにしました。勤怠は秒単位で管理されていることはほとんどありません。これによって、WIAS登録ユーザーのメッセージ数を抑えることができました。

最終的に以下のような流れになっています。

f:id:bobchan1915:20200227131741p:plain
メッセージをPublishするまでの一連の流れ

このようにして、もっとも課金が発生していた時に比べて約97%コスト削減をすることができました。Cloud Functions、Cloud Run、Cloud PubSubの料金は以下のように約1ヶ月でも(開発環境とプロダクション環境を合わせて)1000円行くか行かないかぐらいです。

f:id:bobchan1915:20200227060215p:plain
2020年2月のCloud Run, Cloud FunctionsとCloud PubSubの請求額

いま700人ぐらいのユーザーがいるのでユーザーあたり一人1.4円/月で運用できていることになります。Cloud NATやCloud SQLを入れても一人9.7円/月程度です。

このサービスに月額10円近く払う価値があるのかの議論は置いておいて、多くのコンポーネントを抱えつつも、非常に安くサービスを構築できているのではないかと思います。

2. フォワードプロキシを排除

サーバーレスはIPアドレス固定化ができなく、KOT Web APIがIPアドレス制限を設けているのでフォワードプロキシを置いていました。

f:id:bobchan1915:20200227061901p:plain

IPアドレスを固定するためだけにGoogle Compute Engine(GCE)インスタンスを立てて管理していました。KOT Web APIにはフォワードプロキシのIPアドレスをホワイトリストとして登録していました。社員の出勤時と退勤時にしか呼ばれないにも関わらず、一つコンポーネントが増えることは、問題が起きたときのドリルダウンをしにくくさせます。また、これだけGCEであり、他のコンポーネントと技術スタックが異なるためデプロイ方法を変更する必要があり面倒でした。できるならば、排除したいと最初から思ってました。

2019年12月~2020年2月の間にリリースされたGCPの新たな機能と共にフォワードプロキシを排除することができました。その2つのプロセスを紹介します。

2.1 Serverless VPC Access connectorを使ったPrivate IPによるアクセス

前回の投稿でCloud Functionsに限らず、Cloud RunなどのサーバーレスといわれるプロダクトではIPアドレスを固定することが難しい場合があることを紹介しました。

KOTのWeb APIはIPアドレス制限があるので、どうしてもIPアドレス固定する必要があります。そのため、Node.jsで書いたGoogle Compute Engine(GCE)にフォワードプロキシを立てて、インターネットを通してアクセスをしていました。以下の図でアクセスの様子を示しています。インターネットを通っていることを誇張して書いています。

f:id:bobchan1915:20200227061308p:plain

インターネットに通さなければいけない理由はネットワーク空間がサーバーレスとこちらのGCEインスタンスで異なることが原因です。そのためHTTPS通信を行っていました。

証明書はGoogle Managed SSL証明書を使っていて特に問題はなかったのですが、肝心のドメインの契約が一度切れてしましまったことがあり、その日はWIASによって打刻ができなくなっていました。外形監視などをしておけばよかったと痛感したと同時に、証明書やドメインを管理しなくて済むようにフォワードプロキシを無くしたいと強く思いました。

そのときに、Serverless VPC Access connectorが2019年12月にGAとして登場してきました。端的にこのプロダクトを説明をするならば「Private IPをして違うネットワーク空間のリソースにアクセスをできるようなもの」です。これによってインターネットを通してアクセスする必要はなくなり、ドメインや証明書の管理もする必要がなくなりました。以下の図でリクエストの様子を示しています。

f:id:bobchan1915:20200227061325p:plain

またCloud SQLにもPrivate IPを使ってアクセスをすることができるようになりました。HTTP Load BalancerはInternalなものに置き換えることができ、インターネットを通してフォワードプロキシへアクセスする必要がなくなりました。証明書もドメインも不要になりました。

2.2 Cloud Functions network configurationsとCloud NATよるIPアドレス固定

1.のステップでインターネットを通してフォワードプロキシにアクセスする必要がなくなったのですが、依然としてGCEのフォワードプロキシが存在します。このコンポーネント以外はサーバーレスなプロダクトなので技術スタックの統一という面を考えても、どうしても無くしたかったです。

2020年2月1日にCloud FunctionsのNetwork Configuration(Inbound・Outbound)でネットワークを細かく設定することができるようになりました。このNetwork configurationsとCloud NATを使ってIPアドレスを固定することができるようになります。

f:id:bobchan1915:20200227061341p:plain

Cloud NATをサブネットに配置し、Cloud Routerで繋げるだけでIPアドレスの固定化ができるのです。

遂に、フォワードプロキシを無くすことができました。GCPに新しいプロダクト・機能がリリースされるたびに技術検証などをし、逐一導入できました。一日に出勤を記録するときと、退勤を記録するときにしか呼ばれないコンポーネントを排除することができて、開発面・運用面で非常に楽になりました。

3. 勤怠データの可視化

f:id:bobchan1915:20200227061407p:plain

以前から、勤怠のイベント(出勤、退勤)を通知するCloud PubSub Topicは用意されてありました。勤怠イベントを使って何かアプリケーションを作りたい人が誰でもSubscribeできるようにしました。例えば、チーム内の人がオフィスに来たらチームのSlackチャンネルに通知するなどの色んなアプリケーション例があるでしょう。

それとは別に、WIASの完成度が上がるにつれてメルカリ社員の勤怠の統計を取りたいと思いました。例えば、曜日や季節ごとの出社時間や出社社員数などです。目的としては、今までこのようなデータを取得する方法がなかったので、勤怠に関する施策は非常に少ないのが現状で、何かしらデータが使える可能性を探りたかったからです。メルカリといえばフレックス制やSick Leaveなど社員が働きやすい環境づくりに力を入れていますが、実際は効果はどうなのか、もっと問題はあるのか調査したいと思うようになったからです。施策につながるかは分かりませんが、ひとまず可視化をしてみようと思い立ち、ファーストステップとしてLookerによる可視化を行っています。まだLookerのダッシュボードは実装段階です。

システムとしてはCloud Dataflowでストリーミングジョブを作成して、PubSub Topicから勤怠データをPullしてBigQueryに入れています。そしてBigQueryへクエリを使ってLookerで可視化をします。

データをもとに施策が生まれ、より働きやすい職場づくりができれば嬉しいです。

4. Slackコマンドに絵文字スケジューラの実装

WIASのSlackコマンドではhelloコマンドとbyeコマンドに対して絵文字付きのメッセージが返ってきます。この絵文字は時期によって変化します。例えば、10月20~11月5日まではハロウィンに関する絵文字、12月は冬っぽい絵文字にしていました。

f:id:bobchan1915:20200227061540p:plain
10月下旬のBotからのレスポンス

f:id:bobchan1915:20200227061600p:plain
12月のBotからのレスポンス

この絵文字は絵文字スケジューラというものを実装していて、一年の間で時期ごとに絵文字が定義されています。以下のようになっています(一部抜粋)。

 {
   emoji: {
     hello: ':chocolate_bar:', // 🍫
     bye: ':cupid:', // 💘
   },
   schedule: { startAt: { month: 2, date: 14 }, endAt: { month: 2, date: 14 } },
 },
 {
   emoji: {
     hello: ':snowboarder:', // 🏂
     bye: ':skier:', // ⛷
   },
   schedule: { startAt: { month: 2, date: 15 }, endAt: { month: 2, date: 20 } },
 },
 {
   emoji: {
     hello: ':cat:', // 🐱
     bye: ':cat2:', // 🐈
   },
   schedule: { startAt: { month: 2, date: 21 }, endAt: { month: 2, date: 23 } },
 },

まだWIASを運営して1年もたっていないので今は随時、絵文字を追加していますが、2年目以降は何もしなくても時期ごとに絵文字が変化するようになります。資産として絵文字を残せるわけです。

この昨日の実装の背景として、日頃の業務やプライベートが大変な人が少しでも季節や行事を感じてもらえれば嬉しいなという想いでつけています。

頻繁に変えているわけではないのですが、社内でもたまに絵文字が話題になっていたり、リアクションがあったりするので絵文字を選んでいる方も楽しいです。

これから

少しずつ手を加え続けているので比較的、高い完成度を維持できているのではないかと思っています。しかし、まだまだ取り組みたいことがあります。

1. Slack App Homeによる勤怠ダッシュボード

SlackにはApp Homeというユーザーとボットが1対1で紐づくダッシュボード機能があります。 この機能が公開されてからすぐ技術検証を行い、いまは正式には公開していませんが以下のようなダッシュボードが出るようになっています。

f:id:bobchan1915:20200227060808p:plain
WIASのApp Homeの例

このダッシュボードで見れるのは以下のような情報です。

  • その月の勤怠(出勤、退勤)
  • 統計情報
    • 合計労働時間
    • 残り1日あたり必要労働時間

しかし、欠点としてKOT Web UIでの勤怠打刻の変更や有給やSick Leaveなどのスケジュールを把握できないことです。また、祝日を判定できていません。そのため「統計情報」の部分が正確では有りません。このようなことから、明らかにユーザーの期待に沿うものではないと思って公開していません。しばらくは開発を進める必要があります。

今後はApp Homeをもとに有給の情報や、当月の残り必要労働時間を表示する予定です。しばらくお待ち下さい。KOTではなく、このApp Homeを開けば必要な情報を知ることができるようにしていくつもりです。

2. Slack Enterprise Grid移行による引き継ぎ問題の解決

メルカリは去年、SlackのEnterprise Gridというプランに変更しました。今までのプラン移行前にインストールしたボットはプラン移行後の今でも使うことができています。一見、何も問題が起きているわけでは有りませんでした。しかし、実はプラン移行前に作ったボットと移行後に作ったボットでは違いが有り、それは実行するユーザーIDが異なるという点です。つまり、引き継ぎのためにSlack Bot Collaborationに誰かを追加してボットをリインストールするユーザーIDが変わってしまうのでプラン移行前にWIASへ登録したユーザーは使うことができなくなってしまいます。

ユーザーIDを一括で変更する必要があるので、今後やっていこうと思います。もしプランを移行される方がいらっしゃいましたら、注意してみてください。

3. 勤怠打刻サービスから勤怠管理サービスへ

私は法律に対して無知で、リーガル・法務観点ですら実現可能なのかどうかも分かりませんが、やりたいことには変わらないので書かせていただきます。

勤怠打刻サービスであるWIASは勤怠データの保持をしてもいます。つまり、誰が当月、どのくらい働いたのかなどの情報を持っているので勤怠サービスとしては成り立つ可能性はあります。

WIASに足りない機能としてはUIや有給、勤怠申請などの細かで、必要不可欠な部分があります。しかし、Slack App Homeの登場などによって情勢は変わっているかもしれません。

最後に

ここ半年間でWIASは目に見えるところで大きく変化しました。よりセキュアに、運用しやすく、お金もかからないサービスへと進化しました。目新しさだけで機能を導入するのではなく、ユースケースを考え、技術検証をして、実装・QAを行ってきました。

新卒研修後の2019年7月に正式にリリースをして、今回に至るまでユーザーに対して何か表立ってが大幅に変わったことはあまりありません。しかし、ユーザー体験を損なわずにネットワークレベルの改良、コスト削減やセキュリティリスクの排除などを行い、地道に進化をしていました。

本サービスは、主としてWi-Fi打刻サービスであるものの、Slackコマンドからも打刻できる機能をもっていたことによって、今回の在宅勤務体制での基本的な勤怠記録手法として採用されることになりました。

採用されるまでのプロセスはよく分かってないのですが、採用をされる前から地道に改良を行ってきて、安心感を持って使っていただけてます。リリース時や大きくシステムのコンポーネントを変更するときは負荷試験をやったり、QAをやったりしていました。社内サービスとしてはちょっとやり過ぎではないかと思ったこともありましたが、どうしても完成度の高いものを作りたいと開発時から思っていました。また、その完成度を維持して新技術の登場とともに向上したいとも思っていました。その結果、今回のリモートワーク化も支えているのではないかなと思います。

もちろんまだまだWIASだけでも改良点は多くあり、勤怠に関わらず社員体験の問題は山ほどあるのでいちメルカリ社員として今後、取り組んでいきたいと思っています。自分の職場を自分だけでなく、もっと社員の皆さんにとって快適で魅力的な働きやすい場所にしていきたいと思います。

最後まで読んでいただきありがとうございました。

f:id:bobchan1915:20200227061435p:plain
やりがいを感じるユーザーの声(一部)