AWS Lambda Pythonからpsycopg2でRDS PostgreSQLに接続する
AWS Lambda 関数では、実行に必要なプログラムをZIPでパッケージ化します。 AWS Lambda PythonからRDS PostgreSQL に接続する場合はどうでしょうか?
PostgreSQLアダプターのpsycopg2をビルドしてLambda関数のルートディレクトリに同梱するだけで済みそうですが、話はそう簡単ではありません。
libpqをpsycopg2に動的リンクさせた場合の弊害
psycopg2 はPostgreSQLクライアント用Cインターフェース libpq のラッパーであり、標準では libpq
を動的リンクします。
残念なことに、AWS Lambda の実行環境では libpq
は共有ライブラリとしてインストールされていないため、libpq を動的リンクしていると libpq が見つからず、以下のようなエラーが発生します。
Unable to import module 'lambda_function': libpq.so.5: cannot open shared object file: No such file or directory
libpqをpsycopg2に静的リンクさせて解決
2016年05月時点ではAWS 側が Lambda 実行環境に libpq を共有ライブラリとして提供してくれていない以上、libpq を静的リンクさせて解決します。
以下の手順で作業します。
- AWS Lambda実行環境と同じAMIでEC2を起動
- PostgreSQLのインストール
- psycopg2をビルド(libpqを静的リンク)
- psycopg2を使ったLambda関数の実行
1. AWS Lambda実行環境と同じAMIでEC2を起動
AWS Lamba の実行環境は AMI(OSはAmazon Linux)で提供されています。 現時点では、「AMI name: amzn-ami-hvm-2015.09.1.x86_64-gp2」という名前で、東京リージョンであれば「AMI ID:ami-383c1956」で提供されています。
このAMIでEC2を起動します。
$ uname -a Linux ip-172-31-22-214 4.1.10-17.31.amzn1.x86_64 #1 SMP Sat Oct 24 01:31:37 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
AWS Lambda の実行環境の詳細は次の URL をご確認ください。
http://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html
2. PostgreSQLのインストール
次に psycopg2 のビルドに必要な PostgreSQL をインストールします。
まずは PostgreSQL のコンパイルに必要な開発ツールをインストールします。
$ sudo yum -y groupinstall "Development Tools"
次に PostgreSQL をインストールします。
インストール先を環境変数 PG_DIR
で定義しています。
$ wget https://ftp.postgresql.org/pub/source/v9.4.7/postgresql-9.4.7.tar.gz $ tar zxfv postgresql-9.4.7.tar.gz $ cd postgresql-9.4.7 $ PG_DIR=/tmp/pg $ ./configure --prefix $PG_DIR --without-readline --without-zlib $ make $ make install
インストールされるライブラリ libpq のバージョンと接続先PostgreSQLサーバーのバージョンが異なっていても、問題 なく接続できます。 最新の PostgreSQL をインストールしましょう。
psycopg2 のドキュメントから引用します。
Note psycopg2 usually depends at runtime on the libpq dynamic library. However it can connect to PostgreSQL servers of any supported version, independently of the version of the libpq used: just install the most recent libpq version or the most practical, without trying to match it to the version of the PostgreSQL server you will have to connect to.
3. psycopg2をビルド(libpqを静的リンク)
次に Python 向け PostgreSQL アダプターの psycopg2 をインストールします。
$ cd # back to home directory $ wget http://initd.org/psycopg/tarballs/PSYCOPG-2-6/psycopg2-2.6.1.tar.gz $ tar zxfv psycopg2-2.6.1.tar.gz $ cd psycopg2-2.6.1
ここでビルド設定を記載する setup.cfg
を編集します。
次の2点を修正しています。
- pg_config のパスを先ほどインストールしたパスに書き換え
static_libpq=1
にして静的リンク
修正後の setup.cfg
が以下です。
[build_ext] define= # PSYCOPG_DISPLAY_SIZE enable display size calculation (a little slower) # HAVE_PQFREEMEM should be defined on PostgreSQL >= 7.4 # PSYCOPG_DEBUG can be added to enable verbose debug information # "pg_config" is required to locate PostgreSQL headers and libraries needed to # build psycopg2. If pg_config is not in the path or is installed under a # different name uncomment the following option and set it to the pg_config # full path. pg_config=/tmp/pg/bin/pg_config # XXX CHANGED # Set to 1 to use Python datetime objects for default date/time representation. use_pydatetime=1 # If the build system does not find the mx.DateTime headers, try # uncommenting the following line and setting its value to the right path. #mx_include_dir= # For Windows only: # Set to 1 if the PostgreSQL library was built with OpenSSL. # Required to link in OpenSSL libraries and dependencies. have_ssl=0 # Statically link against the postgresql client library. static_libpq=1 # XXX CHANGED # Add here eventual extra libraries required to link the module. #libraries=
次にライブラリ libpq
のパスを LD_LIBRARY_PATH
で渡して、ビルドを実行します。
$ LD_LIBRARY_PATH=$PG_DIR/lib:$LD_LIBRARY_PATH python setup.py build
ビルドされた psycopg2 モジュールを確認します。
$ ls -1 build/lib.linux-x86_64-2.7/psycopg2/ errorcodes.py extensions.py extras.py __init__.py _json.py pool.py psycopg1.py _psycopg.so _range.py tests tz.py $ file build/lib.linux-x86_64-2.7/psycopg2/_psycopg.so build/lib.linux-x86_64-2.7/psycopg2/_psycopg.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=bccde9363fccf85d899e3eecc4bc97bd07dbd2da, not stripped $ ldd build/lib.linux-x86_64-2.7/psycopg2/_psycopg.so linux-vdso.so.1 => (0x00007ffc61b27000) libpython2.7.so.1.0 => /usr/lib64/libpython2.7.so.1.0 (0x00007f526936e000) libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f5269152000) libc.so.6 => /lib64/libc.so.6 (0x00007f5268d8f000) libdl.so.2 => /lib64/libdl.so.2 (0x00007f5268b8b000) libutil.so.1 => /lib64/libutil.so.1 (0x00007f5268988000) libm.so.6 => /lib64/libm.so.6 (0x00007f5268685000) /lib64/ld-linux-x86-64.so.2 (0x000055b1aaec3000)
4. psycopg2を使ったLambda関数の実行
psycopg2 モジュールをインポートできることを確認するだけのシンプルな Lambda 関数を用意します。
$ mkdir ~/lambda $ cp -r build/lib.linux-x86_64-2.7/psycopg2/ ~/lambda/ $ cd ~/lambda/ $ cat <<EOF >> lambda_function.py > import psycopg2 > def lambda_handler(event, context): > return 'ok' > EOF $ zip -r lambda.zip .
この lambda.zip
を管理画面からアップロードし、「Test」ボタンから実行します。
ログに「ok」と出れば成功です。 おめでとうございます。
Unable to import module 'lambda_function': libpq.so.5: cannot open shared object file: No such file or directory
というようなエラーメッセージが表示された場合は、ビルド手順を遡って確認してください。
まとめ
今回は PostgreSQL/psycopg2 を題材に、Python のC拡張モジュールが共有ライブラリに依存する場合の AWS Lambda での利用方法を解説しました。
毎回この作業をするのは手間なため、 psycopg2 以外にも
- MySQL-Python
- numpy
- OpenCV
- Pillow (PIL)
- LXML
といった他のC共有ライブラリに依存するモジュールをプリコンパイルして AWS Lambda 向けにパッケージ化したライブラリも存在します。
https://github.com/Miserlou/lambda-packages/
今回紹介したアイデアを psycopg2 以外のモジュールにも活かしていただければ幸いです。