LoginSignup
6
0

NestJSをインストールしてDockerで動かすところまでやる

Last updated at Posted at 2023-01-28

概要

NestJSをWindows環境にインストールするところから、Dockerファイルに固めてコンテナ環境上で動作させるところまでの手順を忘備録代わりに残します。

環境は下記のとおりです

環境 バージョン
OS Windows 11 Home 22H2
nodeバージョン v18.13.0
npm バージョン 8.19.3
エディタ Visual Studio Code (*.editorconfigとかフォーマッタとか便利に使う設定を行う)

EoLスケジュール・サポートされる機能の確認

node 18.xは2025/4/30までサポートが行われる予定です。
NodeJSのリリーススケジュールは以下のページに記載があります。
https://github.com/nodejs/release#release-schedule

NodeJSのバージョンとサポートされるECMAScriptの機能について下記のページに記載があります。
https://node.green/

環境構築

voltaのインストール

nodeのバージョン管理ツールとして、「volta」をインストールします
https://docs.volta.sh/guide/getting-started

Windows Installationの項目の「download and run the Windows installer 」からインストーラーをダウンロードしてインストールできます。
※他のnodeのバージョン管理ツールが入っている場合は、先に削除する必要があります

node/npmのインストール及び設定

下記コマンドを実行し、node18をインストールします

volta install node@18

以下参考になりそうな記事

下記コマンドでインストールされていることを確認

PS C:\Users\yuuri> node -v
v18.13.0
PS C:\Users\yuuri> npm -v
8.19.3

nestCLIのインストール

下記コマンドでnestCLIをグローバルインストールします

npm i -g @nestjs/cli

nestjsのプロジェクトの作成及び確認

プロジェクトの作成

インストールしたnestCLIを使用して、nestjsのプロジェクトを作成します。
使用するpackage managerを聞かれるので、使用したいものを選んでプロジェクトの作成を行います。

nest new <project-name>

下記ではnpmを選択しました

nest new nest-sample-project
npm WARN deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead

added 250 packages, and audited 251 packages in 19s

41 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
npm notice
npm notice New major version of npm available! 8.19.3 -> 9.3.1
npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.3.1
npm notice Run npm install -g npm@9.3.1 to update!
npm notice
PS C:\Users\yuuri\sorairo-architecture> nest new nest-sample-project
⚡  We will scaffold your app in a few seconds..

? Which package manager would you ❤️  to use? npm
CREATE nest-sample-project/.eslintrc.js (663 bytes)
CREATE nest-sample-project/.prettierrc (51 bytes)
CREATE nest-sample-project/nest-cli.json (171 bytes)
CREATE nest-sample-project/package.json (1950 bytes)
CREATE nest-sample-project/README.md (3340 bytes)
CREATE nest-sample-project/tsconfig.build.json (97 bytes)
CREATE nest-sample-project/tsconfig.json (546 bytes)
CREATE nest-sample-project/src/app.controller.spec.ts (617 bytes)
CREATE nest-sample-project/src/app.controller.ts (274 bytes)
CREATE nest-sample-project/src/app.module.ts (249 bytes)
CREATE nest-sample-project/src/app.service.ts (142 bytes)
CREATE nest-sample-project/src/main.ts (208 bytes)
CREATE nest-sample-project/test/app.e2e-spec.ts (630 bytes)
CREATE nest-sample-project/test/jest-e2e.json (183 bytes)

✔ Installation in progress... ☕

🚀  Successfully created project nest-sample-project
👉  Get started with the following commands:

$ cd nest-sample-project
$ npm run start


                          Thanks for installing Nest 🙏
                 Please consider donating to our open collective
                        to help us maintain this package.


               🍷  Donate: https://opencollective.com/nest

