Node.jsウェブアプリをDocker化する方法
Dockerは、アプリケーションのパッケージ化と実行を簡素化するコンテナ化プラットフォームです。コンテナは、それぞれ独自のファイルシステムを持つ孤立したプロセスとして実行されますが、ホストのカーネルを共有します。Dockerは、再現可能な開発環境や分散型展開アーキテクチャを実装する手段として、注目を集めています。
Node.jsはバックエンド開発における主要なJavaScriptランタイムです。Node.jsのWebサービスを成功裏に立ち上げるには、ランタイムがインストールされた環境、アプリケーションコードが利用可能な状態、クラッシュ時に自動的に再起動を処理するメカニズムが必要です。
このガイドでは、人気のあるExpressウェブフレームワークを使って作成されたシンプルなNode.jsアプリをコンテナ化するためにDockerを使用します。Dockerは、サービスを実行するために必要なすべてを含む一貫した環境を提供するため、Nodeベースのシステムをデプロイする良い方法です。Dockerデーモンは、フォアグラウンドプロセスがクラッシュした場合に、失敗したコンテナを再起動するための統合サポートを備えており、Node.jsデプロイメントの課題の一つを解決します。
ノードプロジェクトの作成
あなたのアプリケーションの実装の詳細は省略します。プロジェクトのためにディレクトリを作成し、その中にサーバーコードを追加してください。こちらが基本的な
app.js
ポート8080でリッスンし、すべてのリクエストにハードコーディングされたレスポンスで応答する:
const express = require("express");
const app = express();
app.get("*", (req, res) => res.send("<p>It works!</p>"));
app.listen(8080, () => console.log("Listening on 8080"));
npmを使用してプロジェクトにExpressを追加します:
npm init
npm install --save express
アプリを起動して動作確認をしてください。
node app.jsブラウザでlocalhost:8080にアクセスして、サンプルレスポンスを見ることができるはずです。
Dockerfileを書く
さあ、プロジェクトをDocker化する時間です。まず、アプリケーション用のイメージが必要です。イメージは、あなたのコードと依存関係を単一のパッケージとしてカプセル化し、コンテナインスタンスを起動するのに使用します。Dockerfileの指示は、コンテナの初期ファイルシステムの状態を定義します。
サンプルアプリケーションに対応するDockerfileは以下の通りです:
FROM node:16
WORKDIR /app
COPY package.json .
COPY package-lock.json .
RUN npm ci
COPY app.js .
CMD ["app.js"]
このDockerfileは、FROMステートメントを介して公式のNode.js Dockerイメージをベースとして選択します。このイメージはベース内のすべてを継承し、その後、以下の指示に従って追加の内容を加えます。
作業ディレクトリは/appにWORKDIR行によって設定されます。以下のCOPYステートメントは、コンテナイメージ内の/appディレクトリにファイルを配置します。
依存関係のインストール
次のステージはnpmの package.json を追加し、 npm ci を実行することです。これにより、プロジェクトのnpm依存関係 - この場合はExpress - がコンテナのファイルシステム内にインストールされます。
プロジェクトディレクトリ内の既存の node_modules フォルダーをコピーするために COPY node_modules/ . を使用しないでください。これは、他のビルド環境で Dockerfile を再利用できなくします。Dockerfile は、ソースコントロールリポジトリの内容だけで一貫したビルドを作成できるようにするべきです。ファイルやフォルダーが .gitignore に含まれている場合、それは Dockerfile の COPY 命令で参照されるべきではありません。
アプリケーションコードのコピー
npm ci が実行された後、アプリのコードがイメージにコピーされます。この COPY 命令が RUN の後に配置されており、前のコピーから分離されているのは意図的なものです。各命令はイメージ内に新しいレイヤーを作成します; Docker のビルドプロセスは、次回のビルドを加速するために各レイヤーをキャッシュします。1つのレイヤーの内容が変更されると、すべての後続レイヤーのキャッシュは無効になります。
これは、アプリケーションコードは npm ci を実行した後にコピーするべき理由です。コードは通常、npmロックファイルの内容よりも頻繁に変更されます。コードの変更だけが関与するイメージの再構築は、RUN npm ci ステージ(およびすべての前のステージ)を実質的にスキップし、多くの依存関係を持っている場合にプロセスを大幅に加速します。
画像のコマンドを設定する
最終のDockerfileステージでは、CMD命令を使用して、コンテナが起動したときにアプリを自動的に実行します。これは、Node.jsのベースイメージがnodeプロセスをエントリーポイントとして使用するように設定されているためです。CMDは継承されたエントリーポイントに追加され、node app.jsが新しいイメージのフォアグラウンドプロセスとして実行されます。
あなたのイメージを構築する
次に、イメージを構築する必要があります:
docker build -t node-app:latest .Docker は、作業ディレクトリ内の Dockerfile を取り込み、その中の指示を実行し、結果として得られたイメージに node-app:latest というタグを付けます。最後の .(ピリオド)は、イメージビルドコンテキストとして作業ディレクトリを指定します。これにより、Dockerfile の COPY 指示で参照できるパスが決まります。
ビルド最適化
ビルドパフォーマンスを向上させる一つの方法は、プロジェクトのルートに .dockerignore ファイルを追加することです。ファイルには次の内容を記入してください:
node_modules/このファイルは、ビルドコンテキストに含まれない作業ディレクトリ内のパスを定義します。これらをDockerfile内で参照することはできません。node_modulesの場合、このディレクトリの内容はビルドには無関係です。なぜなら、RUN npm ci命令を介して依存関係を新たにインストールするからです。作業ディレクトリ内に既に存在するnode_modulesを特に除外することで、Dockerの一時ビルドコンテキスト場所にすべてのファイルをコピーする必要がなくなります。これにより効率が向上し、ビルドの準備にかかる時間が短縮されます。
コンテナを開始する
この時点で、Dockerを使ってアプリケーションを実行する準備が整いました。
docker run -d
-p 8080:8080
--name my-app
--restart on-failure
node-app:latest
docker runコマンドは、指定されたイメージから新しいコンテナインスタンスを起動するために使用されます。意図した使用ケースに適切にコンテナを設定するために、いくつかの追加のフラグが追加されます:
-d- コンテナのフォアグラウンドプロセスからシェルを切り離し、実際にバックグラウンドサーバーとして実行します。-p- ホストのポート8080をコンテナ内のポート8080にバインドします(これは、私たちのExpressサンプルアプリがリッスンするように設定されたポートです)。つまり、localhost:8080へのトラフィックが対応するコンテナポートに転送されることを意味します。最初のバインド定義の部分を変更することで、ホストポストを異なる値に変更できます。たとえば、8100:8080のようにして、localhost:8100でコンテナにアクセスできます。- --name - コンテナーにフレンドリーな名前を割り当て、他のDocker CLIコマンドでそれを参照するために使用できます。
- --restart - コンテナに適用する再起動ポリシーを選択します。
on-failure設定は、アプリケーションがクラッシュしたためにコンテナが異常終了した場合、Dockerがコンテナを自動的に再起動することを意味します。
前のステップで作成したイメージは、docker run コマンドの最終引数として参照されます。コンテナ ID がターミナルウィンドウに出力されます; localhost:8080 を再度訪問することで、Node.js アプリにアクセスできるはずです。今回は、サーバーがホストにインストールされた node プロセスを使用する代わりに、Docker コンテナ内で実行されています。
要約
Dockerは、アプリケーション全体の環境をコンテナ化することで、Node.js Webサービスのデプロイを支援します。Dockerがインストールされた任意のホスト上で、単一のdocker runコマンドを使用して、イメージからコンテナを起動することができます。これにより、Node.jsのバージョン管理、npmモジュールのインストール、およびアプリケーションプロセスを再起動する必要がある状況の監視という複雑さが解消されます。
コードに変更を加え、更新をリリースしたいときは、Dockerイメージを再ビルドし、古いコンテナを docker rm <container-name> コマンドで削除します。その後、修正されたイメージを使用する置き換えインスタンスを起動できます。
本番環境では、やや異なるルーチンが必要になるかもしれません。docker runを使用して通常のDockerインストールを行うこともできますが、これは最も簡単なアプリケーションを除いては扱いにくい傾向があります。コンテナの設定をリポジトリ内でバージョン管理できるファイルに定義するために、Docker ComposeやKubernetesのようなツールを使用することが一般的です。
これらのメカニズムは、新しいコンテナを起動するたびにdocker runフラグを繰り返す必要をなくします。また、サービスをスケールするためのコンテナの複製や冗長性の提供を容易にします。リモートホストにデプロイする場合は、イメージをDockerレジストリにプッシュして、プロダクションマシンから「プル」できるようにする必要があります。
別のプロダクション特有の考慮事項は、コンテナへのトラフィックをどのようにルーティングするかです。最初はポートバインドで十分かもしれませんが、最終的には同じポートでリッスンしている複数のコンテナを1つのホスト上で使用したい状況に達するでしょう。この場合、リクエストの特性(ドメイン名やヘッダーなど)に基づいて、トラフィックを個々のコンテナポートにルーティングするためにリバースプロキシを展開することができます。




