Azure FunctionsでPythonとMeCabを使う

# 本記事の目的
Azure Data Factoryの前処理で、MeCabを利用した簡単な自然言語処理（形態素解析＋α程度）をしたい。
関数として実装して、後々LogicAppsとか色々なサービスから呼び出せれば便利そう。ということで2つの実装方法を検討した。

1. Azure Functions（本記事）
2. Azure DataBricks（[Azure DatabricksでPythonとMeCabを使う](https://qiita.com/ryokita/items/476bf93b4bf51f99c7cb)）

機械学習のように重たい処理を行うわけではないのでAzure Functionsで十分だろう、ということで実装してみた。

先に結論を書くと
**・Azure FunctionsのHTTP Requestをトリガーとする関数は以下のURLを参考にすれば実装できる**
>Visual Studio Code を使用して Azure Functions プロジェクトを作成する
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-create-first-function-vs-code?pivots=programming-language-csharp

**・Mecabは.vscodeのrequirements.txtに「mecab-python3」を追記すればFunctions側でも使える**

**・Azure Functionsの毎月の無料枠がわりと太っ腹なので、しばらくは無料で試せそう**

あとはいくつか躓いた点の備忘録です。

理解不足の点も多々あるので、間違いなどあればご指摘下さい。

# Azure Functions概要
イベントドリブン型のサーバーレスコンピューティングプラットフォーム。設定したトリガーで起動し、関数の実行中のみコンピューティングリソースを消費するので無駄なコストを削減できる。

### 課金体系
Functionsのホスティングプランには次の3つがあるが、一番左の「従量課金プラン」がいわゆる通常のサーバーレス。今回はこれを選択した。
<img width="1263" alt="スクリーンショット 2020-05-04 19.32.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/d4f14dbf-7699-4dec-ac1f-d6c60dbda4a5.png">
>Azure Functions - 料金設定
https://azure.microsoft.com/ja-jp/services/functions/#pricing

なお、課金は実行数、実行時間、およびメモリの使用量で決まる。毎月リセットされる無料枠があり、「100万実行回数」「400,000GB秒」までは無償らしい。

### ランタイムスタック（言語）
.Net Core, Node.js, Python, Java, Powershell Coreから選択できる。
1つ注意する点だが、ランタイムスタックにPythonを選択した場合、OSはLinuxのみ対応となる。Linuxの従量課金プランは現状、日本の両リージョンでは未対応とのことで、別のリージョンを選択する必要がある。（2020年5月現在）
※東日本リージョンでは、2020年の第2QにGA予定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/b06f7d93-241e-3759-6aef-cabd9d45b985.png)