設定したプロジェクト名と同名のフォルダが作成され、その中にnestjsのプロジェクトが作成されていることが確認できます

 ls .\nest-sample-project\


    ディレクトリ: C:\Users\yuuri\sorairo-architecture\nest-sample-project


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2023/01/21      2:32                node_modules
d-----        2023/01/21      2:31                src
d-----        2023/01/21      2:31                test
-a----        2023/01/21      2:31            663 .eslintrc.js
-a----        2023/01/21      2:32            391 .gitignore
-a----        2023/01/21      2:31             51 .prettierrc
-a----        2023/01/21      2:31            171 nest-cli.json
-a----        2023/01/21      2:32         554915 package-lock.json
-a----        2023/01/21      2:31           1950 package.json
-a----        2023/01/21      2:31           3340 README.md
-a----        2023/01/21      2:31             97 tsconfig.build.json
-a----        2023/01/21      2:31            546 tsconfig.json

アプリケーションの起動

プロジェクトのフォルダに移動し、下記コマンドを実行することでアプリケーションを起動します。
下記コマンドで起動した場合、ファイルに変更を加えた場合自動的にWebサーバーが再起動されます。

npm run start:dev

package.jsonのscriptの箇所には下記のものが定義されています(一部抜粋しています)
開発環境では npm run start:dev もしくは npm run start:debugを利用すればよさそうです

    "build": "nest build",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",

Voltaでのnodeバージョンの固定

プロジェクトのフォルダ内で下記コマンドを実行し、このプロジェクトで利用するnodeのバージョンを固定します。

volta pin node@18
volta pin npm@8

package.json内へに下記のように使用するnodeのバージョンが追記されます。

  "volta": {
    "node": "18.13.0",
    "npm": "8.19.3"
  }

エディタ絡みの環境設定

Visual Studio Codeにプラグインを追加し、.editorconfigによるエディタ設定の適用と保存時のフォーマッタによるコード整形を実行できるようにします。

ぼっちで開発するときはあまりきにしないのですが、複数人で開発するときはこのあたりが入っていないとgit関連の操作やレビューで大変なことになるので設定します。

.editorconfig

EditorConfig for VS CodeプラグインをVSCodeへ追加します

その後プロジェクトルートに.editorconfigファイルを作成します。

.editorconfig
root = true
[*]
# インデント時のスタイル設定(tab/space)
indent_style = space
# インデント時のサイズ設定
indent_size = 2
# 改行コード設定(lf/cr/crlf)
end_of_line = lf
# 文字コード設定(latin1/utf-8/utf-8-bom/utf-16be/utf-16le/...)
charset = utf-8
# 文末スペースの設定(true=削除)
trim_trailing_whitespace = true
# 最終行の改行設定(true=改行あり)
insert_final_newline = true

以上で、ファイルを操作する際に.editorconfigの設定がエディタに反映されるようになります。

参考: https://rfs.jp/sb/vsc/editorconfig.html

フォーマッタなどの設定

デフォルト設定

デフォルトでprettierの設定ファイルである.prettierrcが作成されます。

.prettierrc
{
  "singleQuote": true,
  "trailingComma": "all"
}

またpackage.jsonのscriptsのところへ下記設定が設定されています。

    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",

そのため、npm run formatコマンドを実行することでコードのフォーマットを行うことができます。

npm run format

> nest-sample-project@0.0.1 format
> prettier --write "src/**/*.ts" "test/**/*.ts"

src\app.controller.spec.ts 200ms
src\app.controller.ts 12ms
src\app.module.ts 7ms
src\app.service.ts 6ms
src\main.ts 4ms
test\app.e2e-spec.ts 11ms

VS Codeへプラグインの追加及び設定

ただ、毎回手動でフォーマットを実行するのは面倒くさいというかそのうちやらなくなるので、下記プラグインを追加しファイル保存時に自動的にフォーマッターが動作するようにします

ファイル>ユーザー設定>設定 からVScodeの設定画面を開き、左側のメニューからテキスト エディター>書式設定を選択します
image.png
Format On Paste, Format On Save, Format On Typeのチェックボックスへ要件に応じてチェックを入れます。
ファイル保存時にフォーマッタを動作させる場合、Format On Pasteにチェックを入れます。
image.png

その後、Default Formatterの項目で、Prettierをデフォルトのフォーマッタとして設定します。
image.png

これで、ファイル保存時などにpretterによるコードフォーマットが動作します。
.editorconfigの値はprettierからも参照(一部非対応で参照されないものもあるようですが)され、これに従ってフォーマットが行われるようです。

Linterの設定

デフォルト設定

