🖥️

【3-3】Dockerfile入門!カスタムイメージ作成とNode.js開発環境のDocker化

に公開

Dockerfile入門!カスタムイメージ作成とNode.js開発環境のDocker化

はじめに

前回の記事では、docker rundocker volumeといった基本的なコマンドを学び、コンテナの操作とデータ管理の基礎を固めました。しかし、これまではDocker Hubで公開されている既存のイメージを使うだけでした。

https://zenn.dev/koikoi_infra/articles/b49afcc6a86fc8

今回は、いよいよDockerの真髄であるDockerfileを使い、自分だけのカスタムイメージを作成する方法を学びます。シンプルなWebサーバーから始め、より実践的なNode.jsアプリケーションの開発環境をDocker化するまでを、ステップバイステップで解説します。

この記事では、Dockerfileの書き方からdocker buildコマンドによるイメージのビルドまで、アプリケーションをコンテナ化するための一連のプロセスを、実際に手を動かしながら体験します。

実践①: シンプルなWebサーバーのイメージを作成する

まずはウォーミングアップとして、自作のHTMLファイルを表示するだけの、非常にシンプルなWebサーバーイメージを作成してみましょう。

Step 1: 準備(ディレクトリとHTMLファイルの作成)

ubuntu-server-01上で作業用のディレクトリを作成し、表示したいHTMLファイルを用意します。

# 作業用のディレクトリを作成して移動
mkdir ~/docker-practice
cd ~/docker-practice

# 表示するHTMLファイルを作成
echo '<h1>My Custom Web App</h1><p>Built with Docker!</p>' > index.html
【実践トラブル】echoコマンドで「!: event not found」?

実は最初、echo "..."のようにダブルクォートで上記のコマンドを実行したところ、-bash: !: event not foundというエラーに遭遇しました。

これは、LinuxのBashシェルが、ダブルクォート内であっても感嘆符!を「コマンド履歴の呼び出し」という特別な機能として解釈しようとしたためです。

解決策
echo '...'のようにシングルクォートで文字列全体を囲むことで、!を含むすべての文字が特殊な意味を持たなくなり、ただの文字列として扱われます。これにより、無事にindex.htmlを作成できました。シェルの挙動を学ぶ良い機会となりました。

Step 2: Dockerfileの作成と解説

次に、イメージの設計図となるDockerfileを作成します。

cat > Dockerfile << 'EOF'
# ベースとなるイメージを指定
FROM nginx:alpine

# ホストのファイルをコンテナ内にコピー
COPY index.html /usr/share/nginx/html/

# このコンテナは80番ポートを外部に公開できることを宣言
EXPOSE 80

# コンテナ起動時に実行されるデフォルトのコマンド
CMD ["nginx", "-g", "daemon off;"]
EOF

各命令の意味:

  • FROM nginx:alpine: イメージの土台です。軽量なnginx:alpineイメージをベースにします。
  • COPY index.html ...: ファイルの配置です。先ほど作成したindex.htmlを、Nginxがコンテンツを公開するディレクトリにコピーします。
  • EXPOSE 80: ポートの宣言です。このイメージが80番ポートを使うことをDockerに伝えます(実際にポートを開けるのはdocker run-pオプションです)。
  • CMD ["nginx", ... ]: 起動コマンドの指定です。このイメージからコンテナを起動した際に、デフォルトで実行されるコマンドを指定します。

Step 3: ビルドと実行(docker build, docker run)

設計図ができたので、docker buildコマンドでイメージを構築します。

# カレントディレクトリ(.)のDockerfileを元にイメージをビルド
docker build -t my-web-app:v1.0 .

# ビルドしたイメージからコンテナを起動
docker run -d --name custom-web -p 8082:80 my-web-app:v1.0
  • -t my-web-app:v1.0: ビルドするイメージに名前(my-web-app)とタグ(v1.0)を付けます。
  • .: Dockerfileがある場所(ビルドコンテキスト)を指定します。ここではカレントディレクトリを指します。

ブラウザでhttp://192.168.3.101:8082にアクセスし、"My Custom Web App"と表示されれば成功です!

実践②: Node.js開発環境をDocker化する

次に、より実践的な例として、Node.js(Express)で書かれたWebアプリケーションの開発環境をDockerイメージにしてみましょう。

Step 1: アプリケーションの準備

新しいディレクトリを作成し、Node.jsアプリのソースコードと設定ファイルを用意します。

mkdir ~/nodejs-app
cd ~/nodejs-app
# package.jsonを作成 (アプリの依存ライブラリなどを定義)
cat > package.json << 'EOF'
{
  "name": "docker-node-app",
  "version": "1.0.0",
  "main": "app.js",
  "scripts": { "start": "node app.js" },
  "dependencies": { "express": "^4.18.0" }
}
EOF

# app.jsを作成 (Webサーバーの処理本体)
cat > app.js << 'EOF'
const express = require('express');
const app = express();
const port = 3000;

app.get('/', (req, res) => {
  res.send('<h1>Hello Docker!</h1><p>Node.js Express App</p>');
});

app.listen(port, () => {
  console.log(`App running at http://localhost:${port}`);
});
EOF

Step 2: より高度なDockerfileの作成と解説

Node.jsアプリのDockerfileは、ライブラリのインストール工程が加わるため、少し複雑になります。

cat > Dockerfile << 'EOF'
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]
EOF

各命令の意味:

  • WORKDIR /app: コンテナ内での作業ディレクトリを指定します。これ以降の命令(COPYやRUN)は、この/appディレクトリを基準に実行されます。
  • COPY package*.json ./: package.json(とpackage-lock.json)だけを先にコピーします。
  • RUN npm install: イメージをビルドする過程で実行されるコマンドです。package.jsonを元に、expressなどの必要なライブラリをインストールします。
  • COPY . .: app.jsなど、残りのソースコードを全てコピーします。
【Tips】なぜCOPYを2回に分けるのか?

このCOPYを2段階に分けるのには、ビルド時間の短縮という大きな目的があります。

DockerはDockerfileの各行を「レイヤー」としてキャッシュします。package.jsonに変更がない限り、時間のかかるRUN npm installのレイヤーはキャッシュが再利用されます。これにより、app.jsのちょっとした修正のたびにnpm installが走り直すのを防ぎ、ビルドを劇的に高速化できるのです。

Step 3: ビルドと実行

先ほどと同じように、ビルドして実行します。

# Node.jsアプリのイメージをビルド
docker build -t nodejs-app:v1.0 .

# ビルドしたイメージからコンテナを起動
docker run -d --name my-node-app -p 3000:3000 nodejs-app:v1.0

ブラウザでhttp://192.168.3.101:3000にアクセスし、"Hello Docker!"と表示されれば成功です。docker logs my-node-appでコンテナのログも確認してみましょう。

まとめ

これで、既存のイメージを使うだけでなく、自分たちのアプリケーションに最適化されたイメージを自由に作成できるようになりました。

次回予告

毎回docker runで長いオプションを指定し、複数のコンテナを一つずつ起動するのは大変です。

次回は、複数のコンテナ構成を一つのファイルでスマートに管理・起動できるオーケストレーションツール、Docker Composeについて学んでいきたいと思います!

Discussion