GitLab CI 构建 SpringBoot-2,rabbitmq 原理及作用
<name>dockerlayerdemo</name>
<description>Demo project for Spring Boot layer docker image</description>
<properties>
<java.version>1.8</java.version>
</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.0.RELEASE</version>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
</project>
java 代码并非重点,在 application 类中加了个 http 接口:
package com.bolingcavalry.dockerlayerdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
@SpringBootApplication
@RestController
public class DockerlayerdemoApplication {
public static void main(String[] args) {
SpringApplication.run(DockerlayerdemoApplication.class, args);
}
@RequestMapping(value = "/hello")
public String hello(){
return "hello " + new Date();
}
}
pom.xml 所在目录增加文件夹.m2,里面放入 settings.xml,这是 maven 的配置文件,可以设置您的特殊的 maven 信息;
pom.xml 所在目录增加 Dockerfile 文件,用于制作镜像:
指定基础镜像,这是分阶段构建的前期阶段
FROM openjdk:8u212-jdk-stretch as builder
执行工作目录
WORKDIR application
配置参数
ARG JAR_FILE=target/*.jar
将编译构建得到的 jar 文件复制到镜像空间中
COPY ${JAR_FILE} application.jar
通过工具 spring-boot-jarmode-layertools 从 application.jar 中提取拆分后的构建结果
RUN java -Djarmode=layertools -jar application.jar extract
正式构建镜像
FROM openjdk:8u212-jdk-stretch
WORKDIR application
前一阶段从 jar 中提取除了多个文件,这里分别执行 COPY 命令复制到镜像空间中,每次 COPY 都是一个 layer
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
pom.xml 所在目录增加.gitlab-ci.yml 文件,这就是 CI 时的 pipeline 脚本:
image: maven:3.6.3-jdk-8
variables:
MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode"
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
定义缓存
如果 gitlab runner 是 shell 或者 docker,此缓存功能没有问题
如果是 k8s 环境,要确保已经设置了分布式文件服务作为缓存
cache:
key: dockerlayerdemo-ci-cache
paths:
.m2/repository/
target/*.jar
本次构建的阶段:build package
stages:
package
build
生产 jar 的 job
make_jar:
image: maven:3.6.3-jdk-8
stage: package
tags:
k8s
script:
echo "=============== 开始编译源码,在 target 目录生成 jar 文件 =======
========"
mvn $MAVEN_CLI_OPTS clean compile package -Dmaven.test.skip=true
echo "target 文件夹"
ls target/
生产镜像的 job
make_image:
image: docker:latest
stage: build
tags:
k8s
script:
echo "从缓存中恢复的 target 文件夹"
ls target/
echo "=============== 登录 Harbor ==============="
docker login 192.168.50.43:5888 -u admin -p Harbor12345
echo "=============== 打包 Docker 镜像 : " gitlabci-java-demo:$CI_COMMIT_SHORT_SHA "==============="
docker build -t 192.168.50.43:5888/common/gitlabci-java-demo:$CI_COMMIT_SHORT_SHA .
echo "=============== 推送到镜像仓库 ==============="
docker push 192.168.50.43:5888/common/gitlabci-java-demo:$CI_COMMIT_SHORT_SHA
echo "=============== 登出 ==============="
docker logout
echo "清理掉本次构建的 jar 文件"
rm -rf target/*.jar
关于以上 pipeline 脚本,有下面五点需要注意:
第一:关于 cache,如果您的 gitlab runner 是 shell 或者 docker 类型就无需关注,cache 是直接生效的,但如果您的 gitlab runner 是 K8S 那就要注意了,需要在 gitlab runner 中填写 cache 相关的配置,让分布式文件服务作为 cache 的底层实现;
第二:一共定义了两个 stage:package 和 build,顺序是先 package 再 build,注意生成 jar 的 job 一定要是 package,使用 jar 构建镜像的 job 要是 build,这样在构建镜像的时候才能顺利从缓存中取得 jar;
第三:make_image 这个 job 的脚本中,会执行登录私有镜像仓库的操作,为了操作方便,登录的账号密码都是直接写在脚本里面的,实际使用时请不要这样做,建议使用 Harbor 的机器人账号密码,并且写入 GitLab CI 的环境变量配置页面,而不是直接写在 pipeline 脚本中
第四:tags 参数用来和已有的 GitLab Runner 匹配,请按照您自己的 runner 的情况设置;
第五:生成 docker 镜像的 tag 等于 $CI_COMMIT_SHORT_SHA,这是本次提交的 commit id,因此,每次提交都会导致镜像仓库中多一个镜像,其 tag 等于 commit id;
最终整个工程的内容如下:
至此,所有开发工作已经完成,接下来验证执行情况;
验证 CI
将所有内容提交到 GitLab,如果 CI 环境配置 OK 的话会立即触发构建,下图是构建成功的效果:
先来看 make_jar 的执行情况,如下图,SpringBoot 工程成功构建出 jar 文件:
再看 make_image 执行情况,如下图:
镜像制作成功后,开始推送到 harbor:
最终完成推送,并且清理残留文件:
最后看看 pipeline 的整体情况,如下图:
从上图可知 commit id 是 02307851,因此 Harbor 中应该有 tag 等于 02307851 的镜像,登录 Harbor 查看,如下图红框:
在 K8S 环境验证
接下来要在 K8S 环境验证之前的镜像可以正常运行:
SSH 登录 K8S 环境,执行以下命令,用最新的镜像创建 deployment:
kubectl create deployment dockerlayerdemo \
--image=192.168.50.43:5888/common/gitlabci-java-demo:02307851
执行以下命令创建 NodePort 类型的 service:
kubectl create service nodeport \
dockerlayerdemo --tcp 8080:8080
浏览器访问 http://192.168.50.135:31685/hello ,其中 192.168.50.135 是 K8S 宿主机的 IP 地址,如下图,可以正常访问 SpringBoot 服务:
GitLab CI 的价值
文章看到这里,咱们 pipeline 脚本也写了,镜像有了,K8S 上部署的服务也验证了,这就结束了吗?
—还没有,咱们来感受一下从修改代码到 K8S 环境上生效的流程:
修改 java 代码,如下图:
提交代码:
顺利生成镜像:
在 K8S 环境执行以下命令即可完成镜像更新:
kubectl set image deployment dockerlayerdemo \
gitlabci-java-demo=192.168.50.43:5888/common/gitlabci-java-demo:8735c78d
上述命令中的 gitlabci-java-demo 来自 kubectl describe deployment dockerlayerdemo 结果中,显示的容器名称,如下图红框:
系统提示更新成功:
再次用浏览器访问相同的地址,如下图红框,修改的代码已经生效:
可见借助 GitLab CI,编码到部署之间的过程已被简化,可以更加专注的撸码了;
体验 CD?
除了持续集成(CI),还可以把持续部署(CD)也加入到 pipeline 脚本中,这样我们只需提交代码,对应的镜像会被自动部署到 K8S 环境;
打开.gitlab-ci.yml,增加一个 stage 定义 deploy,如下所示,现在一共有三个 stage 了:
stages:
评论