🐷

Node.jsのDockerfile作成のベストプラクティス

2022/04/30に公開

はじめに

皆さま、Node.jsつかってますでしょうか?

私は様々なプロダクトでNode.jsをDockerコンテナで運用してきたのですが、その中で自分なりに見出してきた、Node.jsのコンテナの環境を作る際の自分なりにたどり着いたベストプラクティスについて共有します

各種ポイント

1. multi stage buildを活用してコンテナイメージを小さくする

Dockerコンテナの中身は小さければ小さいほど理想です。

最近は Node.js = TypeScript くらいにはTypeScriptが当たり前に利用されるようになってきましたよね。

TypeScriptは実際のソースコードとなる .ts ファイルをBuildして .js にしたものが実行される仕組みになっています。

実行時はもととなる .ts ファイルは不要になるので、そういったファイルはない状態でコンテナイメージを作成できることが理想です。

しかし、BuildしたものをわざわざDockerコンテナに最小化して配置するのはとても手間になりますよね。

そこで、Dockerの multi stage build という機能を利用します。

multi stage build とは、Docker Imageのstageを分けることで最終的に利用されるコンテナイメージはそのステージだけにできるという機能なります。

2.production dependencies のみインストールする

npmyarn で管理されているパッケージは、 package.json に記載されています。

その中でも以下のように役割が分かれています。

dependencies ⇒ 実行時に利用するパッケージ

devDependencies ⇒ 開発時にしか利用しないパッケージ(typescriptやlint等)

npm install をする際に —production オプションをつけることで、 dependencies に記載されているパッケージのみがインストールされるので、オプションを指定して、 production dependncies のみインストールし、最終的なコンテナイメージを軽くしましょう。

3. 実行環境では軽量なイメージを使う

コンテナイメージを少しでも軽くするために、できるだけ軽量なイメージを使うようにしましょう。

サンプルは node:14.16.1-alpine を利用していますが distrolessとかを使うと理想的ですね。

4. **.dockerignore ファイルを正しく設定する

リポジトリには、dockerimageの生成に不要なファイルがいくつか存在します。

.dockerignore に不要なファイルを定義して除外しましょう。

build時に生成される node_modulesdist ファイル等も定義しておくのが適切です。

**/node_modules/
**/.git
**/dist/

5. rootユーザーでの起動は辞めよう

言わずもです、rootユーザーでの実行の場合セキュリティ面で予期せぬトラブルや脆弱性を呼ぶ恐れがあるので、 USER を定義して、そのユーザーで実行をしましょう。

USER node

6. npmコマンドでnode.jsを立ち上げない

大体のフレームワークではデフォルトで npm start のような立ち上げコマンドがセットされているかと思います。

しかし、npmコマンドはシグナルをNode.jsに転送することができないので Graceful Shutdown が実行できません。

そのため、tini をインストールし dockerの --init オプションを使って回避します。

## PID1問題に対応する
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]

USER node

EXPOSE 3000

ENV NODE_ENV prod

## 実行コマンドは npm ではなく node を直接実行。
CMD ["node", "dist/src/main"]

最終的なDockerFile

## パッケージのインストール
FROM node:14.16.1-alpine as desp-stage
WORKDIR /app

COPY ./package.json ./package-lock.json ./
RUN npm install --production --no-progress

## buildを実行
FROM node:14.16.1-alpine as build-stage

WORKDIR /work

COPY . /work/

RUN npm install --no-progress

RUN npm run lint

RUN npm run build

## runtime環境を作成
FROM node:14.16.1-alpine as runtime-stage

ENV LANG C.UTF-8
ENV TZ Asia/Tokyo

WORKDIR /app

COPY ./package.json ./package-lock.json ./
COPY --from=desp-stage /app/node_modules ./node_modules
COPY --from=build-stage /work/dist ./dist

## PID1問題に対応する
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]

USER node

EXPOSE 3000

ENV NODE_ENV prod

CMD ["node", "dist/src/main"]

まとめ

今回はNode.jsでDockerfileを作る際の注意点をまとめてみました。

ただなんとなく構築してももちろん動きますが、正しく利用して、コンテナ技術の旨味を最大限生かせるようなプロダクトを作っていきたいですね。

色々やってみて、自分なりにたどり着いた最適解?ではあるのでツッコミがあれば是非コメントください。

Discussion