はじめに
先日、Layerにgemを格納してAWS Lambda関数を動かす記事を書きました。
この記事では、Rubyで実装されたgemを使ってみたのですが、この記事ではネイティブ拡張を使ったgemを使う方法を記します。具体的には、PostgreSQLと接続するためのGemであるpg gemを使う方法を記します。
gemをzipで固めるだけではダメ?
任意の環境でbundle config set --local path 'vendor/bundle' && bundle install
を実行し、zip -r my_ruby_package.zip vendor
にてzipファイルを作成してLayerとして登録、関数にも紐付けして実行するとlibpq.so.5: cannot open shared object file: No such file or directory
のようなエラーメッセージが出力されます。
このエラーメッセージは、今回試したpg gemではOSが持っている共通ライブラリlibpq.so.5
に依存しており、Lambda関数が動作する環境にはこれらの共通ライブラリが入っていないために発生しているのが原因です。
このため、動作環境に対してlibpq.so.5
などをLD_LIBRARY_PATH
に格納されるようにファイルをコピーします。具体的には、lib
ディレクトリを作成し、lib
ディレクトリ以下にlibpq.so.5
などを格納、zip -r my_ruby_package.zip vendor lib
を実行してlib
ディレクトリもzipファイル化しておきます。
このzipファイルがLayerとして登録された場合、Lambdaの環境変数LD_LIBRARY_PATHに含まれるパス/opt/lib
にlibpq.so.5
などのファイルが格納されることになります。
ただ、この手法を続けても/lib64/libm.so.6: version 'GLIB_2.29' not found
というエラーメッセージが出て結局動きませんでした。
Layerを作るためのコンテナイメージを作る
Layerを作るには環境依存が大きいため、Layerを作るためのコンテナイメージを作ることにします。以下のようなDockerfileを用意します。
FROM public.ecr.aws/sam/build-ruby3.2:latest-x86_64 WORKDIR /var/task RUN amazon-linux-extras install -y postgresql14 RUN yum -y install postgresql-devel postgresql RUN gem update bundler CMD ["bash", "make_layer.sh"]
pg gemを動かすために必要なライブラリ等をコピーするためのshell scriptmake_layer.sh
を以下のような中身で用意します。
bundle config --local silence_root_warning true bundle config set clean true bundle config set path '/var/task' bundle install mkdir -p /var/task/lib cp -a /usr/lib64/libpq.so.5.14 /var/task/lib/libpq.so.5 cp -a /usr/lib64/liblber-2.4.so.2.10.7 /var/task/lib/liblber-2.4.so.2 cp -a /usr/lib64/libldap_r-2.4.so.2.10.7 /var/task/lib/libldap_r-2.4.so.2 cp -a /usr/lib64/libsasl2.so.3.0.0 /var/task/lib/libsasl2.so.3 cp -a /usr/lib64/libssl3.so /var/task/lib/libssl3.so cp -a /usr/lib64/libsmime3.so /var/task/lib/libsmime3.so cp -a /usr/lib64/libnss3.so /var/task/lib/libnss3.so cp -a /usr/lib64/libnssutil3.so /var/task/lib/libnssutil3.so cd /var/task zip -r layer.zip .
この作り方は以下の記事を参考にしました。
あとは、docker build -t ruby-lambda-layer .
を実行してコンテナイメージを作成し、docker run --rm -it -v $PWD:/var/task ruby-lambda-layer
を実行するとzipファイルが作成されます。
環境変数LD_PRELOAD
の設定
これでLayerに登録するzipファイルが作成できましたので、登録して実行してみます。すると、/usr/lib64/libnssutil3.so: version 'NSSUTIL_3.82' not found
というエラーメッセージが出ました。
このエラーメッセージの解決にかなり悩みましたが、以下のブログ記事の記載で解決しました。
具体的には、環境変数LD_PRELOAD
に/opt/lib/libnssutil3.so
を設定します。
結果、単純にrequire 'pg'
としただけの以下のプログラムですが無事動作しました。
動作結果。
考察
ここまできてようやくpg gemを動作することができました。動作には、Lambdaの動作環境に強く依存したライブラリのコピーが必要となり、動作させるだけでも一苦労です。また、将来的な動作環境の変更の際にまた試行錯誤するのは大変かと思います。
このため、以下の記事で書いたようにLambdaで動くコンテナイメージを作っておいた方が考えることが少なくて良さそうです。