本文主要记录微服务通过Maven插件实现生成镜像,并发布到本地仓库和远程的中央仓库。在此之前需要安装好相关的环境,如本地docker服务,docker桌面工具Docker Desktop for Windows (官网上也有Docker Desktop for Mac和Docker Desktop for Linux,这里以Windows为例),然后再准备一个SpringBoot微服务就行了。下面开始造起来
注册DockerHub
首先需要注册一个DockerHub账号,DockerHub官网地址。DockerHub是一个和GitHub功能类似的网站,只不过托管的文件不一样,GitHub托管代码,而DockerHub托管镜像。账号注册好了以后我们需要创建一个镜像仓库地址,这和在GitHub上创建代码仓库地址一样。
在dockerhub主要点击Repositories进入到仓库页面,然后点击右上角的Create Repository创建仓库
然后填写仓库名,描述,公有或是私有仓库
创建好了后,来到仓库页面
到这里DockerHub的工作就完成了,我创建了一个spring-boot-demo的镜像仓库,里面什么也没有,只是一个空仓库。接下来要做的就是在本地生成镜像,并把镜像push到这个仓库中
本地运行Docker
本地的windows安装好docker后,打开Docker Desktop如下图,我本地是发布了一些镜像所以这里能看到这些镜像,如果是第一次安装,这里就是空的
在设置的地方可以修改docker的一些配置,比如Cpu个数,内存大小,磁盘大小等。
Docker Desktop中还集成了kubernetes,我们可以选择启用一个单实例的k8s集群来体验容器编排的功能
本地生成镜像
前面准备工作都做完了以后,来到重头戏,实现本地打包时生成镜像并发布到本地的docker服务中,并且实现镜像发布到远程的dockerhub仓库中。需要用到一个docker的插件dockerfile-maven-plugin
默认情况下,该插件通过访问localhost:2375来连接本地docker,可以通过设置DOCKER_HOST 环境变量来连接docker,即
DOCKER_HOST=tcp://<host>:2375
。如果没有设置DOCKER_HOST
环境变量,也可以命令行显示指定DOCKER_HOST
来执行,如我本机指定 DOCKER_HOST:DOCKER_HOST=unix:///var/run/docker.sock mvn clean install docker:build
。
直接上maven配置,下面这个配置是最终的配置,接下来会解释每个配置项的作用
<?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>
<name>spring-boot-demo</name>
<artifactId>spring-boot-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.resource.version>3.1.0</maven.resource.version>
<java.version>1.8</java.version>
<docker.skip.build>build</docker.skip.build>
<docker.image.prefix>lushunjian</docker.image.prefix>
</properties>
<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>
<build>
<plugins>
<!--编译插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- docker的maven插件,官网:https://github.com/spotify/docker-maven-plugin -->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.12</version>
<!--插件绑定到phase, 在install阶段生成镜像-->
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<!--配置变量,包括是否build、imageName、imageTag,非常灵活-->
<!-- <skipDocker>${docker.skip.build}</skipDocker>-->
<!-- 注意imageName一定要是符合正则[a-z0-9-_.]的,否则构建不会成功 -->
<!-- 详见:https://github.com/spotify/docker-maven-plugin Invalid repository name ... only [a-z0-9-_.] are allowed-->
<!-- 如果要将docker镜像push到DockerHub上去的话,这边的路径要和repo路径一致 -->
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<!-- 每次生成两个tag版本,一个本身的版本号,另一个最新的latest版本 -->
<imageTags>
<imageTag>${project.version}</imageTag>
<imageTag>latest</imageTag>
</imageTags>
<!-- 指定Dockerfile所在的路径 -->
<dockerDirectory>${basedir}/src/main/resources</dockerDirectory>
<!--<baseImage>java</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>
<!--push到私有的hub-->
<serverId>docker-hub</serverId>
</configuration>
</plugin>
</plugins>
</build>
</project>
Dockerfile配置,放在SpringBoot的resources目录下
FROM java:8
VOLUME /tmp
ADD spring-boot-demo-1.0-SNAPSHOT.jar app.jar
RUN bash -c 'touch /app.jar'
EXPOSE 8090
ENTRYPOINT ["java","-jar","/app.jar"]
注意这里的EXPOSE
表示暴露的端口,这里的端口不是对外映射的端口,而是容器暴露的端口,这个端口要和微服务进程的端口保持一致,微服务的application.yml
配置如下
server:
port: 8090
插件下载好后,会有几个插件提供的命令
docker:build
是编译生成docker镜像
绑定phase执行
将插件绑定在某个phase执行,在很多场景下,我们有这样的需求,例如执行mvn clean package 时,自动地为我们构建docker镜像,可以吗?答案是肯定的。我们只需要将插件的goal 绑定在某个phase即可。所谓的phase和goal,可以这样理解:maven命令格式是:mvn phase:goal ,例如mvn package docker:build 那么,package 和 docker 都是phase,build 则是goal 。在插件中配置
<executions>
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
本例指的是讲docke
r的build
目标,绑定在package
这个phase
上。也就是说,用户只需要执行mvn package
,就自动执行了mvn docker:build
。
编译生成镜像
运行maven命令 mvn package打包并生成镜像,控制台出现如下日志,说明打包成功
F:\WorkTool\jdk1.8.0_77\bin\java.exe -Dmaven.multiModuleProjectDirectory=F:\workSpace\ideaSpace\spring-boot-demo "-Dmaven.home=D:\Program Files\JetBrains\IntelliJ IDEA 2018.1.6\plugins\maven\lib\maven3" "-Dclassworlds.conf=D:\Program Files\JetBrains\IntelliJ IDEA 2018.1.6\plugins\maven\lib\maven3\bin\m2.conf" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.1.6\lib\idea_rt.jar=58209:D:\Program Files\JetBrains\IntelliJ IDEA 2018.1.6\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\JetBrains\IntelliJ IDEA 2018.1.6\plugins\maven\lib\maven3\boot\plexus-classworlds-2.5.2.jar" org.codehaus.classworlds.Launcher -Didea.version=2018.1.6 -s F:\mavenRepository\settings.xml -Dmaven.repo.local=F:\mavenRepository\m2\repository install -f pom.xml
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building spring-boot-demo 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ spring-boot-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 3 resources
[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] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ spring-boot-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory F:\workSpace\ideaSpace\spring-boot-demo\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ spring-boot-demo ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ spring-boot-demo ---
[INFO]
[INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ spring-boot-demo ---
[INFO] Building jar: F:\workSpace\ideaSpace\spring-boot-demo\target\spring-boot-demo-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.5.3:repackage (repackage) @ spring-boot-demo ---
[INFO] Replacing main artifact with repackaged archive
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ spring-boot-demo ---
[INFO] Installing F:\workSpace\ideaSpace\spring-boot-demo\target\spring-boot-demo-1.0-SNAPSHOT.jar to F:\mavenRepository\m2\repository\org\springframework\boot\spring-boot-demo\1.0-SNAPSHOT\spring-boot-demo-1.0-SNAPSHOT.jar
[INFO] Installing F:\workSpace\ideaSpace\spring-boot-demo\pom.xml to F:\mavenRepository\m2\repository\org\springframework\boot\spring-boot-demo\1.0-SNAPSHOT\spring-boot-demo-1.0-SNAPSHOT.pom
[INFO]
[INFO] --- docker-maven-plugin:0.4.12:build (default) @ spring-boot-demo ---
[INFO] Copying F:\workSpace\ideaSpace\spring-boot-demo\target\spring-boot-demo-1.0-SNAPSHOT.jar -> F:\workSpace\ideaSpace\spring-boot-demo\target\docker\spring-boot-demo-1.0-SNAPSHOT.jar
[INFO] Copying F:\workSpace\ideaSpace\spring-boot-demo\src\main\resources\application.yml -> F:\workSpace\ideaSpace\spring-boot-demo\target\docker\application.yml
[INFO] Copying F:\workSpace\ideaSpace\spring-boot-demo\src\main\resources\Dockerfile -> F:\workSpace\ideaSpace\spring-boot-demo\target\docker\Dockerfile
[INFO] Copying F:\workSpace\ideaSpace\spring-boot-demo\src\main\resources\templates\index.html -> F:\workSpace\ideaSpace\spring-boot-demo\target\docker\templates\index.html
[INFO] Copying F:\workSpace\ideaSpace\spring-boot-demo\src\main\resources\templates\TestFile.txt -> F:\workSpace\ideaSpace\spring-boot-demo\target\docker\templates\TestFile.txt
[INFO] Building image lushunjian/spring-boot-demo
Step 1/6 : FROM java:8
---> d23bdf5b1b1b
Step 2/6 : VOLUME /tmp
---> Using cache
---> d53d269ecf52
Step 3/6 : ADD spring-boot-demo-1.0-SNAPSHOT.jar app.jar
---> 0593cb47509e
Step 4/6 : RUN bash -c 'touch /app.jar'
---> Running in 1432ce028810
Removing intermediate container 1432ce028810
---> e914934fdff7
Step 5/6 : EXPOSE 8090
---> Running in 890de24d1aca
Removing intermediate container 890de24d1aca
---> 0dbfc3addc50
Step 6/6 : ENTRYPOINT ["java","-jar","/app.jar"]
---> Running in 36239d0d5310
Removing intermediate container 36239d0d5310
---> 0f7d64a0de0e
ProgressMessage{id=null, status=null, stream=null, error=null, progress=null, progressDetail=null}
Successfully built 0f7d64a0de0e
Successfully tagged lushunjian/spring-boot-demo:latest
[INFO] Built lushunjian/spring-boot-demo
[INFO] Tagging lushunjian/spring-boot-demo with 1.0-SNAPSHOT
[INFO] Tagging lushunjian/spring-boot-demo with latest
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7.133 s
[INFO] Finished at: 2021-07-25T20:32:23+08:00
[INFO] Final Memory: 38M/745M
[INFO] ------------------------------------------------------------------------
Process finished with exit code 0
同时在Docker Desktop中可以看到刚刚成功推上去的镜像
点击右边蓝色按钮的RUN可以启动容器,容器启动前需要配置本地端口,可以看到容器端口是在Dockerfile中指定的端口,而本地端口是容器对外映射的端口(也就是访问端口),这里我填了8080
运行后,可以在Containers项看到正在运行的容器,可以看到刚刚跑起来的容器运行在8080端口
我们可以在本地通过浏览器访问8080端口,看是否正常
访问接口后正常返回了数据,说明服务已经在docker中正常运行
发布镜像到DockerHub
在最开始时我创建了一个镜像仓库 lushunjian/spring-boot-demo
,在Docker的maven插件配置中有一个配置项,这个配置项的值很重要,它应该和远程仓库路径保持一致,这里我配置成了两个变量
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<!-- 的远程仓库路径为lushunjian/spring-boot-demo,所以它的值应该是配成 -->
<imageName>lushunjian/spring-boot-demo</imageName>
大家根据自己的修改这个配置值,接下来第二步,需要配置远程仓库的服务地址,这里配置需要配置在Maven的 settings.xml
文件中(一开始我是想配置pom中的,因为这样对于初学者来说比较简单直观,后面尝试了很久发现行不通)。在settings.xml
加入如下配置
<servers>
<server>
<id>docker-hub</id>
<username>DockerHub 的账号</username>
<password>DockerHub 的密码</password>
<configuration>
<email>admin@qq.com</email>
</configuration>
</server>
</servers>
然后在插件的配置中加入如下配置关联上述server id
<serverId>docker-hub</serverId>
至此配置全部完毕了,接下来执行插件的命令 docker:push
即可
刷新dockerHub的仓库,会发现刚刚提交上来了两个镜像