ほりひログ

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

Azure Functions の python 関数で、OpenCV を使う

はじめに

Azure Functions で、Python&OpenCV を使うためのメモです。
# 夜中の変なテンションで書き始めたので、所々おかしい部分があるかもしれません。

いきなり結論

カスタム コンテナー を使いましょう。

苦難の道のり

今回のゴールは、

import cv2

これが通ることです。最初のハードルは低く。

検証する関数コードは、HTTP トリガーのテンプレートに、上記の import 文を追加しただけのコードです。

なお、requirements.txt に記載する OpenCV 関連のモジュールは、opencv-contrib-python です。

Azure Functions Core Tools から普通にデプロイ

ここに書かれているやり方です。

docs.microsoft.com

コマンドはこちら。

$ func azure functionapp publish <APP_NAME> --build remote

デプロイした後に、HTTP トリガーを実行してみると、、、

Result: Failure
Exception: ImportError: libgthread-2.0.so.0: cannot open shared object file: No such file or directory
Stack:   File "/usr/local/lib/python3.6/site-packages/azure_functions_worker/dispatcher.py", line 239, in _handle__function_load_request
    func_request.metadata.entry_point)
  File "/usr/local/lib/python3.6/site-packages/azure_functions_worker/loader.py", line 66, in load_function
    mod = importlib.import_module(fullmodname)
  File "/usr/local/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 941, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/site/wwwroot/HttpTrigger/__init__.py", line 4, in <module>
    import cv2
  File "/home/site/wwwroot/.python_packages/lib/python3.6/site-packages/cv2/__init__.py", line 3, in <module>
    from .cv2 import *

見事に、import cv2 でエラーが出ます。
libgthread-2.0.so.0 が無い、と怒られています。
OpenCV の利用に必要なんでしょうか。

Azure Functions Core Tools からバイナリーパッケージと一緒にデプロイ

libgthread-2.0.so.0 opencvググると一発目に出てくる、こちらの Qiita のページに書いてありました。 qiita.com

このエラーは、opencv-python自体はpipでインストールされておりcv2は見つかったものの、依存関係にある外部ライブラリが見つからないというエラーになります。

同じページによると、libopencv-dev を実行環境に入れてやればよさげ。

Azure Functions Core Tools のこちらの オプションを試してみます。

docs.microsoft.com

--additional-packages    ネイティブの依存関係を構築するときにインストールするパッケージの一覧。 (例: python3-dev libevent-dev)。

こんな感じでしょうか。

$ func azure functionapp publish <APP_NAME> --additional-packages "libopencv-dev" --build-native-deps

なにやら、ローカルの docker が動き、docker コンテナー内で apt-get install して、ファイルをローカルにコピーするようです。

Getting site publishing info...
Running 'docker pull mcr.microsoft.com/azure-functions/python:2.0.12493-python3.6-buildenv'....done
Running 'docker exec -t 02bc71 apt-get update'........done
Running 'docker exec -t 02bc71 apt-get install -y libopencv-dev'.................................................................................................done
Running 'docker exec -t 02bc71 chmod +x /python_docker_build.sh'...done
Running 'docker exec -t 02bc71 /python_docker_build.sh'.................done
Running 'docker cp 02bc71:"/.python_packages/." "C:\cases\119062826001861\function1\.python_packages"'...............done
Running 'docker kill 02bc71'....done
Running 'docker exec -t f2f597 chmod +x /ziptofs.sh'...done
Running 'docker exec -t f2f597 /ziptofs.sh'........................done
Running 'docker cp f2f597:"/file.squashfs" "C:\Users\hihorika\AppData\Local\Temp\tmpA3DE.tmp"'.....done
Running 'docker kill f2f597'....done
Uploading package...
Uploading 54.82 MB [##############################################################################]
Upload completed successfully.
Deployment completed successfully.

イケる予感。。。

。。。。。。

だったのですが、同じエラーです。
やっぱり、libgthread-2.0.so.0 が見つかりません。

Result: Failure
Exception: ImportError: libgthread-2.0.so.0: cannot open shared object file: No such file or directory
Stack:   File "/usr/local/lib/python3.6/site-packages/azure_functions_worker/dispatcher.py", line 239, in _handle__function_load_request

    :

  File "/home/site/wwwroot/HttpTrigger/__init__.py", line 4, in <module>
    import cv2
  File "/home/site/wwwroot/.python_packages/lib/python3.6/site-packages/cv2/__init__.py", line 3, in <module>
    from .cv2 import *

どうやら、既定のコンテナーではライブラリーが足りないようです。

カスタム コンテナーで実行環境と関数コードをまとめてデプロイ

最後の手段です。
実行する関数コードと libopencv-dev をインストールしたコンテナー イメージを作成して、丸ごとデプロイします。

コンテナーを使う場合の手順は、下記のドキュメント全てが書いてあります。

docs.microsoft.com

ただし python の関数を作成する時には --python をつけないと、テンプレートの作成ができませんでした。
Azure Functions Core Tools は最新バージョンを使っているつもりなんですが。。。

func new --name MyHttpTrigger --template "HttpTrigger" --python

Dockerfile

Dockerfile は、テンプレートをベースに、libopencv-dev をインストールするよう apt update | upgrade | install 等のコマンドを追記して、docker build でイメージを作成します。

FROM mcr.microsoft.com/azure-functions/python:2.0

ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
    AzureFunctionsJobHost__Logging__Console__IsEnabled=true

COPY . /home/site/wwwroot

RUN apt update && \
    apt upgrade -y && \
    apt install -y libopencv-dev && \
    cd /home/site/wwwroot && \
    pip install -r requirements.txt

作ったイメージは Docker Hub / Azure Container Registry 等のコンテナー レジストリーに push しておきます。

Function App をデプロイ

あとはドキュメント通りに黙々と、

  • ストレージ アカウント
  • App Service プラン
  • Function App

をデプロイし、以下のアプリケーション設定 (環境変数) を 2 つほど設定するだけです。

  • AzureWebJobsDashboard
  • AzureWebJobsStorage

実行してみると、import cv2 は無事通過できました。

この後、OpenCV のモジュールが動作するかは試していませんが、最初の (超低空な) ハードルはクリア、ということで。

おまけ

各リソースのデプロイは、Azure ポータルからもデプロイできます。
でもデプロイした後に、上の 2 つのアプリケーション設定のうち片方 (どっちか忘れた) しか設定されていなかったので、手動で作成して値をコピーしたら動きました。

f:id:horihiro:20190928082948p:plain