Help us understand the problem. What is going on with this article?

PythonのimagecodecsをAWS Lambda Layers用にビルド(あるいはTIFF地獄)

More than 1 year has passed since last update.

TIFF地獄とは

TIFFは暗黒大陸とも言われるほど(言われてない)広大な規格である。いま存在する処理系はどれも、規格のごく一部を実装したものにすぎない。
広大な規格であるがゆえに、そのごく一部を実装するだけでも、莫大な数のライブラリをリンクする必要がある。
2019年1月現在、Christoph Gohlkeという冒険家が、この暗黒大陸に挑戦している。彼が進めているプロジェクトが、tifffileimagecodecsである。
imagecodecsは、「OSSの画像コーデックライブラリをすべて集めてPythonで使えるようにしよう」という壮大なものらしい。
必然的に、AWS Lambdaのような古い環境では、ビルドが非常に面倒である。しかしimagecodecsを避けると、失う面積は大きい。

準備

要Docker。

Numpy / SciPy

AWS公式のNumpy・SciPyレイヤは、PyPIから取れるwhlと互換性がない。おそらくビルド時にいろいろ最適化してある。公式のレイヤをダウンロードする方法はないようなので、諦めてPyPIのwhlをレイヤに固める。

追記:AWS Lambdaの展開後250MB制限がきついので、節約術を使った。strip -sと、共有ライブラリ(gfortran、OpenBLAS)をシンボリックリンクでひとつにまとめた。60MBほどの節約になっている。

Dockerfile
FROM lambci/lambda:build-python3.6
ENV AWS_DEFAULT_REGION ap-northeast-1

ENV HOME /home/hoge
RUN mkdir $HOME
WORKDIR $HOME

RUN pip install --upgrade pip
RUN pip install numpy==1.16.1 scipy==1.2.0 -t ~/python/lib/python3.6/site-packages/
RUN find ~/python/lib/python3.6/site-packages/ -name *.so|xargs -n 1 strip -s
ENV SITE_PACKAGES $HOME/python/lib/python3.6/site-packages/
RUN rm $SITE_PACKAGES/scipy/.libs/libopenblasp-r0-8dca6697.3.0.dev.so $SITE_PACKAGES/scipy/.libs/libgfortran-ed201abd.so.3.0.0
RUN ln -s  $SITE_PACKAGES/numpy/.libs/libopenblasp-r0-382c8f3a.3.5.dev.so $SITE_PACKAGES/scipy/.libs/libopenblasp-r0-8dca6697.3.0.dev.so
RUN ln -s $SITE_PACKAGES/numpy/.libs/libgfortran-ed201abd.so.3.0.0 $SITE_PACKAGES/scipy/.libs/libgfortran-ed201abd.so.3.0.0
CMD zip -ry9 ~/scipy_layer.zip python && cp ~/scipy_layer.zip /share
docker-compose.yml
version: '2'
services:
  app:
    build:
      context: .
    volumes:
      - .:/share
docker-compose build
docker-compose up

できたscipy_layer.zipをAWS Lambdaにレイヤとして登録しておく。

ライブラリのソース

以下のライブラリのソースをダウンロードして、ワークディレクトリに置く。yumで入るものでは古すぎるため。

  • blosc
  • libpng
  • libjpeg-turbo
  • zlib
  • libwebp

imagecodecsもソースが必要。

pip download --no-binary :all: imagecodecs

これ↑だとnumpyも一緒にダウンロードされてしまうが、防ぐ方法が見当たらない。

imagecodecsのsetup.pyを編集

AWS Lambdaのzlibとlibjpeg-turboが古いので、静的リンクする必要がある。extra_objectsのところを加える。

setup.py(一部)
ext_modules = [
    Extension(
        'imagecodecs._imagecodecs',
        sources,
        include_dirs=include_dirs,
        libraries=libraries,
        define_macros=define_macros,
        extra_objects=['/home/hoge/foo/lib/libz.a',
            '/home/hoge/foo/lib64/libturbojpeg.a',
            '/home/hoge/foo/lib64/libjpeg.a'],
    )
]

編集したsetup.pyはワークディレクトリに置く。

Dockerfile
FROM lambci/lambda:build-python3.6
ENV AWS_DEFAULT_REGION ap-northeast-1

ENV HOME /home/hoge
RUN mkdir $HOME
WORKDIR $HOME

