AWS LambdaのNode.jsでiconvを使うのが大変だった件

AWS LambdaのNode.jsでiconvを使うのが大変だった件

皆さんこんにちは。関東地方では梅雨入りもまだと言うのに、暑い日が続いていますがいかがおすごしでしょうか。
私は暑いのが苦手なので、ついに先日、今年初の冷房を使い始めました。笑

さて、最近、AWS Lambdaでiconvを使う必要があったのですが、これが思った以上に大変だったのと、恐らく今後も何回か同様の作業をする事になりそうなので、備忘録を兼ねて解決策を紹介します。

Lambdaでネイティブモジュールを使うのは大変

前提として、node-iconvのようなコンパイル済みのバイナリに依存しているnpm packageでは、npm install時にnode-gypと言うツールで、実行環境(インストールを行った環境)に適したバイナリをコンパイルします。node-iconvでは同梱しているlibiconvがコンパイルされますが、当然ながら開発環境と実行環境でCPUやOSのアーキテクチャに差異があると正しく動作しません。

今回、開発環境ではmacOSを使いましたが、実行環境であるAWS LambdaではAmazon Linuxが使われている為、そのままデプロイを行っても動作しないので、実行環境用にnode-iconvのバイナリを予め作っておく必要があります。

コンパイル環境の準備

まず初めに、AWS Lambdaが実行環境として使っているAmazon Linuxのバージョンを特定する必要がありますが、これはドキュメントに書いてあります。2018年5月17日時点だと、 amzn-ami-hvm-2017.03.1.20170812-x86_64-gp2 が使われている事が分かるので、まずはこのAMIを使ってEC2インスタンスを立ち上げます。(EC2インスタンスの起動については割愛します)

起動されたら、SSHでログインし、パッケージマネージャーの更新とNode.js、それとコンパイルに必要なgcc等をインストールしていきます。
尚、今回Lambdaで使ったNode.jsのバージョンがv8.10だったので、8のrpmを使用します:

$ sudo yum update
...
$ curl -sL https://rpm.nodesource.com/setup_8.x | sudo bash -
...
$ sudo yum install -y nodejs gcc44 gcc-c++ libgcc44 cmake
...
$ node -v
v8.11.2

8.11.2がインストールされましたが、マイナーバージョンのみの違いなのでこれを使います。

コンパイルの実行、バイナリのダウンロード

準備がととのったのでコンパイルします。適当なディレクトリ上で、npm installをするだけです:

$ npm install iconv
> iconv@2.3.0 install /home/ec2-user/node_modules/iconv
> node-gyp rebuild

make: Entering directory `/home/ec2-user/node_modules/iconv/build'
CXX(target) Release/obj.target/iconv/src/binding.o
CC(target) Release/obj.target/iconv/deps/libiconv/lib/iconv.o
CC(target) Release/obj.target/iconv/support/localcharset.o
SOLINK_MODULE(target) Release/obj.target/iconv.node
COPY Release/iconv.node
make: Leaving directory `/home/ec2-user/node_modules/iconv/build'
npm WARN saveError ENOENT: no such file or directory, open '/home/ec2-user/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/home/ec2-user/package.json'
npm WARN ec2-user No description
npm WARN ec2-user No repository field.
npm WARN ec2-user No README data
npm WARN ec2-user No license field.

+ iconv@2.3.0
added 2 packages in 5.524s

無事コンパイルされ、インストールされました。
※実際には、node-iconvは開発で使用している物とバージョンを併せる必要があるので、npm install時のバージョン指定は各自行って下さい。

今回欲しいファイルは node_modules/iconv/build/Release/iconv.node なので、このファイルをSCP等で開発マシンにダウンロードして置き換えますが、勿論今度は開発マシン側が正常に動作しなくなるので、実際にはLambdaへのデプロイ時のみ、都度バイナリが置き換わる仕組みを用意した方が良さそうです。

おまけ

https://github.com/ashtuchkin/iconv-lite/issues/60 が解決されればそもそもこの作業はいらない

参考

https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html
https://qiita.com/mobilebiz/items/17bb278894948016df40