🖼️

Node.js + OpenCV の Dockerイメージを作成してアプリで利用する

2021/05/30に公開

概要

Node.js で OpenCV を使いたいという要件が発生した。現在動いている Node のアプリではベースイメージに node:14.15.3-slim を利用していたため、こちらに OpenCV を追加して利用できる環境を整えることが望ましかった。軽量化やバージョンによって揃えるものが違かったり、公式通りうまくいかずと割とはまる部分があったので備忘として残しておく。

対象のモジュールは下記。

モジュール・バージョン リンク
ベースイメージ node:14.15.3-slim https://hub.docker.com/_/node?tab=tags&page=1&ordering=last_updated&name=14.15.3-slim
OpenCV 4.5.2 https://github.com/opencv/opencv/releases/tag/4.5.2
ライブラリ opencv4nodejs@5.6.0 https://www.npmjs.com/package/opencv4nodejs

ゴール

Dockerを用いたNodeアプリでopencv4nodejsライブラリが使えるようになる

結論

下記DockerfileからDockerイメージを作成し、レジストリにpushしておく。このときopencv4nodejsライブラリをイメージ内でインストールしておく。

FROM node:14.15.3-slim

ENV OPENCV_VERSION 4.5.2
ENV OPENCV4NODEJS_DISABLE_AUTOBUILD=1

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    build-essential cmake git ca-certificates libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* && \
    git clone https://github.com/opencv/opencv.git -b ${OPENCV_VERSION} --depth=1 && \
    cd opencv && \
    mkdir build &&  \
    cd build && \
    cmake_flags="-D CMAKE_BUILD_TYPE=RELEASE \
    -D BUILD_EXAMPLES=OFF \
    -D BUILD_DOCS=OFF \
    -D BUILD_TESTS=OFF \
    -D BUILD_PERF_TESTS=OFF \
    -D BUILD_JAVA=OFF \
    -D BUILD_opencv_apps=OFF \
    -D BUILD_opencv_aruco=OFF \
    -D BUILD_opencv_bgsegm=OFF \
    -D BUILD_opencv_bioinspired=OFF \
    -D BUILD_opencv_ccalib=OFF \
    -D BUILD_opencv_datasets=OFF \
    -D BUILD_opencv_dnn_objdetect=OFF \
    -D BUILD_opencv_dpm=OFF \
    -D BUILD_opencv_fuzzy=OFF \
    -D BUILD_opencv_hfs=OFF \
    -D BUILD_opencv_java_bindings_generator=OFF \
    -D BUILD_opencv_js=OFF \
    -D BUILD_opencv_img_hash=OFF \
    -D BUILD_opencv_line_descriptor=OFF \
    -D BUILD_opencv_optflow=OFF \
    -D BUILD_opencv_phase_unwrapping=OFF \
    -D BUILD_opencv_python3=OFF \
    -D BUILD_opencv_python_bindings_generator=OFF \
    -D BUILD_opencv_reg=OFF \
    -D BUILD_opencv_rgbd=OFF \
    -D BUILD_opencv_saliency=OFF \
    -D BUILD_opencv_shape=OFF \
    -D BUILD_opencv_stereo=OFF \
    -D BUILD_opencv_stitching=OFF \
    -D BUILD_opencv_structured_light=OFF \
    -D BUILD_opencv_superres=OFF \
    -D BUILD_opencv_surface_matching=OFF \
    -D BUILD_opencv_ts=OFF \
    -D BUILD_opencv_xobjdetect=OFF \
    -D BUILD_opencv_xphoto=OFF \
    -D CMAKE_INSTALL_PREFIX=/usr/local"; \
    cmake $cmake_flags .. && \
    make -j $(nproc) && \
    make install && \
    sh -c 'echo "/usr/local/lib" > /etc/ld.so.conf.d/opencv.conf' && \
    ldconfig && \
    cd ../../ && \
    npm install -g opencv4nodejs@5.6.0 --unsafe-perm && \
    rm -rf opencv && \
    apt-get purge -y build-essential cmake git ca-certificates && \
    apt-get autoremove -y --purge

