はじめに
こんにちは、Sawa です!
今回は SpringBoot を MySQL に接続するアプリケーションを Docker で構築してみようと思います。
アプリ側のコンテナと DB 側のコンテナを分けた構成にするので、複数のコンテナの連携で詰まったことのある人はぜひ参考にしてみてください!
さっそく構築していきましょう!
今回のディレクトリ構造
まずは今回作成するプロジェクトの構造を見せておきます。
dockerApp % tree
.
├── build
│ ├── classes
│ │ └── java
│ │ ├── main
│ │ │ └── com
│ │ │ └── docker
│ │ │ └── dockerApp
│ │ │ ├── controller
│ │ │ │ └── UserController.class
│ │ │ ├── DockerAppApplication.class
│ │ │ ├── entity
│ │ │ │ └── User.class
│ │ │ ├── repository
│ │ │ │ └── UserRepository.class
│ │ │ └── service
│ │ │ └── UserService.class
│ │ └── test
│ │ └── com
│ │ └── docker
│ │ └── dockerApp
│ │ └── DockerAppApplicationTests.class
│ ├── generated
│ │ └── sources
│ │ ├── annotationProcessor
│ │ │ └── java
│ │ │ ├── main
│ │ │ └── test
│ │ └── headers
│ │ └── java
│ │ ├── main
│ │ └── test
│ ├── libs
│ │ ├── dockerApp-0.0.1-SNAPSHOT-plain.jar
│ │ └── dockerApp-0.0.1-SNAPSHOT.jar
│ ├── reports
│ │ └── tests
│ │ └── test
│ │ ├── classes
│ │ │ └── com.docker.dockerApp.DockerAppApplicationTests.html
│ │ ├── css
│ │ │ ├── base-style.css
│ │ │ └── style.css
│ │ ├── index.html
│ │ ├── js
│ │ │ └── report.js
│ │ └── packages
│ │ └── com.docker.dockerApp.html
│ ├── resolvedMainClassName
│ ├── resources
│ │ └── main
│ │ ├── application.properties
│ │ ├── static
│ │ └── templates
│ ├── test-results
│ │ └── test
│ │ ├── binary
│ │ │ ├── output.bin
│ │ │ ├── output.bin.idx
│ │ │ └── results.bin
│ │ └── TEST-com.docker.dockerApp.DockerAppApplicationTests.xml
│ └── tmp
│ ├── bootJar
│ │ └── MANIFEST.MF
│ ├── compileJava
│ │ └── previous-compilation-data.bin
│ ├── compileTestJava
│ │ └── previous-compilation-data.bin
│ ├── jar
│ │ └── MANIFEST.MF
│ └── test
├── build.gradle
├── docker-compose.yml
├── Dockerfile
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── HELP.md
├── init.sql
├── settings.gradle
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── docker
│ │ └── dockerApp
│ │ ├── controller
│ │ │ └── UserController.java
│ │ ├── DockerAppApplication.java
│ │ ├── entity
│ │ │ └── User.java
│ │ ├── repository
│ │ │ └── UserRepository.java
│ │ └── service
│ │ └── UserService.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── com
└── docker
└── dockerApp
└── DockerAppApplicationTests.java
プロジェクトの作成
はじめにプロジェクトを立ち上げましょう。
今回は Spring Initializr からプロジェクトを作成します。
今回は以下の画像に設定しましたが、自分の環境に設定してください。

また、Dependencies はこのようになっています。

設定完了したら GENERATE をクリックしてプロジェクトをダウンロードします。
ダウンロードした zip ファイルを解凍してプロジェクトを IDE で開きます。 私はintelliJ を使っているので今回は intelliJ で実装していきます。
Dockerfileの作成
プロジェクトを開いたら Dockerfile を作成していきます。 現在のプロジェクトの状態はこのようになっています。

今回はプロジェクト直下に DockerFIle を作成していきます。 Dockerfile の中身はこんな感じです。
# ベースとなるイメージを指定
FROM openjdk:17-jdk
# コマンドを実行する作業ディレクトリを /app に指定
WORKDIR /app
# プロジェクト内のファイルを WORKDIR で指定したディレクトリにコピー
COPY build/libs/dockerApp-0.0.1-SNAPSHOT.jar /app/dockerapp.jar
# ENTRYPOINT は ビルド時に実行するコマンドを指定できる。
# コマンド java -jar /app/dockerapp.jar でアプリケーション起動したコンテナ内で実行
ENTRYPOINT ["java", "-jar", "/app/dockerapp.jar"]
COPY ではアプリケーションをビルドした際に作成される jar ファイルを指定して、WORKDIR で作成した /app にコピーしています。

作成後のディレクトリ構造を出しておきます。

