404 Not Found

404 Not Found


nginx

Docker コンテナ化デプロイ

Dockerはデプロイの革命です。一度ビルドすればどこでも実行でき, 「私の環境では動く」という言い訳に別れを告げましょう。

1. 学ぶこと


2. 運用エンジニアの実話

(1) ペインポイント: 環境の不一致

BobがローカルでOrderFlowのデバッグを終えて本番にデプロイすると, MySQLドライバーのバージョン不一致, JDKバージョンの違い, タイムゾーン設定の差異によりサービスが起動しませんでした。毎回のデプロイで環境の差異のトラブルシューティングに半日かかり, 「私の環境では動く」がチームの口癖になりました。さらに悪いことに, 3台のサーバーにそれぞれ手動デプロイが必要で, 設定の見落れが頻発しました。

(2) Dockerソリューション

Dockerはアプリケーションとすべての依存関係を1つのイメージにパッケージ化します:

DOCKERFILE
FROM eclipse-temurin:17-jre-alpine
COPY target/orderflow-service.jar /app/app.jar
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

1つのコマンドでデプロイし, 完全に一致した環境を保証します。

(3) 成果

BobがDockerを使い始めてから, イメージはローカルで一度ビルドし, 3台すべてのサーバーが同じソースから取得して実行するため, 環境の不一致が解消されました。1つのDocker Composeコマンドで完全な環境 (OrderFlow, MySQL, Redis)が起動し, 新入社員も5分でローカル実行できるようになりました。


3. マルチステージDockerfile

(1) マルチステージビルドプロセス

100%
graph TD
    A["ステージ1: Builder<br/>Maven + JDK 17<br/>コンパイル + パッケージ"] --> B["orderflow-service.jar"]
    B --> C["ステージ2: Runtime<br/>JRE 17 Alpine<br/>JARのみコピー"]
    C --> D["最終イメージ<br/>~150MB"]

(1) ▶ サンプル:マルチステージDockerfile

DOCKERFILE
# ステージ1: ビルド
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build

# 依存関係キャッシュのため先にpom.xmlをコピー
COPY pom.xml .
RUN mvn dependency:go-offline -B

# ソースをコピーしてビルド
COPY src ./src
RUN mvn package -DskipTests -B

# ステージ2: ランタイム
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app

