Docker:開発から本番環境まで
アプリケーションのコンテナ化のための完全なDockerガイド。Dockerfile、Docker Compose、マルチステージビルド、本番環境へのデプロイを実践的な例で解説します。

Dockerはアプリケーションの開発、テスト、デプロイの方法を根本的に変革しています。アプリケーションとその依存関係をポータブルなコンテナにカプセル化することで、Dockerは有名な「自分のマシンでは動く」という問題を排除し、すべての環境での一貫性を保証します。このガイドでは、最初のDockerfileから本番環境へのデプロイまでの完全な道のりを解説します。
Docker Desktop 5.xは、ネイティブcontainerdサポート、最適化されたリソース管理、シームレスなKubernetes統合など、大幅なパフォーマンス改善をもたらしています。マルチアーキテクチャイメージ(ARM/x86)は現在標準的なプラクティスとなっています。
コンテナ化の基礎
コンテナは、コード、ランタイム、システムライブラリ、設定をパッケージ化する軽量なソフトウェアユニットです。ハードウェアを仮想化する仮想マシンとは異なり、コンテナはホストシステムのカーネルを共有するため、起動が高速でリソース消費が少なくなります。
# terminal
# Docker installation on Ubuntu
sudo apt update
sudo apt install -y docker.io
# Add user to docker group (avoids sudo)
sudo usermod -aG docker $USER
# Verify installation
docker --version
# Docker version 26.1.0, build 1234567
# First container: downloads image and runs
docker run hello-worldこのコマンドはDocker Hubからhello-worldイメージをダウンロードし、確認メッセージを表示するコンテナを起動します。
# terminal
# List running containers
docker ps
# List all containers (including stopped)
docker ps -a
# List downloaded images
docker images
# Remove a container
docker rm <container_id>
# Remove an image
docker rmi <image_name>これらの基本コマンドでコンテナとイメージのライフサイクルを管理します。
最初のDockerfileの作成
DockerfileにはDockerイメージを構築するための命令が含まれています。各命令は最終イメージにレイヤーを作成し、キャッシュと再利用を可能にします。
# Dockerfile
# Base image: Node.js 22 on Alpine Linux (lightweight)
FROM node:22-alpine
# Set working directory in the container
WORKDIR /app
# Copy dependency files first (cache optimization)
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy source code
COPY . .
# Expose port (documentation)
EXPOSE 3000
# Startup command
CMD ["node", "server.js"]命令の順序はキャッシュの最適化にとって極めて重要です。変更頻度の低いファイル(package.json)はソースコードよりも先にコピーする必要があります。
# terminal
# Build image with a tag
docker build -t my-app:1.0 .
# Run the container
docker run -d -p 3000:3000 --name my-app-container my-app:1.0
# Check logs
docker logs my-app-container
# Access container shell
docker exec -it my-app-container sh-dフラグはコンテナをバックグラウンドで実行し、-pはコンテナのポート3000をホストのポート3000にマッピングします。
Alpineイメージは大幅に小さくなっています(Debianの約120MBに対して約5MB)。ただし、glibcの代わりにmusl libcを使用しているため、一部のネイティブ依存関係で非互換性が発生する可能性があります。問題が発生した場合は、Debianベースのイメージ(node:22-slim)の使用が推奨されます。
本番環境のためのマルチステージビルド
マルチステージビルドは、ビルド環境をランタイム環境から分離することで、最適化された本番用イメージを作成します。最終イメージには必要なアーティファクトのみが含まれます。
# Dockerfile.production
# ============================================
# Stage 1: Build
# ============================================
FROM node:22-alpine AS builder
WORKDIR /app
# Copy and install dependencies (including devDependencies)
COPY package*.json ./
RUN npm ci
# Copy source code
COPY . .
# Build the application (TypeScript, bundling, etc.)
RUN npm run build
# ============================================
# Stage 2: Production
# ============================================
FROM node:22-alpine AS production
# Non-root user for security
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copy only necessary files from builder stage
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
COPY /app/package.json ./
# Switch to non-root user
USER nodejs
# Environment variables
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
# Startup command
CMD ["node", "dist/server.js"]このアプローチにより、ビルドツール、devDependencies、ソースファイルを除外し、最終イメージのサイズを大幅に削減できます。
# terminal
# Build with specific file
docker build -f Dockerfile.production -t my-app:production .
# Compare image sizes
docker images | grep my-app
# my-app production abc123 150MB
# my-app 1.0 def456 450MBサイズ削減はプロジェクトによって60〜70%に達し、デプロイ時間の改善と攻撃対象領域の縮小につながります。
ローカルオーケストレーションのためのDocker Compose
Docker Composeはマルチコンテナアプリケーションの管理を簡素化します。YAMLファイルですべてのサービス、設定、依存関係を宣言します。
# docker-compose.yml
version: "3.9"
services:
# Main application
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://cache:6379
volumes:
# Mount source code for hot-reload
- ./src:/app/src
- ./package.json:/app/package.json
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
networks:
- app-network
# PostgreSQL database
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
# Data persistence
- postgres_data:/var/lib/postgresql/data
# Initialization script
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
# Redis cache
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
networks:
- app-network
# Named volumes for persistence
volumes:
postgres_data:
redis_data:
# Dedicated network for isolation
networks:
app-network:
driver: bridgeサービスはDockerの内部ネットワークを通じて名前(db、cache)で通信します。ヘルスチェックにより、アプリケーション起動前に依存関係の準備が完了していることが保証されます。
# terminal
# Start all services
docker compose up -d
# View logs from all services
docker compose logs -f
# Logs from a specific service
docker compose logs -f app
# Stop and remove containers
docker compose down
# Removal including volumes (caution: data loss)
docker compose down -v
# Rebuild after Dockerfile changes
docker compose up -d --buildDevOpsの面接対策はできていますか?
インタラクティブなシミュレーター、flashcards、技術テストで練習しましょう。
シークレットと環境変数の管理
シークレットの安全な管理は本番環境において極めて重要です。Dockerはコンテキストに応じていくつかのアプローチを提供しています。
# docker-compose.override.yml (development only)
version: "3.9"
services:
app:
env_file:
- .env.development
environment:
- DEBUG=true本番環境では、Docker secretsがより高いセキュリティを提供します。
# docker-compose.production.yml
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile.production
secrets:
- db_password
- api_key
environment:
- NODE_ENV=production
- DATABASE_PASSWORD_FILE=/run/secrets/db_password
- API_KEY_FILE=/run/secrets/api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txtアプリケーションコードはマウントされたファイルからシークレットを読み取ります。
const fs = require('fs');
const path = require('path');
// Utility function to read Docker secrets
function readSecret(secretName) {
const secretPath = `/run/secrets/${secretName}`;
// Check if secret file exists
if (fs.existsSync(secretPath)) {
return fs.readFileSync(secretPath, 'utf8').trim();
}
// Fallback to classic environment variables
const envVar = secretName.toUpperCase();
return process.env[envVar];
}
module.exports = {
databasePassword: readSecret('db_password'),
apiKey: readSecret('api_key'),
};このアプローチにより、環境変数やDockerイメージでシークレットが露出することを防ぎます。
Dockerイメージの最適化
いくつかのテクニックによりイメージサイズを削減し、パフォーマンスを向上させることができます。
# Dockerfile.optimized
FROM node:22-alpine AS base
# Install necessary tools in a single layer
RUN apk add --no-cache \
dumb-init \
&& rm -rf /var/cache/apk/*
# ============================================
# Stage: Dependencies
# ============================================
FROM base AS deps
WORKDIR /app
# Copy only lock files for caching
COPY package.json package-lock.json ./
# Install with mounted npm cache (BuildKit)
RUN \
npm ci --only=production
# ============================================
# Stage: Builder
# ============================================
FROM base AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN \
npm ci
COPY . .
RUN npm run build
# ============================================
# Stage: Production
# ============================================
FROM base AS production
# Image metadata
LABEL maintainer="team@example.com"
LABEL version="1.0"
LABEL description="Production-ready Node.js application"
# Non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copy production dependencies
COPY /app/node_modules ./node_modules
# Copy build output
COPY /app/dist ./dist
COPY /app/package.json ./
USER nodejs
ENV NODE_ENV=production
# dumb-init as PID 1 for signal handling
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]dumb-initを使用することで、Unixシグナルの正しい処理が保証され、コンテナのグレースフルシャットダウンが可能になります。
# terminal
# Enable BuildKit for advanced features
export DOCKER_BUILDKIT=1
# Build with cache and detailed output
docker build --progress=plain -t my-app:optimized .
# Analyze image layers
docker history my-app:optimized
# Detailed image inspection
docker inspect my-app:optimizedイメージはTrivyやSnykなどのツールを使用して定期的に脆弱性スキャンを行う必要があります。ベースイメージはセキュリティパッチを含めるために定期的に更新する必要があります。
高度なDockerネットワーキング
Dockerは異なるユースケースに対応する複数のネットワークドライバーを提供しています。
# docker-compose.networking.yml
version: "3.9"
services:
# Publicly accessible frontend
frontend:
build: ./frontend
ports:
- "80:80"
networks:
- frontend-network
- backend-network
# API accessible only from frontend
api:
build: ./api
networks:
- backend-network
- database-network
# No external ports exposed
# Isolated database
database:
image: postgres:16-alpine
networks:
- database-network
# Accessible only by API
networks:
frontend-network:
driver: bridge
backend-network:
driver: bridge
internal: true # No internet access
database-network:
driver: bridge
internal: trueこの設定は最小権限の原則に従ってサービスを分離します。データベースはAPIからのみアクセス可能です。
# terminal
# Inspect Docker networks
docker network ls
# Details of a specific network
docker network inspect app-network
# Create a custom network
docker network create --driver bridge --subnet 172.28.0.0/16 custom-network
# Connect a container to an existing network
docker network connect custom-network my-containerボリュームとデータの永続化
Dockerボリュームはコンテナのライフサイクルを超えてデータを保持します。
# docker-compose.volumes.yml
version: "3.9"
services:
app:
image: my-app:latest
volumes:
# Named volume for persistent data
- app_data:/app/data
# Bind mount for development
- ./uploads:/app/uploads:rw
# Read-only mount for configuration
- ./config:/app/config:ro
backup:
image: alpine
volumes:
# Access same volume for backups
- app_data:/data:ro
- ./backups:/backups
command: |
sh -c "tar czf /backups/backup-$$(date +%Y%m%d).tar.gz /data"
volumes:
app_data:
driver: local
driver_opts:
type: none
device: /path/to/host/data
o: bind名前付きボリュームとバインドマウントの区別は重要です。ボリュームはDockerが管理し、バインドマウントはホストのファイルシステムを直接使用します。
# terminal
# List volumes
docker volume ls
# Inspect a volume
docker volume inspect app_data
# Remove orphaned volumes
docker volume prune
# Backup a volume
docker run --rm -v app_data:/data -v $(pwd):/backup alpine \
tar czf /backup/volume-backup.tar.gz /data本番環境へのデプロイ
堅牢なデプロイワークフローにはビルド、テスト、レジストリへのプッシュが含まれます。
# deploy.sh
#!/bin/bash
set -e
# Variables
REGISTRY="registry.example.com"
IMAGE_NAME="my-app"
VERSION=$(git describe --tags --always)
echo "Building version: $VERSION"
# Build production image
docker build \
-f Dockerfile.production \
-t $REGISTRY/$IMAGE_NAME:$VERSION \
-t $REGISTRY/$IMAGE_NAME:latest \
--build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--build-arg VERSION=$VERSION \
.
# Security scan
echo "Running security scan..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image $REGISTRY/$IMAGE_NAME:$VERSION
# Push to registry
echo "Pushing to registry..."
docker push $REGISTRY/$IMAGE_NAME:$VERSION
docker push $REGISTRY/$IMAGE_NAME:latest
echo "Deployment complete: $REGISTRY/$IMAGE_NAME:$VERSION"サーバーへのデプロイには、専用の本番用composeファイルで設定を調整します。
# docker-compose.prod.yml
version: "3.9"
services:
app:
image: registry.example.com/my-app:latest
restart: always
deploy:
replicas: 3
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"この設定は信頼性の高いデプロイのためのリソース割り当て、更新戦略、ヘルスチェックを定義します。
# terminal
# Production deployment with Docker Compose
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Zero-downtime update (rolling update)
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app
# Rollback if issues occur
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps \
--scale app=0 && \
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps appコンテナのモニタリングとデバッグ
コンテナのモニタリングは本番環境において不可欠です。
# terminal
# Real-time statistics for all containers
docker stats
# Statistics for a specific container with custom format
docker stats my-app --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# Inspect processes in a container
docker top my-app
# Real-time Docker events
docker events --filter container=my-app
# Copy files from/to a container
docker cp my-app:/app/logs/error.log ./error.log詳細なデバッグには、いくつかのテクニックが利用可能です。
# terminal
# Interactive shell in a running container
docker exec -it my-app sh
# Execute a single command
docker exec my-app cat /app/config/settings.json
# Start a container in debug mode
docker run -it --rm --entrypoint sh my-app:latest
# Inspect environment variables
docker exec my-app printenv
# Analyze logs with filters
docker logs my-app --since 1h --tail 100 | grep ERROR# docker-compose.monitoring.yml
version: "3.9"
services:
app:
# ... existing configuration
labels:
- "prometheus.scrape=true"
- "prometheus.port=3000"
- "prometheus.path=/metrics"
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=secret
volumes:
prometheus_data:
grafana_data:このモニタリングスタックにより、コンテナメトリクスの収集と可視化が可能になります。
まとめ
Dockerはすべての環境での一貫性を保証することで開発サイクルを変革します。コンテナ化はポータビリティ、分離性、再現性をもたらします。これらは現代のアプリケーションに不可欠な特性です。
本番環境のためのDockerチェックリスト
- ✅ 最適化されたイメージのためのマルチステージビルド
- ✅ コンテナ内での非rootユーザー
- ✅ すべてのサービスにヘルスチェックを設定
- ✅ Docker secretsまたは安全な環境変数によるシークレット管理
- ✅ リソース制限(CPU、メモリ)の定義
- ✅ 重要なデータの永続化のためのボリューム
- ✅ ファイルローテーション付きの集中ロギング
- ✅ デプロイ前のイメージセキュリティスキャン
- ✅ ダウンタイムなしの更新戦略
- ✅ サービス間の分離されたネットワーク
今すぐ練習を始めましょう!
面接シミュレーターと技術テストで知識をテストしましょう。
Dockerの習得は、すべての現代的な開発者にとって基本的なスキルです。ローカル環境から本番環境のデプロイまで、Dockerはワークフローを標準化し、運用を簡素化します。ここで紹介した概念は、Kubernetesと大規模なコンテナオーケストレーションを探求するための確固たる基盤を形成します。
タグ
共有