docker-compose.yml の作成
先ほど作成した DockerfIle と同じ階層に docker-compose.yml ファイルを作成していきます。 docker-compose.yml は コンテナを起動するときに参照・実行されるファイルです。
ファイルの中身はこんな感じです。
services:
# DB コンテナの設定
db-container:
# Mac の場合は以下のように platform を設定
platform: linux/x86_64
# MySQL の image を指定
image: mysql:8.4.0
# 環境変数を設定
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: mydb
MYSQL_USER: myuser
MYSQL_PASSWORD: mypass
TZ: "Asia/Tokyo"
# 永続化するデータの保存先を指定
volumes:
- db_data:/var/lib/mysql
# コンテナ起動時に init.sql を実行する
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
# ポートを指定
ports:
- "3306:3306"
healthcheck:
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-prootpass" ]
interval: 10s
timeout: 5s
retries: 5
# アプリケーションコンテナの設定
app-container:
# Dockerfile をもとに image を作成
# Dockerfile のあるパスを指定
build: .
# 環境変数を設定
# MySQL の設定と合わせる
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://db-container:3306/mydb
SPRING_DATASOURCE_USERNAME: myuser
SPRING_DATASOURCE_PASSWORD: mypass
SPRING_DATASOURCE_DRIVER-CLASS-NAME: com.mysql.cj.jdbc.Driver
SPRING_JPA_HIBERNATE_DDL-AUTO: update
SPRING_JPA_DATABASE-PLATFORM: org.hibernate.dialect.MySQLDialect
# ポートを指定
ports:
- "8080:8080"
depends_on:
db-container:
condition: service_healthy
# ボリュームを定義
volumes:
db_data:
ターミナルや PowerShell で docker compose up -d を実行すると、このファイルが実行され、以下のようなイメージでコンテナが作成されます。

app-container に Dockerfile で jar ファイルをコピーしたので作成した Spring Boot のアプリケーションを実行します。 db-container は MySQL が入っているコンテナイメージをしたので Spring Boot のアプリケーションは db-container からデータを取得します。
また、app-container は db-container に依存しているため、db-container の起動を待つように condition: service_healty で設定しています。
また、db-container の起動時に テーブルの作成と初期データの挿入を行っています。
docker-compose-yml 作成後のディレクトリ構造も載せておきます。

アプリケーションの作成
ではアプリケーション側のコードを書いていきます。 ここは本質ではないのでざっくりコードを載せておきます。 挙動としては、アプリケーションのリクエストを送ると DB からデータを取ってくる API のようなアプリケーションです。 ディレクトリの構造を画像で載せておきます。

- UserController.java
package com.docker.dockerApp.controller;
import com.docker.dockerApp.entity.User;
import com.docker.dockerApp.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RequiredArgsConstructor
@RestController
public class UserController {
// コンストラクインジェクション
private final UserService userService;
@GetMapping("api/user/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id)
.orElseThrow(() -> new RuntimeException("User not found"));
}
}
- User.java
package com.docker.dockerApp.entity;
import jakarta.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "users")
public class User{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
Long id;
@Column
String name;
}
- UserRepository.java
package com.docker.dockerApp.repository;
import com.docker.dockerApp.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
- UserService.java
package com.docker.dockerApp.service;
import com.docker.dockerApp.entity.User;
import com.docker.dockerApp.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
@RequiredArgsConstructor
@Service
public class UserService {
// コンストラクインジェクション
private final UserRepository userRepository;
// ID で User を取得
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
}
- DockerappApplication.java
package com.dockerapp.dockerapp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DockerappApplication {
public static void main(String[] args) {
SpringApplication.run(DockerappApplication.class, args);
}
}
DB の初期設定
今回のアプリケーションは DB のデータを扱いますが、DB コンテナを起動した時点ではデータもテーブルもありません。 ですので、DB コンテナ起動時にテーブルと作成とデータを入れるための sql ファイルを定義します。 このファイルを実行する部分はすでに docker-compose.yml に定義しています。
- init.sql
CREATE TABLE IF NOT EXISTS mydb.users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);
INSERT INTO mydb.users (name) VALUES
('Alice'),
('Bob'),
('Charlie');
実際に動かしてみよう
まずは Dokcerfile を実行します。
ターミナルやコマンドプロンプトでプロジェクト直下のディレクトリに移動します。
そして、以下のコマンドを実行します。
docker compose build
これで Dockerfile に定義した処理が実行されました。
つぎに、アプリケーションコンテナと DB コンテナを起動していきます。 コンテナを起動するには docker-compose.yml を実行していきます。 以下のコマンドを実行します。
docker compose up -d
これでコンテナが立ち上がりました。
では curl コマンドでアプリケーションのコントローラーにリクエストを送ってみましょう。
curl localhost:8080/api/user/1
{“id”:1,”name”:”Alice”} のような形で DB からデータが取ってこれたと思います。
ちなみに、init.sql では id が4以上の use は設定していないため、もちろんデータは取得できません。
こんなかんじで複数コンテナを使用するアプリケーションを簡単に作成できるのが Docker の魅力ですね!