LightFile for Dropbox
Jpeg画像を劣化を感じない範囲でいい感じに再圧縮してDropboxの空き容量を稼ごうぜ、という処理を自動で行うDropboxサーバーアプリです。
Dropboxアプリというとスマホクライアントアプリを想像しますが、豊富なAPIを使ってDropboxをファイルシステムとするサーバーアプリも独自に開発することができます。
LightFile for Dropboxではこれをサーバーレスでクラウドネイティブに構築しました。
急にユーザーが増えたら自動的にインフラが拡張されるし、誰も使わなければコストがかからない。しかも僕らは何も保守しなくていいともうエンジニアをダメにする楽ちんさです。
ほとんど定石ではあるんですが、利用技術を紹介します。Dropbox上のファイルを自動処理するアプリや、サーバーレスなWebアプリを設計する上での参考になれば幸いです。
利用技術
- インフラ
-
処理系AWS Lambda -
WebゲートウェイAPI Gateway -
DNSRoute53 -
SSL証明書Certificate Manager -
データベースDynamoDB -
分散処理のためのメッセージングSNS -
Dropboxのファイル変更検知Dropbox Webhooks -
開発環境でWebhookを受信ngrok
-
- 開発言語(JavaScript)
-
実行環境Node.js -
Dropbox SDKdropbox -
WebアプリケーションフレームワークExpress -
テンプレートエンジンPug -
サーバーレスフレームワークServerless-
開発用Webサーバserverless-offline -
Expressバインドserverless-express
-
-
処理の概要
大まかな処理の流れです。
ログイン
OAuth2によるごく普通の認証・認可のフローです。Dropbox側にアプリケーションを登録し、アプリのIDとシークレットを用意する必要があります。
ファイルの変更検知と処理
ファイルの変更はWebhooksで受信できます。後述しますが、Webhooks通知には速攻でレスポンスする必要があるので、時間のかかるファイル処理はSNSを経由して別のLambdaプロセスを起動します。
インフラ
AWSのマネージドサービスのみで構成されています。
いわゆるLAMPと対比するとこんな感じです。
- Linux →
AWS - Apache →
API Gateway - MySQL →
DynamoDB - PHP →
Lambda+NodeJS - cron(非同期処理) →
SNS
処理系 Lambda
言わずと知れたFaaS、サーバーレスアプリケーションの実行環境です。
プログラムを書いてデプロイすれば後は勝手に運用してくれる楽ちんさは麻薬的です。
Webゲートウェイ API Gateway
Lambda自体はOSでいうスクリプト言語に相当する処理系なので、HTTP経由でそれを公開するにはApacheやCGIのようなレイヤーが必要です。
それに該当するのがAPI Gatewayです。
REST APIの提供が主要な目的となるのでApacheといろいろ異なりますが、カスタムドメイン機能を用いてWebサーバー的な使い方もできます。
LambdaとAPI Gatewayの操作は、Serverlessフレームワークがいい感じにやってくれます。
DNS Route53
AWSのDNSです。
任意のドメインからAPI Gatewayのカスタムドメインへのエイリアスを貼ることで、独自ドメインでのサービス展開が可能になります。
SSL証明書 Certificate Manager
AWSで稼働するサービス向けに、無償でSSL証明書を準備してくれます。
API Gatewayでカスタムドメインを用意するときに証明書が必要になります。
最終的にサービスを提供する独自ドメインをコモンネームとした証明書を取得します。
データベース DynamoDB
AWSが提供するスケーラブルなNoSQLです。今回のアプリでは、次の用途に使っています。
- DropboxのユーザーIDとアクセストークンの保管
- 処理済みファイルの再処理を防止したり変更を検知するためのリビジョンの保管
サーバーレスとはいえ、アクセストークンの保存に何らかのデータストアが必要です。
ファイル変更検知 Dropbox Webhooks
Dropbox上で予めURLを登録しておくと、ユーザーによるファイル操作を通知を受けることができます。
これをAPI Gatewayで受信し、Lambdaのプログラムを起動します。
非同期処理のためのメッセージング SNS
API Gateway経由でLambdaを実行する場合は、タイムアウトが30秒間という制約を受けます。
画像ファイルの処理に30秒間は心許なく、Dropboxにフォルダを追加されたときなどは大量のタスクが発生することがあります。
また、それ以前に(出典を失念してしまいましたが)Dropbox Webhooks側にも時間的な制約がありました。
更にはDropbox側でもWebhooksの送信先URLのヘルスチェックを実施しているようで、Webhooks URLが何度か応答しないと通知が停止される使用になっているようです。
実際に開発中に通知がこないなーと思ったら停止されていたことがありました。もちろんユーザー向けの管理画面から再度、有効にすることができます。
以上の理由で、Webhooksの受信時は速攻で応答しなければなりません。
そこで今回、実際にファイル操作を行う処理は、SNSを経由して非同期に起動することにしました。SNSのメッセージを通して、実際にファイルの変換処理を行うLambdaファンクションを起動します。
最近、LambdaがSQSにも対応したのでそちらでもよいと思います。
JavaScript
実行環境 Node.js
AWS Labmdaが伝統的にサポートするJavaScriptとNode.jsを利用します。
自分がPythonにあまり詳しくないだけで、もちろん選択肢としてありです。むしろDropbox SDKのサポートはPythonの方が充実しているかもしれません。
Dropbox SDK dropbox
その名もdropboxというNPMモジュールが提供されています。
ユーザーのログインにより認証と認可を行うOAuth2の処理は他のモジュールで頑張らなければならないかなと思っていたら、その辺の処理もしっかりサポートしてくれます(リダイレクト先のURLやcodeからのアクセストークンの取得など)。便利!
このモジュールを利用してDropbox上のファイルのダウンロードや、処理したファイルのアップロードを行います。
Webアプリケーションフレームワーク Express
Node.jsでお馴染みのWebアプリケーションフレームワークです。
LambdaとAPI GatewayでHTTPアクセスには対応できるのですが、往年のCGIのようなプリミティブさしかありません。
ヘッダーが、Cookieが、ファイルアップロードが、HTMLテンプレートが、といった処理を自力で頑張るのはしんどいし危険な香りがします。なのでExpressを利用します。
テンプレートエンジン Pug
これは好みですが、テンプレートエンジンには記述量が少ないPugを使用します。EJSなどでももちろん問題ありません。
サーバーレスフレームワーク Serverless
権限管理、イベント設定、コードの継続的なデプロイなど、Lambdaを使うには付随して必要なことや手間が多く意外と大変です。
ユースケースをCloudFormationに落とし込んでまるっとサポートしてくれるのがサーバーレスフレームワークServerlessです。
最近はSAMの方がトレンドなんですかね、ちょっとキャッチアップできていませんが。
開発用Webサーバ serverless-offline
Serverlessのプラグインです。
API Gatewayの動作をシミュレートする簡易的なWebサーバーを立ち上げてくれます。開発時に必須のプラグインです。
Expressバインド serverless-express
Expressのミドルウェアが期待する入出力と、API Gateway + Lambdaのそれは大きく異なります。その変換を行う仕組みが必要です。
言ってもオブジェクト間のマッピングなので自分で頑張ることもできますが、既存のモジュールを使った方がよいでしょう。
このモジュールを使うと、Expressアプリケーションをラッピングした、API Gateway向けのLambdaハンドラを自動生成することができます。それをエクスポートするだけなので、Lambdaファンクションのハンドラファイルはかなりシンプルに記述できます。


