ほりひログ

所属組織の製品 (Azure とか) に関連する内容が多めだけど、個人の見解であって、所属組織を代表する公式情報ではないです。

VScode の Dev Container がいい感じだった

VSCode の Dev Container

開発に必要な環境を全部コンテナーの中に押し込むことで、開発時の環境差がなくなる素敵な仕組みです (当然Dockerが必要ですが) 。

詳しい説明は公式ドキュメントで。

code.visualstudio.com

今回 (だいぶ周回遅れで) この Dev Container を使ってみました。

事のいきさつ

ふと、Azure Functions で OpenCV をいじってみたくなったのですが、以前まとめたエントリーで使った Python は正直なところ雰囲気で書いているので (Python の非同期実行がよくわからんのです) 、まだ慣れている Node.js で OpenCV を使うことにチャレンジしました。

uncaughtexception.hatenablog.com # 「続」「続々」とあるけど一つだけ。

Node.js で OpenCV を動かすときは、opencv4nodejs を使うのが定石のようです。

www.npmjs.com

How to Install の項目を見ると、node-gyp (未だに読み方がわからない) や Windows Build Tools など、いかにもプラットフォーム依存、ネイティブ バイナリーを使用する感じがします。

Native node modules are built via node-gyp, which already comes with npm by default.
(中略)
On Windows you will furthermore need Windows Build Tools to compile OpenCV and opencv4nodejs.

とりあえず WSL から npm install opencv4nodejsコマンドプロンプトから実行してみます。




。。。



おそい。。。



悠久の時を経て、OpenCVOpencv-contrib、opencv4nodejs のビルド/インストールインストールが終わりました。長すぎます。

しかも <PROJECT_ROOT>/node_modules/opencv4nodejs/build/Release/opencv4nodejs.node から OpenCV のバイナリー ファイル (各種 so ファイル) へのリンクが、ローカル PC 上の絶対パス になっている感じが。
つまり、この opencv4nodejs.node が持つ so ファイルへのリンクのパスが Azure Functions のバスと一致しないと、リンクが切れてしまい Azure Functions 上で OpenCV は動作しません

さらに、Azure Functions へのデプロイでファイルを配置できるのは /home/site/wwwroot 以下だけに制限されています。
なので、ローカル側で /home/site/wwwroot にプロジェクトを作って、そこで opencv4nodejs をインストールしないと、Azure Functions にデプロイした時にリンク切れするわけです。

これはめんどくさい。

そこで Dev Container ですよ

というわけで、Dev Container をうまく使って

  1. ビルド時間
  2. デプロイ後のリンク切れ

を解決します。

ちなみに リモート ビルド

Azure Functions にはデプロイ後に Azure 側の環境で npm install を実行するリモート ビルドという機能があるので、これが使えればリンク切れは解消できるはずです。
しかし opencv4nodejs の依存モジュール opencv-build のインストールに必要な gitcmake が入っていない環境で実行されるため、デプロイ後の npm install で失敗します。
なので、残念ながら opencv4nodejs を使う場合、リモート ビルドは使えません。

ビルド時間の削減

Linux のコンテナーを使ってみたので、以降は Azure Functions 側も Linux OS が前提になります。

環境構築のたびに時間がかかるのであれば、docker build 時にコンテナー イメージ内に押し込んでしまえばいいわけです。

これは簡単です。はい。

RUN apt-get update \
      :
    && apt-get -y install \
        git \
        cmake \
      :
    && npm install opencv4nodejs \
      :

当然、コンテナー イメージのサイズは大きくなり、docker build 時と初回の docker pull にはそこそこ時間がかかりますが、初回の docker pull だけ& OpenCV のビルド時間に比べればかわいいものなので、そこは温かい目で見守ってください。

デプロイ後のリンク切れの解消

上にも書いた通り、開発したプロジェクト パッケージをデプロイすると、Azure Functions は /home/site/wwwroot にマウントします。

ということは、コンテナー内でのビルド環境も /home/site/wwwroot/node_modules/opencv4opencv/... という構成になっていれば、Azure Functions へのデプロイ後も正常に so ファイルへリンクされるはずなので、docker build 時も /home/site/wwwroot 内で npm install opencv4nodejs を実行します。

RUN apt-get update \
      :
    && mkdir -p /home/site/wwwroot \
    && cd /home/site/wwwroot \
    && npm install opencv4nodejs \
      :

これで /home/site/wwwroot/node_modules/opencv4opencv/... といった構成が実現できます。

しかしこのまま /home/site/wwwroot に置いておくと、VSCode から Dev Container を開く時にプロジェクトのルート ディレクトリーを /home/site/wwwroot にマウントした時に、プロジェクト ファイル群で上書き?されてしまい、せっかくインストールした node_modules 自体が見えなくなります。

なので今回は docker build 時にインストールした /home/site/wwwroot/node_modulestar でまとめて、適当なディレクトリーにいったん退避する、というアプローチをとります。

RUN apt-get update \
      :
    && npm install opencv4nodejs \
    && tar -zcf /tmp/node_modules.tar.gz ./node_modules/ \
      :

こうして退避しておいた /tmp/node_modules.tar.gz を Dev Container で開いた時に /home/site/wwwroot に展開すると、/home/site/wwwroot にプロジェクト ファイルと opencv4nodejs が収まった node_modules を両立できます。

ちなみに VSCode で Dev Container を使う時、postAttachCommand という、コンテナーにアタッチした後に実行するコマンドを指定できます。

code.visualstudio.com

ここに /tmp/node_modules.tar.gz を tar で /home/site/wwwroot に展開するコマンドを書いておくことで自動化できます。

出来上がったもの

こちらです。

Dev Container

Dev Container の Dockerfile は、Anthony Chu が公開している Deno worker 向けの Dev Container の作り方をベースにして、OpenCV のビルドに必要なツールや opencv4nodejs をインストールしています。
インストール後のいらないファイル (/home/site/wwwroot/node_modules/opencv-build/opencv/build/ 配下の lib 以外) を削除したりして、イメージサイズ、tar ファイルの削減を試みています。

RUN apt-get update \
      :
    # Install OpenCV for node.js 
    && mkdir -p /home/site/wwwroot \
    && cd /home/site/wwwroot \ 
    && npm install opencv4nodejs \
    && ls -d /home/site/wwwroot/node_modules/opencv-build/opencv/build/* | grep -v "lib" | xargs rm -rf \
    && tar -zcf /tmp/node_modules.tar.gz ./node_modules/ \
    && rm -rf /home/site/wwwroot/* \
      :

devcontainer.json

postAttachCommand の例は、テンプレート リポジトリ内の devcontainer.json として公開しています。

github.com

ぜひお試しあれ。

で、やりたかったものは?

一応できました。

上のテンプレート リポジトリーから作ったOpenCV を使ってグレースケール化 or 線画化するシンプルな関数です。

github.com

実行例

  • グレースケール化
    f:id:horihiro:20201004133519p:plain -> f:id:horihiro:20201004133644p:plain
  • 線画化
    f:id:horihiro:20201004133519p:plain -> f:id:horihiro:20201004133821p:plain

以上。