Docker で Spring Boot(Gradle) + MySQL の開発環境を構築する手順について解説します。
また、チュートリアルとして、データベースからユーザーの一覧を取得し一覧画面へ表示する簡単なWebアプリケーションをあわせて作成していきます。
IDEは IntelliJ IDEA Community Edition、Java は2020年9月にリリースされたJava 15を使っていきます。
【IntelliJ+Docker】Spring Boot+Java15+MySQL環境
OS | macOS Catalina | 10.15.6 |
IDE | IntelliJ IDEA Community Edition | 2020.2.2 |
Docker | Docker Desktop for Mac | 2.4.0.0 |
Docker Engine | 19.03.13 | |
Docker Compose | 1.27.4 | |
アプリケーションサーバー | ubuntu | 20.10 |
Spring Boot | 2.3.4 | |
Java(OpenJDK) | 15 | |
データベースサーバー | MySQL | 5.7.31 |
ホスト環境構築
Docker Desktop for Mac のインストール
dockerhub へアクセスして、「Docker Desktop for Mac」をダウンロード&インストールしてください。
IntelliJ IDEA Community Edition のインストール
IntelliJ IDEA へアクセスして「Community」をダウンロード&インストールしてください。
IntelliJ IDEA へ Spring Boot の Gradle プロジェクトを作成する
IntelliJ IDEA へ Spring Boot のプロジェクトを作成していきますが、「IntelliJ IDEA Community Edition」 では、Spring Boot のプロジェクトが作成できないため、Spring Initializr を使って Spring Boot のプロジェクトを作成します。
Spring Initializr へアクセスして画像のとおり設定をします。
Dependencies には以下を追加してください。
- Spring Boot DevTools
- Spring Web
- Spring Data JPA
- Spring Data JDBC
- MySQL Driver
- Thymeleaf
- Lombok
設定後、下部の GENERATE をクリックして、ダウンロードしたzipファイルを解凍しておきます。
IntelliJ IDEA Community Edition を起動します。
「Open or Import」をクリックして、先ほど解凍したフォルダを開きます。
プロジェクトはこのようになっています。
build.gradle
plugins {
id 'org.springframework.boot' version '2.3.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '15'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
以上で Spring Boot の Gradle プロジェクトが作成できました。
Docker 環境構築
続いて、Docker 環境を構築していきます。
プロジェクトのルートディレクトリに docker を作成します。
├── build.gradle
├── docker
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
アプリケーションサーバー
docker 配下に java のディレクトリ構造を作成します。
docker/
└── java
└── Dockerfile
docker / java / Dockerfile
FROM ubuntu:20.10
RUN apt-get update
RUN apt-get -y install openjdk-15-jdk
ENV JAVA_HOME /usr/lib/jvm/java-15-openjdk-amd64
ENV PATH $JAVA_HOME/bin:$PATH
VOLUME /tmp
RUN mkdir /app
WORKDIR /app
データベースサーバー
docker 配下に mysql のディレクトリ構造を作成します。
docker/
├── java
│ └── Dockerfile
└── mysql
├── Dockerfile
├── conf.d
│ └── my.cnf
└── initdb.d
├── 1_schema.sql
└── 2_initdata.sql
docker / mysql / Dockerfile
FROM mysql:5.7
RUN touch /var/log/mysql/mysqld.log
docker / conf.d / my.cnf
[mysqld]
character-set-server=utf8mb4
explicit-defaults-for-timestamp=1
general-log=1
general-log-file=/var/log/mysql/mysqld.log
[client]
default-character-set=utf8mb4
docker / initdb.d / 1_schema.sql
中身は空で良いです。チュートリアルで追記していきます。
docker / initdb.d / 2_initdata.sql
中身は空で良いです。チュートリアルで追記していきます。
docker-compose.yml
docker 配下に docker-compose.yml を作成します。
docker/
├── docker-compose.yml
├── java
│ └── Dockerfile
└── mysql
├── Dockerfile
├── conf.d
│ └── my.cnf
└── initdb.d
├── 1_schema.sql
└── 2_initdata.sql
docker / docker-compose.yml
version: '3.8'
volumes:
mysql-db:
driver: local
services:
db:
build: ./mysql
image: demo-db:0.0.1
restart: always
volumes:
- ./mysql/conf.d:/etc/mysql/conf.d
- ./mysql/initdb.d:/docker-entrypoint-initdb.d
- ../log/mysql:/var/log/mysql
expose:
- 3306
ports:
- 3306:3306
environment:
MYSQL_DATABASE: demo_db
MYSQL_USER: user
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: rootpassword
app:
build: ./java
image: demo-app:0.0.1
volumes:
- ../:/app
ports:
- "8080:8080"
depends_on:
- db
command: ./gradlew clean bootRun
src/main/resources 配下に application.yml を作成します。
src/main/resources/application.yml
spring:
profiles:
active: develop
---
spring:
profiles: develop
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://db:3306/demo_db
username: user
password: password
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
hibernate:
ddl-auto: update
data:
web:
base-path: /
---
spring:
profiles: production
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://xxxx:3306/demo_db
username: xxxx
password: xxxx
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
hibernate:
ddl-auto: update
data:
web:
base-path: /
build.gradle の末尾に以下のコードを追記します。
build.gradle
bootRun {
jvmArgs = ["-Dspring.profiles.active=develop"]
}
Docker コンテナの起動と動作確認
ビルドしてDocker イメージを作成する
ターミナルを起動後、docker へ移動して、「docker-compose build」コマンドを実行します。
$ cd docker/
$ docker-compose build
数分待つと、Docker イメージが作成されます。
Building db
Step 1/2 : FROM mysql:5.7
5.7: Pulling from library/mysql
:
省略
:
Successfully built ee5c567b0fc6
Successfully tagged demo-app:0.0.1
Docker コンテナを起動する
「docker-compose up -d」コマンドを実行します。
$ docker-compose up -d
コンテナが起動しました。
Creating network "docker_default" with the default driver
Creating docker_db_1 ... done
Creating docker_app_1 ... done
「docker ps」コマンドを実行して、コンテナが起動していることを確認します。
「docker_app_1」と「docker_db_1」が起動していればOKです。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7151b171b959 demo-app:0.0.1 "./gradlew clean boo…" 31 seconds ago Up 30 seconds 0.0.0.0:8080->8080/tcp docker_app_1
7adf9cdc3a39 demo-db:0.0.1 "docker-entrypoint.s…" 32 seconds ago Up 31 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp docker_db_1
Docker コンテナに入ってバージョンを確認する
アプリケーションサーバー
コンテナに入る
$ docker exec -it docker_app_1 bash
OS のバージョンを確認する
# cat /etc/os-release
NAME="Ubuntu"
VERSION="20.10 (Groovy Gorilla)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu Groovy Gorilla (development branch)"
VERSION_ID="20.10"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=groovy
UBUNTU_CODENAME=groovy
Java のバージョンを確認する
# java -version
openjdk version "15" 2020-09-15
OpenJDK Runtime Environment (build 15+36-Ubuntu-1)
OpenJDK 64-Bit Server VM (build 15+36-Ubuntu-1, mixed mode, sharing)
# javac -version
javac 15
コンテナから出る
# exit
データベースサーバー
コンテナに入る
$ docker exec -it docker_db_1 bash
MySQL のバージョンを確認する
# mysql --version
mysql Ver 14.14 Distrib 5.7.31, for Linux (x86_64) using EditLine wrapper
コンテナから出る
# exit
Docker コンテナを停止&削除する
「docker-compose down」コマンドを実行します。
$ docker-compose down
コンテナが停止した後に削除されました。
Stopping docker_app_1 ... done
Stopping docker_db_1 ... done
Removing docker_app_1 ... done
Removing docker_db_1 ... done
Removing network docker_default
「docker ps」コマンドを実行して、コンテナが削除されていることを確認します。
「docker_app_1」と「docker_db_1」が表示されなければOKです。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
アプリケーションの動作確認
DemoApplication.java へ以下の処理を追記します。
src / main / java / DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping; // 追加
import org.springframework.web.bind.annotation.RestController; // 追加
@SpringBootApplication
@RestController // 追加
public class DemoApplication {
// 追加
@RequestMapping("/")
public String home() {
return "Hello World";
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
「docker-compose up -d」コマンドでコンテナを起動します。
$ docker-compose up -d
ログを見てアプリケーションが起動したのを確認したら、以下の URL へアクセスします。
http://localhost:8080「Hello World」が表示されたらアプリケーションの動作確認は完了です。
IntelliJ IDEA のホットデプロイ設定
build.gradle の dependencies に依存パッケージとして「developmentOnly 'org.springframework.boot:spring-boot-devtools'」を追加します。
※既に記載されている場合は追記する必要はありません。
build.gradle
dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools'
}
IntelliJ IDEA -> Preference -> Build, Execution, Deployment -> Compiler で「Build project automatically」にチェックを入れます。
SHIFT + CTRL + A で表示されるウィンドウで「Registry」を検索し、 compiler.automake.allow.when.app.running にチェックを入れます。
ターミナルでコンテナのログを監視して、ホットデプロイの設定を確認していきます。
$ docker-compose logs -f
コードを追記してファイルを保存した後にログが追記されたことを確認したらホットデプロイの設定は完了です。
IntelliJ IDEA のリモートデバック設定
IntelliJ IDEA のメニュー -> Run -> Edit Configurations を開きます。
左上の + ボタンから Remote を選択します。
そのまま「OK」をクリックします。
※ Command line arguments for remote JVM のコマンドをコピーしてください。build.gradle に追記します。
build.gradle の bootRun へ追記します。
build.gradle
bootRun {
jvmArgs = [
"-Dspring.profiles.active=develop",
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
]
}
docker-compose.yml にリモートデバック用のポート「5005」を追記します。
docker / docker-compose.yml
app:
ports:
- "8080:8080"
- "5005:5005"
コンテナを停止&起動します。
$ docker-compose down
$ docker-compose up -d
別のターミナルでコンテナの起動ログを監視します。
$ docker-compose logs -f
ログに「Listening for transport dt_socket at address: 5005」が表示されます。
app_1 | > Task :bootRun
app_1 | Listening for transport dt_socket at address: 5005
app_1 |
app_1 | . ____ _ __ _ _
app_1 | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
app_1 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
app_1 | \\/ ___)| |_)| | | | | || (_| | ) ) ) )
app_1 | ' |____| .__|_| |_|_| |_\__, | / / / /
app_1 | =========|_|==============|___/=/_/_/_/
DemoApplication.java へブレークポイントを配置します。
src / main / java / DemoApplication.java
IntelliJ IDEA の右上にあるデバックツールからリモートデバックを開始します。
コンソールに「Connected to the target VM, address: 'localhost:5005', transport: 'socket'」が表示されます。
http://localhost:8080 へアクセスします。ブレークポイントの位置で処理が止まってデバックをすることができます。
以上でリモートデバックの設定は完了です。
チュートリアル
続いて、データベースからユーザーの一覧を取得して一覧画面へ表示する簡単なWebアプリケーションを作成していきます。
データベース
データベースへテーブルを作成しデータを登録していきます。
テーブル作成の SQL を記載します。
docker / initdb.d / 1_schema.sql
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
first_name varchar(20),
last_name varchar(20),
PRIMARY KEY (id)
);
データ作成の SQL を記載します。
docker / initdb.d / 2_initdata.sql
INSERT INTO users(first_name, last_name) VALUES('Ichiro', 'Suzuki');
INSERT INTO users(first_name, last_name) VALUES('Taro', 'Tanaka');
コンテナを停止&起動します。(1_schema.sql、2_initdata.sql がコンテナ起動時に実行されます)
$ docker-compose down
$ docker-compose up -d
コンテナが起動したことを確認したら、データベースを確認します。
$ docker exec -it docker_db_1 bash
# mysql -u user -ppassword demo_db
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| demo_db |
+--------------------+
2 rows in set (0.00 sec)
mysql> show tables;
+-------------------+
| Tables_in_demo_db |
+-------------------+
| users |
+-------------------+
1 row in set (0.01 sec)
mysql> select * from users;
+----+------------+-----------+
| id | first_name | last_name |
+----+------------+-----------+
| 1 | Ichiro | Suzuki |
| 2 | Taro | Tanaka |
+----+------------+-----------+
2 rows in set (0.00 sec)
テーブルとデータが作成されていればOKです。
アプリケーション
先ほど作成したデータベースのテーブルからデータを取得し、一覧画面に表示するWEBアプリケーションを作成していきます。
最終的にこのような構造になります。
src/
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ └── demo
│ │ ├── DemoApplication.java
│ │ ├── controller
│ │ │ └── UserController.java
│ │ ├── domain
│ │ │ └── User.java
│ │ ├── repository
│ │ │ └── UserRepository.java
│ │ └── service
│ │ └── UserService.java
│ └── resources
│ ├── application.properties
│ ├── application.yml
│ ├── static
│ └── templates
│ └── user
│ └── list.html
src / main / java / com.example.demo / domain / User.java
package com.example.demo.domain;
import lombok.Data;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
@Data
@Entity
@Table(name = "users")
public class User implements Serializable {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String firstName;
private String lastName;
}
src / main / java / com.example.demo / repository / UserRepository.java
package com.example.demo.repository;
import com.example.demo.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
src / main / java / com.example.demo / service / UserService.java
package com.example.demo.service;
import com.example.demo.domain.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
@Transactional
public class UserService {
@Autowired
UserRepository userRepository;
public List<User> findAll() {
return userRepository.findAll();
}
}
src / main / java / com.example.demo / controller / UserController.java
package com.example.demo.controller;
import com.example.demo.domain.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
@Controller
@RequestMapping("users")
public class UserController {
@Autowired
UserService usrService;
@GetMapping(path = "")
String list(Model model) {
List<User> users = usrService.findAll();
model.addAttribute("users", users);
return "user/list";
}
}
src / main / resources / templates / user / list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Users</title>
</head>
<body>
<table>
<thead>
<tr>
<th>id</th>
<th>firstname</th>
<th>lastname</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.id}">id</td>
<td th:text="${user.firstName}">firstName</td>
<td th:text="${user.lastName}">lastName</td>
</tr>
</tbody>
</table>
</body>
</html>
コンテナを停止&起動します。
$ docker-compose down
$ docker-compose up -d
コンテナが起動したら http://localhost:8080/users へアクセスします。
以下のようにユーザー一覧が表示されたら完成です。
静的リソースのホットデプロイ
現状では、Thymeleafのテンプレートの変更が反映されずにアプリケーションサーバーの再起動が必要になり開発効率が悪いので、静的リソースもホットデプロイ可能にしていきます。
build.gradle へ「sourceResources sourceSets.main」を追記します。
build.gradle
bootRun {
sourceResources sourceSets.main
jvmArgs = [
"-Dspring.profiles.active=develop",
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
]
}
application.yml へ「spring.thymeleaf.cache」を追記します。
※deveop (開発)は「false」、production (本番)は「true」を設定し環境に応じてキャッシュの設定を切り替えます。
src / resources / application.yml
spring:
profiles:
active: develop
---
spring:
profiles: develop
thymeleaf:
cache: false
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://db:3306/demo_db
username: user
password: password
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
hibernate:
ddl-auto: update
data:
web:
base-path: /
---
spring:
profiles: production
thymeleaf:
cache: true
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://xxxx:3306/demo_db
username: xxxx
password: xxxx
jpa:
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
show-sql: true
hibernate:
ddl-auto: update
data:
web:
base-path: /
コンテナを停止&起動します。
$ docker-compose down
$ docker-compose up -d
コンテナが起動したら http://localhost:8080/users へアクセスします。
HTMLを変更してブラウザをリロードしてください。
変更が反映されていると思います。
コメント