目次
Dockerfile 入門:自分のアプリをイメージにパッケージ化
Dockerfile は、イメージの作り方を一行ずつ記述するプレーンテキストファイルです。Docker は記述された手順を実行してイメージを作成します。作成したイメージは、nginx と同じように docker run で起動できます。
サンプルプロジェクト
hello-node フォルダーを作成し、その中に 2 つのファイルを置きます。
package.json:
{
"name": "hello-node",
"version": "1.0.0",
"scripts": { "start": "node server.js" }
}
server.js:
const http = require("http");
const server = http.createServer((req, res) => {
res.end("Hello from Docker!\n");
});
server.listen(3000, () => console.log("Listening on port 3000"));
お使いのパソコンにNode.js をインストールする必要はありません。Node.js はイメージ内にあります。これが Docker を使う理由です。
Dockerfile を書く
同じフォルダーに Dockerfile という名前のファイル(拡張子なし)を作成します。
# 1. 公式 Node.js イメージをベースにする
FROM node:24-alpine
# 2. コンテナ内の作業ディレクトリを設定する
WORKDIR /app
# 3. 先にパッケージ一覧をコピーして依存パッケージをインストールする
COPY package*.json ./
RUN npm install --omit=dev
# 4. 残りのコードをコピーする
COPY . .
# 5. コンテナが使用するポートを指定する
EXPOSE 3000
# 6. コンテナの起動時に実行するコマンド
CMD ["npm", "start"]
これでフォルダー内に 3 つのファイルがそろいました。
$ ls
Dockerfile
package.json
server.js
イメージをビルドする:docker build
docker build -t hello-node .
-t hello-node はイメージ名です。最後の . は、現在のディレクトリを**ビルドコンテキスト(build context)**として使うことを表します。Docker はこのディレクトリのファイルをビルドエンジンに送信します。COPY 命令でコピーできるのは、ビルドコンテキスト内のファイルだけです。
ビルド中は、ターミナルに進行状況が動的に表示されます。完了後の画面は次のようになります。
$ docker build -t hello-node .
[+] Building 9.9s (10/10) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 399B 0.0s
=> [internal] load metadata for docker.io/library/node:24-alpine 1.9s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 3.6s
=> => resolve docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 0.0s
=> => sha256:7ce7dcbafb7f750cf0c6be3c30e34372b06dd1182fa67f6ae30782c8150c5444 0B / 444B 7.9s
=> => sha256:02f40d53b06f6b91fec0cd6a6719656da99270bff8ee54ed8dcdf4c157763fc 1.26MB / 1.26MB 0.7s
=> => sha256:3c504d5a797be28f9de43147d49cd8ecb2eea46dfaf7beb8715db695d5f0a 56.52MB / 56.52MB 1.3s
=> => sha256:e2de96513ba9eb53b431787ec8a65cdde380ac4772a3e4c4b714dcfde2a102b 3.85MB / 3.85MB 0.6s
=> => extracting sha256:e2de96513ba9eb53b431787ec8a65cdde380ac4772a3e4c4b714dcfde2a102b5 0.6s
=> => extracting sha256:3c504d5a797be28f9de43147d49cd8ecb2eea46dfaf7beb8715db695d5f0a7f0 2.2s
=> => extracting sha256:02f40d53b06f6b91fec0cd6a6719656da99270bff8ee54ed8dcdf4c157763fc1 0.1s
=> => extracting sha256:7ce7dcbafb7f750cf0c6be3c30e34372b06dd1182fa67f6ae30782c8150c5444 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 759B 0.0s
=> [2/5] WORKDIR /app 0.0s
=> [3/5] COPY package*.json ./ 0.0s
=> [4/5] RUN npm install --omit=dev 1.4s
=> [5/5] COPY . . 0.0s
=> exporting to image 2.7s
=> => exporting layers 0.2s
=> => exporting manifest sha256:b311d231396fab904d11e3fbac8bade8302e8e7401fe14ba1e4a736f4fed 0.0s
=> => exporting config sha256:fa1ad1088d08bf3f3f035dfe9dbed3cf5a008492316926ba23fae3e58d6852 0.0s
=> => exporting attestation manifest sha256:0ea092cede0047cad91ad77043f79ef84c7cab9153913acf 0.0s
=> => exporting manifest list sha256:62664c2b6134a7c365eacb28019730c69b7d1d0fd855c559f6994c3 0.0s
=> => naming to docker.io/library/hello-node:latest 0.0s
=> => unpacking to docker.io/library/hello-node:latest 2.5s
各 => 行が1つのステップです。
| ステップ | 対応する Dockerfile 命令 | 説明 |
|---|---|---|
load build definition | — | Dockerfile を読み込む |
load metadata for …node:24-alpine | FROM | ベースイメージの最新バージョンを確認する |
[1/5] FROM … | FROM node:24-alpine | ベースイメージをダウンロードする(下の sha256:… は各レイヤーのダウンロード状況で、初回は時間がかかります) |
load build context | — | ビルドコンテキストを送信する(今回は小さなファイルが3つだけなので759Bです) |
[2/5] WORKDIR /app | WORKDIR | /app を作成して、作業ディレクトリに切り替える |
[3/5] COPY package*.json ./ | COPY | パッケージ一覧をコピーする |
[4/5] RUN npm install --omit=dev | RUN | イメージ内にパッケージをインストールする |
[5/5] COPY . . | COPY | 残りのコードをコピーする |
exporting to image | — | 結果をイメージとして保存し、hello-node:latest という名前を付ける |
EXPOSE と CMD は設定にすぎず、ビルドステップは生成されません。
実行する
$ docker images hello-node
IMAGE ID DISK USAGE CONTENT SIZE EXTRA
hello-node:latest 62664c2b6134 245MB 62.2MB
$ docker run -d -p 3000:3000 --name hello hello-node
2c4745bd0187d9ba7f0e9af096519ce9a2ad4436395490f5af24cd142f3bef4c
$ curl -s http://localhost:3000
Hello from Docker!
$ docker logs hello
> hello-node@1.0.0 start
> node server.js
Listening on port 3000

