Nuxt.js の Universal SSR を Azure App Service で運用する方法

この記事は [Nuxt.js Advent Calendar 2019](https://qiita.com/advent-calendar/2019/nuxt-js) の 17日目の記事です。

## Nuxt.js（Universal SSR）のホスティング先にはいろいろあるが

Nuxt.js の Universal SSR 構成では、SPA とは異なりホスト先として Node.js のサーバーが必要になります。でも、フロントエンドの開発をしている時にはできるだけインフラには関わりたくないものです。私はできれば、 PaaS/Serverless 環境にスマートにデプロイしたいと常に思っています。

[Nuxt.jsの公式ドキュメントのFAQ](https://ja.nuxtjs.org/faq) でもデプロイに関する記事はほとんどが PaaS/Serverless を前提にしているようです。しかし、公式FAQ（ja版）には代表的な PaaS のひとつである Azure App Service の例がありません。en版にはかろうじて記事があるものの古い内容だったりして、かなりつらい状況です。いまだに Azure は Windows とか C# じゃないとダメだと思われているのでしょうか。だとすると悲しいだけでなく、技術に公平にアーキテクチャを選定／検討する上で、重要な選択肢を見失っている可能性があると言っても過言ではないと思っています（少なくとも Azure はグローバルではかなり支持されているプラットフォームなはずなので）。

そこで、この記事では ~~スルーされているかもしれない~~ Azure App Service を使った Nuxt.js（Universal SSR）のホスティングついて、そのデプロイ方法や各種メリットなどについて個人的見解を書いておきたいと思います。

## Azure App Service の準備

[Azure App Service（Web App）](https://docs.microsoft.com/ja-jp/azure/app-service/overview) は Webアプリをホストするための PaaS であり（Google App Engine 同じような位置づけ）、標準で対応しているスタックには、Node.js は当然ながら、PHP や Ruby などメジャーなものが最初から用意されています。たとえ標準のスタックに無くても Docker イメージを指定することもできるので、事実上スタックの制約はないと言えます。ちなみに OS も Linux と Windows が選べますが、 Node.js を選択すると Linux が自動的に選ばれます~~ので安心して下さい~~。まあ PaaS なので OS を選択する意味すら不要な気がしますが。

App Service（Web App） のプロビジョニングですが、慣れてくると [Azure CLI](https://docs.microsoft.com/ja-jp/cli/azure/?view=azure-cli-latest) や [Terraform](https://docs.microsoft.com/ja-jp/azure/terraform/) で作成したくなるものですが、Azure ポータルの画面から GUI ベースで作成しても全く問題ありません。むしろ最初は GUI の方がとっつきやすいと思います。

プロビジョニングの画面は基本こんな感じで、 Nuxt.js（Universal SSR）をデプロイするなら以下のようにランタイムスタックに Node を指定すればOKです。

<img width="573" alt="2019-12-16_21h09_39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/7bb63628-63d9-b87c-abc3-16e4a060340f.png">

数分でプロビジョニングが完了しますが、アプリケーションをデプロイする前に Nuxt.js 向けの環境変数を設定しておきます。`設定（Settings）` 以下の `構成（Configuration）` 画面を表示し、 `アプリケーション設定（Application settings）` タブで以下のように環境変数を2つ追加しておきます。Nuxt.js 固有の設定はこれだけになります。

<img width="951" alt="2019-12-16_21h16_58.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/a7a1c646-6990-7d2f-5065-1491de8a7791.png">

ちなみに、`全般設定（General settings）` タブでは、 Node.js のバージョンを変更することが可能です。Node.js は V12 系も既に選択できるようになっています。今後、 Node.js のバージョンが上がって少し経つと勝手にここにもバージョンが追加されてくるので、好きな時に更新ができます。このあたりは PaaS ならではのメリットですね。

## Nuxt.js の構成

App Service へのデプロイはさまざまな方法を選択可能ですが、ここでは個人的に最もおすすめな **GitHub + App Service build service** を使った方法を紹介します。今どきなら GitHub Action を使うんじゃないのという声が聞こえてきそうですが、 Nuxt.js を 簡単に CI/CD したい目的だけならこの方法がベストバランスだと思っています。この方法なら YAML を書く必要もありませんからね。アーキテクチャ構成は以下のような感じになります。

<img width="840" alt="2019-12-17_00h00_42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/5bc24685-016c-3991-c541-bdaf1645fdf8.png">

Nuxt.js は `create-nuxt-app` で `Universal (SSR)` を指定して構築しており、GitHub にリポジトリがあることを前提とします。なお、Azure App Service にデプロイするために、 Nuxt.js 側で何か追加で設定しておくことはありません。今回は以下のように作成しました。

<img width="556" alt="2019-12-16_21h28_01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/7b661c37-f1ea-7376-ebe7-89bd9c40d893.png">

一応 `npm run dev` でローカルで正しく起動することを確認しておきます。

<img width="785" alt="2019-12-16_21h31_34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/12806b8f-8547-b2d5-ce03-aba9791fa4fd.png">

## Blue-Green Deployment の準備

あとは App Service で GitHub との連動設定をするだけで基本的にはデプロイが完了してしまうのですが、 ~~それではつまらないので~~実際の現場では [Blue-Green Deployment](https://www.publickey1.jp/blog/14/blue-green_deployment.html) をできるようにするべきだと思うので、App Service にステージング環境を追加してから、そこに GitHub から自動的にデプロイできるようしたいと思います。

ステージング環境の追加は、 App Service の標準機能として準備されており、 [デプロイスロット](https://docs.microsoft.com/ja-jp/azure/app-service/deploy-staging-slots) と呼ばれています。ちなみにこの機能、スロットはプランによって5個～20個まで作成できるので（スロットの追加費用なし）、ステージング環境以外に 任意のスロットを追加して、 A/B テストやカナリアリリースなどを行うこともできます。デプロイメントスロットは以下のように追加します。

<img width="949" alt="2019-12-16_21h48_07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/209cf55c-2117-d13a-b604-e58b966943c8.png">

追加されると、メニュー構成もほとんど同じ App Servie がもう一面できたような感じになります。もちろんURLは専用になるので、外から見ると全く別のサイトになりますし、実際別のコンテナーで起動されています。ちなみに環境変数などの構成は同一にすることも、 production と別の設定にすることもできます。

## GitHub との連動

ステージングスロットが出来たので GitHub から Nuxt.js が自動でデプロイされるように設定します。といっても、以下のように GitHub と接続するだけで、Nuxt.js に関する設定は特に不要です。`設定（Settings）` 以下の `デプロイセンター（Deployment Center）` 画面を表示し、 GitHub を選択します。

<img width="938" alt="2019-12-16_21h52_00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/361b5591-7545-1226-a52c-46cb0292cb23.png">

次にビルド方法を選択する画面に遷移しますが、ここでは 「App Service build service」を選択します。こちらを選ぶことでビルドスクリプトや実行コマンドの設定が一切不要になります。アプリ開発に特化したい場面には正直嬉しい選択肢じゃないでしょうか。

「App Service build service」 は [projectkudu/kudu](https://github.com/projectkudu/kudu) と呼ばれる OSS のデプロイサービスがビルドとデプロイを行う仕組みになっているのですが、App Service (Linux) では、さらに [microsoft/Oryx](https://github.com/microsoft/Oryx) というビルドスクリプト自動生成ツールが働くようになりました。これにより、 Nuxt.js 用のビルドスクリプトや起動コマンドなどは Oryx によって自動生成されるようになり、自分で設定をカスタマイズする必要がなくなりました。この機能は Nuxt に限ったものではないので、他にも恩恵を受けるフレームワークは多そうです。

デプロイの設定が完了すると実際にビルド & デプロイが走り、デプロイセンターの画面から以下のようなログを確認することができます。 Oryx が生成したビルドスクリプトが `Nuxt build` の一連のステップを実行していることがわかります。

<img width="1069" alt="2019-12-16_22h57_12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/4eb723d6-6138-4929-a02d-646183e2cf42.png">

## リリース

無事デプロイが完了したら、ステージングスロット固有の URL で動作確認ができるようになります。Netlify でいうところのプレビューに近い感じですかね。なおこの時に、App Service の [Easy Auth](https://docs.microsoft.com/ja-jp/azure/app-service/overview-authentication-authorization) という認証機能を使うと、ステージングスロットのみを認証付けることができて便利です。Production へのリリースは、 App Service の スロットをスワップさせることで完了します。

<img width="1055" alt="2019-12-16_22h58_19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/c60975bb-d6c2-9341-50c4-b058a32ea7cf.png">

スワップの動きはネットワーク的に Production と Staging を切り替えているだけなので、外から見ると無停止でアプリケーションが更新されたように見えます。

<img width="814" alt="2019-12-16_23h01_26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30318/9a8734b6-4554-78b7-5317-e9895f68a884.png">

これでめでたく Blue-Green Deployment が完了し、 Nuxt.js（Universal SSR）が PaaS 上で CI/CD を実現できた状態になります。Nuxt.js の公式ドキュメントで他のデプロイもいろいろ試しましたが、App Service へのデプロイが今のところ最もステップが少なかったです（次に Heroku が簡単でした）。

## まとめ

いろいろ書いてきましたが、結局 Nuxt.js のデプロイとしてやっていることは以下の4ステップだけです。

1. Nux.js を Universal SSR 構成で構築し、 GitHub にプッシュする
1. Azure に App Serrvice を Node.js でプロビジョニングする
1. Nuxt.js 用に環境変数を2つ設定する
1. App Service と GitHub を接続する

これで、Nuxt.js を 高機能でスケーラブルな PaaS で実現する選択肢が増えたのではないでしょうか。この方法が Nuxt.js 公式ドキュメントにもいつか反映されると嬉しいです（自分でコントリビュートすれば良いかもしれない。

---

**2019.12.23 追記**

GitHub Actions を使ったデプロイ方法について、エントリーを追加しました。
https://k-miyake.github.io/blog/nuxt-actions-appservice/