Azure Functionsを用いたサーバーレスなTwitter botを開発してみた（その２、Azureにリソースを作ってデプロイしたお話）

# これまでのお話

前回の記事はこちら。

https://qiita.com/ataraken/items/ded057b57959d418ec53

Azure Functionsを使ってサーバレスアーキテクチャのTwitter botサービスを作ろうとしたらPowerShellスクリプトの実行ポリシーでドはまりした。今回からはいよいよ開発に着手します、たぶん。

# Azure Functionsのローカル・デバッグ実行でdotnet.exeが見つからない問題

前回の記事でAzure Functionsのローカル実行でできるようになったので試してみます。

## dotnet.exeが見つからない

F5を押下してデバッグ実行したところ、次の表示がされました。よく読むとdotnet.exeが見つからないと言っています。

```powershell
> Executing task: .venv\Scripts\python -m pip install -r requirements.txt <

Requirement already satisfied: azure-functions in simple-twitter-bot\.venv\lib\site-packages (from -r requirements.txt (line 5)) (1.7.0)
WARNING: You are using pip version 20.1.1; however, version 21.1.2 is available.
You should consider upgrading via the 'simple-twitter-bot\.venv\Scripts\python.exe -m pip install --upgrade pip' command.

Terminal will be reused by tasks, press any key to close it.

> Executing task: .venv\Scripts\activate ; func host start <

Found Python version 3.7.9 (python3).

Azure Functions Core Tools
Core Tools Version:       3.0.3477 Commit hash: 5fbb9a76fc00e4168f2cc90d6ff0afe5373afc6d  (64-bit)
Function Runtime Version: 3.0.15584.0

[2021-06-15T15:10:42.877Z] File 'C:\Program Files\dotnet\dotnet.exe' is not found, 'dotnet' invocation will rely on the PATH environment variable.
[2021-06-15T15:10:43.460Z] File 'C:\Program Files\dotnet\dotnet.exe' is not found, 'dotnet' invocation will rely on the PATH environment variable.
[2021-06-15T15:10:43.721Z] File 'C:\Program Files\dotnet\dotnet.exe' is not found, 'dotnet' invocation will rely on the PATH environment variable.

Functions:

        TimerTriggerTweet: timerTrigger

For detailed output, run func with --verbose flag.
[2021-06-15T15:10:46.530Z] Worker process started and initialized.
```

## .Net Core 3系のインストール

MicrosoftのこのページによるとPythonを使う場合も.Netのruntimeが必要になるようです。当方のPythonは3.7だったので、最新の.Net Core 3系をインストールすることにしました。

https://docs.microsoft.com/ja-jp/azure/azure-functions/functions-versions

インストールはこちらのページをご覧ください。当方は執筆時最新の.Net Core Runtime v3.1.16(x64)をインストールしました。

https://docs.microsoft.com/ja-jp/dotnet/core/install/windows?tabs=net50

## インストールして再び実行

再びVSCodeでF5を押下します。今回は無事Core Toolsが起動したようです。

```powershell
> Executing task: .venv\Scripts\python -m pip install -r requirements.txt <

Requirement already satisfied: azure-functions in simple-twitter-bot\.venv\lib\site-packages (from -r requirements.txt (line 5)) (1.7.0)
WARNING: You are using pip version 20.1.1; however, version 21.1.2 is available.
You should consider upgrading via the '\simple-twitter-bot\.venv\Scripts\python.exe -m pip install --upgrade pip' command.

Terminal will be reused by tasks, press any key to close it.

> Executing task: .venv\Scripts\activate ; func host start <

Found Python version 3.7.9 (python3).

Azure Functions Core Tools
Core Tools Version:       3.0.3477 Commit hash: 5fbb9a76fc00e4168f2cc90d6ff0afe5373afc6d  (64-bit)
Function Runtime Version: 3.0.15584.0


Functions:

        TimerTriggerTweet: timerTrigger

For detailed output, run func with --verbose flag.
[2021-06-15T15:37:15.664Z] Worker process started and initialized.
```

# Azure Functionsのローカル・デバッグ実行でStorage Accountに接続できない問題

F5を押下してCore Toolsを起動するたびに次のダイアログが表示されていました。これを解消するためにはAzure Storage Accountに接続するか、ローカルPCにストレージエミュレーターをインストールする必要があります。

![2021-06-16-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/0cc1a67c-6884-03c0-b680-2c7dcb87a394.png)

