無料でお手軽に Azure Static Web Apps に Web サイトをデプロイする

フロントエンドよくわからんという人でも、開発を通じて Web アプリを作りたくなることがあると思います。

本記事では Python x Azure というアドベントカレンダーの趣旨に沿って、 Python 製の静的サイトジェネレーター MkDocs で作った静的サイトを Azure Static Web Apps にデプロイしてみた話を書きます。

- **ソースコード: [mkdocs-on-azure](https://github.com/kenchon/mkdocs-on-azure)**
- **できるもの : [Azure Static Web Apps にデプロイした静的サイト](https://jolly-grass-0e9a8b110.azurestaticapps.net/)** ↓

<img width="60%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/7bae71e4-1fa9-93f6-058b-fa41cb191108.jpeg">

Static Web Apps にデプロイ可能なアプリケーションについての理解を深めるために、
- 静的サイト
- SPA(Single Page Application)
についての話もしていきます。

![スクリーンショット 2020-12-01 1.32.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/d77f7e3b-d467-5ed9-3abd-ee6a6e59c7cc.png)

# 💪 モチベーション

- 👩‍💻　**フロントエンドの技術スタックがない**
- ✨　**簡単・いい感じにウェブページを作りたい**
- ⛏　**メンテしたくない、運用コストかけたくない**

みたいなお気持ちになったことはないでしょうか？？私はあります。

そんなあなたは、これを読むと良いかもしれません。

# 👨‍👩‍👧 対象読者と前提

**この記事は，以下の人にむけた内容になっています**

- Azure を使っていて静的サイト作りたいな〜という人
- 低コストで簡単にウェブページをデプロイ・管理したい人
- Web アプリも Python で作りてえんだ！な人

**また，以下の内容については既知 or 既にあるという前提で話を進めます**

- pip
- Git 使ったことある，GitHub のアカウントがある
- VSCode 使ったことある
- Azure のアカウントがある（今回使う Azure Static Web Apps は無料で利用できます）

# 🤷 どうやってやるのか

大まかな流れは以下のようになります：

**1. 静的サイトジェネレータのセットアップ**
**2. リポジトリのセットアップ**
**3. Azure Static Web Apps のリソース作成**
**4. CI/CD のセットアップ**

次に，使用する **静的サイトジェネレーター** と **Azure Static Web Apps** について説明します

## 静的サイトジェネレーター - [MkDocs](https://www.mkdocs.org/)

**静的サイトジェネレータ** [^1]として，本記事では Python 製の OSS である [MkDocs](https://www.mkdocs.org/) を使います。

MkDocs を使ってみて良いなと思ったのは下記の点です。

- **機能面**
    - プラグインが充実 （数式表現，コードのシンタックスハイライトなど）
    - デフォルトで全文検索機能をサポート
    - テーマが充実 (`pip install` で利用可能)
- **手軽さ**
    - 設定ファイルでカスタマイズ可能（HTML，CSS，JS が隠蔽）
    - 環境構築の簡単さ
        - `pip install mkdocs` && `mkdocs new mysite` && `cd mysite` && `mkdocs serve`

[世の中にはいろんな静的サイトジェネレータがあり](https://jamstack.org/generators/)，その中でもドキュメントサイトに特化した OSS をいくつか使ったことがあるのですが，MkDocs は必要十分な機能を簡単にプラグインとして取り込めるのが良いところです。

> **📚　NOTE**
> 機能を網羅的に知りたい方は，@mebiusbox2 さんの[『MkDocsによるドキュメント作成』](https://qiita.com/mebiusbox2/items/a61d42878266af969e3c) が参考になります。

ちなみに，Python の有名なフロントエンドフレームワーク django や Flask と MkDocs はどのように違うのでしょうか？

前者は基本的にサーバー側で動的に HTML を生成するので，Python 実行環境が必要になります。しかし，MkDocs の場合は HTML を生成するのはビルド時のみなので，あとはビルド時に生成した静的コンテンツをホスティングするだけです。

図にすると以下のようになります。
![スクリーンショット 2020-12-01 1.32.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/d77f7e3b-d467-5ed9-3abd-ee6a6e59c7cc.png)

動的に HTML を生成する django や Flask はサービス提供時も常に Python 実行環境が稼働している必要があるので，Web Apps や VM といったリソースを使う必要があります。

しかし，MkDocs では， CI/CD などでビルドして一度 HTML を生成したら，それをホスティングするサービスさえあれば十分です。そこで，デプロイ先として Azure Static Web Apps や Azure Storage Blob などの，ホスティングに最適化したサービスを選べばコストも抑えられますし，ホスティングサーバの面倒は Azure がみてくれるので（サーバーレス）便利です。

## [Azure Static Web Apps](https://docs.microsoft.com/ja-jp/azure/static-web-apps/overview)

ちょうど最近リリースされたばかりの Azure のリソースで，現在はプレビュー版となっていますが，Azure を使っているなら **今後フロントエンド開発における最良のリソースの選択肢の1つになると注目されている** [^4] ものです (公式 docs は[こちら](https://docs.microsoft.com/ja-jp/azure/static-web-apps/overview))。

理由は，以下の通りです。

- **無料** [^2]
- **高速** [^3]
- **便利機能の数々！**（認証，ブランチごとの自動デプロイ...）
- **サーバーレス**

無料で使えるホスティングサービスには例えば Netlify や GitHub Pages などがありますが，Azure が優れているのは **地理的分散** による高速化のための仕組みや **認証機能**，**ブランチごとの自動デプロイ機能** などがデフォルトでサポートされている点です。またサーバーレスであり，（定義上）リソースのメンテナンスは不要 [^5]です。

Azure Static Web Apps についてのより詳しい話は miyake さんの [『Azure App Service に Static Web Apps が登場！』](https://k-miyake.github.io/blog/appservice-static-webapps/) をご覧ください。

>**📚　NOTE**
>
現在 **Azure Static Web Apps は preview 版** であり， **GitHub + GitHub Actions 以外のコードベースと CI/CD サービスをサポートしていません**[^6] 。以下に該当する方は，**Azure Storage Blob** を用いる方法 [^7] をオススメします。<br><br>
>
> - production 環境で使用することを検討している場合
> - GitHub を利用していない場合（GitLab, Azure DevOps など）
>
>Azure Storage Blob を使ったデプロイの方法は，この記事の後半の方に簡単に書いています。

## 使用する技術スタック

以上をまとめると，この記事で使用する技術スタックは以下の感じになります。

|名前|用途|
|:--|:--|
|**GitHub Repos**|記事・設定・パイプラインの管理|
|**GitHub Actions**|ビルド・デプロイ用パイプライン|
|**MkDocs**|静的サイトジェネレータ。アプリのソースコード（HTML，CSS，JS）を生成する|
|**Azure Static Web Apps**|静的サイトのホスティング|

それでは早速，やり方に入っていきます。

# 📖 具体的な方法

## MkDocs のローカルセットアップ

ローカル環境で mkdocs のセットアップを行います。この手順を省略したい場合は [このリポジトリ](https://github.com/kenchon/mkdocs-on-azure)を clone してください。

**1. インストール**

```bash
pip install mkdocs                # mkdocs のインストール
pip install mkdocs-material       # mkdocs のテーマのインストール
mkdocs new my-docs && cd my-docs  # mkdocs プロジェクトの作成
mkdocs serve                      # 開発サーバーを立ち上げる
```

↑ テーマのインストールは任意ですが，ここでは1番人気のある `mkdocs-material` をインストールします。他のテーマは [こちら](https://github.com/mkdocs/mkdocs/wiki/MkDocs-Themes) から見ることができます。

**2. 動作確認**

プロジェクトのルートディレクトリで `mkdocs serve` を実行して，開発サーバを立ち上げたら ブラウザから [`http://localhost:8000`](http://localhost:8000) にアクセスしてみましょう。

ちなみに，プロダクション用のビルドは `mkdocs build --clean` で実行できます。これを実行すると，`/site` ディレクトリが生成され，その中に静的なコンテンツ (HTML, CSS, JS...) が生成されます。

以降の手順では，このプロジェクトを Git で管理し GitHub 上のリモートリポジトリに push していきます。説明されなくても分かるよ！と言う人は飛ばしてください。

**3. Git リポジトリとして管理する**

動作確認ができたら，このプロジェクトを Git で管理します。

```bash
git init
```

**4. `.gitignore` を作成**

   mkdocs では，コンテンツ（markdown ファイル）や設定ファイルをビルドして HTML,CSS,JS のファイルを生成し，これらを `site` ディレクトリに格納します。ただし，これらは Git で管理したいものではないので，無視するために `.gitignore` ファイルを生成します。

   ```bash
   echo 'site' > .gitignore
   ```

**5. コミットする**

```bash
git add .
git commit -m "init mkdocs project"
```

## GitHub のセットアップ

1. GitHub で空のリポジトリを作成します。

1. 作成したリモートリポジトリに，先ほど作成したローカルリポジトリの内容を push します。

```bash
git remote add origin https://github.com/${user_id}/${repos_name}.git
git branch -M main
git push -u origin main
```

`${user_id}` と `${repos_name}` にはそれぞれあなたの GitHub ユーザー ID と作成したリポジトリ名を指定します。

## Azure Static Web Apps の作成

次は，さっき作った mkdocs の静的コンテンツをデプロイする Azure Static Web Apps のリソースを作成します。

**1. VSCode の Azure Static Web Apps 拡張機能のインストール**

VSCode に下記の拡張機能を追加します。これにより，リポジトリと Azure リソースの統合がシームレスになります。
![vscode-azure-static-web-apps.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/c5c51514-59d6-f483-bf74-8d73acbd4499.png)

以降の手順では，VSCode の Azure Static Web Apps 拡張機能の [README.md](https://github.com/microsoft/vscode-azurestaticwebapps) に書いてある手順を実行します。

**2. リソース作成**

VSCode の左側タブの Azure アイコンをクリックすると，認証のためのダイアログが表示されます。認証に成功して自分のサブスクリプションが VSCode に表示されれば OK です。
そのあと `+` アイコンをクリックすると，リソース作成ダイアログに進みます。
![create-azure-static-web-apps.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/497dc289-3677-f6f1-87e0-bd3673017412.png)
ダイアログには下記の情報を入力します。

- Azure のリソース名 → 例）`document-site`
- デプロイするブランチ名 → 例）`main`
- ソースコードの場所 → `/site`
- Functions コードの場所[^9] → "Skip for now"
- ビルド生成物が配置される場所 → "Skip for now"

**3. GitHub Actions のワークフロー定義ファイルを確認する**

↑ でリソースを作成すると，（GitHub 上の）リモートリポジトリに Azure のボットが自動でワークフロー定義ファイルを commit してきます。`git pull` して確認してみましょう。

すると，`.github/workflows/azure-static-web-apps-{app_name}.yml` というファイルがあるのが確認できると思います。

## GitHub Actions のセットアップ

### ワークフローの修正

先ほど，ワークフロー定義ファイルが自動生成されたのを確認しました。しかし，このままだとパイプラインは失敗します。理由は，以下のジョブが不足しているからです：

1. GitHub Actions の仮想マシンでの Python 実行環境セットアップ
2. mkdocs と mkdocs-material のインストール
3. ビルドして `/site` ディレクトリに 静的コンテンツ (HTML,CSS,JS) を生成する処理

ということで，`uses: actions/checkout@v2` 以降に，それぞれのジョブを以下のように追加してみましょう

```yaml
    steps:
      - uses: actions/checkout@v2

      # 1. GitHub Actions の仮想マシンでの Python 実行環境セットアップ
      - name: Set up Python 3.9
        uses: actions/setup-python@v2
        with:
          python-version: 3.9

      # 2. mkdocs と mkdocs-material のインストール
      - name: Install Python dependencies
        run: |
          python -m pip install --upgrade pip
          pip install mkdocs
          pip install mkdocs-material

      # 3. ビルドして `/site` ディレクトリに HTML,CSS,JS を生成する
      - name: Build Web App
        run: |
          mkdocs build --clean
```

### push & deploy!

それでは更新した内容で commit & push しましょう！

```bash
git add .
git commit -m "ci: Add steps to build web app"
git push origin main
```

※ push すると自動で GitHub Actions が走り，Azure Static Web Apps へのデプロイが走ります。

GitHub Actions の画面を開くと，パイプラインが走っているのが見えます。
![github-actions-running.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/53150abf-183e-1433-d166-2dcab2a38d1b.png)
だいたい 2分ほどでデプロイが完了します。

## デプロイされたページの確認
デプロイされたリソースを Azure Portal で確認してみると...
![deployed-resource.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/74ff6b9a-2148-a714-0ab0-325888f49675.png)

「概要」からウェブサイトの URL をクリックして，所望のページが見えればおｋです。

## おまけ：環境ごとのデプロイ

Static Web Apps では，環境ごとのデプロイを設定レスでやってくれます。

例えば，ステージング用のブランチ `stage` を作ってプルリクを出すと，プルリクを契機に自動でステージング環境用の静的サイトをデプロイしてくれます。試しに，プルリクを発行してみると，こんな感じになりました↓
![pr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/364f897a-ff39-9eec-8a87-a9a80439cf84.png)
同じプルリク画面から，差分とその結果が確認できて便利ですし，1からこういう仕組みを作らなくても良いのがすばらしいです。

# ⭐ まとめ

長くなり色々書いてしまいましたが，特に難しいことをせずともドキュメントサイトをデプロイできるということが分かっていただけたのではないでしょうか！？

# 🧸 おまけ: Azure Strorage Blob へのデプロイ

何らかの都合で，コードベースとして GitLab や Azure DevOps などのサービスを使わねばならないという方も多いと思います。そんな方は Azure Storage を使いましょう！Static Web Apps ほどの便利機能はありませんが，こちらもリソース費用を抑えながら静的サイトをデプロイすることができると言う点で良い選択肢です。

とりあえずデプロイするだけなら，以下の方法でできます。

#### 1. VSCode の Azure Storage 拡張機能のインストール
![スクリーンショット 2020-11-29 15.59.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/7a86b11f-74c7-4b2b-f45a-234194894b7d.png)

#### 2. MkDocs プロジェクトのビルド
リポジトリのルートに移動して，次のコマンドを実行してビルドします。

```bash
mkdocs build --clean
```

すると，`/site` ディレクトリが生成されるので， VSCode のファイルエクスプローラからこのディレクトリを右クリックし，`"Deploy to Static Website via Azure Storage..."` をクリックします。

![スクリーンショット 2020-11-29 16.12.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182818/f9a0ca72-c75f-5025-a889-672ad4a2a0b5.png)

すると例によってリソース作成 or 選択ダイアログが表示されるので，GUI からポチポチしていくと，無事デプロイされるかと思います。

[^1]: **静的サイトジェネレータ** とは，Web サイトの静的コンテンツ（HTML，CSS，JS）を生成するアプリ・フレームワークのことです。生成した HTML,CSS,JS はホスティングサーバからブラウザに配信することができます。ホスティングサーバはあくまでソースコードを配信するだけであって，サーバ上で **動的に HTML を生成することがない**（＝HTML レンダリングのための実行環境が不要）ので，ホスティングに最適化したクラウドサービスを使うことでランニングコストを抑えることができます。静的サイトジェネレータについてもっと興味がある方は [「静的サイトジェネレーター」について網羅的に説明します - Dyno](https://dyno.design/articles/what-is-static-site-generator/) を読んでみてください。<br><br>ちなみに，**「動的サイト」**（とは普通言いませんが） なるものは，ユーザーが Web サイトにアクセスした時に HTML を動的に生成するものです。これには，**HTML がサーバー側・ブラウザ側のどっちで生成されるか** という観点から次の2種類があります。1つは，ユーザーからアクセスが来た時に **サーバー側** で HTML を動的に生成して，生成した HTML をブラウザに配信するアーキテクチャであり， **SSR (Server-Side Rendering)** と呼ばれます。一方，**ブラウザ上** で JavaScript によって HTML を生成する Web アプリのことを **SPA (Single Page Application)** といいます。**SPA** も動的と言えば動的ですが，サーバーサイドに HTML 生成のための実行環境が不要という点では静的サイトと同じであり，Azure Static Web Apps にデプロイ可能です。<br>**SSR**，**SPA**，**静的サイト** の違いをもっと知りたい！という方は，[ACADEMIND, "Dynamic vs SPA vs Static Websites"](https://academind.com/learn/web-dev/dynamic-vs-static-vs-spa/) を読んでみてください。

[^2]: 正式版がリリースされると課金対象になる可能性がありますが，その場合でも App Service などと比較すればごく微々たるものになると想定されます。

[^3]: >Static Web Apps では、静的アセットは従来の Web サーバーから分離され、世界各地の地理的に分散したポイントから提供されます。 この分散により、ファイルがエンド ユーザーに物理的に近づくため、ファイルの提供が大幅に高速になります。 - [Azure Document, "Azure Static Web Apps"](https://docs.microsoft.com/ja-jp/azure/static-web-apps/overview)

[^4]: もちろん，フロントエンドのアーキテクチャにも依存しますが，サーバーサイドに HTML 生成の実行環境を必要としない **静的サイト** や **SPA (Single Page Application)** を採用する場合は最良の選択肢と言えます。

[^5]: リソースのメンテは不要ですが，もちろんフロントエンド部分の依存パッケージ（今回は MkDocs）に関しては，必要に応じてアップデートや脆弱性対応を行う必要があります。

[^6]: 近々 Azure DevOps + Azure Pipelines もサポートされると MS の人が言っています。https://github.com/Azure/static-web-apps/issues/5#issuecomment-727289989

[^7]: Azure Docs, [Azure Storage での静的 Web サイト ホスティング](https://docs.microsoft.com/ja-jp/azure/storage/blobs/storage-blob-static-website)

[^9]: Azure Static Web Apps で "Functions コード"，または "Functions の統合" と呼ばれる機能は、文字通り Static Web Apps と Functions の統合をシームレスにできますよ！という機能です。フロントエンドで Functions で作った API を使うと何が嬉しいのでしょうか？具体的には、次のようなユースケースで役に立ちます↓<br><br>フロントエンド開発において、バックエンドから何かしらの値をとってきて表示したいということがあります。ただし、バックエンドのインタフェースは必ずしもフロントエンドにとってうれしいものではないときがあります。そこで、中間的なバックエンドを作ってバックエンドをラップしようというアプローチがあります（このアーキテクチャを **BFF（Backend for Frontend）** といいます）。フロントエンド寄りの「バックエンド」なので、同じリポジトリで管理できた方が良いということでこのようなオプションが表示されるのだと思います。