好きなURLをAWS Lambda関数に渡し、htmlページがロードされた後、Headless Chromeを使いPDFに変換する。Base64 Dataが返す。
GitHub: puppeteer_on_docker_lambda
作り始めたきっかけ
シンプルなweb serviceを作りたかった。任意のURLをもとに、htmlページをレンダリングし、描画されたイメージをpdfにして保存。Web scrapingやテストの自動化に利用する目的
開発環境の選択
htmlをpdfに変換するだけなら、いろいろ選択肢はある。一番簡単なのはjs libraryを使う。シンプルで軽いので簡単に実装できる。このアプローチの問題は、自由度が低いこと。web serviceを作成した後、他の用途に利用しずらい。例えば、複雑なロジックが必要とするWeb scrapingなどに使うのは難しい。単純にhtmlをpdf化することが目的なら、 html2canvasをチェックしてみると良い。
Puppeteer は Headless ChromeのDevToolsのAPIにアクセスし、主要なChromeの機能を利用できるため、他のプロジェクトに有効利用できそうなので、Puppeteerを今回使うことにした。また、サーバーコストを最小限にするためにサーバーレス AWS Lambdaを使うことにした.
Headless Chromeをより効率的にLambda上で使うために、chrome-aws-lambda を選択した。Lambda layer上に保存できる最大のファイルサイズは50MB以下、chrome-aws-lambdaは 44MBほどで, Layer上に保存できる。サイズが普通のChromeよりも小さいためロード時間が短く、Lambdaの消費時間、コストもよりすくなくなる。
Why use Docker and ECS
AWSが2020年の年末に発表した、Lambdaのコンテナサポート。Dockerを使って、AWS Lambda Imageをローカル開発環境化にすることができる。 以前は、開発、テスト、デプロイするのが難しかったLambda開発だが、より身近にテスト、デプロイしやすくなった。
今までは [Serverless Framework] (https://www.serverless.com/) を使うのがLambdaのローカル開発環境の標準だったが、(以下記事参照). しかし、Serverless Framework は自分のマシンの設定をいじる必要があり、複数のプロジェクトを同時に開発していると不都合がでやすい。なので、Dockerコンテナを使い、AWS Lambda Runtime Emulatorを使いより本番に近い開発環境をローカルで作ることが可能になる。
事前準備
Docker Desktop (Mac OS推奨. プロジェクト内のシェルスクリプトはMac環境でテストされたものなので注意)
- AWS アカウント
- Lambda, Docker, SAM, CloudFormation and ECS関連の基本知識
setup AWS CLI for deloyment
SAM CLI.
必要なパッケージ
以下のパッケージが必要。chrome-aws-lambdaはそれに関連するpuppeteerのバージョンを選ばないと動作しないので注意。
chrome-aws-lambda@10.0.0
puppeteer-core@10.0.0
ローカル開発環境
Dockerをセットアップし、aws-lambda-rieをインストール。以下のシェルを実行する。
./get_lambda_rie.sh
Dockerイメージを作成
2つのdockerコマンドを実行. Docker build and run. Docker イメージが作成され、 localhost:9000 のポートでLambdaが実行できる.
./docker_build_run.sh
ローカルでテスト
ターミナル、もしくはポストマンで以下のコマンドを実行。サイトは自分の好きなサイトに変更。
数秒後にbase 64 dataを受け取る。そのデータをpdfに変更するなら、オンラインサービス例えば、 Base64Guruを利用すると良い
curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{"url": "https://yourfavoritesite.com"}'
AWSにデプロイする
AWS cliを使って、デプロイするのでcredentialやregionなどをconfig fileに必ず設定しておく。 以下ファイルないで、s3 bucketのファイル名を定義しているので、必ずユニークであることを確認しておく。
deploy/deploy_with_sam.sh