本範例在本機分別啟動Spring Boot container與MySQL container並建立連結。
前導知識:
範例環境:
- macOS Catalina
- Docker 19.03.12
- Java 8
- Maven
- Spring Boot 2.3.2.RELEASE
- Spring Boot Data JPA
- Lombok
注意本篇不用Docker Compose,而是用Docker CLI docker run
個別啟動服務。但營運上最佳實踐應該用Docker Compose來配置多容器間的啟動與連結。
啟動MySQL container
請參考「Docker 安裝MySQL」啟動MySQL container。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
741ccdb437ed mysql:8 "docker-entrypoint.s…" 3 months ago Up 2 hours 0.0.0.0:3306->3306/tcp, 33060/tcp mysql8
登入帳號為root
,密碼為12345
,資料庫名稱為mydb
。
在mydb
建立employee
資料表及新增資料。
-- 建立資料表
CREATE TABLE `mydb`.`employee` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NULL,
`age` INT NULL,
PRIMARY KEY (`id`));
-- 新增資料
INSERT INTO `mydb`.`employee` (`name`, `age`) VALUES ('John', '22');
INSERT INTO `mydb`.`employee` (`name`, `age`) VALUES ('Iris', '19');
輸入docker network inspect bridge
檢視MySQL container在Docker預設的bridge network的IP位址。
$ docker network inspect bridge
[
{
"Name": "bridge",
"Id": "73f7694d85e03a253ec075da7a92a9b7b75c63c80a283eecc19acca12c7be364",
"Created": "2020-12-16T14:02:15.804443063Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"741ccdb437ed3aaddce067a62f21c3b65f6ea94629c37d6f5e1014cd675c9619": {
"Name": "mysql8",
"EndpointID": "5001d955c9e3a2d80791e2451af072fc06c802d5dd6ce2295091da9bdb41feb9",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
在回傳JSON格式的Containers
屬性可找到mysql8
container的IPv4Address
為172.17.0.2
,這將設定為Spring Boot application.yml
配置檔中的spring.datasource.url
的jdbc url路徑。
建立Spring Boot專案
參考「建立Spring Boot專案(IntelliJ, Eclipse)」建立Spring Boot專案。
範例用Spring Data JPA來存取MySQL資料庫,所以要在pom.xml
加入spring-boot-start-data-jpa
及MySQL JDBC driver mysql-connector-java
依賴函式庫。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
Spring Boot配置檔application.yml
設定資料庫datasource資訊。
application.yml
server:
servlet:
context-path: /demo
port: 8080
spring:
datasource:
url: jdbc:mysql://172.17.0.2:3306/mydb
username: root
password: 12345
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
database-platform: org.hibernate.dialect.MySQL8Dialect
hibernate:
ddl-auto: update
spring.datasource.url
的IP設為MySQL container在bridge network中的IP位址,因為Spring Boot會運行在container而非本機,容器間是彼此獨立的虛擬機並透過Docker bridge network連結,每個容器有自己在bridge network的IP,所以連線位址不能設為localhost
。 (bridge這字一直讓我想到死亡擱淺)
+-----------------------------------------------+ | host | | +------------------+ +------------------+ | | | spring-boot-demo | | mysql8 | | | | 172.17.0.3 | | 172.17.0.2 | | | +------------------+ +------------------+ | | ^ ^ | | | | | | v v | | +--------------------------------+ | | | Docker bridge network | | | | (docker0) | | | +--------------------------------+ | | ^ | +-----------------------|-----------------------+ v +------------------+ | external network | +------------------+
建立映射employee
資料表的JPA entity類Employee
及存取層EmployeeRepository
。
Employee
package com.abc.demo.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class Employee implements Serializable {
private static final Long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private Integer age;
}
EmployeeRepository
package com.abc.demo.repository;
import com.abc.demo.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
建立DemoController
提供API/employee/all
呼叫EmployeeRepository
查詢資料。
DemoController
package com.abc.demo.controller;
import com.abc.demo.entity.Employee;
import com.abc.demo.repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DemoController {
@Autowired
private EmployeeRepository employeeRepository;
@GetMapping("/employee/all")
public List<Employee> getAllEmployees() {
return employeeRepository.findAll();
}
}
參考github。
建立Spring Boot image
這邊是利用Jib maven plugin來建構Spring Boot image。在pom.xml
加入Jib maven plugin。<image>
為image的名稱。
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>2.7.0</version>
<configuration>
<to>
<image>spring-boot-demo</image>
</to>
</configuration>
</plugin>
設定完以上後在Spring Boot專案根目錄(pom.xml
所在目錄)執行mvn compile jib:dockerBuild
開始建構image。
~/../spring-boot-demo$ mvn compile jib:dockerBuild
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------------< com.abc:spring-boot-demo >----------------------
[INFO] Building spring-boot-demo 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ spring-boot-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ spring-boot-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- jib-maven-plugin:2.7.0:dockerBuild (default-cli) @ spring-boot-demo ---
[WARNING] 'mainClass' configured in 'maven-jar-plugin' is not a valid Java class: ${start-class}
[INFO]
[INFO] Containerizing application to Docker daemon as spring-boot-demo...
[WARNING] Base image 'gcr.io/distroless/java:8' does not use a specific image digest - build may not be reproducible
[INFO] Using base image with digest: sha256:50ffbd05df754f787277c8aaa178a586c69d95da29edc318b8c9cbc21ede0cd1
[INFO]
[INFO] Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, com.abc.demo.DemoApplication]
[INFO]
[INFO] Built image to Docker daemon as spring-boot-demo
[INFO] Executing tasks:
[INFO] [==============================] 100.0% complete
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9.795 s
[INFO] Finished at: 2020-12-19T21:22:11+08:00
[INFO] ------------------------------------------------------------------------
建構好的image會放在本機的Docker daemon中。輸入docker images
檢視可看到建構好的spring-boot-demo
image。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 8 0d64f46acfd1 4 months ago 544MB
spring-boot-demo latest fe16282f0786 51 years ago 169MB
啟動Spring Boot container
輸入docker run -p 8080:8080 --name demo spring-boot-demo:latest
啟動Spring Boot container。
$ docker run -p 8080:8080 --name demo spring-boot-demo:latest
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.2.RELEASE)
2020-12-19 15:09:22.741 INFO 1 --- [ main] c.a.d.DemoApplication : Starting DemoApplication on bfc70ca42ae8 with PID 1 (/app/classes started by root in /)
2020-12-19 15:09:22.756 INFO 1 --- [ main] c.a.d.DemoApplication : No active profile set, falling back to default profiles: default
2020-12-19 15:09:23.722 INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFERRED mode.
2020-12-19 15:09:23.847 INFO 1 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 103ms. Found 1 JPA repository interfaces.
2020-12-19 15:09:24.804 INFO 1 --- [ main] o.s.b.w.e.t.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-12-19 15:09:24.822 INFO 1 --- [ main] o.a.c.c.StandardService : Starting service [Tomcat]
2020-12-19 15:09:24.823 INFO 1 --- [ main] o.a.c.c.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-12-19 15:09:24.936 INFO 1 --- [ main] o.a.c.c.C.[.[.[/demo] : Initializing Spring embedded WebApplicationContext
2020-12-19 15:09:24.936 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2103 ms
2020-12-19 15:09:25.287 INFO 1 --- [ main] o.s.s.c.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-12-19 15:09:25.360 INFO 1 --- [ task-1] o.h.j.i.u.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-12-19 15:09:25.420 WARN 1 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-12-19 15:09:25.482 INFO 1 --- [ task-1] o.h.Version : HHH000412: Hibernate ORM core version 5.4.18.Final
2020-12-19 15:09:25.880 INFO 1 --- [ task-1] o.h.a.c.Version : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-12-19 15:09:26.060 INFO 1 --- [ main] o.s.b.w.e.t.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '/demo'
2020-12-19 15:09:26.065 INFO 1 --- [ main] DeferredRepositoryInitializationListener : Triggering deferred initialization of Spring Data repositories…
2020-12-19 15:09:26.215 INFO 1 --- [ task-1] c.z.h.HikariDataSource : HikariPool-1 - Starting...
2020-12-19 15:09:26.989 INFO 1 --- [ task-1] c.z.h.HikariDataSource : HikariPool-1 - Start completed.
2020-12-19 15:09:27.033 INFO 1 --- [ task-1] o.h.d.Dialect : HHH000400: Using dialect: org.hibernate.dialect.MySQL8Dialect
2020-12-19 15:09:28.245 INFO 1 --- [ task-1] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-12-19 15:09:28.263 INFO 1 --- [ task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-12-19 15:09:28.831 INFO 1 --- [ main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-12-19 15:09:28.843 INFO 1 --- [ main] c.a.d.DemoApplication : Started DemoApplication in 6.981 seconds (JVM running for 8.203)
測試
開啟瀏覽器在url輸入http://localhost:8080/demo/employee/all
可看到以下結果。
沒有留言:
張貼留言