この記事は Elixir Advent Calendar 2024 シリーズ1 5日目の記事です。
概要
Phoenix 1.7 (Phoenix 1.6 以降) では Node.js が同梱されていないため、Chart.js のような純粋な JavaScript ライブラリを静的アセットとして含めようとした際に、外部での Node.js を利用した処理が必要になります。
このようなケースで、Docker マルチステージビルドを利用して効率的かつ軽量に JavaScript ライブラリをインストールする方法をご紹介します。
マルチステージビルドとは?
マルチステージビルドとは、Dockerfile 内で複数のベースイメージを使用し、それぞれのステージで役割を分ける方法です。
これによって必要な部分だけを最終イメージに含めることができ、イメージを軽量化できます。
Dockerfile の例
Node.js だけを含む軽量な Alpine ベースイメージを使用して npm install
し、その成果物を Elixir ベースイメージに統合します。
以下は、Chart.js のみをインストールするシンプルなマルチステージビルドの例です。
# Stage 1: Node.js ベースイメージ (Alpine) で npm install を実行
FROM node:22-alpine AS node-builder
WORKDIR /app/assets
COPY assets/package*.json ./
RUN npm install chart.js
# Stage 2: Elixir ベースイメージでアプリケーションを構築
FROM elixir:1.17.3-slim
RUN apt-get update && apt-get install -y \
inotify-tools
WORKDIR /app
COPY mix.exs mix.lock ./
COPY config config
RUN mix deps.get
RUN mix deps.compile
COPY lib lib
COPY priv priv
COPY assets assets
# node-builder で生成された node_modules を渡す
COPY --from=node-builder /app/assets/node_modules assets/node_modules
RUN mix do compile, phx.digest
CMD ["mix", "phx.server"]
-
※ Phoenix のバージョンは mix.exs で指定
mix.exsdefp deps do [ {:phoenix, "1.7.18"}, ...
なぜマルチステージビルドか?
Node.js を利用するための方法として、ほかには nodenv、n 等のバージョン管理ツールや apt-get
を利用して Node.js をインストールする選択肢があります。しかし、これらの方法を検討する際には考慮すべき事項がそれぞれあり、一方マルチステージビルドを使うと嬉しいポイントがいくつかあります。
マルチステージビルドを使用するメリット
バージョン管理の柔軟性
- Node.js の公式イメージを利用するため、必要に応じて特定のバージョンを簡単に指定できる
- 例えば、node:22-alpine のように、最新バージョンの LTS Alpine ベースイメージを利用可能
軽量化
- Elixir の最終イメージには Node.js を含めないため、イメージサイズが大幅に削減される
- Node.js を利用するステージ(先ほどの例だと node-builder)は、アセットビルド専用の一時的ステージとしてのみ機能する
キャッシュの活用
-
COPY assets/package*.json ./
を実行することで、依存関係が変更されない限りはnpm install
の結果がキャッシュされるので、package.json に変更がなければ再ビルド時にnpm install
再実行をする必要がなくなり、再ビルドの時間を短縮できる
責務の分離
- Node.js を利用するのは JavaScript ライブラリのインストールに必要な第1ステージ (node-builder) のみであり、Node.js に関する処理を完全に分離することで、Elixir ベースのイメージはそのままの構成を維持でき、ビルドプロセス全体の構成管理がシンプルになる
おわりに
マルチステージビルドを使用することで、Node.js が必要な JavaScript ライブラリインストールの処理を独立させることができます。
Elixir ベースのイメージをシンプルに保ちながら軽量なビルドプロセス構成を実現できるので、ぜひ静的アセット管理での活用を検討してみてください。