上記イメージをベースイメージとし、opencv4nodejs をアプリから利用する。

FROM fastlabel/fastlabel-application-api:0.1.1

WORKDIR /usr/src/api

COPY package.json ./
COPY yarn.lock ./

RUN yarn install

COPY . .

EXPOSE 4000

CMD ["/bin/sh", "entrypoint.sh"]
import * as cv from "/usr/local/lib/node_modules/opencv4nodejs";

// use cv

ざっくり解説

RUN instructionに関しては、OpenCV公式とopencv4nodejsが提供するDockerfileを参考に書いた。一部うまくいかない部分があったのでOpenCVのv4.2.0の公式も参考にした。

ざっと下記のような構成になっている。

  1. OpenCVのインストールに必要なパッケージのインストール
  2. OpenCVのインストール
  3. opencv4nodejsをグローバルにインストール

ビルド時間を短くする工夫

git clone

OPENCV_VERSIONでOpenCVのバージョンを4.5.2に指定し、Gitでクローンしてくる際に-b ${OPENCV_VERSION}でタグを指定することで、必要なソースだけを取得する。

また、--depth=1とすることで、shallow cloneを行い履歴を1つだけ、つまり最新版のみをダウンロードする。

これによってクローンサイズは 483.26MB から 83.40MB まで軽減されるため、それだけビルド時間は速くなる。

自動ビルドの抑制

今回は自分でOpenCVをビルドしているので、opencv4nodejs付属の自動ビルドをOPENCV4NODEJS_DISABLE_AUTOBUILD=1止めておく必要がある。

こうしておかないとOpenCVのビルドに7分かかる場合は2倍の14分かかってしまう。

ちなみに下記のように付属の自動ビルドをONにしてシンプルにやってもみたが意図したビルド成果物ができず泣く泣く諦めた。

FROM node:14.15.3-slim

ENV OPENCV4NODEJS_AUTOBUILD_OPENCV_VERSION 4.5.2

RUN apt-get update && \
    apt-get install -y --no-install-recommends \
    git cmake ca-certificates build-essential && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* && \
    npm install -g opencv4nodejs@5.6.0 --unsafe-perm && \
    apt-get purge -y git cmake ca-certificates build-essential

イメージを軽量にする工夫

ここら辺は基本的にベストプラクティスに則っている。

  • apt-get cleanで、apt-get installによって、キャッシュされている全てのdebファイル(apt用のパッケージファイル)を削除
  • apt-get purge -y build-essential cmake git ca-certificatesで、ビルドに使用したパッケージを削除
  • apt-get autoremove -y --purgeで、apt-get updateで更新に伴い必要なくなったパッケージを削除
  • rm -rf opencvで、ビルドに使用したGitプロジェクトを削除

参考
Dockerfile Tips
Dockerfile のベストプラクティス

なぜイメージ化しておくのか

イメージ化しておかないと、各開発者が最初のビルドでOpenCVのビルドに7分かかり絶対にイラついてしまうから。

イメージを一掃してクリーンな環境に戻したい、という気持ちになったときに遠慮なく実行してもらえないのは開発効率を下げてしまう。

なぜopencv4nodejsをイメージ内でインストールしておくのか

package.jsonopencv4nodejs を記述した状態で、ローカル開発の際に npm installyarn add をして他のライブラリをインストールしたいとする。

この際 package.json のチェックが入り、OpenCVがローカルにないためにインストールに失敗してしまう。これを防ぐためにイメージ内であらかじめインストールしておく。

どちらにせよ、ローカル開発においてはローカルにOpenCVがない限り開発に支障をきたすが、OpenCVによる開発担当以外も全員ローカルにOpenCV環境を作らなければならない状態は避けたほうがいい。

ただしグローバルにインストールしているのでアプリ側で import する場合は下記のようにしなければならないので注意。

import * as cv from "/usr/local/lib/node_modules/opencv4nodejs";

Discussion