- イメージのサイズは約245 MBで、その大部分は Node.js 本体(node:24-alpine)です。
docker logsにある> node server.jsはnpm startが出力したメッセージで、その後にアプリケーション自身のListening on port 3000が表示されます。
コードを変更した後に再ビルドする:キャッシュ
server.js のテキストを Hello from Docker! (v2) に変更し、もう一度ビルドします。
$ docker build -t hello-node .
[+] Building 0.4s (10/10) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 399B 0.0s
=> [internal] load metadata for docker.io/library/node:24-alpine 0.3s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 0.0s
=> => resolve docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 294B 0.0s
=> CACHED [2/5] WORKDIR /app 0.0s
=> CACHED [3/5] COPY package*.json ./ 0.0s
=> CACHED [4/5] RUN npm install --omit=dev 0.0s
=> [5/5] COPY . . 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => exporting manifest sha256:667a883ab027b5db474215519a69eef0b162988f9f6384d75c7e6e819ebc 0.0s
=> => exporting config sha256:c3795d6af309587aa9f021692bf628df45d1d38a5e12d05388447644423a01 0.0s
=> => exporting attestation manifest sha256:4598cd1b9ff7979933f7bc06c48df78bb906278618ad587a 0.0s
=> => exporting manifest list sha256:fb09b954543fd2fa9a8d93763672d6a0b8d3ac26e7751e3b3f69b79 0.0s
=> => naming to docker.io/library/hello-node:latest 0.0s
=> => unpacking to docker.io/library/hello-node:latest 0.0s
今回は 0.4 秒しかかかりませんでした。
[2/5]、[3/5]、[4/5]の前に CACHED と表示されています。これらの手順は前回とまったく同じなので、前回の結果がそのまま使われ、npm installは再実行されません。[5/5] COPY . .だけが再実行されました。server.jsが変更されたためです。
$ docker run --rm -d -p 3000:3000 --name hello hello-node
9d4e4fc490b7b2e5d4c4c5ba729359c63271cbfa7934f6fff011a4fd4d6e87ca
$ curl -s http://localhost:3000
Hello from Docker! (v2)
$ docker stop hello
hello
なぜ先に package.json をコピーするのか
Dockerfile の各行はレイヤーを生成し、Docker はそれらのレイヤーをキャッシュします。ある行(およびその行でコピーするファイル)が変更されると、その行以降のすべての手順をやり直す必要があります。
最初に COPY . . を実行すると、コードを 1 行変更するたびに npm install のキャッシュが無効になり、すべてのパッケージを再インストールすることになります。先に package*.json だけをコピーしてからインストールすれば、パッケージ一覧が変更されたときだけ再インストールが必要です。プロジェクトのパッケージが多いほど、その差は大きくなります。詳しくはビルドキャッシュと最適化を参照してください。
.dockerignore:ビルドに不要なファイルを送らない
ローカルで開発していると、フォルダーには node_modules、.git、ログファイルなど、多くのファイルが含まれていることがよくあります。動作を確認するため、フォルダーに 30 MB の node_modules を置いて、もう一度ビルドします。load build context の行に注目してください。
$ docker build -t hello-node .
[+] Building 2.0s (10/10) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 399B 0.0s
=> [internal] load metadata for docker.io/library/node:24-alpine 0.2s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 0.0s
=> => resolve docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 0.0s
=> [internal] load build context 0.4s
=> => transferring context: 30.01MB 0.3s
=> CACHED [2/5] WORKDIR /app 0.0s
=> CACHED [3/5] COPY package*.json ./ 0.0s
=> CACHED [4/5] RUN npm install --omit=dev 0.0s
=> [5/5] COPY . . 0.1s
=> exporting to image 1.3s
=> => exporting layers 1.0s
=> => exporting manifest sha256:5c28f64104e51f64822405ae7a67a9016f62e9c5a4c8425b5b92c47791af 0.0s
=> => exporting config sha256:21b97fa4b8887f00cdf80b0dbeb6658f8dd678b8c4008f8d1dcb5c191e82ba 0.0s
=> => exporting attestation manifest sha256:bc9366f473fd5bb6fdffec42cdcd8155bd12b28cfc6ac23d 0.0s
=> => exporting manifest list sha256:f36d091892aca26420e44f681f5279c82f8881c32eb6d77978d99ee 0.0s
=> => naming to docker.io/library/hello-node:latest 0.0s
=> => unpacking to docker.io/library/hello-node:latest 0.2s
transferring context: 30.01MB:node_modules 全体がビルドエンジンに送られています。また、COPY . . によってイメージにもコピーされ、イメージ内で npm install したバージョンが上書きされます。
.dockerignore を作成し、ビルドに送らないファイルを列挙します。
node_modules
.git
.env
*.log
もう一度ビルドします。
$ docker build -t hello-node .
[+] Building 0.4s (10/10) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 399B 0.0s
=> [internal] load metadata for docker.io/library/node:24-alpine 0.3s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 69B 0.0s
=> [1/5] FROM docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 0.0s
=> => resolve docker.io/library/node:24-alpine@sha256:ebfe2f90462722a7a4de65e91990e97fe0d401 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 159B 0.0s
=> CACHED [2/5] WORKDIR /app 0.0s
=> CACHED [3/5] COPY package*.json ./ 0.0s
=> CACHED [4/5] RUN npm install --omit=dev 0.0s
=> [5/5] COPY . . 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => exporting manifest sha256:c7dd5ef36303b2a808ee6b594946ee3c029769363a3e7eecfbc53f1c3795 0.0s
=> => exporting config sha256:b23dccadd37348cdcc5e2ca60934e15f7f7f309f477651ab90b6f3759e2e25 0.0s
=> => exporting attestation manifest sha256:217b64bdf5112df5d8d6131ebb08c0fbdf3b30db091103a9 0.0s
=> => exporting manifest list sha256:4063457524bbb10ef5c91def983dc1385e7aac61245c61fc8a4d568 0.0s
=> => naming to docker.io/library/hello-node:latest 0.0s
=> => unpacking to docker.io/library/hello-node:latest 0.0s
ビルドコンテキストは 159B になりました。.dockerignore には次の利点があります。
- ビルドが速くなります。特に、大きな
node_modulesや.gitがあるプロジェクトで効果的です。 - これらのファイルを変更しても、
COPY . .のキャッシュが無効になりません。 .envや秘密鍵などの機密ファイルがイメージに含まれるのを防げます。
ホスト上の
node_modulesには、Windows や macOS 向けにコンパイルされたネイティブモジュールが含まれていることがあります。Linux コンテナにコピーすると、エラーが発生する可能性があります。Node.js プロジェクトでは、必ず.dockerignoreでnode_modulesを除外してください。
コンテナを停止する
$ time docker stop hello
hello
real 0m 0.69s
user 0m 0.01s
sys 0m 0.00s
0.7 秒で停止しました。コンテナのメインプロセスは npm です。docker stop が送信した SIGTERM を node に転送し、正常に終了します。node を直接メインプロセスとして実行する場合は、プログラム内で SIGTERM を処理する必要があります。そうしないと、docker stop は 10 秒間待つことになります。実測については、Dockerfile 命令一覧を参照してください。
ヒント
alpineまたはslimバージョンのベースイメージを選ぶと、イメージのサイズを大幅に小さくできます(サイズの比較はイメージを参照)。node:24-alpineのように、ベースイメージのバージョンを固定してください。nodeだけにしないでください。CMDには JSON 配列形式(["npm", "start"])を使うことをおすすめします。動作が最も明確になります。
次のセクションでは、Dockerfile の各命令について詳しく説明します。
まとめ
- Dockerfile は
FROMから始まり、各命令が 1 つのレイヤーを生成します。 docker build -t 名前 .でビルドします。最後の.はビルドコンテキストです。- パッケージ一覧を先にコピーしてからパッケージをインストールすると、コードだけを変更したときにキャッシュを利用できます。
.dockerignoreでnode_modules、.git、.envなど、不要なファイルを除外します。
練習問題
練習 1:server.js の応答メッセージを変更して再ビルドすると、どのステップに CACHED と表示されますか?
解答
FROM、WORKDIR、COPY package*.json、RUN npm install はすべて CACHED です。コードだけが変更されたため、COPY . . 以降のステップだけが再実行されます。
練習 2:Dockerfile の COPY package*.json ./ と RUN npm install を COPY . . の後に移動し、server.js をもう一度変更して再ビルドすると、何が変わりますか?
解答
RUN npm install も再実行されるため、ビルドが遅くなります。COPY . . の内容が変わると、それ以降の各レイヤーを再構築する必要があるためです。詳しくはビルドキャッシュを参照してください。
よくある質問
Dockerfile の名前は必ず Dockerfile にする必要がありますか?
デフォルトではそうです。他の名前を使う場合は docker build -f ファイル名 . で指定します。オプションは [docker build](/ja/docker/docker-build/) を参照してください。
自分のパソコンに Node.js をインストールする必要がありますか?
必要ありません。Node.js はイメージ内にあり、ビルドも実行もコンテナ内で行います。
自分で Dockerfile を書きたくありません。生成するツールはありますか?
Docker Desktop には docker init が組み込まれています。いくつかの質問に答えると Dockerfile と compose.yaml を生成できます。実際の使用例は [docker init](/ja/docker/docker-init/) を参照してください。
更新日 2026年10月14日