【初心者向け】Azure Functions をローカルで実行してみる

# はじめに

本記事は「__これからAzure Functionsを始めようとしている方向け__」の記事になります。
まずはAzureへのデプロイの前、ローカル環境構築～ロカールでお試しに動かすまでとなります。

# Azure Functiosとは

まとめるとサーバレスでスクリプト実行できるPaaSサービスです。
ここでサーバーレスとはサーバーを使わないという意味ではなくサーバーはAzureが管理するためユーザは管理する必要がないということになります。

詳しくは公式ドキュメントをご覧下さい。
[Azureuf Functionsの公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/azure-functions/)

# この記事ので行うこと

Windows環境で、[Azureuf Functionsの公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/azure-functions/)に沿ってPythonを例にローカルにFunctionsプロジェクトを作成し、ローカルで実行させます。

前提として __VSCode__ と __Python__ は導入されているものとします。

※202/6/29時点でPythonの対応している最新バージョンはAzure Functionsのランタイムが4.xの場合3.10です。またPythonのプログラミングモデルv2が対応したのでそちらを採用しています。

※この記事ではAzure Functionsのランタイムは4.x、Pythonのバージョンは3.10で進めます。

# 環境準備

## Azure Functions Core Tools のインストール

[こちら](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-run-local?tabs=windows%2Cportal%2Cbash&pivots=programming-language-python#install-the-azure-functions-core-tools) から __v4.x - Windows 64 ビット__ のインストーラをダウンロードしてインストールしてください。
(VSCodeでのデバッグには64ビットである必要があります。)
画面に従えば問題なくインストールできると思います。

:::note warn
たまにVSCodeがAzure Functions Core Toolsを認識せずデバッグできない場合がありますが、端末を再起動すること解消します。
:::

## Visual Studio Code の拡張機能のインストール

以下の拡張機能をVSCodeにインストールします。
* Python拡張機能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/5863537e-2eef-02bb-7252-e53991753bae.png)

* Azure Functions 拡張機能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/c2d8f5ac-0931-5b8a-4b68-eae427fc7b17.png)

* Azurite V3 拡張機能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/a5abd3f4-eb37-aa0f-3aa3-863ecfc94719.png)

# プロジェクトの作成

