【Azure App Service | Node.js】GitHub Actions を利用して継続デプロイしてみよう【 トラブルガイド付】

GitHub Actions を利用して継続デプロイしてみよう
Q. 「オンボーディングで最もしんどかった部分はどこでしたか?」
\ Azure App Service 一択ウ〜!/
Azure サービス群の中でも屈指の面倒臭さを誇る(当社比)Azure App Service でのデプロイ方法ガイド、始まり始まり。
本記事は公式ドキュメントを参考に作成しています。

使用技術

  • Node.js
  • Express.js
  • Visual Studio Code
  • GitLens(Visual Studio Code の拡張機能)
  • Azure App Service
  • GitHub
  • GitHub Actions

この記事を読んでわかること

Azure App Service に GitHub Actions を使用してアプリケーションを継続的にデプロイするための具体的な手順。
つまり「main ブランチへ push するたび自動的に Azure App Service へデプロイする」ことができるようになります。

この記事では説明しないこと

  • サブスクリプション、リソースグループ、リソースなど Azure 特有の用語の数々。詳しく知りたい方はこちらをまず読んでみてください。
  • Node.js や GitHub など使用技術に関する基礎知識

大まかな手順の流れ

  1. デプロイしたいものを用意する
  2. リソースグループを作成する
  3. Azure App Service リソースを作成する
  4. GitHub Actions が使えるよう準備する
  5. CI/CD 経由でデプロイ

1. デプロイしたいものを用意

Node.js 製であればなんでも OK。基本の流れは同じです。
今回は Express.js でささっと用意しちゃいます。
1.png
2.png
3.png
今回は「2-Deploy-app-to-azure」の中身だけ使用します。他は消しちゃって OK。
4.png
4.5.png

2. Azure Portal からリソースグループを作成

アカウント作成・サブスクリプションの設定についての説明・手順は省きます。
まだの方は Azure 公式サイトより無料アカウントを作成し Azure Portal へアクセス・ログインしてサブスクリプションの用意まで行ってください。
手順を詳しく知りたい人はこちら
Azure Portal からリソースグループを作成していきます。
5.png
6.png
7.png
8.png
無事作成されれば成功。

3. Azure App Service リソースの作成

2で作成したリソースグループの中に Azure App Service リソースを作っていきます。
まずはプランの作成から。
8.1.png
9.png
Azure Service プランを選択し「作成」をクリック。
11.png
今回はさくっと試したいだけなので価格レベルを Free F1 へ変更。ここを変更しておかないと月額課金が発生するため注意!
「確認および作成」→「作成」でリソース作成が完了します。作成まで少し時間がかかることもあるのでコーヒーでも啜りながら待ちましょう。
12.png
完了したら App Service で Web アプリのリソースを作っていきます。
9.1.png
9.2.png
9.3.png
「確認および作成」→「作成」で完了。こちらも時間がかかるためコーヒーをひと啜りしながら待ちましょう。完了後リソースに移動するとこんな感じ。
9.4.png

4. GitHub Actions の準備をしよう

まずは GitHub にレポジトリを作成しましょう。詳細な手順は省きます。GitHub アカウントを持っていない方は GitHub 公式サイトでアカウントを作成・ログインしてください。
レポジトリの作成が完了したらこんな感じのページが広がっているはず。URL をコピーしておきます。
14.png
1で開いていた Visual Studio Code へ戻ります。リモート先を自身で作成したレポジトリへ変更しちゃいましょう。
16.png
※ REMOTES なんて見当たらないよ!という人は拡張機能「GitLens」を導入してください。
17.png
name: origin
url: コピーした URL を貼り付け
完了したらさっそくコミット&ブランチの発行を試してみてください。最新のものに反映されていれば成功。
18.png
ここからが本番。 GitHub Actions 経由でデプロイする準備を進めていきます。
3で作成した App Service へ移動し、GitHub Actions と連携させていきます。
19.png
20.png
保存するとあら便利、 GitHub 側で勝手に設定ファイルを生成してくれます。
22.png

5. CI/CD 経由でデプロイ

