在容器化愈演愈烈的大背景下,本文来介绍下对于SpringBoot服务如何通过Docker的方式进行构建打包
构建Docker镜像
POM配置
首先通过Maven插件 spring-boot-maven-plugin 来构建SpringBoot服务的Jar包,其中可以通过finalName标签实现对Jar包重命名。然后利用Maven插件 dockerfile-maven-plugin 配置镜像信息及镜像构建。POM相关配置如下所示
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
| ... <groupId>com.aaron</groupId> <artifactId>SpringBootDocker</artifactId> <packaging>jar</packaging> <version>1.1</version> <description>基于Spring Boot的Docker构建实践</description>
<build>
<finalName>SpringBootDockerDemo</finalName>
<plugins>
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.4.1</version> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> <configuration> <mainClass>com.aaron.SpringBootDocker.DockerApplication</mainClass> </configuration> </plugin>
<plugin> <groupId>com.spotify</groupId> <artifactId>dockerfile-maven-plugin</artifactId> <version>1.4.13</version> <executions> <execution> <goals> <goal>build</goal> <goal>push</goal> </goals> </execution> </executions> <configuration> <repository>aaron1995/spring_boot_docker_demo</repository> <tag>${project.version}</tag> <buildArgs> <jarFileName>${project.build.finalName}</jarFileName> </buildArgs> </configuration> </plugin>
</plugins> </build> ...
|
Dockerfile
现在我们在POM文件所在的同级目录中建立一个Dockerfile文件。这里说明下由于SpringBoot内嵌的Tomcat默认使用/tmp作为工作目录,故这里我们将其设置为镜像的挂载目录,以便镜像使用者可按需进行持久化。Dockerfile内容如下所示,可以看到还是非常简单的
1 2 3 4 5 6 7 8 9 10 11 12
| FROM java:8
VOLUME /tmp
ARG jarFileName
COPY target/${jarFileName}.jar app.jar
ENTRYPOINT java -jar app.jar
EXPOSE 4001
|
镜像构建
至此,对于一个SpringBoot服务而已,其构建镜像所需的相关配置均已完成。我们直接点击Maven工具栏的package即可完成生成Jar包、镜像构建
服务编排
通常一个SpringBoot服务会依赖多个中间件环境,Redis、MQ、DB等。为此将SpringBoot服务与其他中间件服务一同进行编排Compose,显然会大大方便开发、调试、管理等工作。这里我们的SpringBoot服务依赖了Redis、MySQL。 故首先在POM文件所在的同级目录中添加一个docker-compose.yml文件。其内容如下所示。其中我们通过自定义网络实现为各容器分配静态IP
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
| version: '3.9'
services:
Web-Service: image: aaron1995/spring_boot_docker_demo:1.1 container_name: Web-Service ports: - "8080:4001" networks: dev_net: ipv4_address: 121.121.121.10 depends_on: - Redis-Service-2 - MySQL-Service-2
Redis-Service-2: image: redis:6.2.3-alpine3.13 container_name: Redis-Service-2 command: redis-server --requirepass 123456 ports: - "7001:6379" networks: dev_net: ipv4_address: 121.121.121.11
MySQL-Service-2: image: mysql:5.7 container_name: MySQL-Service-2 ports: - "7002:3306" environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: db1 networks: dev_net: ipv4_address: 121.121.121.12
networks: dev_net: ipam: config: - subnet: 121.121.121.0/24
|
既然Web服务与中间件服务都编排在一起了。那容器间的网络通信该如何进行呢?具体有两种方式:容器IP、容器名。SpringBoot的application.yml部分配置如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| server: port: 4001
spring: application: name: HelloDocker redis: database: 0 host: 121.121.121.11 port: 6379 password: 123456 datasource: url: jdbc:mysql://MySQL-Service-2:3306/db1?allowPublicKeyRetrieval=true&useSSL=false username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver
|
现在直接通过IDEA运行该docker compose即可,如下所示。服务部署成功
验证测试
服务构建、部署成功后,还需在MySQL容器中执行SQL脚本以完成建库建表等初始化操作。一切准备就绪后,现在来进行验证测试。该SpringBoot服务的Controller接口功能如下所示
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
| @RestController @RequestMapping("Hello") public class TestController {
@Autowired private RedisTemplate redisTemplate;
@Autowired private PeopleMapper peopleMapper;
@GetMapping("/welcome") public String welcome() { return "Hello Spring Boot, By Docker"; }
@GetMapping("/add2Redis") public String add2Redis(@RequestParam String key, @RequestParam String value) { redisTemplate.opsForValue().set(key, value); return "add data 2 redis: success"; }
@GetMapping("/getByRedis") public String getByRedis(@RequestParam String key) { String value = (String)redisTemplate.opsForValue().get(key); return "["+key+"]: " + value; }
@GetMapping("/add2DB") public String add2DB(@RequestParam String name, @RequestParam String sex) { PeopleInfo peopleInfo = PeopleInfo.builder() .name(name) .sex(sex) .job("Teacher") .build(); peopleMapper.insert(peopleInfo); return "add data 2 DB: success"; }
@GetMapping("/getByDB") public String getByDB(@RequestParam String name) { Map map = new HashMap(); map.put("name", name); List<PeopleInfo> list = peopleMapper.selectByMap(map); return "["+name+"]: " + list; }
}
|
测试结果如下所示,符合预期
Note
- docker compose中还可以使用已存在的网络,docker-compose.yml文件中通过如下方式进行声明
1 2 3 4 5 6 7 8 9
| version: '3.9'
...
networks: web_net: external: true
|
docker compose中depends_on选项只能保证先启动所依赖服务再启动自身服务。但并不会等待依赖服务完全启动完毕后再启动自身
如果期望对SpringBoot服务容器进行调试,可以在Dockerfile中对Jar包启动命令添加-agentlib:jdwp相关配置,并通过EXPOSE声明调试端口。然后在创建该服务容器时,需要对外映射相应的调试端口。这里选用5005为调试端口,Dockerfile示例如下
1 2 3 4
| ENTRYPOINT java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar app.jar
EXPOSE 5005
|
最后在IDEA中通过remote实现远程调试即可