# 非rootユーザーを作成
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# ビルダーからJARをコピー
COPY --from=builder /build/target/*.jar app.jar

# 所有権を設定
RUN chown -R appuser:appgroup /app
USER appuser

# ポートを公開
EXPOSE 8080

# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1

# アプリケーションを実行
ENTRYPOINT ["java", \
  "-XX:+UseG1GC", \
  "-XX:MaxRAMPercentage=75.0", \
  "-jar", "app.jar"]

出力:

TEXT
// 実行成功
ベースイメージ サイズ セキュリティ ユースケース
eclipse-temurin:17 ~450MB 開発/テスト
eclipse-temurin:17-jre-alpine ~170MB 中高 本番推奨
gcr.io/distroless/java17-debian12 ~100MB 最高 究極のセキュリティ

(2) ▶ サンプル:Distrolessイメージ

DOCKERFILE
# ステージ1: ビルド
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B

# ステージ2: Distrolessランタイム
FROM gcr.io/distroless/java17-debian12
COPY --from=builder /build/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

出力:

TEXT
// 実行成功
💡 ヒント: Distrolessイメージにはシェル (sh/bash)がないため, docker exec -itでデバッグモードに入ることができません。本番環境では推奨されますが, デバッグにはAlpineイメージを使用してください。


4. .dockerignoreとイメージ最適化

(1) ▶ サンプル:.dockerignore

TEXT
# .dockerignore
.git
.github
.idea
*.md
target/
!target/*.jar
node_modules/
*.log
.env
docker-compose*.yml
Dockerfile*
最適化戦略 効果 説明
.dockerignore 不要ファイルの除外 ビルドコンテキストの削減 .git, .ideaなどを送信しない
先にpom.xmlをCOPY 依存関係をキャッシュ 依存関係が変更されていなければダウンロードをスキップ
マルチステージビルド 最終イメージサイズの削減 Mavenやソースコードを含まない
JDKではなくJRE ~200MB節約 ランタイムにコンパイルツールは不要
Alpineベースイメージ ~280MB削減 musl libcを使用する最小Linuxシステム

5. Docker Compose マルチサービス編成

(1) ▶ サンプル:OrderFlow + MySQL + Redis

YAML
# docker-compose.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_HOST: mysql
      DB_USERNAME: orderflow
      DB_PASSWORD: ${DB_PASSWORD:-orderflow123}
      REDIS_HOST: redis
    depends_on:
      mysql:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: orderflow
      MYSQL_USER: orderflow
      MYSQL_PASSWORD: ${DB_PASSWORD:-orderflow123}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root123}
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

volumes:
  mysql-data:
  redis-data:

出力:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(2) ▶ サンプル:起動と検証

BASH
# 全サービスをビルドして起動
docker compose up -d --build

# サービスステータスを確認
docker compose ps

# アプリケーションログを表示
docker compose logs -f app

# APIをテスト
curl http://localhost:8080/actuator/health

# 全サービスを停止
docker compose down -v

出力:

TEXT
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours
コマンド 説明
docker compose up -d バックグラウンドで起動
docker compose down 停止してコンテナを削除
docker compose logs -f app アプリログを表示
docker compose ps サービスステータスを表示
docker compose build イメージのみビルド

6. 環境変数注入

(1) ▶ サンプル:設定の外部化

YAML
# application-prod.yml (Dockerイメージ内)
spring:
  datasource:
    url: jdbc:mysql://${DB_HOST:localhost}:3306/orderflow
    username: ${DB_USERNAME:orderflow}
    password: ${DB_PASSWORD:defaultpass}
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: ${REDIS_PORT:6379}
設定方法 優先度 適用シナリオ
Docker Compose environment 簡易デプロイ
.env ファイル ローカル開発
K8s ConfigMap / Secret 本番環境
TEXT
# .env ファイル
DB_PASSWORD=secure_password_here
DB_ROOT_PASSWORD=root_secure_here
REDIS_PASSWORD=redis_secure_here

7. 総合サンプル:OrderFlowの完全Docker化

DOCKERFILE
# Dockerfile
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build
COPY pom.xml .
RUN mvn dependency:go-offline -B
COPY src ./src
RUN mvn package -DskipTests -B

FROM eclipse-temurin:17-jre-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --from=builder /build/target/*.jar app.jar
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 8080 8081
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/actuator/health || exit 1
ENTRYPOINT ["java", "-XX:+UseG1GC", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
YAML
# docker-compose.yml
version: "3.9"
services:
  app:
    build: .
    ports:
      - "8080:8080"
      - "8081:8081"
    environment:
      SPRING_PROFILES_ACTIVE: prod
      DB_HOST: mysql
      DB_USERNAME: orderflow
      DB_PASSWORD: ${DB_PASSWORD:-orderflow123}
      REDIS_HOST: redis
      MANAGEMENT_SERVER_PORT: "8081"
    depends_on:
      mysql: { condition: service_healthy }
      redis: { condition: service_healthy }
    restart: unless-stopped

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: orderflow
      MYSQL_USER: orderflow
      MYSQL_PASSWORD: ${DB_PASSWORD:-orderflow123}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root123}
    volumes: [mysql-data:/var/lib/mysql]
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s; timeout: 5s; retries: 10

  redis:
    image: redis:7-alpine
    volumes: [redis-data:/data]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s; timeout: 3s; retries: 5

volumes:
  mysql-data:
  redis-data:

❓ よくある質問

Q Dockerイメージが大きすぎるのはなぜですか?
A デフォルトではフルJDKイメージ (~450MB)が使用されます。最適化方法:1) JDKの代わりにJREを使用 (~170MB); 2) Alpineベースイメージを使用; 3) Distrolessを使用 (~100MB); 4) GraalVMネイティブイメージを使用 (~50MB)。
Q Dockerコンテナ内のアプリケーションをデバッグする方法は?
A 1) docker compose logs -f app でログを表示; 2) docker compose exec app sh でコンテナに入る (Alpineイメージ); 3) リモートデバッグ:ENTRYPOINT ["java", "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", "-jar", "app.jar"]
Q depends_onhealthcheckの関係は?
A depends_onはコンテナの起動順序のみを保証し, サービスの準備完了は保証しません。condition: service_healthyと併用すると, Docker Composeはヘルスチェックが通るまで待機してから依存サービスを起動します。
Q イメージビルド時間を短縮するには?
A 1) 先にpom.xmlをコピーしてmvn dependency:go-offlineを実行し, Dockerレイヤーキャッシュを活用; 2) .dockerignoreで無関係ファイルを除外; 3) BuildKitのキャッシュマウントを使用。
Q Docker Composeは本番で使えますか?
A Docker Composeはローカル開発やシンプルなデプロイに適しています。本番環境ではDocker SwarmやKubernetesを推奨します。オートスケーリング, ローリングアップデート, 自己修復などの機能があります。
Q Alpineイメージの落とし穴は?
A 1) glibcではなくmusl libcを使用するため, 一部のネイティブライブラリに互換性がない場合がある; 2) タイムゾーンデータの追加インストールが必要; 3) 高並列下でDNS解決にバグがある (ndots問題)。AlpineはJREのみのシナリオで推奨されます。

📖 まとめ


📝 練習問題

  1. 基本問題 (難易度:⭐): OrderFlowのDockerfileを書き, イメージをビルドしてローカルで実行し, /actuator/healthが"UP"を返すことを確認してください。

  2. 応用問題 (難易度:⭐⭐): docker-compose.ymlを書いてOrderFlow, MySQL, Redisの3サービスを編成し, healthcheckdepends_onを設定して正しい起動順序を確保してください。

  3. チャレンジ (難易度:⭐⭐⭐): イメージを150MB以下に最適化し, Distrolessベースイメージを使用し, JVMメモリパラメータ (MaxRAMPercentage)を設定し, 非rootユーザーとセキュリティ関連設定を追加してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%