対応リージョンは「[リージョン別の利用可能な製品](https://azure.microsoft.com/ja-jp/global-infrastructure/services/?products=functions&regions=us-east,us-east-2,us-central,us-north-central,us-south-central,us-west-central,us-west,us-west-2,japan-west,japan-east)」から確認できる。

### トリガー
Functionsを起動するためのトリガーには、HTTP Request, Timer, BLOB, eventhubなどがある。今回は使い勝手が良さそうなのでとりあえずHTTP Requestで実装。BLOBファイルが作成されたら実行、というのも使いやすそう。

# Azure PortalからFunctions Appsを作成
従量課金プランでPythonを使いたかったので、今回はとりあえずリージョンをEast Asiaで作成。（従量課金プラン Linuxに対応していれば別にどこでも良い。地理的にはソウルのKorea Centralの方が近かった。）
<img width="906" alt="スクリーンショット 2020-05-04 20.08.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/9b3bff7e-da27-b6bf-62e3-702f3bc82def.png">

これでLinuxの従量課金が選択できるようになった。
またFunctionsからリンクするストレージアカウントも用意する必要があるので、今回は新規作成。
<img width="753" alt="スクリーンショット 2020-05-04 20.10.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/2817965b-3e82-32ab-ca77-d81c8fbdd366.png">

監視のためのApplication Insightもこのタイミングで作成しておく。
これで実行のログが確認できるようになる。

<img width="574" alt="スクリーンショット 2020-05-04 20.12.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/43922c90-0ab0-92ae-217f-d19874a11a87.png">

これで作成すると4つのリソースが作成される。
1. 関数アプリ(Functionsのこと)
2. Application Insights
3. ストレージアカウント
4. App Serviceプラン

料金プランで消費量（サーバーレス）を選択したのに、なぜApp Serviceプランというリソースが作成されるのかは不明。

# ローカルでの開発とFunctionsへのデプロイ
.NETの場合はAzure Portalの画面上で関数を作成できるのだが、Pythonの場合には残念ながら対応していない。したがってローカルで開発とテストを実行した後、Functionsにデプロイする、という手順になる。

<img width="529" alt="スクリーンショット 2020-05-04 20.53.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/2ce1b0c6-f378-21c6-c02f-a16f68f7972f.png">

<img width="545" alt="スクリーンショット 2020-05-04 20.56.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/d7e014ab-9034-51e4-e7f4-42059c9e9b1e.png">


具体的な手順は以下の記事を参照して進めた。

>Visual Studio Code を使用して Azure Functions プロジェクトを作成する
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-create-first-function-vs-code?pivots=programming-language-csharp

ローカルに以下の環境が必要になるので、インストールしておく。
・Node.js
・Python 3.8、Python 3.7、Python 3.6のいずれか
・Visual Studio Code
・Visual Studio Code 用の Python 拡張機能
・Visual Studio Code 用 Azure Functions 拡張機能
・Azure Functions Core Tools 

# 躓いた点

### 1. ローカル環境でのAzure Functions実行時のエラー1
FunctionsをLocalで実行する際に権限エラー。

エラーメッセージ
`このシステムではスクリプトの実行が無効になっているため、ファイルを読み込むことができません。`

PowerShellのExecution Policyを変更する必要があった。
初回だけExecutionPlicyをRemoteSignedにあげて実行すると、その後はRestrictedに戻してもエラーは解消された。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/7357777d-ad27-67fa-724e-d2bcd7a9bf15.png)

>About Execution Policies
https://docs.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7


### 2. ローカル環境でのAzure Functions実行時のエラー2
\_\_init\_\_.py上での以下のコードでエラー
`import azure.functions as func`

エラーメッセージ
`Unable to import 'azure.functions' pylint(import-error) [3, 1]`

ローカルに複数のPythonバージョンが混在していることによる問題だったようで、他のランタイムに切り替えることによって解決。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/f65ea1cd-e865-17d8-ec3c-83f1f571df2f.png)

### 3. FunctionsへのMeCabの追加
.vscodeのrequirements.txtに対してライブラリを追記をするとFunctions側でpip installしてくれるっぽい。
ということで以下を追記。
・mecab-python3

あとはいつも通り。

```python:__init__.py
import MeCab
mecab = MeCab.Tagger("-Ochasen")
parsedsentence = mecab.parse(sentence)
```

# まとめ
FunctionsにおけるMeCab利用は意外にもすぐできた。今回の目的に合致するものはFunctionsで十分。
（ただしmecabの辞書本体がどこにあるのか含め、なぜ動くのかいまいち理解できてません...）

従量課金プランは最大のタイムアウトまでの時間が10分になっているので、あくまでも軽量の実行用。もう少し手の込んだものを実行する場合は、Functionsでも別のホスティングプランを利用するか、いっそDataBricksを採用するのが良さそう。

>Azure Functions のスケールとホスティング
https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-scale#service-limits

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/a6966db2-3c62-694c-da12-2d36ef84c3a4.png)


### 費用の確認
Azure PortalからMetricを呼び出すことによって、かかった費用が分かる。
「入力の1文に対してMecabで形態素解析した結果を返す」というシンプルな関数を1回実行した結果が以下の通り。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/63da438d-6580-a752-d80e-a9e110bbbbf7.png)

単位はFunction Execution Unitを選択すればよい。Function Execution Unitの単位は【MB milliseconds】なので、これを課金の単位である【GB seconds】に変換する。

今回は163.58 k = 163,580 MB millisecondsなので、
163,580 / 1,024,000 = 0.15974609375 【GB seconds】

毎月「100万実行回数」「400,000GB秒」までは無償ということで、多少テストするくらいなら全然無償枠で収まりそう。
もちろん、同時に作成しているBLOBなどの課金は発生するので注意。