さっそく GitHub Actions 経由でデプロイしてみましょう。
やたら長ったらしい名前の .github/workflows/main_fwywdazuredeployintro.ymlファイルを deploy.yml へ名称変更しプッシュしてみます。
25.png
deploy.yml のデフォルト設定では「main ブランチに push した際にデプロイが走る」ようになっています。デプロイが終わったらさっそく確認しにいきましょう。
23.png
おや・・・ Azure App Service くんのようすが・・・?
24.png
出たよ。親の顔より見た光景。
アプリケーション起動時に出るエラー。Azure App Service 触ったことがある人は絶対に見たことあるやつです。私はもはや :( を見ると首の後ろがキュッ・・・となります。

:( Application Error に遭遇した時の対処法

基本は以下のステップで地道にエラーを潰していく形になります。
  1. ログを確認する
  2. 原因っぽい箇所を探して試行錯誤する

1. ログを確認する

Azure Portal で 該当する Azure App Service を開き「問題の診断と解決」を覗いてみましょう。下部にある「ウェブアプリダウン」をクリック。
33.png
現在7つの問題を抱えている模様。詳しく見ていきます。
26.png

2. 原因っぽい箇所を探して試行錯誤する

7つの問題のうち直接アプリケーションエラーを引き起こしていそうなものを探しましょう。
27.png
下から4つは「有料プランにアップグレードしてほしいな❤️と言っているだけ」「警告で起動失敗に関するものではなさそう」っぽいことが読み取れます。上の3つがアプリケーションエラーに関わっていそうです。さらに分析するとサーバーエラーは起動に失敗しているがために出ているものと予想されます。ここから「コンテナクラッシュ・コンテナの問題を解決すれば起動できそう!」と算段がつけられますね。
一番怪しげな「コンテナクラッシュ」から見ていくと・・・
28.png
原因がひとつ判明。アプリをスタートさせるためのコマンドが設定されていないようです。さっそく設定しにいきましょう。左バーの設定内にある構成をクリックし全般設定を覗いてみます。
29.png
確かにスタートアップコマンドが空っぽになってますね。ではここに npm start ないし yarn start をいれよう!と通常はなるわけですが、入れても動かないため注意。直接 node コマンドを叩きます。package.json の start スクリプトを確認してみましょう。
30.png
"start": "cross-env-shell DEBUG=express:* node index.js"となってますね。cross-env-shell DEBUG=express:*はそれぞれ環境変数・デバッグに関するコマンドなので省略。node index.jsだけ抜き出して入力・保存します。
31.png
「再起動しますか?」と聞かれるため了承して保存完了です。
ここで一度アプリを覗いてみましょう。
32.png
以前としてアプリケーションエラーは出たまま。まだ対処していないエラーが残っているからですね。もう一つの問題だった「コンテナの問題」を見てみます。
34.png
どうやらコンテナがタイムアウトしたせいで起動できていないみたい・・・というのが見て取れます。説明が若干分かり難いのですが、「アプリケーションの設定でWEBSITES_CONTAINER_START_TIME_LIMITの設定時間を伸ばしてみてね」ということらしいです。さっそく変更しにいきます。
35.png
保存するとアプリケーションが再起動されます。これで正常に起動できるはずッ・・・
36.png
で、できた〜〜〜!!!(クソデカボイス) おつかれさまでした・・・!

トラブルガイド

最後に Next.js で作ったアプリケーションと NestJS で作った API をそれぞれデプロイした時に遭遇したエラーと対処法をご紹介。 同じ症状に苦しんでいるあなたに届きますように・・・!

デプロイに時間がかかりすぎる

アプリケーションの容量が小さくない場合、デプロイまでに凄まじい時間がかかります。私の場合 Next.js で作った簡単な Todo アプリをデプロイするのに50分かかりました。50分待った挙句デプロイに失敗した日には泡吹いて倒れそうになります・・・(実体験)。
対処法として deploy.yml ファイルの中身をいじってアプリを zip 化→ GitHub Actions にアップロード→ zip を Azure App Service へアップロードするよう変更することが挙げられます。私はこれでデプロイ時間を7分まで短縮できました。恐るべきビフォーアフター!
参考に Next.js をデプロイした時のコードの中身を載せておきます。
name: Build and deploy Node.js app to Azure Web App - fwywdTodoAppApi on: push: branches: - main workflow_dispatch: jobs: build: runs-on: ubuntu-latest env: DATABASE_URL: ${{ secrets.DATABASE_URL }} DB_PASSWORD: ${{ secrets.DB_PASSWORD }} DB_USERNAME: ${{ secrets.DB_USERNAME }} steps: - uses: actions/checkout@v2 - name: Set up Node.js version uses: actions/setup-node@v1 with: node-version: '16.x' - name: yarn install, build, and test run: | yarn install yarn build yarn test-setup yarn test:unit yarn test:integration - name: delete node_modules dir run: rm -rf node_modules - name: Zip all files for upload between jobs run: zip --symlinks -r next.zip ./* - name: Upload artifact for deployment job uses: actions/upload-artifact@v2 with: name: node-app path: next.zip deploy: runs-on: ubuntu-latest needs: build environment: name: 'Production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} steps: - name: Download artifact from build job uses: actions/download-artifact@v2 with: name: node-app - name: yarn install production run: yarn install --production - name: 'Deploy to Azure Web App' id: deploy-to-webapp uses: azure/webapps-deploy@v2 with: app-name: 'fwywdtodoapp' slot-name: 'Production' publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_630E8D07124C436791B1A377E09DC005}} package: next.zip - name: Delete zip file run: rm next.zip

デプロイが途中で止まる

こちらもアプリケーションの容量がそこそこある時に発生。node_modulesnpm install または yarn install する際ダウンロードに時間がかかりすぎてタイムアウトするパターンです。プロジェクトフォルダ直下に.yarnrc ファイルを作って以下を記述することにより回避可能。
network-timeout 600000

スタートアップコマンドに関するエラー

使用するフレームワークによってはデフォルトのスタートアップコマンドが効かないことがあります。私の場合は Azure App Service 内の構成→全般設定→スタートアップコマンドに以下を入力することで解決しました。参考にどうぞ。
Next.js の場合・・・node_modules/next/dist/bin/next start
NestJS の場合・・・node dist/main

何が原因でエラーが起きているのかわからないとき

「アプリケーションエラーが起きてる。問題の診断と解決を見たけどそれっぽいエラーが見つからない。なんでッ!?」
これもよく遭遇します。そんな時はログストリームをみるべし。
38.png
アプリを再起動→ログストリーム(プレビュー)でリアルタイムにログ出力ができるためどこで失敗しているのかわかりやすくなります。

番外編: NestJS デプロイで詰まった時の解決策(体験談)

状況としては GitHub Actions で zip 形式でデプロイしただけの状態で Azure Portal で何も設定していない・・・という時間軸です。
経緯まで書くと大ボリュームになるので結論だけ置いときます。
  1. deploy.yml の中身はこちら(デプロイ時間が短縮できます)。
name: Build and deploy Node.js app to Azure Web App - fwywdAzureDeployNest on: push: branches: - master workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Node.js version uses: actions/setup-node@v1 with: node-version: '16.x' - name: npm install, build, and test run: | npm install npm run build --if-present npm run test --if-present - name: Zip all files for upload between jobs run: zip --symlinks -r nest.zip ./* - name: Upload artifact for deployment job uses: actions/upload-artifact@v2 with: name: node-app path: nest.zip deploy: runs-on: ubuntu-latest needs: build environment: name: 'Production' url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} steps: - name: Download artifact from build job uses: actions/download-artifact@v2 with: name: node-app - name: 'Deploy to Azure Web App' id: deploy-to-webapp uses: azure/webapps-deploy@v2 with: app-name: 'fwywdAzureDeployNest' slot-name: 'Production' publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_2A62A5035C4241D78AC3A13D8D636DCA }} package: nest.zip - name: Delete zip file run: rm nest.zip
  1. 「Azure App Service 内の構成→全般設定→スタートアップコマンド に node dist/main と入力する
  2. 「Azure App Service 内の構成→ アプリケーションの設定→ 新しいアプリケーション設定」で「名前:WEBSITES_CONTAINER_START_TIME_LIMIT」「値:600」と設定
  3. package.json の script 内にあるコマンド “start:prod” を以下に置き換える
"start:prod": "yarn build && node dist/main",
  1. main.ts のポート設定を変更する
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); const port = process.env.PORT || 3000; await app.listen(port); } bootstrap();

この3つを抑えていれば Azure App Service も怖くない(多分)

  • スタートアップコマンドが適切に設定されているか
  • コンテナがタイムアウトしていないか
  • その他、なんらかの理由でコンテナがクラッシュしていないか(ログストリームで確認可能)
特に上2つは Node.js でのデプロイだと再現性高く目にするエラー(当社比)なので真っ先に確認してみる価値アリ。 この記事で Azure App Service アレルギーに罹っている人が1人でも減りますように・・・!