木讷大叔爱运维

应用上K8S第二步:Maven打包

Image

Image

对于spring boot项目我们一般使用Maven或Gradle进行编译打包,也可以借助docker plugin进行镜像打包并push到远程仓库。因此在经过《应用上K8S第一步:Dockerfile参数化构建技巧》一文,我们已经确定了Dockerfile,那么应用上K8S第二步:maven打包就可以开始了。

现在我们就来介绍下maven打包,大家感兴趣的话也可以和gradle打包对比下,并进一步掌握这两种方式。

手动构建spring boot应用

一、helloworld应用

1.创建spring boot工程

#使用IntelliJ IDEA的“Spring Assistant”插件创建spring web项目Group id: com.dockerArtifact id: hellworldProject name: helloworldPackage name: com.docker.helloworld

2.创建HelloworldController

vim HelloworldControllerpackage com.docker.helloworld;
import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
@RestControllerpublic class HelloworldController {
private static final Logger logger= LoggerFactory.getLogger(HelloworldController.class); @RequestMapping("/") public String index(){ return "Hello world"; }}

3.编译运行

mvn clean package && java -jar target/helloworld-0.0.1-SNAPSHOT.jar

此时我们可以通过localhost:8080访问了。

二、docker构建镜像

1.Dockerfile

#基础镜像java 1.8From java#匿名数据卷,在启动容器时忘记挂载数据卷,会自动挂载到匿名卷VOLUME /tmpARG JAR_FILE=target/*.jarCOPY ${JAR_FILE} helloworld.jarENTRYPOINT ["java","-jar","/helloworld.jar"]

2.构建镜像并运行容器

#从dockerfile构建镜像[root@test]# docker build -t docker/helloworld .
#docker/helloworld就是我们构建的新镜像[root@test]# docker image lsREPOSITORY TAG IMAGE ID CREATED SIZEdocker/helloworld latest 75c3d26e3c57 8 minutes ago 661MB
#运行容器[root@test]# docker run -d -p 8080:8080 --name hellworld docker/helloworld#查看镜像[root@test]# docker ps -lCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESfeb8b6ef9941 docker/helloworld "java -jar /hellowor…" 8 minutes ago Up 8 minutes 0.0.0.0:8080->8080/tcp hellworld

此时我们可以通过localhost:8080访问了。

以上是通过手动运行jar包及通过镜像的形式运行应用,其实mvn也提供相应的插件自动构建镜像。

mvn构建docker镜像

一、配置mvn插件

docker-maven-plugin作用是配合Dockerfile构建docker镜像,推送到docker仓库。还可以使用dockerfile-maven-plugin

#添加docker-maven-plugin到pom.xml#版本已经使用新版本<plugin>  <groupId>com.spotify</groupId>  <artifactId>docker-maven-plugin</artifactId>  <version>1.2.2</version>  <configuration>    <imageName>docker/helloworld</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>

其中:
(1)imageName 是构建镜像的名称;
(2)dockerDirectory是Dockerfile位置,最好是只有Dockerfile,因为在mvn打包时此目录下的所有文件包括Dockerfile将会被copy到${project.build.directory}/docker目录下,此目录默认是target/docker目录;
(3)resource作用是将${project.build.directory}目录下的${project.build.finalName}.jar,复制到${project.build.directory}/docker目录下;如:将target/helloworld-0.0.1-SNAPSHOT.jar 文件复制到target/docker/下;

二、构建运行

[root@test]# mvn clean package docker:build
[INFO] --- docker-maven-plugin:1.2.2:build (default-cli) @ helloworld ---[INFO] Using authentication suppliers: [ConfigFileRegistryAuthSupplier][INFO] Copying /home/test/java/docker/helloworld/target/helloworld-0.0.1-SNAPSHOT.jar -> /home/test/java/docker/helloworld/target/docker/helloworld-0.0.1-SNAPSHOT.jar[INFO] Copying src/main/docker/Dockerfile -> /home/test/java/docker/helloworld/target/docker/Dockerfile[INFO] Building image docker/helloworldStep 1/5 : From java
Pulling from library/java5040bd298390: Pull complete fce5728aad85: Pull complete 76610ec20bf5: Pull complete 60170fec2151: Pull complete e98f73de8f0d: Pull complete 11f7af24ed9c: Pull complete 49e2d6393f32: Pull complete bb9cdec9c7f3: Pull complete Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9dStatus: Downloaded newer image for java:latest ---> d23bdf5b1b1bStep 2/5 : VOLUME /tmp
---> Running in fa47a820cd54Removing intermediate container fa47a820cd54 ---> 434e44760301Step 3/5 : ARG JAR_FILE=*.jar
---> Running in 3e0266b2cb16Removing intermediate container 3e0266b2cb16 ---> aa3be3ab4dafStep 4/5 : COPY ${JAR_FILE} helloworld.jar
---> 73cfed161b79Step 5/5 : ENTRYPOINT ["java","-jar","/helloworld.jar"]
---> Running in 1e6d6ec86542Removing intermediate container 1e6d6ec86542 ---> 9027adc2d0a4ProgressMessage{id=null, status=null, stream=null, error=null, progress=null, progressDetail=null}Successfully built 9027adc2d0a4Successfully tagged docker/helloworld:latest

