AzureFunctions＋Dashでお手軽ダッシュボード

# はじめに

ちょっとしたご縁からAzureを使用することになりました。
今回はそこで集めていたデータの可視化をしようとGrafanaに出してみましたが、元々のテーブルレイアウトがそこまで深く考えて設計していなかったこともあり、いまいちしっくりこず。最終的にDashをつかってダッシュボードを手作りしてみました。

環境は最初、WebAppServiceに立てていたんですが、無料プランではそもそも常時起動が設定できなかったり、B1以上のプランにして常時起動させていてもリポジトリにPushしてからビルド待ちで数分かかってしまうなどどうも使い勝手が悪く。[Azure Functions で Flask/WSGI のAPIを動かす](https://qiita.com/hkato/items/263c09095aaf53d6fcf7)という素晴らしい記事を見つけて試しにうごかしてみたら、割と満足がいく結果になった[^1]ので記録に残しておきます。

[^1]: すごく早いっていうわけではないけど、WebAppServiceより起動までが早い気がするのと、VSCodeのAzurePluginで配備すれば、配備終了＝利用可能になるタイミングなので使っていてストレスが少ない（気がする）。かつ、ほぼ無料の枠内で運用可能。


# 環境構築

[クイックスタート: コマンド ラインから Azure に Python 関数を作成する](https://docs.microsoft.com/ja-jp/azure/azure-functions/create-first-function-vs-code-python)に従って、サンプルプロジェクトを一度つくってみて構築＆練習。開発に使用する仮想環境はvenvでもcondaでも使っているもので大丈夫。

なお、2021/11時点ではAppleSilicon版のM1 MacはローカルでFunctionsが実行できない[^2]ので推奨しません。DockerImageも動くものがないし。もちろんただのFlaskアプリとして動作確認はできるので、開発できないことはありません。
[^2]: azure-functions-core-toolsの中に持っているcythonバイナリがx86固定。バイナリ入れ替えたり試してみたけど、いまのところ動かせてない......


# 関数を作成する

プロジェクトフォルダを作成して、その中で関数を作成します。nameはどうせurlとしては使われないので、覚えやすいもの、わかりやすいもので大丈夫。またflask側で認証を行えるのでauthlevelはanonymousでOK。

```
mkdir sample_project && cd sample_project
func new --name functions_wsgi --template "HTTP trigger" --authlevel "anonymous"
```

# Dashプロジェクトを追加する

今回はサンプルとして[dash-bootstrap-components](https://github.com/facultyai/dash-bootstrap-components)のexamplesの中から使用します。
[responsive-collapsible-sidebar](https://github.com/facultyai/dash-bootstrap-components/blob/main/examples/python/templates/multi-page-apps/responsive-collapsible-sidebar)の中身を、dash_appというフォルダを作成していれておきます。

この時点でsample_projectの中身はこんな感じの構造になっているはず。

```
.
├── dash_app
│   ├── assets
│   │   └── responsive-sidebar.css
│   └── sidebar.py
├── functions_wsgi
│   ├── __init__.py
│   └── function.json
├── getting_started.md
├── host.json
├── local.settings.json
└── requirements.txt
```

いったん単体で動作確認をしておきます。

```
# 必要なモジュールを追加する
# requirements.txtにdashとdash_bootstrap_componentsを追加してから
pip install -r requirements.txt

# 起動
python dash_app/sidebar.py 
```

`Dash is running on http://127.0.0.1:8888/`のようにローカルのアドレスがでるのでURLを開いて、Sidebarが出ていることを確認しておきます。


# 関数をアプリケーションにつなぎこむ

[Azure Functions と WSGI/ASGIを統合](https://qiita.com/hkato/items/263c09095aaf53d6fcf7#azure-functions-%E3%81%A8-wsgiasgi%E3%82%92%E7%B5%B1%E5%90%88)と同様に、下記のファイルを修正します。

* functions_wsgi/function.json
* host.json

dash_appフォルダにアプリケーションの本体があるため、`functions_wsgi/__init__.py`はこんな感じになります。

```
import logging

import azure.functions as func

from dash_app.sidebar import app

def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    # WSGI Application
    return func.WsgiMiddleware(app.server.wsgi_app).handle(req, context)
```

appの引数に`__name__,`を追加しておきます（これを忘れると、assetsのなかのcssが読み込まれずstyleが反映しない）

```
app = dash.Dash(
+    __name__,
    external_stylesheets=[dbc.themes.BOOTSTRAP],
    meta_tags=[
        {"name": "viewport", "content": "width=device-width, initial-scale=1"}
    ],
)

```


`func start`でfunctionから目的のDashアプリが開けることを確認します。`http
://localhost:7071`などでアクセスできるようになります。

確認できたら、あとはサンプルプロジェクトで試したようにAzureにデプロイするだけ！


# （おまけ）Basic認証の導入
[dash_authのサンプル](https://dash.plotly.com/authentication)をみつつ、最低限のセキュリティとしてBasic認証を入れておきます。このときアカウントを別ファイルで管理する場合は、`from dash_app.user import ~`のようにフォルダをつけておかないと読み込みません。
単体で行う動作確認と統合して関数を経由して動作確認する場合では、このフォルダがパスに必要かどうかが変わってくるので要注意。


## 参考サイト

* [Azure Functions で Flask/WSGI のAPIを動かす](https://qiita.com/hkato/items/263c09095aaf53d6fcf7)
* [クイックスタート: コマンド ラインから Azure に Python 関数を作成する](https://docs.microsoft.com/ja-jp/azure/azure-functions/create-first-function-vs-code-python)
* [dash-bootstrap-components](https://github.com/facultyai/dash-bootstrap-components)