AWS Lambdaでnode-gypを使う

node-gypはC/C++のライブラリをNode.jsで使うためのビルドシステムで、これはnpm installした時の環境に適した形のバイナリを生成してくれる。 しかしAWS Lambdaでライブラリを使う場合はnode_modules含む実行ファイルをzipで固めそれをアップロード必要があり、node-gypを含んだLambda functionはLinux環境でnpm installを叩かなければならない。

自分はこれの解決にDockerを用いている。

FROM node:4

RUN apt-get update \
  && apt-get install -y --no-install-recommends sudo zip make gcc g++ libc-dev python \

  && apt-get clean && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/*

WORKDIR /usr/local/app
RUN mkdir lib

COPY package.json .
RUN npm install

COPY .babelrc .
COPY src ./src

上記Dockerfileと

{
  "name": "lambda",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build:babel": "babel ./src/ -d ./lib/",
    "build:zip": "zip -r ./app.zip ./lib/ ./node_modules/",
    "build": "npm run build:babel && npm run build:zip && sudo mv app.zip dist",
    "docker": "docker build -t builder .",
    "release": "npm run docker && docker run --rm -v `pwd`/dist:/usr/local/app/dist builder npm run build"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-cli": "^6.24.1",
    "babel-preset-es2015": "^6.24.1"
  },
  "dependencies": {
    "pg-query-normalizer": "1.0.0"
  }
}

上記package.jsonを用意することでワンコマンドでzipを作成できる。

やっていることはとても単純で、docker buildのタイミングでnpm installを叩いておき、そのイメージを使ってzipファイルを作成しているだけだ。 docker runのタイミングでカレントディレクトリをすべてマウントさせとけばDockerfileいらなくね?と思うかもしれないが、いちいちビルドをするだけでローカルのnode_modulesがlinux実行用に書き換わるのは正直クソ(node-gypのビルドにも時間はかかる)なので辞めた方が良い。

Dockerはbuildを意識する必要が無いし、まぁ今時の開発環境ならDockerの1つや2つインストールされているものだと思っているので、AWS LambdaでC/C++ライブラリを使いたい人は是非参考にして欲しい。

ところで

なんでAWS LambdaでC/C++ライブラリを使うのかって話なんだが、まぁこれを使いたかったからだ。 https://github.com/munisystem/pg-query-normalizer

仕事の話にはなるんだが最近RDSのログからクエリを抜き出しBigQueryにいい感じに挿入するAWS Lambda function (https://github.com/munisystem/kuroneko) を作成していて、これによって突っ込まれたログからSlow Queryの可視化を行ってWEBアプリケーションの速度改善に繋げようという目論見がある。

Slow Queryのもととなるコードを探すためにはある程度正規化されている必要があって、例えば

  • SELECT * FROM Users WHERE id IN(1,3,5,7);
  • SELECT * FROM Users WHERE id IN(1);

は同一クエリと判断されて欲しい。

これを可能にするライブラリが必要だったのだが、クエリをパースするものはあっても正規化を行うものはnpm上には存在していないために自前でライブラリを用意する必要が発生してしまった。 クエリのノーマライズの実現には

  • Queryの妥当性を検証する
  • QueryをASTのように感じで扱えるようにparseする
  • parseされたQueryをwalkして対象箇所を置き換える

みたいなのが必要になると思うんだけど、これをNode.jsで一から実装するには時間が足りない。 そのためそれが出来るC++ライブラリをNode.jsで使うためにnode-gypを使用するに至った訳だ。

とはいえnode-gypで書かれたものをメンテナンスするのも大変なので、土日とか時間あるときにエイヤってC++に依存しない実装に置き換えようかって気分はある。あとは気力の問題。

全然話関係ないんだけど、今めっちゃ眠くて何書いているかすらわからんので目が覚めたあとの自分にリファクタを任せます。