デフォルトでeslinterの設定ファイルである.eslintrc.jsが作成されます。
下記コマンドを実行することで、linterを動作させることができます(自動的にコードが修正されます)。

npm run lint

> nest-sample-project@0.0.1 lint
> eslint "{src,apps,libs,test}/**/*.ts" --fix

VS Codeへプラグインの追加及び設定

下記プラグインをVS Codeに追加します。
https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint

プラグインを追加することで、linterによる構文検査結果をVScode上で確認することができます
image.png

参考:

環境変数及び設定ファイルの作成

  • ローカル環境での実行は.envファイルを読み込む
  • その他環境での実行時は、環境変数から設定を読み込む

形で実装していきます。

configurationモジュールの追加

下記コマンドでconfigurationモジュールを追加します

npm i --save @nestjs/config

app.module.ts内でconfigurationモジュールを読み込むよう設定を追加します
この例ではnest内のすべてのモジュールから利用できるように、isGlobal:trueを設定しています。
https://docs.nestjs.com/techniques/configuration#use-module-globally
またprocess.envへのアクセス(configurationモジュールは内部でdotenvを利用しています)が遅くなる場合があるそうなので、cache: trueを設定しています。
https://docs.nestjs.com/techniques/configuration#cache-environment-variables

app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config'; //追加

