AWS Lambda(Node.js)にsharp(Native Module)をデプロイする方法

sharpはNode.js向けの画像変換ライブラリです。sharpは内部ではCで書かれたネイティブモジュールのlibvipsを利用しています。libvipsのインストールについては、メジャーなプラットフォーム(macOSやLinux x64,Linux ARM64, Windows)向けには予めビルドされたネイティブモジュール(バイナリ)が用意されており、sharpのインストール時にそれらが ./node_modules/sharp/vendor/ に展開される格好になっています。

ここで問題になってくるのが、macOSなどでインストールしたネイティブモジュールをそのままLambdaにデプロイしてしまうと、プラットフォームの違いによりlibvipsが動作しないことです。この問題についてはこれまでもいくつか類似記事が出ていますが、最新情報をまとめてみます。

パターン1: npm install

最もシンプルな方法で、sharpのドキュメントにも書かれています。

Installation - sharp - High performance Node.js image processing

rm -rf node_modules/sharp
npm install --arch=x64 --platform=linux sharp

npm install コマンド実行時に --arch=x64 --platform=linux を指定することでLambdaのプラットフォームで利用可能なネイティブモジュールをインストールします。

どのプラットフォームでインストールされたかは、以下で確認ができます。

$ cat node_modules/sharp/vendor/platform.json
"linux-x64"

ちなみに、 --arch=x64 --platform=linux のオプションは package.jsonarchplatform を上書きするオプションです。その他 package.json の項目はすべて --key=value の形式で実行時に上書きすることができます。

$ npm help
Specify configs in the ini-formatted file:
    /Users/igarashi.ryosuke/.npmrc
or on the command line via: npm <command> --key value
Config info can be viewed via: npm help config

パターン2: LambdaのDockerイメージでインストールする

Lambdaと同じプラットフォームのイメージの中でインストールする方法です。これもsharpのドキュメントに書かれていますが、現在はamazon公式のLambdaのDockerイメージがありますのでそちらを使用しましょう。node12はまだ用意されていませんのでnode10.xで。

こちらの方法であれば yarn コマンドを使うこともできますのでここからは yarn を使っていきます。なお、 yarn.lock ファイルにはインストール時のプラットフォーム情報は記録されないのでローカルと共有して使うことができます。

rm -rf node_modules/sharp
docker run -it --rm \
  -v "$PWD":/var/task \
  -w /var/task \
  amazon/lambda-build-node10.x \
  /bin/bash -c " \
  npm install -g yarn && \
  yarn install --production"

また、ローカル環境の ./node_modules は変更せずに、Dockerイメージの中でモジュールのインストールしてデプロイまでやってしまう場合は . をマウントしつつ ./node_modules だけをDocker Volumeにマウントしてあげれば良いです。

docker run -it --rm \
  -v "$PWD":/var/task \
  -v node_modules:/var/task/node_modules \
  -w /var/task \
  amazon/lambda-build-node10.x \
  /bin/bash -c " \
  npm install -g yarn && \
  yarn install --production"

Docker Volumeは --rm オプションでは削除されないので、使い終わったら明示的に削除しましょう。

docker volume rm node_modules

関連記事

Node.jsのNative ModuleをAmazon Linux on Dockerでコンパイルする | Developers.IO

TypeScriptなら ./dist をLambdaのルートに指定して、モジュールを ./dist/node_modules に配置してデプロイしてもよさそうです。

CircleCI と GitHub で AWS SAM のサーバーレスアプリを自動デプロイしてみた (開発環境 & 本番環境) | Developers.IO

CI環境ならLambdaのDockerイメージを使ってインストールできるので良いですね。