新規に作成したフォルダでプロジェクトを作成します。ここでは`~\func_test\`というフォルダを作成してそこにプロジェクトを作ります。指示に従っていけばOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/d0d3d14a-6818-29d0-8b8a-44a2bc5ce702.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/5ea8d7c7-d306-6bb7-5c73-ef09e7570e9a.png)

今回はPythonのプログラミングモデルv2で作成するので画像の一番上を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/785c4e96-4083-4da4-2503-113d643fa4d9.png)

pythonのバージョンを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/c2f58780-1b6e-ce42-3a81-6a52f504f708.png)

起動トリガーを選択します。HTTP triggerは公式ドキュメントにあるので、今回は __Timer trigger__ を選択します。（他に何があるかは公式ドキュメントをご覧ください。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/41f21e79-57e6-f8f6-da0e-cf21329464a6.png)

起動関数名を入力します。今回はデフォルトのままにします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/7cfe557b-3f36-560f-af1c-aae4e59e7015.png)

起動時間（間隔間隔）を設定します。ここでは10分間隔で起動するようにするために`0 */10 * * * *`と入力します。
※これは後から変更できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/08de6973-9c17-d8d3-c3d7-68ba80bfaf4a.png)

次のようなファイルが作られプロジェクトが作成されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/858a132f-2210-a0fe-4639-b54c70710e59.png)

# 稼働確認

1. 実行されることが分かるように以下のコードを最終行に追加します。

    ```py:function_app.py
    ・・・
    print('Hello World')
    ```

1. `<F1>`でコマンドパレットを開き`Azurite: Start`でAzuriteを起動します。Visual Studio Codeの下部ステータスバーにこのような表示がされればOKです。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/f4cb38c6-1f26-f254-c7a2-b24b8e0a644b.png)

1. `<F5>`でローカル起動します。すると次のようなポップアップが表示されるので、ここで`Use Local Emulator`を選択します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3473569/8a04db6d-1c51-d404-2098-4a88c6aec2bc.png)

    これを選択すると、__local.settings.json__ ファイルの __AzureWebJobsStorage__ という設定値に`UseDevelopmentStorage=true`が追加されます。先ににこの設定をしておけば上記ポップアップは出力されません。

    起動すると以下のようにターミナルに出力されると思います。
    
    ```powershell
     フォルダー func-test で実行するタスク: .venv\Scripts\python -m pip install -r requirements.txt 
    
    Requirement already satisfied: azure-functions in c:\~\func-test\.venv\lib\site-packages (from -r requirements.txt (line 5)) (1.15.0)
    
    [notice] A new release of pip is available: 23.0.1 -> 23.1.2
    [notice] To update, run: C:\~\func-test\.venv\Scripts\python.exe -m pip install --upgrade pip
     *  ターミナルはタスクで再利用されます、閉じるには任意のキーを押してください。 
    
     *  フォルダー func-test で実行するタスク: .venv\Scripts\activate ; func host start 
    
    .venv\Scripts\activate : このシステムではスクリプトの実行が無効になっているため、ファイル C:\~\func-test\.venv\Scripts\Activate.ps1 を読み込むことができません。詳細については、「about_Execution_Polic
    ies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。
    発生場所 行:1 文字:1
    + .venv\Scripts\activate ; func host start
    + ~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : セキュリティ エラー: (: ) []、PSSecurityException
        + FullyQualifiedErrorId : UnauthorizedAccess
    Found Python version 3.10.11 (py).
    
    Azure Functions Core Tools
    Core Tools Version:       4.0.5198 Commit hash: N/A  (64-bit)
    Function Runtime Version: 4.21.1.20667
    
    [2023-06-29T07:57:54.454Z] Worker process started and initialized.
    
    Functions:
    
            TimerTrigger: timerTrigger
    
    For detailed output, run func with --verbose flag.
    [2023-06-29T07:57:54.685Z] Executing 'Functions.TimerTrigger' (Reason='Timer fired at 2023-06-29T16:57:54.6576473+09:00', Id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
    [2023-06-29T07:57:54.687Z] Trigger Details: UnscheduledInvocationReason: RunOnStartup
    [2023-06-29T07:57:54.738Z] Hello World
    [2023-06-29T07:57:54.744Z] Python timer trigger function ran at 2023-06-29T07:57:54.735865+00:00
    [2023-06-29T07:57:54.766Z] Executed 'Functions.TimerTrigger' (Succeeded, Id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, Duration=97ms)
    [2023-06-29T07:57:59.422Z] Host lock lease acquired by instance ID 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'.
    [2023-06-29T08:00:00.017Z] Executing 'Functions.TimerTrigger' (Reason='Timer fired at 2023-06-29T17:00:00.0167479+09:00', Id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)
    [2023-06-29T08:00:00.023Z] Hello World
    [2023-06-29T08:00:00.024Z] Executed 'Functions.TimerTrigger' (Succeeded, Id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, Duration=7ms)
    [2023-06-29T08:00:00.023Z] Python timer trigger function ran at 2023-06-29T08:00:00.021396+00:00
    ```
    
    途中エラーが出るかもしれませんが`Hello World`が表示されればOKです。
    
1. ターミナル上で`<Ctrl + c>`を入力すると停止できます。

# 起動に関する設定の変更

起動設定は次のデコレーターで記述されています。

```py:function_app.py
@app.schedule(schedule="0 */10 * * * *", arg_name="myTimer", run_on_startup=True,
              use_monitor=False) 
```

このデコレーターについての公式ドキュメントは[こちら](https://learn.microsoft.com/ja-jp/azure/azure-functions/functions-bindings-timer?tabs=python-v2%2Cin-process&pivots=programming-language-python)になります。

各プロパティは次の通りです。

1. schedule
    起動タイミングを設定します。NCRONTAB式またはTimeSpan値で指定しますが、TimeSpan値はApp Serviceプランでのみしか使えないです。CRON式は後述します。
1. arg_name
    関数コード内のタイマー オブジェクトを表す変数の名前です。
1. run_on_startup
    関数をランタイムの開始時に呼び出すかを指定します。つまり、デプロイ直後や、再起動、スケールアウトするときなどに関数がscheduleに関係なく実行されます。プロジェクトはTrueで作成されますが基本的には __False__ を設定します。
1. use_monitor
    ここにTrueを設定した場合、再起動した場合でもスケジュールが正しく維持されることが保証されます。

### NCRONTAB式について

フィールドの各項目は次の通りです。

```{second} {minute} {hour} {day} {month} {day-of-week}```

例えば `0 5 * * * *` であれば second=0, minute=5 なので毎時5分0秒にに実行されます。
`*` はすべての値を表すので `0 * 5 * * *` は5時台に毎分実行されます。
`-` は範囲をを表すので `5-7 * * * * *` であれば毎分5, 6, 7秒の3回実行となります。
`,` は列挙する際に使えるので `5,8,10 * * * * *` は毎分5，8，10秒の3回実行です。
`/` は間隔値を示し次のように使用します。`0 */5 * * * *` これは5分ごとの実行を表します。

month, day-week は数値、名前、もしくは名前の省略形で記述できます。
注意点としてNCRONTAB式は __タイム ゾーンは協定世界時 (UTC)__ です。
ただしLinux従量課金プラン以外ではタイムゾーンの指定ができます。

# おわりに

本記事では冒頭に示した通りローカルでAzure Functionsをローカルで実行することを目的としました。いかがでしたでしょうか。
この後、Azureリソースを作成しデプロイを行えばAzure上で実行できます。ただし無料の試用サブスクリプションでないと料金が発生しますのでお気を付けください。

もし記事に訂正などあればコメントでご指摘いただけますと幸いです。

それでは。

# 参考資料
* Azure Functionsの公式ドキュメント：[https://learn.microsoft.com/ja-jp/azure/azure-functions/](https://learn.microsoft.com/ja-jp/azure/azure-functions/)