@Module({
  imports: [
    ConfigModule.forRoot({ //追加
      isGlobal: true,      //追加
      cache: true,         //追加
    }),                    //追加
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

設定値の読み取り

動作確認用に.envファイルを設定しました。

.env
ENVIRONMENT=local

app.service.tsで、configServiceを読み込み、.get('<環境変数名>')メソッドを使うことで設定値を読み込むことができます。

app.service.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; //追加

@Injectable()
export class AppService {
  private valueFromEnvFile: string; //追加

  constructor(private configService: ConfigService) {
    this.valueFromEnvFile = this.configService.get<string>('ENVIRONMENT'); //追加
  }

  getHello(): string {
    return 'valueFromEnvfile: ' + this.valueFromEnvFile; //追加
  }
}

下記コマンドで開発用サーバーを起動します

npm run start:dev

localhost:3000へアクセスして動作確認を行います。
.envファイルの中身が表示されることが確認できました。
image3.png

環境変数の利用

.envファイルと環境変数で同じ名前が設定されていた場合、環境変数の設定が優先して利用されます。
そのためローカル環境以外の環境では、環境変数を設定しておくことで.envがなくても環境変数の値を参照して動作させる、もしくは.envの値を環境変数で設定値を上書きして動作させることができます。

詳細はあとの章に記載しますが、例えばdocker-composeで環境変数を設定することで、.envから値を読み出すのと同様の手順で環境変数の値をプログラム内から使用することができます。

docker-compose.yml
version: '3'
services:
  nest-sample-project:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./src:/usr/src/app/src
    ports:
      - 3001:3000
    environment:
      - ENVIRONMENT=docker #環境変数の設定
    command: ['npm', 'run', 'start:dev']

image.png

参考:

Docker環境の整備

  • 開発環境用
    • Dockerfile.dev
    • docker-compose
  • 本番環境用
    • Dockerfile

を用意します。
開発環境では、他のDockerイメージと連携させて動作させるような環境の開発環境構築を想定しています。(単体で動かすだけであればnode run start:devすればいいかと思います)

本番環境はApp RunnerやCloud Run、K8sなどで動作させることを想定しています。

.dockerignoreの準備

プロジェクトのルートディレクトリに下記ファイルを用意します

.dockerignore

node_modules
npm-debug.log
dist

Dockerfile
Dockerfile.dev
docker-compose.yml

.env

.editorconfig
.eslintrc.js
.prettierrc
README.md

.git
.gitignore
.dockerignore

開発環境の構築

参考: https://nodejs.org/ja/docs/guides/nodejs-docker-webapp/

プロジェクトルートにそれぞれファイルを配置します。

Dockerfile.dev
FROM node:18
ENV NODE_ENV development
WORKDIR /usr/src/app

COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000

CMD ["npm", "run","start:dev"]
docker-compose.yml
version: '3'
services:
  nest-sample-project:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - ./src:/usr/src/app/src
    ports:
      - 3000:3000
    environment:
      - ENVIRONMENT=docker
    command: ['npm', 'run', 'start:dev']

ビルド

下記コマンドでDockerイメージのビルドを実行します

docker-compose build

起動

下記コマンドでdocker-composeで定義した開発環境をバックグラウンドで起動します。

docker-compose up -d

ログ確認

下記コマンドでログの確認を行えます
-fオプションを追加することで、継続的にログを確認できます。
その他にDocker Desktopの画面でログの確認等を行えます

docker-compose logs 

コンテナ内のシェルへアクセスする

下記コマンドでコンテナ内部のシェルへアクセスすることができます。
nest-sample-projectの箇所がdocker-composeで指定したサービス名に対応します。

docker-compose exec nest-sample-project bash
root@c4393687a35c:/usr/src/app#

exitコマンドでシェルから抜けられます

終了

下記コマンドで起動したコンテナを終了できます。

docker-compose down
[+] Running 2/2
 - Container nest-sample-project-nest-sample-project-1  Removed                                                                                                       1.0s
 - Network nest-sample-project_default                  Removed 

キャッシュを利用せずにビルドを行う

下記コマンドで
たまにキャッシュが悪さをするときがあるので、そういったときはこのコマンドを利用してコンテナを再ビルドします。

docker-compose build --no-cache

参考: https://pointsandlines.jp/server-infra/docker/docker-compose-build-no-cache

本番環境のDockerfileの作成

主にsnyk社が自社の技術ブログで公開している「10 best practices to containerize Node.js web applications with Docker」とnodejsのリポジトリにある「Docker and Node.js Best Practices」を参考に、色々試行錯誤しながら作成しました。

# ビルド環境
FROM node:18.13.0-bullseye-slim as builder
WORKDIR /usr/src/app

## ビルド必要なパッケージをインストール
COPY package*.json ./
RUN npm install

## ビルドの実施
COPY . /usr/src/app
RUN npm run build

# 実行環境
FROM node:18.13.0-bullseye-slim
ENV NODE_ENV production
WORKDIR /usr/src/app

## ビルド環境からビルド済みのファイル等をコピーし、当該フォルダのオーナーをnodeユーザーへ変更
COPY --from=builder --chown=node:node /usr/src/app/ /usr/src/app/ 
## 動作に必要なパッケージのインストール
RUN npm ci --only=production
EXPOSE 3000

## nodeユーザーとして実行
USER node
CMD ["node", "dist/main"]

Dockerイメージのビルド及び動作確認

下記コマンドでDockerfileをビルドし、prod-nest-sample-project というタグを付与します

docker build . -t prod-nest-sample-project

下記コマンドでprod-nest-sample-projectのタグがついたイメージを起動し、localhostの3000番ポートとコンテナの3000番ポートを接続します

docker run -p 3000:3000 -t prod-nest-sample-project

ブラウザでアクセスし、動作確認を行えます。

image.png

(追記)nestjsの終了時の処理

コンテナの終了時などにOSからSIGTERMが発行され、これを受けてnestjsでプログラムの終了処理を実行することができます。
https://docs.nestjs.com/fundamentals/lifecycle-events#application-shutdown

このあたりの機能はデフォルトでは無効(メモリリソースを使う処理のため)になっていますが、 main.tsを下記のように変更することで有効にすることができます。

main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Starts listening for shutdown hooks
  app.enableShutdownHooks(); //追加

  await app.listen(3000);
}
bootstrap();

で、上記機能を有効にすることで、きちんとSIGTERMを取り扱ってくれるようです。(ドキュメント等の記載では特に見つけられていないです・・・)
dockerを停止する際に10秒後のSIGKILLが発行されるまでコンテナが終了しなかったものが、上記処理を有効にすることで10秒待たずに終了するようになりました。

(追記) huskyによるpre-commitの利用

huskyを導入することで、git commit実行直前に任意のコマンド(npm run formatとか)を実行することができます。

Automatic (recommended)にしたがって、huskyを導入します。
その後.husky/pre-commitを編集することで任意のコマンドを実行することができます

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run format #追加
npm test
6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0