ども!今回は、nest.jsのDocker環境プロダクションビルドをGitHub Actions寄りに変更しました。CI上でnpmのキャッシュを活用して断続的なビルドを効率的に行えるように調整しています。アプリケーションのビルドをCI上に切り出すことで、テストの結果などもGitHub上で管理することができます。
はじまり
ども!すんごく珍しく二日連続で出社して、一年ぶりに同期と顔を合わせてびっくりされた龍ちゃんです。入社当時からリモートワークだと、偶然以外で誰かと会おうとすると気合が必要になりますね。同じ部署だとイベントで会いますけど、別部署の同期とはなかなかですね..
さて!今回はnest.jsの運用環境周りにまつわる話です。前回は、nest.jsのプロダクション環境用Dockerfileを作成して、Azure Web Appsにデプロイしました。前回のファイルでも、デプロイ自体は問題ありません。ですが、以下の問題点があります。
- アプリケーションのビルドが内部に閉じている
- npmで実行するテストコードの結果をGitHub Actionsで表示できない
- 不要なnode_modulesもコンテナの中に含まれてしまう
GitHub Actionsでテストを回しにくい環境は、あまり理想的ではありませんよね。極力効率化するのがエンジニアの使命であるということにしましょう。
それでは本題になります。
GitHub Actionsでnest.jsの軽量プロダクション環境
今回用意するのは、Dockerfile.prod
とワークフロー用のyml
になります。ディレクトリ構造としては以下になります。アプリケーションとしては、すべてbackend
に収まっている形になります。
.
├── Dockerfile
├── Dockerfile.prod # 運用環境用Dockerfile
├── docker-compose.yml
└── backend/ # Nest.jsのプロジェクト
Dockerfile.prod
ARG NODE_VER
FROM node:20.10.0
USER node
WORKDIR /home/node/app
COPY --chown=node:node ./backend/node_modules/ ./node_modules/
COPY --chown=node:node ./backend/dist ./dist/
EXPOSE 3000
ENTRYPOINT ["node", "dist/main.js"]
こちらのDockerfileでは、node_modules
とビルド済みのアプリdist
をコピーしています。
GitHub Actionsワークフロー
デプロイのために、トークンを発行して環境”production
”にTOKEN_CLASSIC
として設定しています。GitHub Actionsのフローの流れを説明します。
3つの段階に分けています。今回意識した部分としては、以下になります。
- actions/setup-nodeを用いてキャッシュを活用する
- 処理の目的ごとに分ける。アプリケーションのビルドを切り出す
- アプリケーションのビルドは、actions/cacheを使用して共通化する
- CIでテストの実行場所を確保する
実際のワークフローが以下になります。
#
name: Development CI
# Configures this workflow to run every time a change is pushed to the branch called `release`.
on:
push:
branches: ["main"]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
NODE_VERSION: "20.x"
jobs:
build-app:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{env.NODE_VERSION}}
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: npm install node_modules
run: npm install
working-directory: backend
# ここでCI上のテスト実行場所を確保する
- name: npm build
run: npm run build
working-directory: backend
- name: Upload Cache app
uses: actions/cache@v4
with:
path: backend/dist/
key: ${{ runner.os }}-app-${{ github.sha }}
build-and-push-image:
runs-on: ubuntu-latest
needs: "build-app"
environment: "production"
outputs:
image-name: ${{steps.build-name.outputs.container}}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download Cache app
uses: actions/cache@v4
with:
path: backend/dist/
key: ${{ runner.os }}-app-${{ github.sha }}
- uses: actions/setup-node@v4
with:
node-version: ${{env.NODE_VERSION}}
cache: "npm"
cache-dependency-path: "**/package-lock.json"
- name: npm install production
run: npm install --production
working-directory: backend
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.TOKEN_CLASSIC }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: ${{ github.sha }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.prod
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: build container name
id: build-name
run: |
echo "container=${{steps.meta.outputs.tags}}" >> $GITHUB_OUTPUT
deploy-to-webapps:
runs-on: ubuntu-latest
needs: [build-and-push-image]
environment: "production"
steps:
- uses: azure/webapps-deploy@v2
with:
app-name: "line-liff-backend"
publish-profile: ${{ secrets.AZURE_WEBAPP_PUBLISH_PROFILE }}
images: "${{needs.build-and-push-image.outputs.image-name}}"
アプリケーションとイメージのビルド時の二度「npm install」しているのは、アプリケーションのビルドで作成したnode_modules
をキャッシュで共通化すると、不要なファイルが共有される可能性が多くあるのと、actions/setup-nodeの機能でキャッシュが効果的で、npm install —production
で実行に必要なnode_modules
を作成するという目的があります。
WEB Appsのデプロイ部分に関しては、以下の記事を参考にしていただければ幸いです。トークンの発行部分なども併せて解説しています。
おわり
ども!Azure Web Appsにデプロイまでを記事にしたので、もう書くことないやと思っていたら上司からの声が降ってきて自分の考慮の甘さではずかしくなりました。
今までは、フロントのE2Eテストとコンポーネントの画面見ながらのテストばかりやっていたので、コードでテストを書くという経験がほぼないです。これからは、テストも書くという経験も発信できればなと思いますわ!
ではでは!
Twitterもよろしく~