LoginSignup
1
0

共有ライブラリに依存するライブラリを Lambda レイヤーとして使うことはできるのか

Posted at

背景

ライブラリをインストールしたLambdaレイヤーを作成したら .so ファイルが見つからないというエラーが出ることがある。

libGL.so.1: cannot open shared object file: No such file or directory

コンテナイメージを作るのが簡単だが、Lambdaレイヤーとして共有ライブラリまで含めてパッケージ化することはできるのか?

前提

ライブラリはなんでもいいのだが、opencvをPython3.9 Lambdaで使うという目標でトライしてみる。
opencv-pythonライブラリを普通にインストールしてLambdaレイヤーにすると、冒頭のエラーが出る。
opencvを使う方法は以下が考えられる。今回は3ができそうというところまで試した。
1. コンテナイメージとしてデプロイする
2. 共有ライブラリに依存しないライブラリを使う。
opencv-pythonは.soファイルへの依存があったが、opencv-python-headlessは意識しないでも使えた。違いについては調べていない。
3. 依存モジュールを全部調べて明示的にレイヤーに入れる

現実的なのは1,2と思うので、3をあえてやる必要はないと思う。
そもそもできたとしてもLambdaレイヤーのサイズquotaギリギリになりそうなので、仮に今opencvでできたとしても、将来や他のパッケージでできる保証はない。
つまり今回の検証は割と無駄なことをしていると思う。

結果まとめ

上記のニュアンスからもわかる通り、できそうであるというところまで確認したが最後まではやっていない。

わかったこと

  • .so ファイルをLambdaで読み取れる範囲(LD_LIBRARY_PATH)におくことは可能
  • yum installしたパッケージとその依存モジュールを特定のディレクトリに配置する方法
  • 共有ライブラリに依存するライブラリのLambdaレイヤーを作成する方法はあまり実用的ではなさそうなこと

できなかったこと

  • opencv-pythonがどの.soファイルに依存するのか正確に調べる方法がわからなかった。
    エラーが出るたびにそのファイルをレイヤーに含める、ということでエラー自体は解消はできる見込みだが、明示された仕様に基づいていないため今動いたとしても突然使えなくなる可能性あり。

手順概要

参考

公式が昔サンプルを出していた。lambda-opencv
必要なモジュールと依存関係をDockerでインストールしている。
しかし最終更新が1年以上前と古いためか、このままだとビルドができなかった。少なくとも .so ファイルのインストールはしていない。サンプルはあくまでサンプルのため、動作しなくてもメンテナンスされる保証はない。
しかし大元はこのDockerfileを参考に使わせてもらった。

流れ

  1. opencvを指定したディレクトリにインストールする(サンプルのまんま)
  2. 依存モジュールを含んでいるライブラリmesa-libGLも指定したディレクトリにyum install
  3. 1.2. 両方をzip化
  4. コンテナからホストにzipファイルを取り出す
  5. レイヤーにして、Lambdaでimport cv2を試す

手順詳細

まずは .so ファイルがないエラーが出るところまで行い、流れを確認

EC2のAmazon Linux 2インスタンス起動。dockerをインストール。
先述のlambda-opencvのDockerfileを編集する。git cloneでも、新たにファイル作ってコピペでも良い。
そのままだと時間がかかった上にビルドに失敗するので、まずは以下を変更する

  • FROM amazonlinux => FROM amazonlinux:2 1
  • Python3.9 のところ以外は消す。
  • Python3.9関連で一箇所ディレクトリにtypoがあって3.8になっているので、そこは直す

続いて、以下のコマンドを実行。
--rmオプションはコンテナ終了時に破棄してくれるのでちょっと試したりする時とかホストへのコピーだけしたいときにとても便利。

sudo docker build --tag=lambda-layer-factory:latest .
sudo docker run --rm -it -v $(pwd):/data lambda-layer-factory cp /packages/cv2-python39.zip /data

これをレイヤーにして、Python3.9 Lambdaに追加する。Lambdaにはlambda-opencvサンプルのapp.pyのコードを貼り付ける。
ここで冒頭のエラーが出て、.soファイルが足りないことを知る。

libGL.so.1: cannot open shared object file: No such file or directory

これはAmazon Linux 2にはデフォルトで入っていないようだ。

sudo docker run --rm -ti amazonlinux:2 /bin/bash
find / -name libGL.so* ## ない

libGL.so.1が見つからないエラー自体の対処法

mesa-libGL 2というライブラリをインストールすればいいらしい。

sudo docker run --rm -ti amazonlinux:2 /bin/bash
find / -name libGL.so* ## ない
sudo yum install mesa-libGL -y
find / -name libGL.so*
## /usr/lib64/libGL.so.1
## /usr/lib64/libGL.so.1.7.0

Amazon Linux 2 のコンテナ内ならここにPython3を入れればもうimport cv2が実行できたが、Lambdaは読み取りのパスが違うためかエラーの解消にはならない。

yumでディレクトリを指定したインストールをする

--installrootというオプションを使うようだ。

普通に実行すると以下のようなエラーが大量に出た。

