本記事のDockerロゴは、公式ガイドラインに沿って使用しています。
https://www.docker.com/ja-jp/company/newsroom/media-resources/
はじめに
ポートフォリオを作成していく中で、「自分の環境では動くが、採用担当者の環境では動かない」という可能性を考えました。Java のバージョン違いやデータベースの設定差異、OSの違いなどが原因で、せっかく作ったアプリケーションが評価してもらえないリスクがあります。そこで Docker を使用して環境を統一し、どの環境でも確実に動作するようにしました。
Dockerに触れたことのない方でも、「プロジェクトのセットアップ」セクションの手順に従うことで、簡単にアプリケーションを起動できます。ハンズオンでの体験ができますので、ぜひお試しください。
ディレクトリ構成
expensecalendar/
├── .env.example # 環境変数のテンプレート(ダミーパスワードを記述)
├── .env # 実際の環境変数(.gitignoreに含める)
├── expensecalendar-backend/
│ ├── Dockerfile # イメージ
│ ├── build.gradle # backendのビルド設定(依存、プラグインなど)
│ └── src/
│ └── main/
│ └── resources/
│ └── application-dev.properties # 開発環境用DB設定
├── build.gradle # rootのビルド設定(Heroku用にStageタスクのみ定義)
├── compose.yaml # Docker Compose設定
└── settings.gradle # Multi-Project Build構成の定義
.env
# Spring Bootのプロファイル
SPRING_PROFILES_ACTIVE=dev
# PostgreSQLの設定(Dockerコンテナ用)
POSTGRES_DB=expensecalendar_db
POSTGRES_USER=springuser
POSTGRES_PASSWORD=mypassword
# Spring Bootアプリケーションのデータソース設定
SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/expensecalendar_db
SPRING_DATASOURCE_USERNAME=springuser
SPRING_DATASOURCE_PASSWORD=mypassword
こちらは実際に使用する環境変数の設定です。
重要ポイント
- PostgreSQL設定: Dockerコンテナの初期化に使用
- Spring Boot設定: アプリケーションからのDB接続に使用
-
ホスト名:
localhost
ではなくdb
(Docker Composeのサービス名)を指定 -
パスワード: 実際に使用するものを記述し、必ず
.gitignore
に含めてください
✅.env
ファイルを使用するメリット
- 設定の一元管理: 環境変数を1つのファイルで管理
- セキュリティ: 機密情報をコードから分離
- 環境切り替え: 開発・本番環境の設定を簡単に変更
-
チーム開発:
.env.example
でテンプレート共有
Docker Secretsを使わない理由
本番環境ではDocker Secretsの使用が推奨されますが、今回の目的は「ローカルでの環境の固定」と「機密情報をコードから分離」することにあるため、.env
ファイルを採用しています。
※Docker Secretsについて詳しく知りたい方は、「参考文献」セクションの書籍や公式ドキュメントをご参照ください。
.env.example
# Spring Bootのプロファイル
SPRING_PROFILES_ACTIVE=dev
# PostgreSQLの設定(Dockerコンテナ用)
POSTGRES_DB=expensecalendar_db
POSTGRES_USER=springuser
POSTGRES_PASSWORD=yourpassword
# Spring Bootアプリケーションのデータソース設定
SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/expensecalendar_db
SPRING_DATASOURCE_USERNAME=springuser
SPRING_DATASOURCE_PASSWORD=yourpassword
GitHubに公開するダミーパスワードが書かれた環境変数のテンプレートです。
採用担当者の方はコピーして.env
を作成し、実際のパスワードに変更します。
application-dev.properties
#== ローカル開発環境の設定 ==
#Postgresのドライバの設定
spring.datasource.driver-class-name=org.postgresql.Driver
#PostgresのURLの設定
spring.datasource.url=${SPRING_DATASOURCE_URL}
#Postgresのユーザ名の設定
spring.datasource.username=${SPRING_DATASOURCE_USERNAME}
#Postgresのパスワードの設定
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD}
#SQLスクリプトの初期化の設定
spring.sql.init.mode=always
spring.sql.init.schema-locations=classpath:schema.sql
spring.sql.init.data-locations=classpath:data.sql
#Log表示設定
logging.level.com.ozeken.expensecalendar=DEBUG
# MyBatisのマッパーXMLの場所を指定
mybatis.mapper-locations=classpath:mapper/*.xml
# MyBatisで使うエンティティのパッケージ指定
mybatis.type-aliases-package=com.ozeken.expensecalendar.entity
# Thymeleafのキャッシュを無効にする
spring.thymeleaf.cache=false
開発環境用のDB設定です。
重要ポイント
-
環境変数の活用:
${SPRING_DATASOURCE_URL}
などのプレースホルダーに.env
で定義した値が挿入される -
プロファイル:
application-dev.properties
はSPRING_PROFILES_ACTIVE=dev
で有効になる
gradleファイル
backend
にプロジェクトをまとめてDocker化する場合、gradleファイルの設定に苦労したため、gradleファイルの記述も解説します。
rootのbuild.gradle
// ==========================================
// Multi-Project Build用 ルートプロジェクト設定
// expensecalendar-backendプロジェクトのタスクをルートから実行可能にする
// ==========================================
// Heroku等のデプロイプラットフォーム用
tasks.register("stage") {
group = "build"
description = "Prepares application for deployment"
dependsOn("expensecalendar-backend:bootJar")
}
Heroku など一部の PaaS 環境に対応するために、カスタムの stage
タスクを定義しています。Heroku の Java ビルドパックでは、デフォルトで ./gradlew stage
が実行されるため、このタスクを定義して bootJar
に依存させることで、デプロイ時に自動的に JAR が生成されるようにしています。
特別な処理が不要であれば、この stage
タスクだけ定義しておけば問題ありません。
rootのsettings.gradle
// プロジェクト全体の名前を定義
rootProject.name = 'expensecalendar'
// backendプロジェクトを Multi-Project Build として含める
include('expensecalendar-backend')
include('expensecalendar-backend')
:
expensecalendar-backend
をexpensecalendar
のサブプロジェクトと認識させるための設定です。
この2つのgradleファイルでrootからbackendのプロジェクトをビルドすることができます。
backendのgradleファイル
通常のSpring Bootプロジェクトと同じ書き方のため、説明は省略します。
Dockerfile
# Build stage
FROM eclipse-temurin:17-jdk AS build
WORKDIR /app
# 依存関係をキャッシュするためにビルドに必要なファイルを先にコピー
COPY build.gradle settings.gradle gradlew gradlew.bat ./
COPY gradle ./gradle
COPY expensecalendar-backend/build.gradle ./expensecalendar-backend/
# gradlewに実行権限を付与(Linux環境なら必須)
RUN chmod +x gradlew
# ソースコードをコピー
COPY expensecalendar-backend/src ./expensecalendar-backend/src
# ビルド実行(デーモンを使わずクリーンビルド)
RUN ./gradlew clean build --no-daemon
# Runtime stage
FROM eclipse-temurin:17-jre
WORKDIR /app
# build stageからビルド済みjarだけをコピー
COPY --from=build /app/expensecalendar-backend/build/libs/*.jar app.jar
# アプリ起動
CMD ["java", "-jar", "app.jar"]
1. Build Stage(ビルドステージ)
# Build stage
FROM eclipse-temurin:17-jdk AS build
-
FROM eclipse-temurin:17-jdk
: Java 17のJDK(開発用)をベースイメージとして使用 -
AS build
: このステージに「build」という名前を付ける(後で参照するため)
WORKDIR /app
-
WORKDIR
: コンテナ内の作業ディレクトリを/app
に設定 - 以降のコマンドは全て
/app
ディレクトリで実行される
2. 依存関係のキャッシュ戦略
# 依存関係をキャッシュするためにビルドに必要なファイルを先にコピー
COPY build.gradle settings.gradle gradlew gradlew.bat ./
COPY gradle ./gradle
COPY expensecalendar-backend/build.gradle ./expensecalendar-backend/
なぜ先にGradleファイルをコピーするのか
- Dockerはレイヤーキャッシュという仕組みがあり、ファイルが変更されていなければ前回のビルド結果を再利用する
-
build.gradle
が変わらなければ、依存関係のダウンロードをスキップできる
コピーの順序が重要
- 設定ファイル(変更頻度:低)→ キャッシュしやすい
- ソースコード(変更頻度:高)→ 最後にコピー
そのため設定ファイルを先にコピーしておきます。
3. 実行権限の付与
# gradlewに実行権限を付与(Linux環境なら必須)
RUN chmod +x gradlew
chmod +x
: Linuxコンテナでは明示的に権限設定が必要なため、gradlewファイルに実行権限を与えます。
4. ソースコードのコピーとビルド
コピー
# ソースコードをコピー
COPY expensecalendar-backend/src ./expensecalendar-backend/src
- 変更が多いため、最後にソースコードをコピー
ビルド
# ビルド実行(デーモンを使わずクリーンビルド)
RUN ./gradlew clean build --no-daemon
-
clean build
: 古いビルド結果を削除してから新しくビルド -
--no-daemon
: Gradleデーモンを使わない(コンテナ内では不要で、メモリ節約)
5. Runtime Stage(実行段階)
# Runtime stage
FROM eclipse-temurin:17-jre
- 実行段階ではJRE(実行用)をベースイメージとして設定
(JDKと比べて軽量なため)
WORKDIR /app
# build stageからビルド済みjarだけをコピー
COPY --from=build /app/expensecalendar-backend/build/libs/*.jar app.jar
-
--from=build
: 前のbuildステージからファイルを参照 - 重要 : ソースコードやGradleキャッシュは含まれない、JARファイルのみ
# アプリ起動
CMD ["java", "-jar", "app.jar"]
- コンテナ起動時に実行されるコマンド
6. 最終的な効果
この Dockerfile により以下が実現されます:
- 軽量なイメージ : JRE + JARファイルのみ
- 高速ビルド : キャッシュ戦略により依存関係の再ダウンロードを削減
- 安全性 : ソースコードが本番イメージに含まれない
compose.yaml
services:
expensecalendar-backend:
build:
context: .
dockerfile: expensecalendar-backend/Dockerfile
env_file:
- .env
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE}
depends_on:
- db
db:
image: postgres:15
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
ports:
- "5432:5432"
サービス構成
-
expensecalendar-backend:
Spring Bootアプリケーションの設定 -
db:
PostgreSQLデータベースの設定
重要ポイント
-
env_file:
:.env
ファイルの環境変数を自動読み込み -
depends_on:
: データベースが起動してからアプリケーションを開始 -
ports:
: ホストからコンテナへのポート転送(8080→8080、5432→5432) -
build:
:Dockerfile
からイメージをビルドしてSpring Bootアプリを実行
プロジェクトのセットアップ
実際に動作を確認したい方は、以下をインストールし、リポジトリをクローンしてお試しください:
必要なツール
- Docker Desktop (必須): コンテナ実行環境
- pgAdmin (任意): データベースをGUIで確認
Docker Desktop
pgAdmin(任意)
https://www.postgresql.jp/download
データベースの中身をGUIで確認できるため、動作確認に便利です。
リポジトリのクローンと.env
の作成
git clone https://github.com/ktr0203ozeken/expensecalendar.git
cd expensecalendar
cp .env.example .env
nano .env
コンテナのビルド・起動
操作手順
docker compose up
コンテナをビルド&起動します。初回は時間がかかる場合があります。
ビルド確認ログ
[+] Running 3/3
✔ Network expensecalendar_default Created 0.0s
✔ Container expensecalendar-db-1 Created 0.1s
✔ Container expensecalendar-expensecalendar-backend-1 Created 0.1s
コンテナの作成・起動が確認できます。
DBログインログ
expensecalendar-backend-1 | ~~ : The following 1 profile is active: "dev"
expensecalendar-backend-1 | ~~ : HikariPool-1 - Starting...
expensecalendar-backend-1 | ~~ : HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@13bdf540
expensecalendar-backend-1 | ~~ : HikariPool-1 - Start completed.
dev
プロファイルが使われPostgreSQL
にログインできていることが確認できます。
アプリケーション起動のログ
expensecalendar-backend-1 | ~~ : Tomcat started on port 8080 (http) with context path '/'
expensecalendar-backend-1 | ~~ : Started ExpensecalendarApplication in 2.621 seconds
アプリケーションが正常に起動し、8080ポート
で利用可能になります。
コンテナの停止・削除
1. コンテナの停止
実行中のコンテナを停止するには、ターミナルで Ctrl + C
を押します。
2. コンテナの削除
docker compose down
停止ログ
[+] Stopping 2/2
✔ Container expensecalendar-expensecalendar-backend-1 Stopped 0.5s
✔ Container expensecalendar-db-1 Stopped 0.5s
コンテナが停止されていることが確認できます。
削除ログ
[+] Running 3/3
✔ Container expensecalendar-expensecalendar-backend-1 Removed 0.0s
✔ Container expensecalendar-db-1 Removed 0.0s
✔ Network expensecalendar_default Removed 0.6s
コンテナとネットワークが正常に削除されていることが確認できます。
まとめ
このようにDockerを使用しイメージの固定化をすることで、「自分の環境では動くが、他人の環境では動かない」という横断的関心事を解決することができます。
皆さんの参考になったら幸いです。
参考文献
本記事の執筆にあたり、以下の資料を参考にしました。