余談ですが、このダイアログはTimerTriggerの場合に表示されるようです。

## ストレージエミュレーターの構築

`Learn more`ボタンを押下すると次のページに遷移しました。こちらを参考にしてAzureのストレージエミュレーターを構築します。

https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-emulator

具体的には上記のURLに「スタンドアロンのインストーラー」へのリンクが張ってあるので、それをクリックしてインストーラーをダウンロードしてインストールするだけです。そうすると`Microsoft Azure Storage Emulator`がインストールされるのでクリックしてください。

![2021-06-18-000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/650b15b7-14db-0eba-4da9-1eb47777366d.png)

クリックするとコマンドプロンプトが立ち上がるのですが、エミュレーターの起動に失敗しました。どうやら初回仕様時にSQL Serverを指定して`AzureStorageEmulator.exe init`コマンドを実行しなければならないようです。Microsoft SQL ServerがローカルPCにインストールされている必要があり、Azure Storage EmulatorはそのSQLサーバーとAzure Core Toolsの接続を行ってくれるツールのようです。コマンドは以下のURLからご確認ください。

https://docs.microsoft.com/ja-jp/azure/storage/common/storage-use-emulator#options

ローカルPCにSQL Serverを構築するのが面倒なのでこの手法はここで諦めました。どちらにしても最終的にAzureにFunctionsをデプロイするので、Azure Storage Accountを作成してそこに直接つなぐことにします。

## Azure Storage Accountの作成

今回はこちらの記事を参考にVSCodeからAzure上にAzure Functionsリソースを作成します。Azure Functionsを作成するとAzure Storage Accountも同時に作成されます。

https://docs.microsoft.com/ja-jp/azure/azure-functions/create-first-function-vs-code-python#publish-the-project-to-azure

## VSCodeからAzure Functionsサービスの作成

VSCodeでアジュールのマークを押下します。

![2021-06-19-000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/f99ed500-aa90-5282-36c2-af5d42b0531f.png)

続いて、FUNCTIONSと文字列があるところに5つアイコンが並んでいますが、真ん中の雲に上矢印（↑）のあるアイコンを押下します。そうすると、VSCodeでチュートリアルのようなものが始まるのでそれに従って入力します。まずはAzureへのサインインでした。（この辺はバージョンによって変わるので適宜対応してください。）

![2021-06-19-001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/29eec7c9-2f1e-d6dc-d226-6a792e8c2144.png)

続いて、新しいFunctsionsを作成するか聞かれました。今回は`Create new Functions App in Azure...`を選択しました。

![2021-06-19-002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/edd36058-4878-37a5-316b-c1d069f9930d.png)

Pythonのラインタイムが聞かれたのでローカルPCにインストールしているバージョンを記載するとよいと思います。

![2021-06-19-003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/c57532ed-e696-5db4-bd8a-370e1ec219e8.png)

![2021-06-19-004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/fa8e43b9-cab3-5f45-7eda-36765df1aa05.png)

適宜対応してください。Azure Functionsリソースを作成すると、リソースグループと次のリソースが作成されます。

* App Service プラン
* Application Insights
* Azure Functions
* Azure Storage Account

VSCodeから作成するとAzure FunctionsがApp Serviceプランで作成されるようです。Azure Portalでの表示を抜粋します。App ServiceプランはVMを借りるのと同じだと考えるとよいと思います。コストを考えると従量課金プランが望ましいのでイマイチですね。従量課金プランがサーバーレス・アーキテクチャに近しいと思います。（すみません、ちゃんとわかってないです。）

![2021-06-19-005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/8849935b-8843-dcdc-a51a-e7a62f5018bd.png)

## Azure PortalからAzure Functionsを従量課金プランで作成する

VSCodeからAzure Functionsリソースを作成したところ、App Serviceプランで作成されたのでAzure Portalからリソースを作成することにします。先ほど作成したリソースはリソースグループごと削除しました。

なお、`Create new Functions App in Azure...Adbanced`を選択すると選べるのかもしれませんが、今回は調査しませんでした。

### Azure PortalでAzure Functionsを作成

リソースグループを作成し、そこでAzure Functionsリソースを生成します。リソースの追加でAzure Functionsを選択してください。日本語名は`関数アプリ`でした。

![2021-06-21-030.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/bb29e627-e2f0-9cf4-54cd-ae171d0c0cfd.png)

`ホスティング`で「プラン」→「プランの種類」→「消費量（サーバーレス）」を選択してください。