COPY c-blosc-master.zip .
RUN unzip c-blosc-master.zip
COPY libpng-libpng16.zip .
RUN unzip libpng-libpng16.zip
COPY libjpeg-turbo-master.zip .
RUN unzip libjpeg-turbo-master.zip
COPY zlib-1.2.11.tar.gz .
RUN tar zxf zlib-1.2.11.tar.gz
COPY libwebp-1.0.1.zip .
RUN unzip libwebp-1.0.1.zip

RUN yum update -y
RUN sed -i -e 's/enabled=0/enabled=1/' /etc/yum.repos.d/epel.repo
RUN yum install -y cmake3 openjpeg2-devel libzstd-devel lz4-devel jxrlib-devel nasm
RUN pip install --upgrade pip
RUN pip install numpy==1.16.0
RUN mkdir foo

RUN mkdir ~/c-blosc-master/build
WORKDIR $HOME/c-blosc-master/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo ..
RUN make install

RUN mkdir ~/libpng-libpng16/build
WORKDIR $HOME/libpng-libpng16/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo ..
RUN make install

RUN mkdir ~/libjpeg-turbo-master/build
WORKDIR $HOME/libjpeg-turbo-master/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo -D CMAKE_POSITION_INDEPENDENT_CODE=ON ..
RUN make install

WORKDIR $HOME/zlib-1.2.11
RUN CFLAGS='-mstackrealign -fPIC -O3' ./configure --prefix=$HOME/foo
RUN make install

RUN mkdir ~/libwebp-1.0.1/build
WORKDIR $HOME/libwebp-1.0.1/build
RUN cmake3 -D CMAKE_INSTALL_PREFIX=$HOME/foo ..
RUN make install

ENV LD_LIBRARY_PATH $LD_LIBRARY_PATH:$HOME/foo/lib:$HOME/foo/lib64
ENV CPATH $HOME/foo/include
ENV LIBRARY_PATH $HOME/foo/lib:$HOME/foo/lib64

WORKDIR $HOME
COPY imagecodecs-2019.1.1.tar.gz .
RUN tar zxf imagecodecs-2019.1.1.tar.gz
COPY setup.py $HOME/imagecodecs-2019.1.1
WORKDIR $HOME/imagecodecs-2019.1.1
RUN mkdir ~/bar
RUN pip install . -t ~/bar

RUN mkdir -p ~/fuga/python/lib/python3.6/site-packages
RUN mv ~/bar/imagecodecs* ~/fuga/python/lib/python3.6/site-packages
RUN mkdir ~/fuga/lib
RUN cp -d /usr/lib64/libopenjp2.so* /usr/lib64/libzstd.so* /usr/lib64/liblz4.so* \
    /usr/lib64/libjpegxr.so* \
    /usr/lib64/libjxrglue.so* ~/foo/lib/*.so* \
    ~/foo/lib64/*.so* ~/fuga/lib/

WORKDIR $HOME/fuga
CMD zip -ry9 ~/imagecodecs_layer.zip * && cp ~/imagecodecs_layer.zip /share

上記のDockerfileと、さきほどと同じdocker-compose.ymlを、ワークディレクトリに置く。

ビルド

ワークディレクトリには以下のファイルがあるものとする。

$ ls
c-blosc-master.zip  Dockerfile             imagecodecs-2019.1.1.tar.gz  libpng-libpng16.zip  setup.py
docker-compose.yml  imagecodecs_layer.zip  libjpeg-turbo-master.zip     libwebp-1.0.1.zip    zlib-1.2.11.tar.gz

もしファイル名が違う場合はDockerfileを編集。

docker-compose build
docker-compose up

ワークディレクトリにimagecodecs_layer.zipができる。

import

imagecodecsをインポートする前に、レイヤ内の共有ライブラリをロードする。こうしないと共有ライブラリが見つからずにエラーになる。

import ctypes
ctypes.CDLL('/opt/lib/liblz4.so.1')
ctypes.CDLL('/opt/lib/libzstd.so.1')
ctypes.CDLL('/opt/lib/libpng16.so.16')
ctypes.CDLL('/opt/lib/libblosc.so.1')
ctypes.CDLL('/opt/lib/libopenjp2.so.7')
ctypes.CDLL('/opt/lib/libjpegxr.so.0')
ctypes.CDLL('/opt/lib/libjxrglue.so.0')
import imagecodecs

これらも静的リンクするほうがいいかも。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away