使用Docker构建、发布Spring Boot应用

1. 简介

Docker 是一个开源的应用容器引擎,让开发人员可以将应用打包部署到一个可移植的容器中,然后发布到任何流行的Linux服务器上,容器是完全使用沙箱机制,相互之间不会有任何接口。

一个完整的Docker有以下几个部分组成:

  1. Docker Client 客户端
  2. Docker Daemon 守护进程
  3. Docker Image 镜像
  4. Docker Container 容器

2. 准备工作

为了通过Docker来构建与部署Spring Boot应用, 我们需要有一个安装过 Docker 环境的服务器来打包 Spring Boot 项目, 因此在进行项目构建时, 需要安装Java、Maven和Docker的应用环境,建议使用Linux系统来构建Docker镜像。 Java与Maven环境安装与配置在这就不详细介绍了,Docker环境安装与配置可以参考之前写过的文章《Docker容器安装与部署》。

环境配置

  • Java版本: JDK1.8+
  • Maven版本: Maven3.0+
  • Docker版本: Docker 17.06+

3. 构建Spring Boot项目

1. 配置Maven项目依赖文件pom.xml

  • 添加Spring Boot项目依赖
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0</version>
        <relativePath/>
</parent>
  • 添加Spring Boot项目相关的依赖包
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Java Mail -->
        <dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.6.1</version>
        </dependency>
</dependencies>
  • 创建Controller, 提供服务接口, 此处提供一个测试接口
@RestController
public class HelloController {
    
    @RequestMapping("/sayHello")
    public String sayHello() {
        return "Hello Spring Boot!";
    }
}
  • 创建Spring Boot启动类
@SpringBootApplication
public class HelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }
}

添加完毕后启动项目,启动成功后浏览器访问:http://localhost:8080/sayHello,调用接口页面返回:Hello Spring Boot!,说明 Spring Boot 项目工作正常。

4. Spring Boot项目中添加Docker构建组件

支持Spring Boot项目Docker容器构建的组件有许多个, 此处选择com.spotify:docker-maven-plugin组件进行构建。

  • 修改pom.xml文件, 添加Docker编译支持组件
<properties>
      <docker.image.prefix>myProject</docker.image.prefix>
</properties>
<build>
      <plugins>
          <!-- Spring Boot Build Plugin -->
          <plugin>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
          <!-- Docker maven plugin -->
          <plugin>
              <groupId>com.spotify</groupId>
              <artifactId>docker-maven-plugin</artifactId>
              <version>1.2.0</version>
              <configuration>
                  <imageName>${docker.image.prefix}/${project.artifactId}:${project.version}</imageName>
                  <dockerDirectory>src/main/docker</dockerDirectory>
                  <resources>
                      <resource>
                          <targetPath>/</targetPath>
                          <directory>${project.build.directory}</directory>
                          <include>${project.build.finalName}.jar</include>
                      </resource>
                  </resources>
              </configuration>
          </plugin>
          <!-- Docker maven plugin -->
      </plugins>
</build>

说明 :

  1. ${docker.image.prefix}为pom文件中properties中添加的配置;
  2. ${project.artifactId}为pom文件中的artifactId属性,${project.version}为pom文件中的version属性;
  3. dockerDirectory属性配置dockerfile文件的存放路径/src/main/docker
  4. resources属性中配置复制 jar 包到 docker 容器指定目录配置,将target下编译的Jar包复制到docker目录下。

我们也可以通过spotify的docker-maven-plugin组件配置, 自动创建Docker镜像,绕过下面创建Dockerfile的步骤, 直接生成Docker镜像:

<properties>
      <docker.image.prefix>myProject</docker.image.prefix>
</properties>
<build>
      <plugins>
          <!-- Spring Boot Build Plugin -->
          <plugin>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-maven-plugin</artifactId>
          </plugin>
          <!-- Docker maven plugin -->
          <plugin>
              <groupId>com.spotify</groupId>
              <artifactId>docker-maven-plugin</artifactId>
              <version>1.2.0</version>
              <configuration>
                  <imageName>${docker.image.prefix}/${project.artifactId}:${project.version}</imageName>
                  <baseImage>docker.io/cemmersb/centos-jdk8:latest</baseImage>
                  <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
                  <resources>
                      <resource>
                          <targetPath>/</targetPath>
                          <directory>${project.build.directory}</directory>
                          <include>${project.build.finalName}.jar</include>
                      </resource>
                  </resources>
              </configuration>
          </plugin>
          <!-- Docker maven plugin -->
      </plugins>
</build>

说明:

  1. 使用baseImage属性配置Docker容器依赖的基础镜像信息;
  2. 使用entryPoint属性配置Docker容器的入口命令。
  • src/main/docker下创建Dockerfile
FROM docker.io/cemmersb/centos-jdk8:latest
MAINTAINER garyond
VOLUME /tmp
ADD hello-service:0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

关于Dockerfile的相关命令可以参考我的文章《使用Dockerfile构建Docker镜像》, 这里我就不详细讲解了。

  • 编译项目, 进行Docker镜像打包, 执行命令mvn clean package docker:build
$ mvn clean package docker:build
...
Step 1/5 : FROM docker.io/cemmersb/centos-jdk8:latest
 ---> ccba23a213f2
Step 2/5 : MAINTAINER zhangjiayang <zhangjiayang@sczq.com.cn>
 ---> 0de52ae244d4
Step 3/5 : VOLUME /tmp
 ---> 3d25b5f60bb8
Step 4/5 : ADD hello-service-0.0.1-SNAPSHOT.jar app.jar
 ---> a02fcec9904c
