はじめに
「自分のPCでは動くのに、本番環境では動かない」
「新しいメンバーが開発環境を構築するのに丸1日かかる」
こんな経験はありませんか?Dockerを使えば、これらの問題を解決できます。
この記事では、Dockerの基本概念から実践的な使い方まで、段階的に解説します。記事を読み終える頃には、自信を持ってDockerを使った開発環境を構築できるようになっているはずです。
ただ Dockerは
コマンドを覚えるだけだと
次のような実務の壁で詰まりがちです。
- composeは動くが 本番で再現しない
- ボリュームの扱いを間違えて データが消える
- ビルドキャッシュが効かず ずっと遅い
- 開発環境は便利だが CIで壊れる
この記事は
単なるコマンド集ではなく
なぜそう設計するのか まで含めて整理します。
先に結論 迷ったらこの方針
開発composeと本番composeを分ける
- 開発はホットリロード最優先
- 本番は再現性と最小構成最優先
同じcomposeで両方を満たすのは難しいので
dev用とprod用を分ける方が結局速いです。
ビルドはキャッシュ前提でレイヤーを切る
Dockerfileの書き方で
体感速度が大きく変わります。
依存インストールとアプリコードのレイヤーを分離し
変更が多い部分でキャッシュを壊さないのがコツです。
永続化はボリューム 明示して扱う
何を残し 何を捨てるか を明示しないと
環境構築は再現できません。
DBなどの状態は named volume などで管理し
消して良い手順もセットで用意すると運用が楽です。
Dockerとは何か
コンテナ技術の概要
Dockerは、アプリケーションとその依存関係を「コンテナ」という軽量な仮想環境にパッケージングする技術です。
従来の仮想マシンとの違い
| 特徴 | 仮想マシン | Docker |
|---|---|---|
| 起動時間 | 数分 | 数秒 |
| サイズ | GB単位 | MB単位 |
| OS | 各VMに完全なOS | ホストOSのカーネルを共有 |
| オーバーヘッド | 大きい | 小さい |
| 分離レベル | 完全 | プロセスレベル |
Dockerの基本概念
イメージ(Image)
アプリケーションとその実行に必要なすべてのファイル、設定、依存関係を含むテンプレート。読み取り専用。
コンテナ(Container)
イメージから作成された実行中のインスタンス。読み書き可能なレイヤーを持つ。
Dockerfile
イメージを作成するための設計図。テキストファイルにコマンドを記述。
Docker Hub
イメージを共有・配布するためのレジストリサービス。
Dockerのインストールと初期設定
Windows/Macの場合
Docker Desktopをインストールします。
# インストール確認
docker --version
docker compose version
Linuxの場合
# Ubuntu/Debianの場合
sudo apt update
sudo apt install docker.io docker-compose-v2
# サービスの起動
sudo systemctl start docker
sudo systemctl enable docker
# 現在のユーザーをdockerグループに追加(sudoなしで実行可能に)
sudo usermod -aG docker $USER
# 再ログインが必要
動作確認
# Hello Worldコンテナを実行
docker run hello-world
# 出力例
# Hello from Docker!
# This message shows that your installation appears to be working correctly.
基本的なDockerコマンド
イメージの操作
# イメージの検索
docker search nginx
# イメージのダウンロード
docker pull nginx:latest
docker pull node:20-alpine
# ローカルのイメージ一覧
docker images
# イメージの詳細情報
docker inspect nginx:latest
# イメージの削除
docker rmi nginx:latest
# 未使用イメージの一括削除
docker image prune -a
コンテナの操作
# コンテナの起動
docker run nginx
# バックグラウンドで起動(デタッチモード)
docker run -d nginx
# 名前を付けて起動
docker run -d --name my-nginx nginx
# ポートマッピング
docker run -d -p 8080:80 nginx
# ホストの8080ポートをコンテナの80ポートにマッピング
# 環境変数を設定
docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql
# ボリュームマウント
docker run -d -v /host/path:/container/path nginx
docker run -d -v $(pwd):/app node:20
# 実行中のコンテナ一覧
docker ps
# すべてのコンテナ一覧(停止中含む)
docker ps -a
# コンテナのログ
docker logs my-nginx
docker logs -f my-nginx # リアルタイム表示
# コンテナの停止
docker stop my-nginx
# コンテナの削除
docker rm my-nginx
# 停止と同時に削除
docker rm -f my-nginx
# すべての停止中コンテナを削除
docker container prune
コンテナ内での操作
# コンテナ内でコマンドを実行
docker exec my-nginx ls -la
# コンテナ内でシェルを起動(対話モード)
docker exec -it my-nginx bash
docker exec -it my-nginx sh # bashがない場合
# 起動時にシェルで入る
docker run -it ubuntu bash
Dockerfileの書き方
基本的なDockerfile
# ベースイメージを指定
FROM node:20-alpine
# 作業ディレクトリを設定
WORKDIR /app
# 依存関係ファイルをコピー
COPY package*.json ./
# 依存関係をインストール
RUN npm ci
# アプリケーションのソースコードをコピー
COPY . .
# ポートを公開
EXPOSE 3000
# アプリケーションを起動
CMD ["npm", "start"]
Dockerfileのベストプラクティス
# 軽量なベースイメージを使用
FROM node:20-alpine
# NG: FROM node:20 (フルイメージは大きい)
# マルチステージビルドで最終イメージを軽量化
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
# レイヤーキャッシュを活用
# 変更頻度の低いものを先にコピー
COPY package*.json ./
RUN npm ci
# ソースコードは後でコピー
COPY . .
# .dockerignoreファイルを使用
# .dockerignoreファイルに以下を記述
# node_modules
# .git
# *.log
# .env
# 非rootユーザーで実行
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# 具体的なタグを使用
FROM node:20.10.0-alpine
# NG: FROM node:latest
実践的なDockerfile例
Python Flask アプリケーション
FROM python:3.12-slim
WORKDIR /app
# 依存関係を先にインストール(キャッシュ活用)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
# 非rootユーザーを作成
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 5000
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]
Go アプリケーション(マルチステージビルド)
# ビルドステージ
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .
# 実行ステージ
FROM scratch
COPY --from=builder /app/main /main
EXPOSE 8080
ENTRYPOINT ["/main"]
Docker Compose
Docker Composeとは
複数のコンテナを定義・管理するためのツールです。YAMLファイルで設定を記述します。
基本的なdocker-compose.yml
services:
web:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
depends_on:
- db
- redis
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
redis:
image: redis:7-alpine
volumes:
postgres_data:
Docker Composeコマンド
# コンテナを起動
docker compose up
# バックグラウンドで起動
docker compose up -d
# 特定のサービスだけ起動
docker compose up -d web
# ビルドして起動
docker compose up --build
# ログを表示
docker compose logs
docker compose logs -f web
# コンテナを停止
docker compose down
# ボリュームも削除
docker compose down -v
# サービスの状態確認
docker compose ps
# 特定のサービスでコマンド実行
docker compose exec web bash
docker compose exec db psql -U user -d myapp
実践的なDocker Compose設定
開発環境と本番環境の分離
# docker-compose.yml(共通設定)
services:
web:
build:
context: .
dockerfile: Dockerfile
depends_on:
- db
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
# docker-compose.override.yml(開発環境、自動的に読み込まれる)
services:
web:
build:
target: development
volumes:
- .:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- NODE_ENV=development
db:
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: devpassword
# docker-compose.prod.yml(本番環境)
services:
web:
build:
target: production
ports:
- "80:3000"
environment:
- NODE_ENV=production
restart: always
db:
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
restart: always
secrets:
db_password:
file: ./secrets/db_password.txt
# 本番環境で起動
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
ヘルスチェックの設定
services:
web:
build: .
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout: 5s
retries: 5
実践的なユースケース
Node.js + React + PostgreSQL の開発環境
services:
frontend:
build:
context: ./frontend
dockerfile: Dockerfile.dev
volumes:
- ./frontend:/app
- /app/node_modules
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://localhost:4000
backend:
build:
context: ./backend
dockerfile: Dockerfile.dev
volumes:
- ./backend:/app
- /app/node_modules
ports:
- "4000:4000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 5s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
adminer:
image: adminer
ports:
- "8080:8080"
volumes:
postgres_data:
redis_data:
Nginx リバースプロキシ構成
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- web
web:
build: .
expose:
- "3000"
environment:
- NODE_ENV=production
# nginx.conf
events {
worker_connections 1024;
}
http {
upstream web {
server web:3000;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://web;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
}
ネットワークとボリューム
Dockerネットワーク
# ネットワーク一覧
docker network ls
# ネットワークの作成
docker network create my-network
# コンテナをネットワークに接続して起動
docker run -d --name web --network my-network nginx
docker run -d --name api --network my-network node:20
# 既存のコンテナをネットワークに接続
docker network connect my-network existing-container
# ネットワークの詳細確認
docker network inspect my-network
Docker Composeでは自動的にネットワークが作成されます。
services:
web:
networks:
- frontend
- backend
api:
networks:
- backend
db:
networks:
- backend
networks:
frontend:
backend:
ボリューム
# ボリュームの作成
docker volume create my-data
# ボリューム一覧
docker volume ls
# ボリュームの詳細
docker volume inspect my-data
# ボリュームの削除
docker volume rm my-data
# 未使用ボリュームの一括削除
docker volume prune
ボリュームの種類
services:
db:
image: postgres:16-alpine
volumes:
# 名前付きボリューム(永続化、Dockerが管理)
- postgres_data:/var/lib/postgresql/data
# バインドマウント(ホストのディレクトリをマウント)
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
# 匿名ボリューム
- /var/log/postgresql
volumes:
postgres_data:
# 外部で作成したボリュームを使用
# external: true
セキュリティのベストプラクティス
イメージのセキュリティ
# 信頼できるベースイメージを使用
FROM node:20-alpine
# 特定のバージョンを指定
FROM node:20.10.0-alpine
# 非rootユーザーで実行
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
USER nextjs
# 必要最小限のパッケージのみインストール
RUN apk add --no-cache tini
# マルチステージビルドで不要なファイルを除外
FROM node:20-alpine AS builder
# ビルド処理
FROM node:20-alpine AS runner
# 必要なファイルのみコピー
シークレット管理
# Docker Composeでのシークレット管理
services:
web:
image: myapp
secrets:
- db_password
- api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
environment: API_KEY
# Docker Swarmモードでのシークレット
docker secret create db_password ./password.txt
ネットワークセキュリティ
services:
web:
networks:
- frontend
api:
networks:
- frontend
- backend
# 内部ネットワークのみに公開
expose:
- "4000"
db:
networks:
- backend
# ポートを外部に公開しない
networks:
frontend:
backend:
internal: true # 外部からのアクセスを遮断
トラブルシューティング
よくある問題と解決策
コンテナが起動しない
# ログを確認
docker logs container-name
# 詳細情報を確認
docker inspect container-name
# インタラクティブモードで起動してデバッグ
docker run -it --entrypoint /bin/sh image-name
ポートが使用中
# 使用中のポートを確認
docker ps --format "{{.Ports}}"
# ホスト側で使用中のポートを確認
# Windows
netstat -ano | findstr :3000
# Linux/Mac
lsof -i :3000
ディスク容量不足
# Docker使用量を確認
docker system df
# 未使用リソースを一括削除
docker system prune -a --volumes
# 特定のリソースを削除
docker container prune
docker image prune -a
docker volume prune
docker network prune
ビルドが遅い
# BuildKitを有効化(高速化)
DOCKER_BUILDKIT=1 docker build .
# キャッシュを活用
docker build --cache-from myapp:latest .
まとめ
この記事では、Dockerの基礎から実践的な使い方まで解説しました。
学んだこと
| トピック | 内容 |
|---|---|
| 基本概念 | イメージ、コンテナ、Dockerfile |
| 基本コマンド | run, build, exec, logs |
| Dockerfile | ベストプラクティス、マルチステージビルド |
| Docker Compose | 複数コンテナの管理、環境分離 |
| ネットワーク | コンテナ間通信、セキュリティ |
| ボリューム | データの永続化 |
| セキュリティ | 非root実行、シークレット管理 |
Dockerを使いこなすことで、「自分のPCでは動く」問題を解消し、チーム全体の開発効率を大幅に向上させることができます。まずは小さなプロジェクトから始めて、徐々に複雑な構成に挑戦してみてください!