プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2022/06/20

[Docker] OpenCV + python のマルチステージビルドを行う

event_note2022/06/20 0:45

Python の OpenCV で GStreamer を使う場合、自前でビルドする必要があり、そのための Dockerfile を以前作成しました。

その時の記事が以下です。

ただ、これで作成した Docker イメージのサイズが 2.5GB くらいあり、もっとサイズを小さくできないかなと考えていました。

その過程でマルチステージビルドというものを知ったのですが、OpenCV のビルドの成果物をどうやって最後のステージに持ってきたらよいか分からず悩んでいたら、以下のページを見つけました。

ビルドステージでは、make install の後、ldconfig で共有リンクを作成しています。
リリースステージでは /usr/local/lib 以下を全てコピーし、ldconfig で共有リンクを作成しています。

というわけで、これを参考にマルチステージビルドを行ってみました。 ただ、私の場合、/usr/local/lib のコピーだけでは依存パッケージが不足していて動かなかったので、/usr/lib/usr/share の中身も全部コピーしたら動くようになりました。

COPY --from=build /usr/lib /usr/lib
COPY --from=build /usr/share /usr/share
COPY --from=build /usr/local/lib /usr/local/lib
RUN ldconfig

以上で、イメージサイズを 1.9GB まで減らせました。
ここらへん、本当に依存しているパッケージだけをコピーするようにできれば、まだまだサイズを小さくできそうではあります。

以下、Dockerfile のサンプルです。

# Stage1: build
FROM ubuntu:20.04 as build

# tzdataのタイムゾーン入力を求められないようにする
# https://tmyoda.hatenablog.com/entry/20210124/1611416396
ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt update && \
    apt install -y --no-install-recommends \
    software-properties-common && \
    update-ca-certificates && \
    # 必要なパッケージをインストール
    apt install -y \
    python3-pip \
    libgl1-mesa-dev \
    libsm6 \
    git \
    wget \
    unzip \
    # OpenCV: Required build dependencies
    # https://docs.opencv.org/4.x/d2/de6/tutorial_py_setup_in_ubuntu.html
    cmake \
    python3-dev \
    python3-numpy \
    libavcodec-dev \
    libavformat-dev \
    libswscale-dev \
    libgtk2.0-dev \
    libgtk-3-dev \
    # OpenCV: Optional Dependencies
    libpng-dev \
    libjpeg-dev \
    libopenexr-dev \
    libtiff-dev \
    libwebp-dev \
    # OpenCV のビルド時に -D WITH_QT=ON を設定するために以下が必要だった
    qtbase5-dev \
    qtdeclarative5-dev \
    # gstreamer のインストール
    # https://gstreamer.freedesktop.org/documentation/installing/on-linux.html
    libgstreamer1.0-dev \
    libgstreamer-plugins-base1.0-dev \
    libgstreamer-plugins-bad1.0-dev \
    gstreamer1.0-plugins-base \
    gstreamer1.0-plugins-good \
    gstreamer1.0-plugins-bad \
    gstreamer1.0-plugins-ugly \
    gstreamer1.0-libav \
    gstreamer1.0-doc \
    gstreamer1.0-tools \
    gstreamer1.0-x \
    gstreamer1.0-alsa \
    gstreamer1.0-gl \
    gstreamer1.0-gtk3 \
    gstreamer1.0-qt5 \
    gstreamer1.0-pulseaudio && \
    # aptのクリア
    apt autoremove && apt clean && rm -rf /var/lib/apt/lists/*

# OpenCV のビルドとインストール
# https://docs.opencv.org/master/d2/de6/tutorial_py_setup_in_ubuntu.html
# https://toriten1024.hatenablog.com/entry/2018/09/29/012205
# https://dlrecord.hatenablog.com/entry/2017/12/15/145356
# https://atmarkit.itmedia.co.jp/ait/articles/1704/10/news134.html
RUN mkdir opencv && \
    cd /opencv && \
    # OpenCV のダウンロード
    wget https://github.com/opencv/opencv/archive/4.5.3.zip --no-check-certificate && \
    unzip 4.5.3.zip && \
    # OpenCV の cmake
    cd /opencv/opencv-4.5.3 && \
    mkdir build && \
    cd /opencv/opencv-4.5.3/build && \
    cmake -D CMAKE_BUILD_TYPE=RELEASE \
    -D BUILD_DOCS=OFF \
    -D BUILD_EXAMPLES=OFF \
    -D BUILD_TESTS=OFF \
    -D WITH_1394=OFF \
    -D WITH_GSTREAMER=ON \
    -D WITH_FFMPEG=ON \
    # cv2.waitKey() を使うためには WITH_QT=ON にする必要があった
    -D WITH_QT=ON \
    .. && \
    # OpenCV のビルド ※ $(nproc) はコア数を返す、make を並列処理させて高速化したい場合は指定する
    #make -j${nproc} && \
    #make -j2 && \
    make && \
    make install && \
    ldconfig && \
    # 後始末
    rm -rf /opencv


# Stage2: release
FROM ubuntu:20.04

# tzdataのタイムゾーン入力を求められないようにする
# https://tmyoda.hatenablog.com/entry/20210124/1611416396
ENV TZ=Asia/Tokyo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt update && \
    # 必要なパッケージをインストール
    apt install -y --no-install-recommends \
    python3-pip \
    python3-numpy \
    # aptのクリア
    && \
    apt autoremove && apt clean && rm -rf /var/lib/apt/lists/*

# Copy binary and dependencies
COPY --from=build /usr/lib /usr/lib
COPY --from=build /usr/share /usr/share
COPY --from=build /usr/local/lib /usr/local/lib
RUN ldconfig

# 必要なプラグインをインストール
RUN pip3 install \
    aiohttp \
    rich \
    ruamel.yaml \
    pymongo==3.12.0 \
    influxdb \
    flask \
    flask-cors \
    pytest \
    pytest-mock \
    mongomock \
    websockets \
    --trusted-host pypi.org \
    --trusted-host files.pythonhosted.org

# bash起動
CMD [ "/bin/bash" ]