![2021-06-21-031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/8efead6d-87b7-3ddc-a664-bd0175d3ea9c.png)

移行はデフォルトの設定で作成しました。

### VSCodeからプロジェクトをデプロイする

VSCodeのAzureボタンを押下して`Deploy to Function App...`を押下します。先ほど作成したAzure Functionsリソースが選択できるようになります。（白色で塗りつぶしているところに表示されます。）それを押下してください。

![2021-06-21-032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/25d756d4-ecef-f9d3-46be-856d59614ec4.png)

ダイアログが表示され、デプロイされるか問われます。大丈夫だったら`Deploy`ボタンを押下します。

![2021-06-21-021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/7d27e690-fb1b-7d8d-8879-30831c904a61.png)

VSCodeの向かって右下に次の表示がなされます。デプロイ中のようです。

![2021-06-21-033.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/5e682374-0655-36b5-7d68-9ab83cfbac3a.png)

完了すると次の表示になります。

![2021-06-21-034.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/96b91cc6-028d-2671-5d00-088a53c12ee6.png)

## ローカル実行時にAzure Storage Accountに接続するための設定

Azure PortalからStorage Accountの接続文字列を取得します。ストレージアカウントのアクセスキー画面に遷移してください。`キーの表示`ボタンを押下して接続文字列を表示しコピーしてください。

![2021-06-21-035.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/5bda0b80-f8f3-1b14-9ffe-5172ad0c2ee7.png)

`local.settings.json`に`AzureWebJobsStorage`というキーがあります。

```json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "python",
(以下略)
```

先ほどコピーした接続文字列を貼り付けてください。

```json
{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "<Azure Storage Accountの接続文字列>",
    "FUNCTIONS_WORKER_RUNTIME": "python",
(以下略)
```

F5を押下してローカルデバッグ実行します。今度はダイアログが表示されることなく実行できました。

```powershell
> Executing task: .venv\Scripts\python -m pip install -r requirements.txt <

Requirement already satisfied: azure-functions in \simple-twitter-bot\.venv\lib\site-packages (from -r requirements.txt (line 5)) (1.7.0)
WARNING: You are using pip version 20.1.1; however, version 21.1.2 is available.
You should consider upgrading via the '\simple-twitter-bot\.venv\Scripts\python.exe -m pip install --upgrade pip' command.

Terminal will be reused by tasks, press any key to close it.

> Executing task: .venv\Scripts\activate ; func host start <

Found Python version 3.7.9 (python3).

Azure Functions Core Tools
Core Tools Version:       3.0.3477 Commit hash: 5fbb9a76fc00e4168f2cc90d6ff0afe5373afc6d  (64-bit)
Function Runtime Version: 3.0.15584.0


Functions:

        TimerTriggerTweet: timerTrigger

For detailed output, run func with --verbose flag.
[2021-06-19T13:12:48.026Z] Worker process started and initialized.
[2021-06-19T13:12:50.482Z] Host lock lease acquired by instance ID '000000000000000000000000919C9F3E'.
```

# 関数のデバッグ実行

TimerTriggerの関数はF5でデバッグ実行してしばらく待つと実行されますが、意思を持って実行する方法を解説します。

Core Toolsを実行したまま、VSCodeのアジュールアイコンを押下し、Azure:Functions領域に移動し、実行したい関数を選んで（今回の場合は`TimerTriggerTweet`）右クリック、`Execute Function Now...`を押下します。

![2021-06-16-000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1278980/a2b7ac5b-be8f-0eb9-6812-2a509d5e3ee7.png)

コンソールに次のようなログが表示されました。

```powershell
[2021-06-21T14:49:53.294Z] Executing 'Functions.TimerTriggerTweet' (Reason='This function was programmatically called via the host APIs.', Id=121725c7-d3c2-4fea-9436-22095b5cb57b)
[2021-06-21T14:49:53.304Z] Python timer trigger function ran at 2021-06-21T14:49:53.301974+00:00
[2021-06-21T14:49:53.308Z] Executed 'Functions.TimerTriggerTweet' (Succeeded, Id=121725c7-d3c2-4fea-9436-22095b5cb57b, Duration=171ms)
```

# まとめ

プロジェクトをAzureにFunctionsリソースを作成し、そこにプロジェクトをデプロイしました。Azure Storage Accountの接続文字列をローカル実行の設定に追加し、ローカル・デバッグ実行することができました。次回はいよいよコードを書きます。