Docker入門 — エンジニアが最初に知るべきコンテナの基本と実践
はじめに
「自分のPCでは動くのに、サーバーでは動かない」――エンジニアなら一度は経験する問題です。OS の違い、ライブラリのバージョン差異、環境変数の設定漏れ。原因は多岐にわたりますが、Docker はこの「環境差異」という問題を根本から解決してくれます。
この記事では、Docker を初めて使うエンジニアに向けて、基本概念からインストール、Dockerfile の書き方、docker-compose による複数コンテナの管理、そして実践的なコンテナ化の例まで体系的に解説します。
Docker とは何か
Docker は コンテナ型仮想化 のプラットフォームです。アプリケーションとその実行に必要な環境(OS、ライブラリ、設定ファイル)をひとまとめにした「コンテナ」を作成・実行できます。
仮想マシン(VM)との違い
Docker を理解する上で、従来の仮想マシン(VirtualBox や VMware)との違いを押さえておきましょう。
| 項目 | 仮想マシン(VM) | Docker コンテナ |
|---|---|---|
| 仮想化の対象 | OS 全体 | アプリケーション層のみ |
| 起動時間 | 数分 | 数秒 |
| メモリ消費 | 数GB | 数十MB〜数百MB |
| ディスク容量 | 数十GB | 数十MB〜数百MB |
| パフォーマンス | オーバーヘッド大 | ほぼネイティブ |
| 可搬性 | 低い | 高い(イメージで配布) |
VM はハードウェアレベルからOS全体を仮想化するのに対し、Docker はホストOS のカーネルを共有しながらアプリケーション層だけを分離します。そのため、起動が速く、リソース消費が少ないのが特徴です。
Docker の基本用語
Docker を使い始める前に、以下の用語を理解しておきましょう。
イメージ(Image)
コンテナの設計図。OS、ライブラリ、アプリケーションコードが含まれる読み取り専用のテンプレートです。Dockerfile から作成します。
コンテナ(Container) イメージから起動された実行環境。イメージのインスタンスとも言えます。起動・停止・削除ができ、同じイメージから複数のコンテナを作成できます。
Dockerfile イメージの構築手順を記述したテキストファイル。ベースイメージの指定、ファイルのコピー、コマンドの実行などを定義します。
Docker Hub
Docker イメージの公開レジストリ。公式イメージ(Node.js、Python、PostgreSQL など)が公開されており、docker pull でダウンロードできます。
ボリューム(Volume) コンテナのデータを永続化する仕組み。コンテナを削除してもボリュームのデータは残ります。
インストール
macOS の場合
macOS では Docker Desktop をインストールするのが最も簡単です。
# Homebrew でインストール
brew install --cask docker
# インストール確認
docker --version
docker compose version
Docker Desktop を起動すると、メニューバーにクジラのアイコンが表示されます。これが表示されていれば Docker デーモンが起動しています。
Linux(Ubuntu)の場合
Linux ではコマンドラインからインストールします。
# 古いバージョンを削除
sudo apt-get remove docker docker-engine docker.io containerd runc
# 必要なパッケージをインストール
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
# Docker の公式GPGキーを追加
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# リポジトリを追加
echo \
"deb [arch=$(dpkg --print-architecture) \
signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Docker をインストール
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# 一般ユーザーで docker コマンドを使えるようにする
sudo usermod -aG docker $USER
動作確認
インストール後、以下のコマンドで動作を確認します。
# Hello World コンテナを実行
docker run hello-world
# 正常に動作していれば "Hello from Docker!" と表示される
Dockerfile の書き方
Dockerfile はイメージの構築手順を定義するファイルです。基本的な書き方を Node.js アプリケーションを例に解説します。
基本構成
# ベースイメージの指定
FROM node:22-slim
# 作業ディレクトリの設定
WORKDIR /app
# 依存関係ファイルを先にコピー(キャッシュ効率化)
COPY package.json package-lock.json ./
# 依存関係のインストール
RUN npm ci --only=production
# アプリケーションコードをコピー
COPY . .
# ポートの公開
EXPOSE 3000
# 起動コマンド
CMD ["node", "server.js"]
主要な命令の解説
FROM — ベースイメージを指定します。Docker Hub で公開されている公式イメージを使うのが基本です。node:22-slim のように -slim や -alpine を付けると軽量なバリアントを使えます。
WORKDIR — コンテナ内の作業ディレクトリを設定します。以降の命令はこのディレクトリを基準に実行されます。
COPY — ホストのファイルをコンテナにコピーします。.dockerignore に記載したファイルはコピーされません。
RUN — ビルド時にコマンドを実行します。パッケージのインストールやビルドコマンドの実行に使います。
EXPOSE — コンテナが使用するポートを宣言します。ドキュメント的な意味合いが強く、実際のポートマッピングは docker run -p で行います。
CMD — コンテナ起動時に実行されるデフォルトコマンドです。docker run 時に上書き可能です。
ビルドキャッシュを活用する
Dockerfile の書き順でビルド速度が大きく変わります。ポイントは 変更頻度の低いものを先に、高いものを後に書く ことです。
# 良い例: 依存関係ファイルを先にコピー
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# 悪い例: 全ファイルを先にコピー
COPY . .
RUN npm ci
良い例では、package.json が変更されない限り npm ci のレイヤーはキャッシュされます。悪い例では、ソースコードの変更だけで npm ci が毎回実行されてしまいます。
.dockerignore の設定
.gitignore と同様に、Docker ビルドコンテキストから除外するファイルを指定します。
node_modules
.git
.env
*.log
dist
.DS_Store
node_modules をコンテキストに含めるとビルドが遅くなるだけでなく、ホストの node_modules がコンテナにコピーされてバイナリ互換の問題を引き起こす原因になります。必ず除外しましょう。
マルチステージビルド
本番イメージを軽量にするために、ビルドと実行を別ステージに分けるテクニックです。
# ビルドステージ
FROM node:22-slim AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# 実行ステージ
FROM node:22-slim AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["node", "dist/server.js"]
ビルドに必要なツール(TypeScript コンパイラなど)は builder ステージにだけ存在し、最終イメージには含まれません。これによりイメージサイズを大幅に削減できます。
docker-compose で複数コンテナを管理する
実際の開発では、アプリケーションサーバー・データベース・キャッシュなど複数のサービスを連携させることが一般的です。docker-compose を使えば、これらを一つの設定ファイルで管理できます。
基本的な docker-compose.yml
services:
app:
build: .
ports:
- '3000:3000'
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- REDIS_URL=redis://cache:6379
depends_on:
- db
- cache
volumes:
- .:/app
- /app/node_modules
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
ports:
- '5432:5432'
volumes:
- pgdata:/var/lib/postgresql/data
cache:
image: redis:7-alpine
ports:
- '6379:6379'
volumes:
pgdata:
よく使うコマンド
# 全サービスをバックグラウンドで起動
docker compose up -d
# ログを確認
docker compose logs -f app
# 特定のサービスだけ再ビルドして起動
docker compose up -d --build app
# 全サービスを停止
docker compose down
# ボリュームも含めて全て削除
docker compose down -v
開発環境と本番環境の分離
docker-compose.override.yml を使うと、開発環境固有の設定を分離できます。
# docker-compose.yml(共通設定)
services:
app:
build: .
environment:
- NODE_ENV=production
# docker-compose.override.yml(開発用の上書き)
services:
app:
volumes:
- .:/app
environment:
- NODE_ENV=development
command: npm run dev
docker compose up を実行すると、自動的に docker-compose.yml と docker-compose.override.yml がマージされます。本番デプロイ時は -f オプションで明示的にファイルを指定します。
実践例: FX Bot のコンテナ化
自動売買 Bot を Docker でコンテナ化する例を見てみましょう。Python で書かれた Bot をコンテナ化し、サーバー上で安定稼働させるケースを想定します。
Dockerfile
FROM python:3.12-slim
WORKDIR /app
# システム依存パッケージ
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc && \
rm -rf /var/lib/apt/lists/*
# 依存関係のインストール
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# アプリケーションコード
COPY . .
# 非rootユーザーで実行(セキュリティ対策)
RUN useradd -m botuser
USER botuser
CMD ["python", "main.py"]
docker-compose.yml
services:
bot:
build: .
restart: always
environment:
- API_KEY=${API_KEY}
- API_SECRET=${API_SECRET}
env_file:
- .env
volumes:
- ./logs:/app/logs
- ./data:/app/data
logging:
driver: json-file
options:
max-size: '10m'
max-file: '3'
ポイント
restart: alwaysでコンテナがクラッシュしても自動復旧.envファイル で API キーなどの機密情報を管理(Git にはコミットしない)- ログのローテーション で
max-sizeとmax-fileを指定し、ディスク容量の枯渇を防止 - 非root ユーザー でコンテナを実行し、セキュリティリスクを低減
実践例: Web アプリのコンテナ化
Astro で構築した静的サイトを Docker でコンテナ化する例です。
Dockerfile
# ビルドステージ
FROM node:22-slim AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
# 配信ステージ
FROM nginx:alpine AS runner
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# gzip 圧縮
gzip on;
gzip_types text/css application/javascript application/json;
# SPA対応: 存在しないパスは index.html にフォールバック
location / {
try_files $uri $uri/ /index.html;
}
# 静的アセットのキャッシュ
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
マルチステージビルドにより、最終イメージには Nginx と静的ファイルだけが含まれ、Node.js やビルドツールは含まれません。イメージサイズは数十MB に収まります。
よくあるトラブルシューティング
1. ポートの競合
Error: Bind for 0.0.0.0:3000 failed: port is already allocated
ホスト側の 3000 番ポートが既に使用されています。
# 使用中のプロセスを確認
lsof -i :3000
# ポートマッピングを変更して起動
docker compose up -d # 3001:3000 に変更
2. ディスク容量の不足
Docker は使わなくなったイメージやコンテナを自動で削除しません。定期的にクリーンアップしましょう。
# 使っていないイメージ・コンテナ・ボリュームを削除
docker system prune -a
# 現在のディスク使用量を確認
docker system df
3. コンテナ内でファイルの変更が反映されない
ボリュームマウントのパスが間違っている可能性があります。
# コンテナ内に入って確認
docker compose exec app sh
# ファイルを確認
ls -la /app
docker-compose.yml の volumes セクションのパスが正しいか確認してください。
4. ビルドが遅い
.dockerignore に node_modules や .git が含まれているか確認しましょう。また、Dockerfile の命令順序がキャッシュを活かせる構成になっているかも重要です。
# ビルドキャッシュを確認
docker builder du
# キャッシュをクリア
docker builder prune
5. コンテナのログを確認したい
# リアルタイムでログを追跡
docker compose logs -f app
# 最新100行を表示
docker compose logs --tail=100 app
# 特定の時間以降のログ
docker compose logs --since="2026-05-22T10:00:00" app
6. 環境変数が読み込まれない
.env ファイルの読み込み優先順位に注意してください。
environment:で直接指定 → 最優先env_file:で指定したファイル → 2番目- シェルの環境変数 → 3番目
# コンテナ内の環境変数を確認
docker compose exec app env | sort
Docker を使いこなすためのベストプラクティス
イメージを軽量に保つ
-slimや-alpineバリアント を使う- マルチステージビルド でビルドツールを最終イメージに含めない
- 不要なファイルをコピーしない(
.dockerignoreを活用) - レイヤー数を減らす(
RUNを&&で連結)
# 良い例: 1レイヤーにまとめる
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc && \
rm -rf /var/lib/apt/lists/*
# 悪い例: 3レイヤーになる
RUN apt-get update
RUN apt-get install -y gcc
RUN rm -rf /var/lib/apt/lists/*
セキュリティを意識する
- 非root ユーザー でコンテナを実行する
- イメージを定期的にアップデート する(脆弱性対策)
- 機密情報はイメージに含めない(
.envやシークレット管理ツールを使う) - 信頼できるベースイメージ を使う(Docker Official Images)
タグを明示する
# 良い例: バージョンを明示
FROM node:22-slim
# 悪い例: latest は何が入るかわからない
FROM node:latest
latest タグはビルドのたびに異なるバージョンが使われる可能性があり、再現性がありません。必ずバージョンを明示しましょう。
まとめ
Docker はエンジニアにとって、開発環境の構築とデプロイを劇的に効率化するツールです。
| ポイント | 内容 |
|---|---|
| Docker とは | アプリと環境をまとめたコンテナ型仮想化 |
| Dockerfile | イメージの設計図。キャッシュ効率を意識して書く |
| docker-compose | 複数コンテナを一括管理 |
| ベストプラクティス | 軽量イメージ、非root実行、タグ明示 |
最初は「Hello World」コンテナの実行から始めて、自分のプロジェクトを段階的にコンテナ化していくのがおすすめです。一度 Docker での開発に慣れると、「環境差異」に悩まされることは激減します。