AzureFunctionsのBLOB Storage Triggerを利用したファイルのコピー処理

# 初めに

以前書いた[「AzureCLIでAzureFunctionsの構築」](https://qiita.com/kadoi82uma/items/1ab33a3d428fa11cef42)の続きのようなものです。

本記事の目標は、「コンテナーAに画像をアップロードした際に、コンテナーBにコピーする」ことです。
コンテナーへのアップロードのイベントの起因として、`AzureFunctions`を動かします。

イベントの起因には以下のような方法が考えられます。

![Function方法.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176243/9cf58fa1-7f9a-0731-4df5-2ed8d5efbd69.png)

- `BLOB Storage Trigger`の利用
- `Event Grid Trigger`の利用
- `Queue Storage Trigger`の利用

今回は `BLOB Storage Trigger`の利用を試します。

先に結論を書いてしまいますが、`Event Grid Trigger`の方が方法としてはいいようです。

> 注意
BLOB 専用ストレージ アカウントの場合、高スケールの場合、または待機時間を短縮する場合は、Blob ストレージ トリガーではなく、Event Grid トリガーを使用してください。 詳しくは、「[トリガー](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-storage-blob?tabs=python#trigger)」セクションをご覧ください。

# 環境

Windows10 / Powershell / python3.6.8 / .NET Core SDK 2.1.x

## .NET Core SDK のインストール

以下 Trigger を利用する際に必要なためインストールします。

[URL]https://dotnet.microsoft.com/download/thank-you/dotnet-sdk-2.1.802-windows-x64-installer


# 以降の前提条件

- Azureアカウント作成済
- Resouse Groupe 作成済
- Storageアカウント作成済
- Storage Container を input用 output用で2つ構築済
- .NET Core SDK インストール済

# `BLOB Storage Trigger`の利用

まず、BLOB Storage Trigger を利用する場合の手順を記載します。

## プロジェクトの作成

```powershell

# 1. プロジェクトの作成

func init blobTriggerPj

# 2. pythonを選択

# 3. フォルダ移動

cd blobTriggerPj

# 4. 関数アプリの作成

func new

# 5. Azure Blob Storage trigger　を選択

# 6. 関数名を「blobTrigger」に設定

Select a template: Azure Blob Storage trigger
Function name: [BlobTrigger] blobTrigger

```

## 設定ファイルの更新

自動作成された各種ファイルを今回の目標に合わせた形へと修正します。

### `host.json` の更新

BLOBストレージバインディングは、拡張機能を登録する必要があるようです。[公式情報](https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-register#extension-bundles)

```json:host.json
{
    "version": "2.0",
    "extensionBundle": {
        "id": "Microsoft.Azure.Functions.ExtensionBundle",
        "version": "[1.*, 2.0.0)"
    }
}
```

### `function.json` の更新

デフォルトでは、inputのバインディングのみ記載されているので、outputのバインディングを追加します。
また、それに合わせてinputの名称を調整します。

```json:function.json-更新前
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "myblob",
      "type": "blobTrigger",
      "direction": "in",
      "path": "samples-workitems/{name}",
      "connection": ""
    }
  ]
}
```

```json:function.json-更新後
{
  "scriptFile": "__init__.py",
  "bindings": [
    {
      "name": "inputblob",
      "type": "blobTrigger",
      "direction": "in",
      "path": "container-a/{name}",
      "connection": ""
    },
    {
      "name":"outputblob",
      "type":"blob",
      "direction":"out",
      "path":"container-b/{name}-Copy",
      "connection":""
    }
  ]
}
```

`connection`は、不要ではなく空文字の指定が必要です。
connectionは、このバインドに使用するストレージ接続文字列を含むアプリ設定の名前です。
空文字に設定した場合は、Functionsランタイムは、アプリ設定内の AzureWebJobsStorage という名前の既定のストレージ接続文字列を使用するためです。
接続文字列に関しては[以前の記事](https://qiita.com/kadoi82uma/items/1ab33a3d428fa11cef42#localsettingsjson)を参考。

### `__init__.py`の更新

構築したての`__init__.py`には、ログを出力する機能のみ記載されているので、ファイルを出力するコードを記載します。
引数には、`function.json`のbindingsで指定したinとoutのnameを用います。

```py:__init__.py
import logging

import azure.functions as func


def main(inputblob: func.InputStream,outputblob: func.Out[func.InputStream]):
    logging.info(f"Python blob trigger function processed blob \n"
                 f"Name: {inputblob.name}\n"
                 f"Blob Size: {inputblob.length} bytes")
    outputblob.set(inputblob)

```

## 関数アプリのデプロイ

上記の修正した関数アプリをデプロイします。

```powershell
func azure functionapp publish <作成済関数アプリ名> --build remote
```

## 関数アプリの動作確認

デプロイ完了後、作成しているコンテナーAにファイルをアップし、コンテナーBにコピーされることを確認します。
ここが時間がかかります。(5 ~ 10分ほど)


関数アプリで正しく処理がされていることが確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176243/75f17f57-81c1-b551-d9a2-e49757033559.png)

コンテナーBにファイルがコピーされていることも確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176243/40a14fb5-1902-ef51-4758-ddc8dd3c126a.png)


## 終わり
BLOB Storage Trigger　は、上記のような流れで作成できました。
ただ、BLOB　Storage へのアップロードから関数アプリ起動までにだいぶ時間がかかりました。
[公式情報]には以下場合には、Event Grid を利用するように勧めています。

・BLOB ストレージ アカウントを利用する場合
  →　BLOB Storage Trigger は、汎用のStorageアカウントのみ利用可能なため
・高スケール(1秒あたり100を超えるBLOBの更新が発生する場合)
・待機時間の最小化を目指す場合
　→ 関数アプリを従量課金プランで使用しているときに、関数アプリがアイドル状態になっている場合、新しい BLOB の処理が最大で 10 分間遅延する可能性がある
　　解決方法：「従量課金プランからApp Serviceプランへ変更する」または「Event Grid Triggerを利用する」


構築方法としては、後述のEvent Gridを利用するパターンよりも楽なため、速度にこだわりのない要件であればこちらを選択しても良いのかもしれません。