其中:第5,6行可以看到copy过程,对应pom.xml中的<resource>配置,剩下为从Dockerfile构建镜像过程。

#查看镜像[root@test]# docker image lsREPOSITORY          TAG                 IMAGE ID            CREATED             SIZEdocker/helloworld   latest              9027adc2d0a4        About an hour ago   661MB#运行docker run -d -p 8080:8080  --name helloworld docker/helloworld```

从以上看出此镜像只有默认的tag:latest,如果需要加上版本信息,需在pom.xml中添加如下配置:

#configuration段中添加<imageTags>  <imageTag>v1</imageTag></imageTags>

或者

#在命令行直接打tagmvn clean package docker:build -DdockerImageTags=v1

以上配置或手动添加tag后,最终镜像如下:

#构建完成后,同一image_id 但有不同的tag[root@test]# docker image lsREPOSITORY          TAG                 IMAGE ID            CREATED             SIZEdocker/helloworld   v1                  6f3b482d2b78        10 seconds ago      661MBdocker/helloworld   latest              6f3b482d2b78        10 seconds ago      661MB

三、推送镜像到远程仓库阿里云镜像库

1.创建仓库

在阿里云容器镜像服务创建仓库

registry.cn-qingdao.aliyuncs.com/test/test

2.mvn配置镜像库认证

vim settings.xml#servers块中插入<server>  <id>docker-aliyun</id>  #阿里云开通的账户名  <username>xxxx</username>  #开通镜像服务的密码,不是阿里云登录密码  <password>xxxxx</password>  <configuration>    #阿里云绑定邮箱    <email>xxxxx</email>  </configuration></server>

3.pom.xml配置

<build>  <plugins>    <plugin>      <groupId>org.springframework.boot</groupId>      <artifactId>spring-boot-maven-plugin</artifactId>    </plugin>    <plugin>      <groupId>com.spotify</groupId>      <artifactId>docker-maven-plugin</artifactId>      <version>1.2.2</version>      <configuration>          <!--镜像名称要于阿里云镜像仓库对应,否则无法push到阿里云仓库-->          <!--功能等于docker tag docker/helloworld registry.cn-qingdao.aliyuncs.com/test/test-->        <imageName>registry.cn-qingdao.aliyuncs.com/test/test</imageName>        <dockerDirectory>src/main/docker</dockerDirectory>        <resources>          <resource>            <targetPath>/</targetPath>            <directory>${project.build.directory}</directory>            <include>${project.build.finalName}.jar</include>          </resource>        </resources>        <!--一定与mvn的id对应-->        <serverId>docker-aliyun</serverId>        <registryUrl>registry.cn-qingdao.aliyuncs.com/test/test</registryUrl>      </configuration>    </plugin>  </plugins></build>

注意:

(1)<imageName>名称一定要与与阿里云镜像仓库对应,否则无法push到阿里云仓库,功能等于:

docker tag docker/helloworld registry.cn-qingdao.aliyuncs.com/test/test

(2)<serverId>一定要与mvn镜像库的id对应

以上两项不对,镜像将默认推送至docker.io。

4.构建并push

#构建,只推送tag为v3至远程仓库[root@test]# mvn clean package docker:build -DdockerImageTags=v3 -DpushImageTag[root@test]# docker image lsREPOSITORY                                              TAG                 IMAGE ID            CREATED              SIZEregistry.cn-qingdao.aliyuncs.com/test/test   latest              08d5a09985a1        20 seconds ago       661MBregistry.cn-qingdao.aliyuncs.com/test/test   v3                  08d5a09985a1        20 seconds ago       661MB

注意:

