App Service on Linux Node runtime における GitHub Actions デプロイ時間を短縮する工夫

# はじめに

この記事は、以下の記事の続編です。

https://qiita.com/georgeOsdDev@github/items/a4aa7b7aa43a0114d96f

https://qiita.com/georgeOsdDev@github/items/7884fabcabc1cb30eb88

https://qiita.com/georgeOsdDev@github/items/03e5fc8bb9e35d0de792

各デプロイ方法を用いた場合に、少しでも時間を短縮するTipsを記載していきます。


# GitHub Actions を用いる場合の Tips

# デフォルトで作成されるワークフローファイル

[ポータル操作](https://learn.microsoft.com/ja-jp/azure/app-service/deploy-github-actions?tabs=applevel#use-the-deployment-center
)をもとに、GitHub リポジトリを Web Apps に紐づけると以下のようなワークフローファイルがコミットされます。

このワークフローはとても遅いです。


```yaml:.github/workflows/main_node-deploy-speed-test.yml
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - node-deploy-speed-test

on:
  push:
    branches:
      - main
  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: '18.x'

      - name: npm install, build, and test
        run: |
          npm install
          npm run build --if-present
          npm run test --if-present

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v2
        with:
          name: node-app
          path: .

  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: 'node-deploy-speed-test'
          slot-name: 'Production'
          publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_DA6945DB028A4CA8B66671AACA81ECC8 }}
          package: .
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/d64b9984-d942-0b95-52a8-2d95c33c07af.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/ce27bc07-2892-7c11-14dd-de0f838b395e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/e602879c-aa53-f752-6f7a-7e40c46c1382.png)

### どこに時間がかかっているのか

- `npm install,build and test` ステップはそこまで遅くなさそうです。
- `build` と `deploy` 2 つのジョブ間で Artifact を共有するための `Upload/Download artifact`合わせて 17分くらいかかっています。
- `Deploy to Azure Web App` のステップでは6分近くかかっています。以前の[VSCode によるローカルビルド + zip デプロイ](https://qiita.com/georgeOsdDev@github/items/a4aa7b7aa43a0114d96f#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%93%E3%83%AB%E3%83%89--zip-%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%AE%E5%AE%9F%E8%A1%8C) と同じくらいです。


## GitHub Actions を早くする工夫

ベースとなるアイディアは前回までの記事と同じです。

また、以下の記事も参考にし、Artifact のアップロード、ダウンロードも短縮していきます。

https://azureossd.github.io/2022/10/18/NextJS-deployment-on-App-Service-Linux/index.html#github-actions-1

https://uncaughtexception.hatenablog.com/entry/2022/05/07/160811

変更ポイントは以下です。

- `actions/checkout`、`actions/setup-node`、`actions/setup-node`、`upload-artifact`、`download-artifact` を `@v3` に変更する
- `actions/setup-node`では、npm cache を有効にする
- [`.next/cache` をキャッシュ](https://nextjs.org/docs/advanced-features/ci-build-caching#github-actions)するようにする。
- `build` ステップは分割して 
    - `npm ci` を用いる。([actions/setup-node](https://github.com/actions/setup-node) 利用時の標準的なやり方)
    - `npm prune` を追加
    - `postbuild` での指定をやめて、Stepにて `node_modules.tar.gz` と `oryx-manifest.toml` を作成する([前回の記事](https://qiita.com/georgeOsdDev@github/items/03e5fc8bb9e35d0de792#%E6%A4%9C%E8%A8%BC)と同じ)
    - `npm test` は今回の記事のスコープ外なので削除
- `upload-artifact`、`download-artifact` では、zip 圧縮したファイルを利用する

※`.next/cache` のキャッシュや、`npm prune` は前回までのローカルビルド、リモートビルドでは試していません。

```yaml:.github/workflows/main_node-deploy-speed-test.yml変更後
# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
# More GitHub Actions for Azure: https://github.com/Azure/actions

name: Build and deploy Node.js app to Azure Web App - node-deploy-speed-test

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set up Node.js version
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'
          cache: 'npm'

      - uses: actions/cache@v3
        with:
          # See here for caching with `yarn` https://github.com/actions/cache/blob/main/examples.md#node---yarn or you can leverage caching with actions/setup-node https://github.com/actions/setup-node
          path: |
            ${{ github.workspace }}/.next/cache
          # Generate a new cache whenever packages or source files change.
          key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }}
          # If source files changed but packages didn't, rebuild from a prior cache.
          restore-keys: |
            ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-

      - name: npm ci
        run: npm ci --no-fund

      - name: npm build
        run: npm run build --if-present

      - name: npm prune
        run:  npm prune --production --no-audit --no-fund

      - name: Craete node_modules.tar.gz
        run: sh ./preapareLocalCompressedNodeModule.sh

      - name: Zip all files for upload between jobs
        run: zip next.zip ./* .next -qr

      - name: Upload artifact for deployment job
        uses: actions/upload-artifact@v3
        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@v3
        with:
          name: node-app

      - name: 'Deploy to Azure Web App'
        id: deploy-to-webapp
        uses: azure/webapps-deploy@v2
        with:
          app-name: 'node-deploy-speed-test'
          slot-name: 'Production'
          publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_DA6945DB028A4CA8B66671AACA81ECC8 }}
          package: next.zip

      - name: Delete zip file
        run: rm next.zip

```

## 結果

Artifact のアップロード・ダウンロードはそれぞれ12分47秒 -> 28秒、4分6秒 -> 4秒に短縮されました。
`Deploy to Azure Web App`も 5分58秒 -> 30秒に短縮されました。
トータルで 32分49秒 -> 2分23秒と短縮されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/0c01fcf8-11dd-7eeb-394c-3baa91209c12.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/c5eaf9bb-bcd3-946a-7214-9b90cb913ad3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/660b5236-459a-9ff0-875e-de66b7924711.png)

念のため、App Service 側で `$home/site/wwwroot` の削除した状態で再度 Actions を実行してみても同等の結果でした。

また、package-lock.jsonに変更がない場合にソース変更があった場合は .next/cache のキャッシュが効くため `npm build` もある程度早くなります。