Node.jsのDockerfile作成のベストプラクティス
はじめに
皆さま、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 のみインストールする
npm
や yarn
で管理されているパッケージは、 package.json
に記載されています。
その中でも以下のように役割が分かれています。
dependencies
⇒ 実行時に利用するパッケージ
devDependencies
⇒ 開発時にしか利用しないパッケージ(typescriptやlint等)
npm install
をする際に —production
オプションをつけることで、 dependencies
に記載されているパッケージのみがインストールされるので、オプションを指定して、 production dependncies のみインストールし、最終的なコンテナイメージを軽くしましょう。
3. 実行環境では軽量なイメージを使う
コンテナイメージを少しでも軽くするために、できるだけ軽量なイメージを使うようにしましょう。
サンプルは node:14.16.1-alpine
を利用していますが distrolessとかを使うと理想的ですね。
.dockerignore
ファイルを正しく設定する
4. **リポジトリには、dockerimageの生成に不要なファイルがいくつか存在します。
.dockerignore
に不要なファイルを定義して除外しましょう。
build時に生成される node_modules
や dist
ファイル等も定義しておくのが適切です。
**/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