Step 5/5 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
 ---> Running in a5cf7c699a3f
Removing intermediate container a5cf7c699a3f
 ---> 2177684b649d
ProgressMessage{id=null, status=null, stream=null, error=null, progress=null, progressDetail=null}
Successfully built 2177684b649d
Successfully tagged hello-service:0.0.1-SNAPSHOT
...

由于Docker构建时需要下载基础镜像信息, 所以过程会比较长, 所以我只粘贴了最后一部分编译的信息。编译完成后, 查看主机的镜像信息。

$ docker images
REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
hello-service                    0.0.1-SNAPSHOT      2177684b649d        13 minutes ago      875MB
rabbitmq                         3-management        75472a5c510b        2 months ago        149MB
cemmersb/centos-jdk8             latest              ccba23a213f2        3 years ago         858MB
  • Docker镜像生成后, 运行Docker容器
$ docker run -d -p 8080:8080 hello-service:0.0.1-SNAPSHOT

通过docker ps命令查询Docker容器是否正常启动

$ docker ps

容器成功启动后,打开浏览器访问:http://localhost:8080/sayHello,调用接口页面返回:Hello Spring Boot!,说明 Spring Boot 项目Docker容器工作正常。

5. 构建Spring Boot多模块依赖工程

由于Spring Boot项目开发中经常会遇见多模块的应用, 比如下面有一个应用sword, 应用中包含以下几个模块, 其中需要部署三个应用sword-api、sword-job、sword-admin

  • sword-common 公共模块(基础依赖)
  • sword-util 常用工具模块(基础依赖)
  • sword-api 接口模块(独立部署)
  • sword-job 应用采集模块(独立部署)
  • sword-admin 管理后台模块(独立部署)

5.1 准备工作

  • 配置总体项目POM
<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
    <modelVersion>4.0.0</modelVersion>  
    <groupId>com.garyond</groupId>  
    <artifactId>sword</artifactId>  
    <version>2.0.0</version>  
    <packaging>pom</packaging>  
  
    <modules>  
        <module>sword-common</module> <!--核心业务 -->  
        <module>sword-admin</module><!-- 后台 -->  
        <module>sword-api</module><!-- API -->
        <module>sword-job</module><!-- 定时采集程序 -->  
        <module>sword-util</module><!-- 常用工具 -->  
    </modules>  
  
    <parent>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-parent</artifactId>  
        <version>2.0.4.RELEASE</version>  
        <relativePath/> <!-- lookup parent from repository -->  
    </parent>  
      
</project>  
  • 配置sword-common等基础依赖模块
<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
    <modelVersion>4.0.0</modelVersion>  
  
    <artifactId>sword-common</artifactId>  
    <packaging>jar</packaging>  
  
    <parent>  
        <groupId>com.garyod</groupId>  
        <artifactId>sword</artifactId>  
        <version>2.0.0</version>  
        <relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->  
    </parent>  
  
    <properties>  
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>  
        <java.version>1.8</java.version>  
        <relativePath/>  
    </properties>  
  
    <dependencies>  
        ......  
    </dependencies>  
  
    <build>  
        <plugins>  
            <plugin>  
                <groupId>org.springframework.boot</groupId>  
                <artifactId>spring-boot-maven-plugin</artifactId>  
                <configuration>  
                    <classifier>exec</classifier>  
                </configuration>  
            </plugin>           
        </plugins>  
    </build>  
  
</project>  
  • 配置sword-api、sword-admin等应用模块
<?xml version="1.0" encoding="UTF-8"?>  
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
    <modelVersion>4.0.0</modelVersion>  
  
    <artifactId>sword-api</artifactId>  
    <packaging>jar</packaging>  
  
    <parent>  
        <groupId>com.garyond</groupId>  
        <artifactId>sword</artifactId>  
        <version>2.0.0</version>  
        <relativePath>../pom.xml</relativePath>   
    </parent>  
  
    <properties>  
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>  
        <java.version>1.8</java.version>  
    </properties>  
  
    <dependencies>  
        ......  
    </dependencies>  
  
    <build>  
        <plugins>  
            <plugin>  
                <groupId>org.springframework.boot</groupId>  
                <artifactId>spring-boot-maven-plugin</artifactId>  
                <configuration>  
                    <executable>true</executable>  
                </configuration>  
            </plugin>  
              
            <!-- Docker maven plugin -->  
            <plugin>  
                <groupId>com.spotify</groupId>  
                <artifactId>docker-maven-plugin</artifactId>  
                <version>1.2.0</version>  
                <configuration>  
                    <imageName>${project.groupId}/${project.artifactId}:${project.version}</imageName>  
                    <baseImage>java:8</baseImage>
                    <maintainer>garyond</maintainer>
                    <entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
                    <resources>  
                        <resource>  
                            <targetPath>/</targetPath>  
                            <directory>${project.build.directory}</directory>  
                            <include>${project.build.finalName}.jar</include>  
                        </resource>  
                    </resources>  
                </configuration>  
            </plugin>  
            <!-- Docker maven plugin -->  
        </plugins>  
    </build>  
</project>  

5.2 应用部署

  1. 进入父工程(sword)pom文件所在目录,打包编译,将依赖包放至本地仓库
mvn clean install package -Dmaven.test.skip
  1. 分别进入各模块(sword-api/sword-admin/sword-job)目录,使用 Docker构建镜像
mvn package docker:build -Dmaven.test.skip
  1. 运行Docker容器
docker run -d -p 8080:8080 --name=sword-admin sword-admin:2.0.0

推荐阅读更多精彩内容