#若不加-DdockerImageTags=v3 ,会将所有构建的镜像(v3及latest)都上传。mvn clean package docker:build -DpushImageTag#若不加-DpushImageTag,只会构建镜像并tag 为v3,但不会push。mvn clean package docker:build -DdockerImageTags=v3

Image

四、绑定Docker命令到Maven各个阶段

Docker 构建过程为 build、tag、push,其中build、tag对应mvn的package阶段,push对应mvn的deploy。通过将docker命令绑定到mvn的各个阶段,我们可以通过mvn deploy实现构建并push的过程。
补充:

maven各阶段功能:
(1)package阶段实现项目编译、单元测试、打包功能,但没有把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库;
(2) install阶段实现项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库,但没有布署到远程maven私服仓库;
(3) deploy阶段实现项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库;

1.pom.xml配置插件

<!--一定添加maven-deploy-plugin,通过skip跳过artifact仓库--><plugin>  <groupId>org.apache.maven.plugins</groupId>  <artifactId>maven-deploy-plugin</artifactId>  <configuration>    <skip>true</skip>  </configuration></plugin><plugin>  <groupId>com.spotify</groupId>  <artifactId>docker-maven-plugin</artifactId>  <version>1.2.2</version>  <configuration>    <imageName>registry.cn-qingdao.aliyuncs.com/test/test</imageName>    <dockerDirectory>src/main/docker</dockerDirectory>    <resources>      <resource>        <targetPath>/</targetPath>        <directory>${project.build.directory}</directory>        <include>${project.build.finalName}.jar</include>      </resource>    </resources>    <imageTags>      <imageTag>${project.version}</imageTag>    </imageTags>    <serverId>docker-aliyun</serverId>    <pushImage>true</pushImage>    <registryUrl>registry.cn-qingdao.aliyuncs.com/test/test</registryUrl>  </configuration>  <executions>    <execution>      <id>build-image</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>registry.cn-qingdao.aliyuncs.com/test/test:${project.version}</image>        <newName>registry.cn-qingdao.aliyuncs.com/test/test:${project.version}</newName>      </configuration>    </execution>    <execution>      <id>push-image</id>      <phase>deploy</phase>      <goals>        <goal>push</goal>      </goals>      <configuration>        <imageName>registry.cn-qingdao.aliyuncs.com/test/test:${project.version}</imageName>      </configuration>    </execution>  </executions></plugin>

注意:一定要通过maven-deploy-plugin设置skip,通过skip跳过artifact仓库,否则mvn deploy  推送时报错:

Deployment failed: repository element was not specified in the POM inside distributionManagement element or in -DaltDeploymentRepository=id::layout::url parameter

2.执行命令

#构建、tag 不推送mvn clean package#构建、tag及推送到远程仓库mvn clean deploy #跳过整个过程,不执行实际命令,可用测试流程是否正常mvn clean deploy -DskipDocker#跳过构建-DskipDockerBuild#跳过tag-DskipDockerTag#跳过push-DskipDockerPush

注意:
(1)经测试发现,实际<goal>tag</goal>阶段实际并不会进行tag,而是通过<imageName>registry.cn-qingdao.aliyuncs.com/test/test</imageName>配置进行tag,如果你的imageName设置不正确,则不会推送到自定义仓库,而是默认的官方仓库;
(2)此方式会将镜像的所有tag 版本及历史版本,如v3和latest及所有历史版本v2,v1都会推送到远程仓库;

总结

通过对比,"docker命令绑定mvn各阶段"方式不太灵活,而且会将镜像所有tag进行推送,但提供了一套模板用于自动化流程,可根据实际情况选择。


docker部署springboot的过程算是亲自实践了下,但是在实际过程中还需要考虑以下问题:
1.java应用的运行时数据、日志文件的持久化;
2.镜像命令、目录问题、私有镜像库等的配合使用;
3.流水线操作实现测试、版本发布等;
4.docker 运行状态监控等;

运维从来都不满足于实现部署的某个过程,需要综合考虑提供一套整体的运维方案,这个还需要不断实践总结。

对的那条路,往往不是最好走的!

精彩文章合集

文章推荐

☞【合集】运维思索系列
☞【合集】运维管理系列
☞【合集】运维监控之路
☞【合集】蓝鲸之路
☞【合集】CI/CD之路
☞【合集】Ansible之路

札记:质量管理分为四个阶段,即Plan(计划)、Do(执行)、Check(检查)和 Act(处理)。

-- PDCA循环

喜欢这篇文章,记得点赞+在看哦~