はじめに
猫大好きエンジニアの福田です。
皆さんはDockerfileでNode.jsをインストールするとき、どうやってインストールしていますか?
良くみる方法
Node.jsのインストール方法を調べると、こちらのサイトを参考に以下のようにインストールする方法を良く見かけますが、
メジャーバージョンだけを指定してインストールするため、完全なバージョンを指定することができません。
少ないコマンドで済むので簡単ではありますが、指定したメジャーバージョンの最新バージョンがインストールされてしまいます。
FROM ruby:3.2.2-bullseye
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get update \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "sh", "-cx" ]
CMD [ "node --version && npm --version && npx --version" ]
# 実行結果
# docker build -t ruby-node-apt:latest . -f Dockerfile.apt
# docker run --rm ruby-node-apt:latest
# + node --version
# v18.16.0
# + npm --version
# 9.5.1
# + npx --version
# 9.5.1
そこで、バージョン指定してインストールする2通りの方法を紹介します。
2つの方法
- バイナリからインストール
- マルチステージビルドを利用してインストール
※Node.jsのイメージを利用すればバージョンを指定することができますが、本記事では
他のイメージを使いつつNode.jsの固定のバージョンをインストールしたいというような状況を想定しています。
バイナリからインストール
https://nodejs.org/dist/ に各バージョンごとにアーキテクチャのバイナリがありますので、そちらをダウンロードして利用する方法です。
FROM ruby:3.2.2-bullseye
ENV NODE_VERSION 18.16.0
RUN export ARCH=$(uname -m | sed 's/aarch64/arm64/' | sed 's/x86_64/x64/') \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" \
&& tar -xJf "node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs
ENTRYPOINT [ "sh", "-cx" ]
CMD [ "node --version && npm --version && npx --version" ]
# 実行結果
# docker build -t ruby-node-binary:latest . -f Dockerfile.binary
# docker run --rm ruby-node-binary:latest
# + node --version
# v18.16.0
# + npm --version
# 9.5.1
# + npx --version
# 9.5.1
本番等で利用する場合は、GPGで改竄されていないかをチェックし、利用するイメージもslimにして軽量化するなどした方が良いでしょう。
GPG version
FROM ruby:3.2.2-slim-bullseye
ENV NODE_VERSION 18.16.0
RUN export buildDeps='curl xz-utils ca-certificates gnupg2 dirmngr' \
&& apt-get update \
&& apt-get install -y --no-install-recommends ${buildDeps} \
&& rm -rf /var/lib/apt/lists/* \
&& set -ex \
&& for key in \
# https://github.com/nodejs/node#release-keys
4ED778F539E3634C779C87C6D7062848A1AB005C \
141F07595B7B3FFE74309A937405533BE57C7D57 \
74F12602B6F1C4E913FAA37AD3A89613643B6201 \
8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 \
C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \
108F52B48DB57BB0CC439B2997B01419BD92F80A \
; do \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "${key}"; \
done \
&& export ARCH=$(uname -m | sed 's/aarch64/arm64/' | sed 's/x86_64/x64/') \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v${NODE_VERSION}/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v${NODE_VERSION}-linux-${ARCH}.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-v${NODE_VERSION}-linux-${ARCH}.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
&& apt-get purge -y --auto-remove ${buildDeps}
ENTRYPOINT [ "sh", "-cx" ]
CMD [ "node --version && npm --version && npx --version" ]
マルチステージビルドを利用してインストール
マルチステージビルドを利用すると以下のように書くことができます。
FROM node:18.16.0-bullseye-slim as node
FROM ruby:3.2.2-slim-bullseye
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
RUN ln -s /usr/local/bin/node /usr/local/bin/nodejs \
&& ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npx
ENTRYPOINT [ "sh", "-cx" ]
CMD [ "node --version && npm --version && npx --version" ]
# 実行結果
# docker build -t ruby-node-multi-stage-build:latest . -f Dockerfile.multi-stage-build
# docker run --rm ruby-node-multi-stage-build:latest
# + node --version
# v18.16.0
# + npm --version
# 9.5.1
# + npx --version
# 9.5.1
まとめ
マルチステージビルドでのインストールについては、同じディストリビューションでないと利用できないと思いますがバイナリからインストールに比べるとビルド時間も早く、ビルド後のイメージの容量も少なくなるようです。
#
# slimを利用しているGPG version版と、マルチステージビルドの容量を比較
#
➜ docker images | grep -e binary-gpg -e multi-stage-build
ruby-node-binary-gpg latest ca1955fa80a4 40 seconds ago 321MB # バイナリ版
ruby-node-multi-stage-build latest 16c9b4c9d71c 6 minutes ago 269MB # マルチステージ版
Node.jsをバージョン指定でインストールする2つの方法についてご紹介いたしました。
どなたかの参考になれば幸いです。