sudo docker run --rm -ti amazonlinux:2 /bin/bash
sudo yum install --installroot=/home/ec2-user/lambda  libglvnd-glx
## 以下のエラー
Repository 'amzn2-core': Error parsing config: Error parsing "mirrorlist = '$awsproto://$amazonlinux.$awsregion.$awsdomain/$releasever/$product/$target/x86_64/mirror.list'": URL must be http, ftp, file or https not 
...

どうも--installrootで指定するディレクトリに以下の内容を持つyumの設定ファイルなど 3が必要なようだ4

cat /lambda/etc/yum.repos.d/amzn2-core.repo 
...
[amzn2-core]
name=Amazon Linux 2 core repository
mirrorlist=$awsproto://$amazonlinux.$awsregion.$awsdomain/$releasever/$product/$target/$basearch/mirror.list

Amazon Linux 2の場合は以下にあるので、こちらをコピーすれば良い
/etc/yum.repos.d/amzn2-core.repo

出来上がったDockerfile

Lambdaでは/opt/libというパスがLD_LIBRARY_PATHとして読み込み対象であることに注意して5、python lib が同じ階層になるようにzip化する。
結局サンプルと違うのは以下。先述の変更点3点にlibGl.soを書いただけである。

  • FROM amazonlinux => FROM amazonlinux:2に変更
  • Python3.9以外削除
  • パスのタイポを修正
  • libGl.soをインストールしzipへ含める
FROM amazonlinux:2

RUN yum update -y
RUN yum install gcc openssl-devel bzip2-devel libffi-devel wget tar gzip zip make -y

# Install Python 3.9
WORKDIR /
RUN wget https://www.python.org/ftp/python/3.9.10/Python-3.9.10.tgz
RUN tar -xzvf Python-3.9.10.tgz
WORKDIR /Python-3.9.10
RUN ./configure --enable-optimizations
RUN make altinstall

# Install Python packages
RUN mkdir /packages
RUN echo "opencv-python" >> /packages/requirements.txt
RUN mkdir -p /packages/opencv-python-3.9/python/lib/python3.9/site-packages
RUN pip3.9 install -r /packages/requirements.txt -t /packages/opencv-python-3.9/python/lib/python3.9/site-packages

# Install libGl.so
# To install to original dir, it has to have /etc/yum.repos.d/amzn2-core.repo
RUN mkdir -p /temp && \
    cp -r /etc/ /temp/etc && \
    yum install --installroot=/temp --releasever=2 mesa-libGL -y
RUN cp -a /temp/usr/lib64/libGL.so* /packages/opencv-python-3.9/lib

# ls /packages/opencv-python-3.9/ ==> python lib

# Create zip files for Lambda Layer deployment
WORKDIR /packages/opencv-python-3.9/
RUN zip -r9 /packages/cv2-python39.zip .
WORKDIR /packages/
RUN rm -rf /packages/opencv-python-3.9/

上記のDockerfileを使った最終結果

同じようにzipを取り出す。

sudo docker build --tag=lambda-layer-factory:latest .
sudo docker run --rm -it -v $(pwd):/data lambda-layer-factory cp /packages/cv2-python39.zip /data

Lambdaレイヤーを作成し、Lambdaを動かす(import cv2する)と...
libGL.so.1ではなく他の共有ライブラリが見つからないというエラーになる。やったね。

"libgthread-2.0.so.0: cannot open shared object file: No such file or directory",

これを繰り返せばいつかは動くはずだが、不毛なのでここで止め。
多分下の記事にある.soファイルを全部入れれば解決するのだけど、この一覧をスマートに取得する方法がわからなかった。これはやはりエラーメッセージをみてその.soを入れて、を繰り返したようです。
https://qiita.com/suzuki-navi/items/d58d402422b784264d95

そのほか試してダメだったこと

Amazon Linux 2に含まれる.soファイルの大半をレイヤーに含める

サイズ的に無理だった。
Dockerfileに以下を追記して、/usr/lib64ディレクトリにある.soとつくファイルを全てLambdaのLD_LIBRARY_PATHに置けば一気に必要そうなファイルを置けるのでは、と思ったがレイヤーサイズ上限に引っかかりレイヤー作成できず。

# Amazon Linux 2 image has more .so files than in Lambda's PATH
RUN mkdir -p /packages/opencv-python-3.9/lib
RUN cp -r /usr/lib64/*.so* /packages/opencv-python-3.9/lib

結構サイズでかい。全部入れるのは無理でした

du /usr/lib64 -sh
74M     /usr/lib64

LD_LIBRARY_PATHをLambda実行時に書き換える

os.environ['LD_LIBRARY_PATH']を書き換えるといけるという情報があった気がしたが、Lambdaでは通じなかった。これはPythonの実行前じゃないとダメ。。?なようで、使える状況が限られている or できないものなのかもしれない。

  1. 2023/06時点でamazonlinuxのlatestがAmazon Linux 2023になっているので、それで失敗するようになったようだ。

  2. sudo yum install libglvnd-glxでもこれらの.soファイルは追加できた

  3. amzn2-core.repo ファイル単体をコピーしただけでは流石にダメだった

  4. yumdaのDockerfile(yum installをLambdaレイヤー用に依存関係込みで行ってくれるサードパーティ製のツール)とディレクトリ指定でyum installする記事を参考にした。

  5. Lambdaでprint(os.environ['LD_LIBRARY_PATH'])を実行してもなんとなくわかる

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0