LightFile for Dropbox
Jpeg画像を劣化を感じない範囲でいい感じに再圧縮してDropboxの空き容量を稼ごうぜ、という処理を自動で行うDropboxサーバーアプリです。
Dropboxアプリというとスマホクライアントアプリを想像しますが、豊富なAPIを使ってDropboxをファイルシステムとするサーバーアプリも独自に開発することができます。
LightFile for Dropboxではこれをサーバーレスでクラウドネイティブに構築しました。
急にユーザーが増えたら自動的にインフラが拡張されるし、誰も使わなければコストがかからない。しかも僕らは何も保守しなくていいともうエンジニアをダメにする楽ちんさです。
ほとんど定石ではあるんですが、利用技術を紹介します。Dropbox上のファイルを自動処理するアプリや、サーバーレスなWebアプリを設計する上での参考になれば幸いです。
利用技術
- インフラ
-
処理系
AWS Lambda -
Webゲートウェイ
API Gateway -
DNS
Route53 -
SSL証明書
Certificate Manager -
データベース
DynamoDB -
分散処理のためのメッセージング
SNS -
Dropboxのファイル変更検知
Dropbox Webhooks -
開発環境でWebhookを受信
ngrok
-
- 開発言語(JavaScript)
-
実行環境
Node.js -
Dropbox SDK
dropbox -
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ファンクションのハンドラファイルはかなりシンプルに記述できます。