Azure App Service に Static Web Apps が登場!

Microsoft Build 2020 で、Azure App Service の新たなオプションとして 静的サイトのホスティング を実現する Static Web Apps が発表になりました(執筆時点で Public Preview)。JavaScript で開発する Web フロントエンドのメインストリームである SPA(Single Page Application))や 静的サイトジェネレータを使う JAMstack の運用に最適化されているそうです。

詳細は 公式アナウンス公式ドキュメント: Azure Static Web Apps documentation を読んだ方がいいですが、ざっと現在わかっている機能や特徴を書いておきます。

App Service Static Web Apps の概要

App Service Static Web Apps(名前が長いので以下 Static Web Apps)は、HTML, CSS, JS 等の静的コンテンツを配信するのに最適化されたサービスで、誤解を恐れずに言うならば、Azure 版の Netlify や Firebase Hosting に相当するものです。

Static Web Apps で現在できることはおおよそ以下の通りのようです。

  • Web アプリケーションホスティング(無償!)
  • コンテンツのグローバル配信(どういう仕組みでグローバルなのかは調査中)
  • 統合された API (Azure Functions と統合!)
  • GitHub Actions によるビルド & デプロイ(GitHub 連動必須)
  • カスタムドメイン対応、SSL証明書の自動更新(無償!)
  • ビルトインの認証機構を利用可能(外部認証プロバイダー利用)
  • プレビューデプロイ機能(PR 中のブランチも自動デプロイされる)

無償で静的サイトのホスティングが可能に

SPA など JavaScript ベースの 静的な Web サイトは基本的にブラウザ上で実行されるので、実行時にサーバー上のランタイムは必要ありません。しかし、これまで Azure の Web アプリケーションの実行環境である App Service にはサーバーランタイムしか選択肢が無かったので、SPA のデプロイ先に困っていました(Blob Storage の Static website hosting オプションも存在はしていましたが、プロダクションで利用するには非力でした)。そのため、バックエンドは Azure 上にデプロイして、フロントエンドは Netlify 等を使うという構成になることも多くありました(それはそれで良い面もある)。

今後は、Azure 上での SPA のデプロイ先に迷うことはなくなり、Static Web Apps を使うのがコスト的にも技術的にもベストになると思います。ファイルサイズの制限はあるらしいですが、基本的には無償でホスティングできるのも嬉しいです。

ちなみに Static Web Apps は SSR(サーバーサイドレンダリング)には対応していません。Azure 上で SSR を実現したい場合は、同じ App Service の Web Apps(Node.js スタック) の方を使う感じになるかと。

ホスティングの配信基盤に内部的に何が使われているかは調査中ですが、そのうち App Service の強者の誰かが解明してくれると思います。

JAMstack の運用も可能 になる GitHub Actions を統合

Hugo、Jekyll、Gatsby に代表される JAMstack (JAMstack については 公式サイトを参照)は、最終的には静的サイトとして配信されますが、ビルド時に動的なコンテンツを組み立てる仕組みのため、ホスティング環境に加えてビルド環境が必要になります。なお、JAMstack 的な構成を SSG(Static Site Generator)と表現することもあるようです。

Static Web Apps では、ビルド環境として GitHub Actions が使われるように統合されているため(というか GitHub Actions がいやでも付いてくる仕組み)、コンテンツ更新の都度ビルドを走らせる JAMstack の運用が可能になっています。

Static Web Apps をプロビジョニングすると生成される GitHub Actions の YAML は以下のような感じでした。

name: Azure Static Web Apps CI/CD

on:
push:
branches:
- master
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- master

jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v1
- name: Build And Deploy
id: builddeploy
uses: Azure/static-web-apps-deploy@v0.0.1-preview
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_MANGO_SKY_08CD30A00 }}
repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
action: 'upload'
###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
app_location: '/' # App source code path
api_location: 'api' # Api source code path - optional
app_artifact_location: 'dist' # Built app content directory - optional
###### End of Repository/Build Configurations ######

# (以下省略)

actions/checkout@v1 というアクションでビルドとデプロイをまとめて実行している感じですね。ドキュメント によると、一応ビルドコマンドはカスタマイズができるようですが、個人的にはビルド部分は普通に書きたいので、単純にビルド結果を Static Web Apps にデプロイするだけだけのタスクが欲しいところです。

Azure Functions との統合

これが Static Web Apps の最大の強みかもしれません。Static Web Apps が Azure Functions と同じ App Service ファミリーとして追加されたので、両者の統合はかなりシームレスな感じです。

試しに、Vue.js と Azure Functions の組み合わせを両方とも TypeScript で作ってみました。特別な設定はせずそれぞれを CLI で標準的なプロジェクトとして作成し、以下のような Monorepo 的な混在したプロジェクトにして GitHub にプッシュしました。あとは、 Static Web Apps をプロビジョニングするだけで、フロントエンドもバックエンドも自動でデプロイされてクラウド上に公開されました。コード書いて数分でフルスタックのサーバーレスアプリが公開可能になる世界が現実になったということです。

.
├── api (バックエンドのソース)
│ ├── host.json
│ ├── local.settings.json
│ ├── package-lock.json
│ ├── package.json
│ ├── tsconfig.json
│ └── vue-backend
├── package-lock.json
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src (フロントエンドのソース)
│ ├── App.vue
│ ├── assets
│ ├── components
│ ├── main.ts
│ ├── router
│ ├── shims-tsx.d.ts
│ ├── shims-vue.d.ts
│ └── views
└── tsconfig.json

このようにフロントエンドのプロジェクトをルートに作成し Azure Functions のプロジェクトを特定ディレクトリ配下(この例では /api)に共存させていれば、フロントエンドと API が同一ホスト上に自動的にデプロイされます。

GitHub Actions のログをみると、App Service で使われているビルドエンジンである Oryx が両方のアプリケーション設定を解釈して、いい感じにビルド&デプロイしているようです。

フロントエンドとバックエンドが統合されることで、CORS の設定や認証の統合といった比較的ハマりやすい作業から解放されるメリットはあると思います(もちろん、凝ったことをやりたければ別々にデプロイした方が良いでしょう)。また、フロントエンドとバックエンドで言語を共通化(JavaScript または TypeScript)するメリットもあるでしょう。
ちなみに、Azure Functions は Node.js ランタイム限定のようです(残念ながら C# では書けないようです)。無償プランなのでコールドスタートの制約もあると思います。

これ以外にもいくつかテーマはあるので、各種検証しつつこのブログやどこかの登壇の機会などで説明していきたいと思います。とりあえず、関連する MS Learn がすでにあるみたいなので、このあたりから試すのもありかも。

個人的に、この手のサービスの必要性を訴え続けてかれこれ3年くらい経っていますし、Microsoft MVP としての活動でもフィードバックをし続けてきたつもりなので、今回この Static Web Apps をリリースしてくれた App Service チームに感謝したいです。