一个 Spring Boot 应用在开发完成后,我们可以打包成一个 jar 文件直接运行。通常使用 nohup java -jar app.jar & (或写个脚本) 的方式使其在服务器后台运行。
Docker 技术的发展为微服务落地提供了更加便利的环境,使用 Docker 部署 Spring Boot 其实非常简单,借助 Docker 我们可以做到运行环境与服务器环境的隔离,同时可以方便的管理发布版本。通过版本 tag 不仅可以方便的回滚到相应版本,使用端口映射也可以方便的配合蓝绿发布。
本文测试和开发环境如下:
CentOS Linux release 7.6.1810
Spring Boot 2.1.2.RELEASE
Apache Maven 3.0.5 (Red Hat 3.0.5-17)
Java version: 1.8.0_162
Docker version 18.09.1, build 4c52b90
1. 先做一个简单的Spring Boot Web 工程
1.1 Boot版本和依赖如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<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>
</dependencies>
1.2 创建一个简单的Controller:
@RestController
public class DockerController {
@RequestMapping("/hello")
public String index() {
return "Hello Docker!";
}
}
1.3 启动Spring Boot,简单测试可用。
2. Spring Boot 项目添加 Docker 支持
2.1 在 pom.xml的properties中添加 Docker 镜像名称:
<properties>
<docker.image.prefix>springboot</docker.image.prefix>
</properties>
2.2 plugins 中添加 Docker 构建插件:
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<imageName>${docker.image.prefix}/${project.artifactId}</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>
2.3 在工程中创建目录src/main/docker,并在其下创建 Dockerfile 文件
Dockerfile 文件用来说明如何来构建镜像。
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD hello-docker-0.0.1-SNAPSHOT.jar helloapp.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/helloapp.jar"]
Dockerfile 文件很简单,首先构建 Jdk 基础环境,然后添加 Spring Boot Jar 到镜像中:
-FROM ,表示使用 Jdk8 环境 为基础镜像,如果镜像不是本地的会从 DockerHub 进行下载
-VOLUME ,VOLUME 指向了一个/tmp的目录,由于 Spring Boot 使用内置的Tomcat容器,Tomcat 默认使用/tmp作为工作目录。这个命令的效果是:在宿主机的/var/lib/docker目录下创建一个临时文件并把它链接到容器中的/tmp目录
-ADD ,拷贝文件并且重命名
-ENTRYPOINT ,为了缩短 Tomcat 的启动时间,添加java.security.egd的系统属性指向/dev/urandom作为 ENTRYPOINT
3. 准备构建打包环境
在Windows上打包构建不是特别方便,故选取CentOS为例。
3.1 Docker
3.1.1 Docker 要求 CentOS 系统的内核版本高于 3.10
查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker 。
[root@spark ~]# uname -r
3.10.0-693.el7.x86_64
3.1.2 使用 root 权限登录 Centos。确保 yum 包更新到最新。
[root@spark ~]# sudo yum update
Loaded plugins: fastestmirror, langpacks
base | 3.6 kB 00:00:00
extras
.....以下省略.....
3.1.3 卸载旧版本(如果安装过旧版本的话)
$ sudo yum remove docker docker-common docker-selinux docker-engine
[root@spark ~]# sudo yum remove docker docker-common docker-selinux docker-engine
Loaded plugins: fastestmirror, langpacks
No Match for argument: docker
No Match for argument: docker-common
No Match for argument: docker-engine
Resolving Dependencies
--> Running transaction check
---> Package container-selinux.noarch 2:2.74-1.el7 will be erased
--> Processing Dependency: container-selinux >= 2.9 for package: docker-ce-18.03.1.ce-1.el7.centos.x86_64
--> Running transaction check
---> Package docker-ce.x86_64 0:18.03.1.ce-1.el7.centos will be erased
--> Finished Dependency Resolution
Dependencies Resolved
=================================================================================================================================================================================================
Package Arch Version Repository Size
=================================================================================================================================================================================================
Removing:
container-selinux noarch 2:2.74-1.el7 @extras 37 k
Removing for dependencies:
docker-ce x86_64 18.03.1.ce-1.el7.centos @docker-ce-stable 151 M
Transaction Summary
=================================================================================================================================================================================================
Remove 1 Package (+1 Dependent package)
Installed size: 151 M
Is this ok [y/N]: y
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Erasing : docker-ce-18.03.1.ce-1.el7.centos.x86_64 1/2
Erasing : 2:container-selinux-2.74-1.el7.noarch 2/2
Verifying : 2:container-selinux-2.74-1.el7.noarch 1/2
Verifying : docker-ce-18.03.1.ce-1.el7.centos.x86_64 2/2
Removed:
container-selinux.noarch 2:2.74-1.el7
Dependency Removed:
docker-ce.x86_64 0:18.03.1.ce-1.el7.centos
Complete!
3.1.4 安装需要的软件包
yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的
[root@spark ~]# sudo yum install -y yum-utils device-mapper-persistent-data lvm2
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* base: mirrors.163.com
* extras: mirrors.163.com
* updates: centos.ustc.edu.cn
base | 3.6 kB 00:00:00
extras | 3.4 kB 00:00:00
mysql-connectors-community | 2.5 kB 00:00:00
mysql-tools-community | 2.5 kB 00:00:00
mysql57-community | 2.5 kB 00:00:00
updates | 3.4 kB 00:00:00
mysql-connectors-community/x86_64/primary_db | 29 kB 00:00:00
Package yum-utils-1.1.31-50.el7.noarch already installed and latest version
Package device-mapper-persistent-data-0.7.3-3.el7.x86_64 already installed and latest version
Package 7:lvm2-2.02.180-10.el7_6.2.x86_64 already installed and latest version
Nothing to do
3.1.5 设置yum源
[root@spark ~]# sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
Loaded plugins: fastestmirror, langpacks
adding repo from: https://download.docker.com/linux/centos/docker-ce.repo
grabbing file https://download.docker.com/linux/centos/docker-ce.repo to /etc/yum.repos.d/docker-ce.repo
repo saved to /etc/yum.repos.d/docker-ce.repo
3.1.6 查看所有仓库中所有docker版本
[root@spark ~]# yum list docker-ce --showduplicates | sort -r
* updates: centos.ustc.edu.cn
Loading mirror speeds from cached hostfile
Loaded plugins: fastestmirror, langpacks
* extras: mirrors.163.com
docker-ce.x86_64 3:18.09.1-3.el7 docker-ce-stable
docker-ce.x86_64 3:18.09.0-3.el7 docker-ce-stable
docker-ce.x86_64 18.06.1.ce-3.el7 docker-ce-stable
docker-ce.x86_64 18.06.0.ce-3.el7 docker-ce-stable
docker-ce.x86_64 18.03.1.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 18.03.0.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.12.1.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.12.0.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.09.1.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.09.0.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.06.2.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.06.1.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.06.0.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.03.3.ce-1.el7 docker-ce-stable
docker-ce.x86_64 17.03.2.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
* base: mirrors.163.com
Available Packages
3.1.7 安装
[root@spark ~]# sudo yum install docker-ce
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* base: mirrors.163.com
* extras: mirrors.163.com
* updates: centos.ustc.edu.cn
Resolving Dependencies
--> Running transaction check
---> Package docker-ce.x86_64 3:18.09.1-3.el7 will be installed
--> Processing Dependency: container-selinux >= 2.9 for package: 3:docker-ce-18.09.1-3.el7.x86_64
--> Processing Dependency: containerd.io for package: 3:docker-ce-18.09.1-3.el7.x86_64
--> Processing Dependency: docker-ce-cli for package: 3:docker-ce-18.09.1-3.el7.x86_64
--> Running transaction check
---> Package container-selinux.noarch 2:2.74-1.el7 will be installed
---> Package containerd.io.x86_64 0:1.2.2-3.el7 will be installed
---> Package docker-ce-cli.x86_64 1:18.09.1-3.el7 will be installed
--> Finished Dependency Resolution
Dependencies Resolved
=================================================================================================================================================================================================
Package Arch Version Repository Size
=================================================================================================================================================================================================
Installing:
docker-ce x86_64 3:18.09.1-3.el7 docker-ce-stable 19 M
Installing for dependencies:
container-selinux noarch 2:2.74-1.el7 extras 38 k
containerd.io x86_64 1.2.2-3.el7 docker-ce-stable 22 M
docker-ce-cli x86_64 1:18.09.1-3.el7 docker-ce-stable 14 M
Transaction Summary
=================================================================================================================================================================================================
Install 1 Package (+3 Dependent packages)
Total download size: 55 M
Installed size: 235 M
Is this ok [y/d/N]: N
Exiting on user command
Your transaction was saved, rerun it with:
yum load-transaction /tmp/yum_save_tx.2019-01-16.08-57.CfQg8F.yumtx
3.1.8 旧版本存在错误
因为之前已经安装过旧版本的docker,在安装的时候或许有类似报错:
Transaction check error:
file /usr/bin/docker from install of docker-ce-17.12.0.ce-1.el7.centos.x86_64 conflicts with file from package docker-common-2:1.12.6-68.gitec8512b.el7.centos.x86_64
file /usr/bin/docker-containerd from install of docker-ce-17.12.0.ce-1.el7.centos.x86_64 conflicts with file from package docker-common-2:1.12.6-68.gitec8512b.el7.centos.x86_64
file /usr/bin/docker-containerd-shim from install of docker-ce-17.12.0.ce-1.el7.centos.x86_64 conflicts with file from package docker-common-2:1.12.6-68.gitec8512b.el7.centos.x86_64
file /usr/bin/dockerd from install of docker-ce-17.12.0.ce-1.el7.centos.x86_64 conflicts with file from package docker-common-2:1.12.6-68.gitec8512b.el7.centos.x86_64
可以卸载旧版本的包
$ sudo yum erase docker-common-2:1.12.6-68.gitec8512b.el7.centos.x86_64
3.2 启动
3.3 使用Docker 中国加速器
vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://registry.docker-cn.com"],
"live-restore": true
}
重新启动docker,注意上面文件准确, 否则会出错。
systemctl restart docker
3.4 安装JDK和Maven
此处略过过程,不清楚者可以搜索。
4. 使用 Docker 部署 Spring Boot 项目
把工程copy到CentOS,
4.1 可以先做个测试:
mvn package
java -jar target/hello-docker-0.0.1-SNAPSHOT.jar
4.2 构建
[root@spark hello-docker]# mvn package docker:build
[root@spark hello-docker]# mvn package docker:build
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building hello-docker 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ hello-docker ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ hello-docker ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ hello-docker ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /root/wsun/hello-docker/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ hello-docker ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ hello-docker ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.example.demo.HelloDockerApplicationTests
15:02:30.699 [main] DEBUG org.springframework.test.context.junit4.SpringJUnit4ClassRunner - SpringJUnit4ClassRunner constructor called with [class com.example.demo.HelloDockerApplicationTests]
..............此处省略一大段.................
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.2.RELEASE)
2019-01-16 15:02:33.921 INFO 112939 --- [ main] c.e.demo.HelloDockerApplicationTests : Starting HelloDockerApplicationTests on spark with PID 112939 (started by root in /root/wsun/hello-docker)
2019-01-16 15:02:33.922 INFO 112939 --- [ main] c.e.demo.HelloDockerApplicationTests : No active profile set, falling back to default profiles: default
2019-01-16 15:02:37.613 INFO 112939 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-01-16 15:02:38.355 INFO 112939 --- [ main] c.e.demo.HelloDockerApplicationTests : Started HelloDockerApplicationTests in 6.782 seconds (JVM running for 8.874)
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.475 s - in com.example.demo.HelloDockerApplicationTests
2019-01-16 15:02:38.844 INFO 112939 --- [ Thread-2] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:3.1.1:jar (default-jar) @ hello-docker ---
[INFO] Building jar: /root/wsun/hello-docker/target/hello-docker-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.1.2.RELEASE:repackage (repackage) @ hello-docker ---
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
[INFO] Replacing main artifact with repackaged archive
[INFO]
[INFO] --- docker-maven-plugin:1.0.0:build (default-cli) @ hello-docker ---
15:02:44.320 [main] DEBUG com.spotify.docker.client.DockerCertificates - /root/.docker/ca.pem, /root/.docker/key.pem or /root/.docker/cert.pem does not exist, not using SSL
15:02:44.712 [main] DEBUG com.spotify.docker.client.DockerConfigReader - Using configfile: /root/.dockercfg
[INFO] Using authentication suppliers: [ConfigFileRegistryAuthSupplier]
[INFO] Copying /root/wsun/hello-docker/target/hello-docker-0.0.1-SNAPSHOT.jar -> /root/wsun/hello-docker/target/docker/hello-docker-0.0.1-SNAPSHOT.jar
[INFO] Copying src/main/docker/Dockerfile -> /root/wsun/hello-docker/target/docker/Dockerfile
[INFO] Building image springboot/hello-docker
Step 1/4 : FROM openjdk:8-jdk-alpine
---> 04060a9dfc39
Step 2/4 : VOLUME /tmp
---> Using cache
---> 6902c065fe96
Step 3/4 : ADD hello-docker-0.0.1-SNAPSHOT.jar helloapp.jar
---> e176d3a85dfd
Step 4/4 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/helloapp.jar"]
---> Running in ee9ce9b3d247
Removing intermediate container ee9ce9b3d247
---> 865352bb0109
ProgressMessage{id=null, status=null, stream=null, error=null, progress=null, progressDetail=null}
Successfully built 865352bb0109
Successfully tagged springboot/hello-docker:latest
[INFO] Built springboot/hello-docker
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 26.952s
[INFO] Finished at: Wed Jan 16 15:02:50 CST 2019
[INFO] Final Memory: 28M/97M
[INFO] ------------------------------------------------------------------------
4.3 查看镜像:
[root@spark hello-docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
springboot/hello-docker latest 865352bb0109 2 minutes ago 119MB
4.4 启动
[root@spark hello-docker]# docker run -p 8080:8080 -t springboot/hello-docker
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.2.RELEASE)
2019-01-16 07:08:03.677 INFO 1 --- [ main] com.example.demo.HelloDockerApplication : Starting HelloDockerApplication v0.0.1-SNAPSHOT on e4a6703bcdeb with PID 1 (/helloapp.jar started by root in /)
2019-01-16 07:08:03.680 INFO 1 --- [ main] com.example.demo.HelloDockerApplication : No active profile set, falling back to default profiles: default
2019-01-16 07:08:08.768 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2019-01-16 07:08:08.941 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2019-01-16 07:08:08.941 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.14]
2019-01-16 07:08:08.988 INFO 1 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64/server:/usr/lib/jvm/java-1.8-openjdk/jre/lib/amd64:/usr/lib/jvm/java-1.8-openjdk/jre/../lib/amd64:/usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib]
2019-01-16 07:08:09.273 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2019-01-16 07:08:09.273 INFO 1 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 5353 ms
2019-01-16 07:08:10.058 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2019-01-16 07:08:10.763 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2019-01-16 07:08:10.796 INFO 1 --- [ main] com.example.demo.HelloDockerApplication : Started HelloDockerApplication in 8.838 seconds (JVM running for 10.178)
[root@spark hello-docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e4a6703bcdeb springboot/hello-docker "java -Djava.securit…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp serene_buck
4.5 测试
5. 使用jar包部署
我们习惯于windows下IDE开发,也可以采用先打了Jar包,然后在Linux上做镜像。
5.1 windows环境构建jar包
在centos7 ~ 创建一个文件夹docker 里面放置 上面的Dockerfile 和 springBoot 打包的项目
[root@spark docker]# ls
Dockerfile hello-docker-0.0.1-SNAPSHOT.jar
创建一个新的Dockerfile:
[root@spark docker]# cat Dockerfile
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD hello-docker-0.0.1-SNAPSHOT.jar helloapp1.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/helloapp1.jar"]
5.2 构建镜像
[root@spark docker]# docker build -t docker .
Sending build context to Docker daemon 16.68MB
Step 1/4 : FROM openjdk:8-jdk-alpine
---> 04060a9dfc39
Step 2/4 : VOLUME /tmp
---> Using cache
---> 6902c065fe96
Step 3/4 : ADD hello-docker-0.0.1-SNAPSHOT.jar helloapp1.jar
---> f49885f6520b
Step 4/4 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/helloapp1.jar"]
---> Running in bd96d074f542
Removing intermediate container bd96d074f542
---> d8731e6fca73
Successfully built d8731e6fca73
Successfully tagged docker:latest
[root@spark docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker latest d8731e6fca73 43 seconds ago 119MB
springboot/hello-docker latest 865352bb0109 About an hour ago 119MB
5.3 启动
[root@spark docker]# docker run -d -p 8090:8090 docker
1badb56ee4610621c47053cd504f79835832e31e024ec0e1ae900c24e600185c