Contents
1. Java アプリケーションの準備
このセクションでは、ローカルでビルドが成功している前提として Maven または Gradle を使った標準的な手順と、JDK バージョン選定のポイントを整理します。ローカルとコンテナ環境で同一 JDK を使用することで「動くはずがローカルだけ」問題を防げます。
1‑1. ビルドツール別セットアップ手順
Maven と Gradle はどちらも公式にサポートされているビルドシステムです。以下では、プロジェクトの雛形作成から依存関係定義、テスト実行までを具体的に示します。
Maven プロジェクトの雛形作成
|
1 2 3 4 5 6 7 |
# Maven の場合(groupId と artifactId は自分のプロジェクトに合わせて変更) mvn archetype:generate \ -DgroupId=com.example \ -DartifactId=myapp \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false |
Gradle (Groovy DSL) プロジェクトの雛形作成
|
1 2 |
gradle init --type java-application |
依存関係の定義例(Spring Boot 3.2 系)
| ビルドツール | ファイル名 | 主要依存関係サンプル |
|---|---|---|
| Maven | pom.xml |
xml<br><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>3.2.0</version></dependency><br> |
| Gradle | build.gradle |
groovy<br>implementation 'org.springframework.boot:spring-boot-starter-web:3.2.0'<br> |
テストの実行
ビルド時に自動で単体テストが走ります。失敗した場合は ローカルで必ず修正 し、CI に流す前にグリーン状態を確保してください。
|
1 2 3 4 5 6 |
# Maven mvn clean test # Gradle ./gradlew clean test |
ポイント:テストが成功していることはコンテナ化後のトラブル防止に直結します。
1‑2. JDK バージョン選定と環境変数設定
長期サポート (LTS) が保証された OpenJDK 17(または Oracle JDK 17)を推奨します。Docker イメージでも同一バージョンを使用すれば、ローカルと本番での動作差異がほぼゼロになります。
|
1 2 3 4 5 |
# macOS / Linux の例(OpenJDK 17 を /opt に展開) curl -L https://download.java.net/java/GA/jdk17/openjdk-17_linux-x64_bin.tar.gz | tar -xz -C /opt/ export JAVA_HOME=/opt/jdk-17 export PATH=$JAVA_HOME/bin:$PATH |
注意:
JAVA_HOMEとPATHの設定はシェルのプロファイル(例:~/.bashrc)に永続化しておくと便利です。
2. マルチステージ Dockerfile の作成
この章では、ビルドステージとランタイムステージを分離した マルチステージ Dockerfile を作り、不要ファイルの除外や非 root ユーザー設定によるセキュリティ向上手法を解説します。
2‑1. .dockerignore のベストプラクティス
Docker ビルド時にコンテナへコピーされるファイルは 最小限 に抑えるべきです。以下は一般的な Java プロジェクトで推奨する .dockerignore の例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# .dockerignore target/ build/ .git/ .github/ .idea/ *.iml *.log Dockerfile* README.md docs/ |
target//build/:ビルド成果物はステージングで生成するため除外.git/系:リポジトリ情報は不要- IDE 関連ファイル (
.idea/,*.iml):コンテナサイズを増やすだけ
ポイント:
.dockerignoreが適切に設定されていれば、ビルドキャッシュのヒット率が上がり、CI の実行時間も短縮できます。
2‑2. マルチステージ Dockerfile(完全版)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# ------------------------------------------------- # Stage 1 – ビルダー (Maven + JDK 17) # ------------------------------------------------- FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /app # ① 依存関係だけ先に取得 → キャッシュ活用 COPY pom.xml . RUN mvn -B dependency:go-offline # ② ソースコード全体をコピーしてビルド COPY src ./src RUN mvn -B package -DskipTests # ------------------------------------------------- # Stage 2 – ランタイム (JRE only) # ------------------------------------------------- FROM eclipse-temurin:17-jre-alpine AS runtime WORKDIR /app # 非 root ユーザー作成(セキュリティ強化) RUN addgroup -S appgroup && adduser -S appuser -G appgroup USER appuser # ビルダーから JAR をコピー COPY --from=builder /app/target/*.jar ./app.jar EXPOSE 8080 # ------------------------------------------------- # ヘルスチェック設定(Spring Actuator がある場合) # ------------------------------------------------- HEALTHCHECK --interval=30s --timeout=5s \ --start-period=10s \ CMD curl -fsS http://localhost:8080/actuator/health || \ # Spring Actuator が未導入の場合はデフォルトエンドポイントにフォールバック curl -fsS http://localhost:8080/health || exit 1 ENTRYPOINT ["java","-jar","/app/app.jar"] |
ヘルスチェックの条件付き説明
- Spring Actuator が導入済み:
/actuator/healthが推奨エンドポイントです。 - Actuator 未導入の場合:上記 Dockerfile では
curl http://localhost:8080/healthにフォールバックします(標準的な/healthエンドポイントを自前で実装するか、単にステータスコード 200 を返すシンプルなサーブレットでも可)。
このように 2 段階のチェック を入れることで、ヘルスチェックが原因でコンテナが再起動するリスクを低減できます。
3. ローカルでのイメージビルドとテスト
ローカル環境でイメージが期待通りに動くか確認した後にレジストリへプッシュします。タグ付けやヘルスチェック結果の取得方法を具体例と共に示します。
3‑1. ビルドコマンドとタグ戦略
|
1 2 3 |
# キャッシュ活用しつつビルド(最終ステージだけを対象) docker build -t myorg/myapp:1.2.3 -t myorg/myapp:latest . |
1.2.3:セマンティックバージョニングで明示的に管理latest:常に最新イメージとして参照できるエイリアス
3‑2. コンテナ起動とヘルスチェック確認
|
1 2 3 4 5 6 7 8 |
docker run -d --name myapp-local \ -p 8080:8080 \ -e SPRING_PROFILES_ACTIVE=dev \ myorg/myapp:1.2.3 # ヘルスステータスを取得(Docker が自動で実行したヘルスチェックの結果) docker inspect --format='{{json .State.Health}}' myapp-local |
期待される出力例
|
1 2 3 4 5 6 |
{ "Status":"healthy", "FailingStreak":0, "Log":[{"Start":"2024-05-01T12:00:00Z","End":"2024-05-01T12:00:02Z","Output":"HTTP/1.1 200 OK"}] } |
ポイント:
curl http://localhost:8080/actuator/healthが200を返すことをローカルで手動確認できれば、本番環境でも同様に機能する可能性が高いです。
4. コンテナレジストリへのプッシュと管理
安全にイメージを共有・デプロイできるよう、Docker Hub と GitHub Container Registry (GHCR) の両方でプッシュ手順を示します。シークレットは必ず GitHub Secrets 等で暗号化して扱います。
4‑1. Docker Hub へのプッシュ
|
1 2 3 4 |
docker login -u <USERNAME> -p <PERSONAL_ACCESS_TOKEN> docker push myorg/myapp:1.2.3 docker push myorg/myapp:latest |
- 公式情報:Docker Hub の無料プランはパブリックレポジトリ 1 GB、プライベートレポジトリ 1 GB が上限です(2024 年 5 月時点)。最新の制限は Docker Hub Pricing を参照してください。
4‑2. GitHub Packages / GHCR へのプッシュ例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# .github/workflows/docker-publish.yml(抜粋) name: Publish Docker Image on: push: branches: [ main ] jobs: build-and-push: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Login to GHCR env: CR_PAT: ${{ secrets.GHCR_TOKEN }} run: echo "$CR_PAT" | docker login ghcr.io -u ${{ github.actor }} --password-stdin - name: Build image run: | IMAGE=ghcr.io/${{ github.repository_owner }}/myapp:${{ github.sha }} docker build -t $IMAGE . docker tag $IMAGE ghcr.io/${{ github.repository_owner }}/myapp:latest - name: Push images run: | docker push ghcr.io/${{ github.repository_owner }}/myapp:${{ github.sha }} docker push ghcr.io/${{ github.repository_owner }}/myapp:latest |
ポイント:
GHCR_TOKENはwrite:packages, delete:packages権限を持つ Personal Access Token を使用してください。
5. 本番環境へのデプロイ
ここでは、Back4App Containers と AWS EC2(Docker Run) の二つの代表的なデプロイ先について、設定手順と注意点を詳述します。
5‑1. Back4App Containers のデプロイ手順
Back4App はサーバーレスに近いコンテナホスティングサービスで、無料プランでも 1 CPU・2 GB RAM(2024 年 5 月時点)を利用可能です。公式情報は以下のページをご確認ください。
👉 https://www.back4app.com/pricing
※ 本記事執筆時点の情報です。最新プランは必ず公式サイトでご確認ください。
デプロイフロー(UI 操作中心)
- コンテナプロジェクト作成
-
Back4App ダッシュボード → New Container Project → 名前と対象レジストリ(Docker Hub または GHCR)を指定。
-
環境変数・シークレット設定
-
DATABASE_URL,API_KEYなど機密情報は UI の Secrets タブで登録し、コンテナ起動時に自動注入します。 -
ポートとヘルスチェックの定義
- 公開ポート:
8080(Spring Boot デフォルト) -
ヘルスエンドポイント:
/actuator/healthか、前述のフォールバック先/healthを選択。 -
自動デプロイ設定
- Deploy on push オプションを有効化すると、GitHub のリポジトリに新しいタグがプッシュされた瞬間に Back5App が最新イメージを取得し再起動します。
重要なベストプラクティス
| 項目 | 推奨設定 |
|---|---|
| CPU/メモリ上限 | 無料枠の 1 CPU・2 GB RAM を超えないように、JVM オプションでヒープサイズを -Xmx768m 程度に抑える |
| ログ出力先 | 標準出力 (System.out) に集約し、Back4App のログビューアで確認できる形にする |
| スケールアウト | 必要に応じて有料プランへアップグレードし、水平スケーリングを利用 |
5‑2. AWS EC2(Docker Run)での本番構成
AWS 上で従来型の VM に Docker コンテナを直接走らせるケースです。インスタンスは t3.medium (2 vCPU・4 GB RAM) を最低ラインとし、必要に応じてスケールアウトします。
手順概要
- EC2 インスタンス作成
-
Amazon Linux 2023 AMI →
t3.medium推奨(無料枠対象外の場合はt3.nanoでも可) -
Docker のインストールと起動
|
1 2 3 4 5 6 |
# Docker CE を公式スクリプトでインストール sudo yum update -y curl -fsSL https://get.docker.com | sh sudo systemctl enable --now docker sudo usermod -aG docker $USER # ログアウト・再ログインが必要 |
- イメージ取得と実行
|
1 2 3 4 5 6 7 8 9 |
docker pull myorg/myapp:1.2.3 docker run -d \ --name myapp-prod \ --restart unless-stopped \ -p 80:8080 \ -e SPRING_PROFILES_ACTIVE=prod \ myorg/myapp:1.2.3 |
- systemd ユニットで永続的に管理(再起動時の自動復旧)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# /etc/systemd/system/myapp.service [Unit] Description=Java Spring Boot Docker Application After=docker.service Requires=docker.service [Service] Restart=always ExecStart=/usr/bin/docker run --rm -p 80:8080 \ -e SPRING_PROFILES_ACTIVE=prod \ myorg/myapp:1.2.3 ExecStop=/usr/bin/docker stop $(/usr/bin/docker ps -q --filter ancestor=myorg/myapp) [Install] WantedBy=multi-user.target |
|
1 2 3 |
sudo systemctl daemon-reload sudo systemctl enable --now myapp.service |
AWS での追加安全策
| 項目 | 推奨設定 |
|---|---|
| セキュリティグループ | インバウンドは 80 と 443 のみ許可、他ポートは遮断 |
| IAM ロール | EC2 に最小権限のロールを付与し、ECR からプライベートイメージを取得できるようにする |
| モニタリング | CloudWatch メトリクスと docker logs を組み合わせてヘルスチェック結果も可視化 |
6. CI/CD パイプライン構築とトラブルシューティング
自動化は手作業ミスを防ぎ、デプロイ速度を劇的に向上させます。以下では GitHub Actions を例に、ビルド・テスト・Docker イメージ生成・レジストリプッシュ・Back4App デプロイまでのフローを示します。
6‑1. 完全版 GitHub Actions ワークフロー
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
name: CI/CD for Java Docker Application on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' distribution: 'temurin' - name: Build with Maven run: mvn -B package --file pom.xml - name: Run unit tests run: mvn test docker-build-push: needs: build-test runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Log in to Docker Hub env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_PASS }} run: echo "$DOCKERHUB_TOKEN" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin - name: Build Docker image run: | IMAGE_TAG=${{ github.sha }} docker build -t myorg/myapp:$IMAGE_TAG . docker tag myorg/myapp:$IMAGE_TAG myorg/myapp:latest - name: Push images to Docker Hub run: | docker push myorg/myapp:${{ github.sha }} docker push myorg/myapp:latest deploy-back4app: needs: docker-build-push runs-on: ubuntu-latest steps: - name: Trigger Back4App deployment via API env: BACK4APP_TOKEN: ${{ secrets.BACK4APP_API_TOKEN }} run: | curl -X POST https://api.back4app.com/v1/containers/deploy \ -H "Authorization: Bearer $BACK4APP_TOKEN" \ -H "Content-Type: application/json" \ -d '{"image":"myorg/myapp:latest"}' |
ワークフローのポイント
- ビルドキャッシュ:
docker buildの前にactions/cache@v3を入れると Maven と Docker レイヤーが再利用でき、実行時間が 30 % 程度短縮します。 - シークレット管理:
DOCKERHUB_PASSは Read‑only の Personal Access Token(レジストリへのプッシュ権限のみ)を使用し、漏洩リスクを最小化。
6‑2. よくあるエラーと対処法チェックリスト
| エラーシナリオ | 原因例 | 推奨対策 |
|---|---|---|
Port already allocated(ポート競合) |
同一ホスト上で別コンテナが 8080 を占有 | docker ps で確認し、-p 8081:8080 のようにマッピング変更 |
OutOfMemoryError(メモリ不足) |
コンテナに割り当てた RAM が JVM デフォルトより小さい | -e JAVA_OPTS="-Xmx512m" をコンテナ起動時に追加、または EC2 のインスタンスタイプを上位へ |
| ヘルスチェックが失敗する | アプリ起動遅延や Actuator 未導入 | HEALTHCHECK に --start-period=30s を付与し、フォールバックエンドポイント /health を実装 |
| CI で Docker Hub ログイン失敗 | シークレット名ミスマッチ、トークン権限不足 | GitHub Secrets → 正しいキー名かつ write:packages, delete:packages 権限を持つ PAT を再作成 |
| ビルドキャッシュが無視される | .dockerignore が不適切で頻繁にコピー対象が変わる |
必要最小限のファイルだけ COPY し、pom.xml のみ先行コピーして依存関係レイヤーを固定化 |
トラブルシューティングのコツ:エラーは「ローカル再現できるか」→「CI 環境変数・シークレットの有無」→「コンテナ起動オプション」の順に切り分けて調査すると、原因特定が格段に速くなります。
7. まとめ
1️⃣ ローカルビルドは Maven/Gradle + JDK 17 で統一し、テストを必ずパスさせる
2️⃣ マルチステージ Dockerfile によりイメージサイズ ≤ 100 MB、.dockerignore と非 root ユーザーでセキュリティ強化
3️⃣ ヘルスチェックは Actuator 有無に応じたフォールバック を組み込み、本番環境の自動復旧を保証
4️⃣ レジストリは Docker Hub / GHCR 両方で管理し、タグ付け戦略(バージョン + latest)でロールバックも容易に
5️⃣ Back4App の無料プランは 1 CPU・2 GB RAM が上限。最新情報は公式ページ(https://www.back4app.com/pricing)を必ず確認
6️⃣ AWS EC2 でも systemd ユニットと --restart unless-stopped を併用 すれば、インフラ障害時の二重保護が実現
7️⃣ GitHub Actions による CI/CD がビルド・テスト・デプロイを自動化し、エラーはチェックリストで迅速に解決
以上の手順とベストプラクティスを踏めば、Java アプリケーションを Docker コンテナとして 安全・高速・低コスト に本番環境へ展開できます。ぜひ実際にサンプルリポジトリ(例: github.com/your-org/java-docker-sample)をクローンし、手順どおりに試してみてください。質問や改善点があれば、GitHub Issues でお気軽にフィードバックいただけると嬉しいです。