Docker是一个使用广泛的Linux容器管理工具包,它允许用户创建镜像,并将其容器实例化。通过本指南,我们可以学习到如何使用Docker部署Spring Boot项目。
先决条件
开发之前,你必须具备如下条件才能够开发:
- 你必须在物理机或虚拟机上安装Docker,如果条件不允许,你可以是在本机安装Docker Toolbox,使用Docker Machine创建Docker引擎。关于Docker Toolbox的安装请查看官方安装方式。关于Docker的安装你可以参考:在Centos上安装Docker
- 你需要掌握Spring Boot框架的变成模型
步骤一:构建Spring Boot项目
- 使用IDE工具(推荐Idea)构建Spring Boot项目:项目名称demo。在该项目中,我们发布一个Rest API服务,端点是:
/user
。由于发布的是基于Http的API接口,因此pom中需要引入spring-boot-starter-web
构建。 - 开发
DemoController
代码如下:
@RestController
public class DemoController {
@RequestMapping("/user")
public Map<String,Object> getUser() {
Map<String, Object> userMap = new HashMap<>(16);
userMap.put("name", "Jim");
userMap.put("sex", "male");
userMap.put("age", 22);
return userMap;
}
}
- 编辑
application.yml
文件:
server:
port: 8080
spring:
application:
name: demo-service
现在,一个微服务项目就开发完成了!
步骤二:使用插件构建项目
一般来说,开发完Spring Boot项目后,需要编写Dockerfile,然后使用mvn clean package
命令对项目进行编译和打包,然后通过运行docker build
命令,构建项目的镜像。这样做显得比较繁琐,为了提升开发效率,减轻开发人员工作量,我们可以使用docker-maven-plugin
插件。通过该插件,你可以通过Maven构建的Docker镜像。使用mvn com.spotify:docker-maven-plugin:<version>:help -Ddetail=true
命令查看docker-maven-plugin
插件帮助信息。
- 在pom文件中引入,添加
docker-maven-plugin
插件
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
</plugin>
- 配置
docker-maven-plugin
。在构建之前,我们需要配置该插件,详细配置请参考这里:
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<dockerHost>https://172.20.5.79:2376</dockerHost>
<dockerCertPath>C:\\Users\\Administrator\\.docker\\machine\\cert_79</dockerCertPath>
<baseImage>openjdk:8-alpine</baseImage>
<imageName>${project.build.finalName}</imageName>
<imageTags>
<imageTag>${project.version}</imageTag>
<imageTag>latest</imageTag>
</imageTags>
<entryPoint>["java", "-jar", "/${project.build.finalName}.jar"]</entryPoint>
<exposes>
<expose>8080</expose>
</exposes>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
解释一下上述配置:
-
dockerHost
:配置Docker引擎的地址,格式为:http|https://ip:port -
dockerCertPath
:如果Docker引擎开启了TLS验证功能,你必须要将客户端证书、秘钥以及CA证书的路径配置在这里 -
baseImage
:基准镜像。与Dockerfile中的FROM <baseImage>
指令一致 -
imageName
:镜像名称。由项目名称与版本号组成 -
imageTags
:镜像Tag。默认是项目的版本号与latest -
entryPoint
:容器启动时,执行的命令 -
exposes
:需要暴漏的端口 -
resource.targetPath
:打包好的项目文件放置在容器的位置,targetPath路径必须要与entryPoint中的一致 -
resource.directory
:项目的构建目录 -
resource.include
:需要将项目构建目录中哪些内容拷贝至镜像中
步骤三:构建Docker镜像
- 配置完插件后,我们就可以构建Docker镜像了:
mvn clean package docker:build
- 使用Docker客户端查看docker 镜像:
[root@docker-test sentinel]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
demo 1.0 506b7ed0635e 22 seconds ago 116MB
- 运行demo:1.0镜像,将demo服务的8080端口映射到宿主机:
[root@docker-test sentinel]# docker run -itd --name demo -p 8080:8080 demo:1.0
dc5cd4780f8e42a4bf6ea0a696dbf3289785f2044e66aec8ba43213dd86a4fcc
- 查看已运行容器:
[root@docker-test sentinel]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dc5cd4780f8e demo:1.0 "java -jar /demo.jar" 4 seconds ago Up 3 seconds 0.0.0.0:8080->8080/tcp demo
步骤四:将镜像上传至私有的镜像仓储中
在上述步骤中,我们已经实现了如何使用docker-maven-plugin
插件将Spring Boot项目构建至Docker中。那么,如何将镜像推送至私有仓储呢?
在本实例中,我们采用阿里云的镜像仓储。
- 修改setting.xml文件,通常来说,该文件在
~/.m2
目录下。在setting.xml文件中添加server
节点,用于登录阿里云的镜像注册中心:
<server>
<id>docker-repository</id>
<username>some_user</username>
<password>some_password</password>
</server>
- username:登录阿里云注册中心用户名
- password:登录阿里云注册中心的密码
由于要保证密码的安全性,这里不采用明文,需要对密码进行加密。加密方式参考:maven密码加密。步骤如下:
- 首先使用你的私有仓库访问密码生成主密码:
mvn --encrypt-master-password <password>
- 其次在
settings.xml
文件的同级目录创建settings-security.xml
文件,将主密码写入:
<?xml version="1.0" encoding="UTF-8"?>
<settingsSecurity>
<master>{Ns0JM49fW9gHMTZ44n*****************=}</master>
</settingsSecurity>
- 最后使用你的私有仓库访问密码生成服务密码,将生成的密码写入到
settings.xml
的<server>中:
mvn --encrypt-password <password>
<server>
<id>docker-repository</id>
<username>some_user</username>
<password>{73eo3WYROT0HkVfHD0cUF2Z/dtaGVtSPJ1TOsFLuO08=}</password>
</server>
- 修改pom.xml文件:
<?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.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.2</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<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>
<docker.host>https://172.20.5.79:2376</docker.host>
<docker.cert.path>C:\\Users\\Administrator\\.docker\\machine\\cert_79</docker.cert.path>
<docker.repository>registry.cn-hangzhou.aliyuncs.com/mark0614</docker.repository>
<docker.registry.name>${project.artifactId}</docker.registry.name>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<id>package</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
<execution>
<id>tag-image</id>
<phase>package</phase>
<goals>
<goal>tag</goal>
</goals>
<configuration>
<image>${project.build.finalName}:${project.version}</image>
<newName>${docker.repository}/${project.build.finalName}:${project.version}</newName>
</configuration>
</execution>
<execution>
<id>install</id>
<phase>install</phase>
<goals>
<goal>push</goal>
</goals>
<configuration>
<imageName>${docker.repository}/${project.build.finalName}:${project.version}</imageName>
</configuration>
</execution>
</executions>
<configuration>
<dockerHost>${docker.host}</dockerHost>
<dockerCertPath>${docker.cert.path}</dockerCertPath>
<baseImage>openjdk:8-jre-alpine</baseImage>
<imageName>${project.build.finalName}</imageName>
<serverId>docker-repository</serverId>
<registryUrl>${docker.repository}</registryUrl>
<imageTags>
<imageTag>${project.version}</imageTag>
<imageTag>latest</imageTag>
</imageTags>
<entryPoint>["java", "-jar", "/var/app/${artifactId}/${project.build.finalName}.jar"]</entryPoint>
<exposes>
<expose>8080</expose>
</exposes>
<resources>
<resource>
<targetPath>/var/app/${project.build.finalName}/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
</project>
解释一下:
-
executions
:docker-maven-plugin
会在不同的构建阶段执行不同的目标任务。- package阶段进行docker build操作
- package阶段同时执行tag操作,将镜像重新tag成
docker-repository:port/namespace/imageName:version
格式 - install或者deploy阶段进行push操作
-
registryUrl
:镜像注册中心地址 -
serverId
:配置在settings.xml中的server
认证信息
- 构建项目:
mvn clean package -Dmaven.test.skip=true
打印出如下信息,说明你成功了:
[INFO] Pushing registry.cn-hangzhou.aliyuncs.com/mark0614/demo:0.0.2
The push refers to repository [registry.cn-hangzhou.aliyuncs.com/mark0614/demo]
3148c56ca932: Preparing
69cc5717c281: Preparing
5b1e27e74327: Preparing
04a094fe844e: Preparing
3148c56ca932: Pushing [> ] 165.9kB/14.51MB
69cc5717c281: Layer already exists
3148c56ca932: Pushing [=> ] 493.6kB/14.51MB
04a094fe844e: Layer already exists
3148c56ca932: Pushing [==> ] 821.2kB/14.51MB
5b1e27e74327: Layer already exists
3148c56ca932: Pushing [====> ] 1.313MB/14.51MB
3148c56ca932: Pushing [=======> ] 2.132MB/14.51MB
3148c56ca932: Pushing [========> ] 2.46MB/14.51MB
3148c56ca932: Pushing [=========> ] 2.787MB/14.51MB
3148c56ca932: Pushing [===========> ] 3.279MB/14.51MB
3148c56ca932: Pushing [============> ] 3.607MB/14.51MB
3148c56ca932: Pushing [==============> ] 4.098MB/14.51MB
3148c56ca932: Pushing [===============> ] 4.426MB/14.51MB
http://note.youdao.com/noteshare?id=07e1e408801bdf8d907ab81850dd60c2&sub=CEE7634AEEE542F3A07EDC31DC0EFAC9