Jenkins Pipeline 是 Jenkins 提供的一种用于持续集成和持续交付(CI/CD)的脚本化流程工具。它允许你通过编写一个 Jenkinsfile 文件来定义整个构建、测试和部署的流程。本文介绍打包springcloud项目,react项目为docker镜像
文章目录
- 1.项目结构
- 2.项目打包改造
- 2.1. lhm-emp 模块pom.xml
- 2.2. lhm-emp 模块Dockerfile
- 2.3. lhm-web模块Dockerfile
- 3. Jenkins docker pipeline编写
- 3.1.GIT_CREDENTIALS_ID: git 认证参数
- your-credentials-id
- 3.2.pipeline编写
- 3.3.执行打包任务
- 3.4. 导入镜像
- 4. 打包不同架构的基础image
- 4.1. java
- 4.2. nginx
- 5. 注意
- 5.1. 拉取http仓库 connect: connection refused"
- 5.2. Jenkinsfile Pipeline 打包镜像缓慢解决办法
- 5.3. unauthorized to access repository
- 6. 打包增量
- 6.1. docker打包增量例子
1.项目结构
├─Jenkinsfile jenkinsfile打包脚本文件
├─lhm-emp 微服务模块 emp服务
├─lhm-eureka 微服务模块 eureka服务
├─lhm-gateway 微服务模块 gateway服务
├─lhm-order 微服务模块 order服务
└─lhm-web 前端服务
项目地址 https://gitee.com/liuhaomin/springcloud
测试请求
## eureka
http://localhost:8100
## order
http://localhost:9000/api/order/info
## emp
http://localhost:9000/api/emp/info
## web
http://localhost:3000
2.项目打包改造
根pom.xml
<build>
<finalName>${project.artifactId}</finalName>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>${docker.plugin.version}</version>
<configuration>
<username>${docker.username}</username>
<password>${docker.password}</password>
<repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
<!--<tag>${os_tag}-${project.version}</tag>-->
<tag>${os_tag}${project.version}</tag>
<useMavenSettingsForAuth>true</useMavenSettingsForAuth>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
<skip>${dockerfile.skip}</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
2.1. lhm-emp 模块pom.xml
lhm-gateway,lhm-eureka,lhm-order 类似
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${emp.main.class}</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<configuration>
<username>${docker.username}</username>
<password>${docker.password}</password>
<repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
<tag>${os_tag}${lhm.project.version}</tag>
<useMavenSettingsForAuth>true</useMavenSettingsForAuth>
<buildArgs>
<OS_ARCH>${os_arch}</OS_ARCH>
<CACHEFROM>${last_version}</CACHEFROM>
<TARGETPLATFORM>${os_arch.dockerimage}</TARGETPLATFORM>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
<EXPOSE_PORT>${emp.port}</EXPOSE_PORT>
<MAINCLASS>${emp.main.class}</MAINCLASS>
</buildArgs>
<skip>${dockerfile.skip}</skip>
</configuration>
<executions>
<execution>
<id>default</id>
<phase>package</phase>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
2.2. lhm-emp 模块Dockerfile
lhm-gateway,lhm-eureka,lhm-order 类似
ARG TARGETPLATFORM
ARG OS_ARCH
# 指定基础镜像,这是分阶段构建的前期阶段
FROM 192.168.56.10/lhm/openjdk:8-jdk-alpine as builder
# 执行工作目录
WORKDIR target
# 配置参数
ARG JAR_FILE=target/*.jar
# 将编译构建得到的jar文件复制到镜像空间中
COPY ${JAR_FILE} application.jar
RUN unzip application.jar
FROM --platform=$OS_ARCH $TARGETPLATFORM
ARG EXPOSE_PORT
ARG JAR_FILE
ARG MAINCLASS
ARG DEPENDENCY=target
COPY --from=builder ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=builder ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=builder ${DEPENDENCY}/BOOT-INF/classes /app
EXPOSE ${EXPOSE_PORT}
ARG CACHEFROM
LABEL description=" cache from ${CACHEFROM}"
ENV JAVA_OPTS ""
ENV AGENT_AFTER_JAR ""
ENV MAINCLASS ${MAINCLASS}
CMD java ${JAVA_OPTS} -Dspring.profiles.active=docker -Dservice_name=${MAINCLASS} -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/opt/logs/jvm/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/logs/jvm/dump.hprof -Djava.security.egd=file:/dev/./urandom -Denv=dev -Duser.timezone=GMT+08 ${AGENT_AFTER_JAR} -cp app:app/lib2/*:app/lib/* ${MAINCLASS}
2.3. lhm-web模块Dockerfile
ARG TARGETPLATFORM
ARG OS_ARCH
FROM --platform=$OS_ARCH $TARGETPLATFORM
ENV HTML_PATH=/usr/share/nginx/html
WORKDIR $HTML_PATH
COPY ./build $HTML_PATH
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
3. Jenkins docker pipeline编写
3.1.GIT_CREDENTIALS_ID: git 认证参数
your-credentials-id
3.2.pipeline编写
pipeline {
agent any
options {
// 最长保留90天,最多保留3个构建
buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '90', numToKeepStr: '3')
}
environment{
GIT_CREDENTIALS_ID="fd76875f-1862-43d4-adf4-6462e3b3c7f4"
SERVER_GIT_URL="https://gitee.com/liuhaomin/springcloud.git"
WEB_GIT_URL="https://gitee.com/liuhaomin/springcloud.git"
HARBOR_URL="192.168.56.10"
HARBOR_USERNAME="admin"
HARBOR_PASSWORD="Harbor12345"
// 192.168.56.10/lhm/lhm-web
HARBOR_NAMESPACE="lhm"
JAVACOMPILEIMAGE="192.168.56.10/lhm/openjdk:8-jdk-alpine"
JAVA_OS_ARCH_DOCKERIMAGE="192.168.56.10/lhm/adoptopenjdk/openjdk8-openj9:alpine-slim"
NGINX_OS_ARCH_DOCKERIMAGE="192.168.56.10/lhm/nginx:1.26.1-alpine"
OS_ARCH="linux/amd64"
OS_TAG="x86"
}
parameters {
listGitBranches(branchFilter: '.*', credentialsId: '${env.GIT_CREDENTIALS_ID}', defaultValue: 'master', listSize: '5', name: 'SERVER_BRANCH_NAME', quickFilterEnabled: false, remoteURL: 'https://gitee.com/liuhaomin/springcloud.git', selectedValue: 'DEFAULT', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
listGitBranches(branchFilter: '.*', credentialsId: '${env.GIT_CREDENTIALS_ID}', defaultValue: 'master', listSize: '5', name: 'WEB_BRANCH_NAME', quickFilterEnabled: false, remoteURL: 'https://gitee.com/liuhaomin/springcloud.git', selectedValue: 'DEFAULT', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
string(defaultValue: '1.0.1', description: '版本号', name: 'VERSION', trim: true)
booleanParam(defaultValue: false, description: '是否跳过构建', name: 'SKIP_BUILD')
extendedChoice(description: '选择需要打包的服务模块(多选)', multiSelectDelimiter: ',', name: 'SERVICES', quoteValue: false, saveJSONParameterToFile: false,type: 'PT_CHECKBOX', value: 'lhm-eureka,lhm-gateway,lhm-emp,lhm-order,web-lhm-web',defaultValue: '',visibleItemCount: 5)
}
stages {
stage('SERVER拉取代码') {
when {
expression {
if (Boolean.valueOf("${SKIP_BUILD}")){
echo "跳过SERVER拉取代码,直接打包镜像文件"
return !Boolean.valueOf("${SKIP_BUILD}")
}
def services = "${SERVICES}"
def index = services.indexOf('web-')
if (index > 0) {
services = services.substring(0, index - 1)
}
if( services.size() > 0 && index != 0) {
echo "包含服务,SERVER拉取代码"
return true
}
echo "没有包含SERVER 跳过SERVER拉取代码"
return false
}
}
steps {
script {
deleteDir()
sh "ls"
echo "SERVER检出代码..."
def scmVars = checkout([
$class:"GitSCM",
branches:[[name:"${SERVER_BRANCH_NAME}"]],
doGenerateSubmoduleConfigurations: false,
gitTool: "Default",
submoduleCfg: [],
userRemoteConfigs:[[credentialsId:"${env.GIT_CREDENTIALS_ID}",url:"${env.SERVER_GIT_URL}"]]
]
)
sh "echo '${scmVars}'"
sh "ls"
}
}
}
stage('SERVER构建打包') {
agent {
docker{
image 'maven:3.8-adoptopenjdk-8-openj9'
reuseNode true
args '-v /root/.docker/config.json:/root/.docker/config.json -v /home/jenkins/repository:/root/.m2 -v /var/run/docker.sock:/var/run/docker.sock'
}
}
when {
expression {
if (Boolean.valueOf("${SKIP_BUILD}")){
echo "跳过SERVER构建打包,直接打包镜像文件"
return !Boolean.valueOf("${SKIP_BUILD}")
}
def services = "${SERVICES}"
def index = services.indexOf('web-')
if (index > 0) {
services = services.substring(0, index - 1)
}
if( services.size() > 0 && index != 0) {
echo "包含服务,SERVER构建打包"
return true
}
echo "没有包含SERVER 跳过SERVER构建打包"
return false
}
}
steps {
script {
def moduleListStr = "${SERVICES}"
echo "$moduleListStr"
def index = moduleListStr.indexOf('web-')
if (index > 0) {
moduleListStr = moduleListStr.substring(0, index - 1)
}
def moduleList = moduleListStr.split(',')
def count = moduleList.size()
if (count <= 0) {
echo "请选择构建模块..."
sh 'exit 1'
}
def JAVACOMPILEIMAGE="${env.JAVACOMPILEIMAGE}"
def JAVA_OS_ARCH_DOCKERIMAGE="${env.JAVA_OS_ARCH_DOCKERIMAGE}"
def OS_ARCH="${env.OS_ARCH}"
def OS_TAG="${env.OS_TAG}"
if("${OS_TAG}"=="x86"){
OS_TAG=""
}
def moduleStr = moduleList.join(",:")
echo "打包构建Version: ${VERSION}"
sh "mvn -T 1C -pl :${moduleStr} -am clean package -Dmaven.test.skip=true -Ddockerfile.skip=false -Dlhm.project.version=${VERSION} -Dos_arch=${OS_ARCH} -Dos_tag=${OS_TAG} -Dos_arch.dockerimage=${JAVA_OS_ARCH_DOCKERIMAGE} -Djava.compileimage=${JAVACOMPILEIMAGE}"
for (int i = 0; i < count; ++i) {
def serviceName = "${moduleList[i]}"
sh "docker tag ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION} ${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}"
}
}
}
}
stage('WEB拉取代码') {
when {
expression {
if (Boolean.valueOf("${SKIP_BUILD}")){
echo "跳过WEB拉取代码,直接打包镜像文件"
return !Boolean.valueOf("${SKIP_BUILD}")
}
def services = "${SERVICES}"
def index = services.indexOf('web-lhm-web')
if (index != -1 ) {
echo "包含web-lhm-web 打包WEB镜像"
return true
}
echo "没有包含web-lhm-web 跳过WEB拉取代码"
return false
}
}
steps {
script {
deleteDir()
sh "ls"
echo "WEB检出代码..."
def scmVars = checkout([
$class:"GitSCM",
branches:[[name:"${WEB_BRANCH_NAME}"]],
doGenerateSubmoduleConfigurations: false,
gitTool: "Default",
submoduleCfg: [],
userRemoteConfigs:[[credentialsId:"${env.GIT_CREDENTIALS_ID}",url:"${env.WEB_GIT_URL}"]]
]
)
sh "echo '${scmVars}'"
sh "ls"
}
}
}
stage('WEB构建打包') {
agent {
docker{
image 'node:16-alpine'
reuseNode true
args ''
}
}
when {
expression {
if (Boolean.valueOf("${SKIP_BUILD}")){
echo "跳过WEB构建打包,直接打包镜像文件"
return !Boolean.valueOf("${SKIP_BUILD}")
}
def services = "${SERVICES}"
def index = services.lastIndexOf('web-lhm-web')
if (index != -1 ) {
echo "包含web-lhm-web 打包WEB镜像"
return true
}
echo "没有包含web-lhm-web 跳过WEB构建打包"
return false
}
}
steps {
script {
echo "依赖安装..."
sh "cd lhm-web && yarn config set registry https://registry.npmmirror.com/ && yarn install --network-concurrency 4 && yarn build"
echo "打包构建Version: ${VERSION}"
def NGINX_OS_ARCH_DOCKERIMAGE="${env.NGINX_OS_ARCH_DOCKERIMAGE}"
def OS_ARCH="${env.OS_ARCH}"
def OS_TAG="${env.OS_TAG}"
if("${OS_TAG}"=="x86"){
OS_TAG=""
}
// try {
// // 默认先打包增量,不成功再直接打包
// sh "docker pull ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION}"
// echo "基于[${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION}]缓存构建"
// sh "cd lhm-web && docker build --cache-from ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION} --build-arg TARGETPLATFORM=${NGINX_OS_ARCH_DOCKERIMAGE} --build-arg OS_ARCH=${OS_ARCH} -t ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION_NO} . && docker images"
// } catch (err) {
// def error = "${e.toString()}"
// echo "不走缓存构建,没有找到[${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${LAST_VERSION}]镜像"
// if(error.contains("not found")){
// sh "cd lhm-web && docker build --build-arg TARGETPLATFORM=${NGINX_OS_ARCH_DOCKERIMAGE} --build-arg OS_ARCH=${OS_ARCH} -t ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION} .&& docker images"
// } else {
// throw err
// }
// }
// 登录一下,防止找到不基础镜像
sh "docker login ${env.HARBOR_URL} -u ${env.HARBOR_USERNAME} -p ${env.HARBOR_PASSWORD}"
sh "cd lhm-web && docker build --build-arg TARGETPLATFORM=\'${NGINX_OS_ARCH_DOCKERIMAGE}\' --build-arg OS_ARCH=${OS_ARCH} -t ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION} .&& docker images"
sh "docker tag ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION} ${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION}"
// 登录harbor docker 仓库中心
echo "----<<<< 登录harbor docker 仓库中心 >>>>-----"
sh "docker login ${env.HARBOR_URL} -u ${env.HARBOR_USERNAME} -p ${env.HARBOR_PASSWORD}"
// 推送镜像到harhor docker 仓库中心
echo "----<<<< 推送镜像到harhor docker 仓库中心 >>>>-----"
sh "docker push ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/lhm-web:${OS_TAG}${VERSION}"
}
}
}
stage('镜像打包') {
steps {
script {
def OS_TAG="${env.OS_TAG}"
if("${OS_TAG}"=="x86"){
OS_TAG=""
}
echo "${SERVICES}"
def serviceListStr = "${SERVICES}"
if (serviceListStr.isEmpty()) {
echo "请选择构建模块..."
sh 'exit 1'
}
def batchImages = []
serviceListStr = serviceListStr.replace("web-","")
def serviceList = serviceListStr.split(',')
def count = serviceList.size()
def fullFolderstr = "${WORKSPACE}/full/${VERSION}/"
sh "mkdir -p ${fullFolderstr} && cd ${fullFolderstr}"
echo "use version: ${VERSION}"
echo "OS_TAG: ${OS_TAG}"
sh "cd ${fullFolderstr} && touch images-load.sh && touch images-list.txt && echo '#!/bin/sh' > images-load.sh && echo 'echo \"==== starting to load images ====\"' >> images-load.sh && echo 'echo \"==== 服务导入镜像 ====\"' >> images-load.sh"
sh "cd ${fullFolderstr} && echo ' ' > images-list.txt"
sh "docker login ${env.HARBOR_URL} -u ${env.HARBOR_USERNAME} -p ${env.HARBOR_PASSWORD}"
for (int i = 0; i < count; ++i) {
def serviceName = "${serviceList[i]}"
sh "docker pull ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}"
sh "docker tag ${env.HARBOR_URL}/${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION} ${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}"
batchImages.add("${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}")
sh "cd ${fullFolderstr} && echo '- ${env.HARBOR_NAMESPACE}/${serviceName}:${OS_TAG}${VERSION}' >> images-list.txt"
}
def batchImagesStr = batchImages.join(" ")
def grepVersion = "${VERSION}".replaceAll(".", ".")
sh "cd ${fullFolderstr} && echo 'docker load -i app.tar.gz' >> images-load.sh"
sh "cd ${fullFolderstr} && echo 'echo \"==== 查看服务镜像 ====\"' >> images-load.sh && echo 'docker images | grep -E ${grepVersion}' >> images-load.sh && echo 'echo \"==== end to load images ====\"' >> images-load.sh"
sh "docker images | grep -E ${grepVersion}"
// 打包全量
sh "docker save ${batchImagesStr}|gzip > ${fullFolderstr}/app.tar.gz"
//sh "cd ${WORKSPACE}/full && ls && rm -rf ${OS_TAG}${VERSION}.zip && zip -r ${OS_TAG}${VERSION}.zip ${VERSION}/* && cp ${WORKSPACE}/full/${OS_TAG}${VERSION}.zip ${WORKSPACE}"
sh "cd ${WORKSPACE}/full && ls && rm -rf ${OS_TAG}${VERSION}.zip && tar -czvf ${OS_TAG}${VERSION}.tar.gz ${VERSION}/* && cp ${WORKSPACE}/full/${OS_TAG}${VERSION}.tar.gz ${WORKSPACE}"
try {
// -f :显示时进行过滤 “dangling=true”: 表示过滤并显示悬挂状态的镜像,即没有被标签引用或者被其他层依赖的镜像 -q :只显示image id
//sh "docker images -q -f dangling=true | xargs docker rmi -f"
// 清理打包的镜像
sh "docker images --format '{{.Repository}}:{{.Tag}}' | grep '${env.HARBOR_NAMESPACE}/' | xargs docker rmi -f"
} catch (err) {}
archiveArtifacts artifacts: '*.tar.gz', onlyIfSuccessful: true
echo "==== starting to export images ===="
echo "请下载Last Successful Artifacts构建成品!!!"
}
}
}
}
}
3.3.执行打包任务
3.4. 导入镜像
下载后执行
tar -xzvf 1.0.0.tar.gz
cd 1.0.0
bash images-load.sh
4. 打包不同架构的基础image
推荐一部分
4.1. java
# x86_64
adoptopenjdk/openjdk8-openj9:alpine-slim
# aarch64
arm64v8/adoptopenjdk:8-jre-openj9
4.2. nginx
# x86_64
nginx:1.26.1-alpine
# aarch64
nginx:1.26.1-alpine
5. 注意
5.1. 拉取http仓库 connect: connection refused"
[ERROR] Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build (default) on project lhm-eureka: Could not pull cache-from image: Request error: POST unix://localhost:80/images/create?fromImage=192.168.56.10%2Flhm%2Flhm-eureka&tag=x86_641.0.0: 500, body: {"message":"Get https://192.168.56.10/v2/: dial tcp 192.168.56.10:443: connect: connection refused"} -> [Help 1]
解决办法:
设置docker的仓库地址
vi /etc/docker/daemon.json
新增 registry-mirrors:["http://192.168.56.10"],
"insecure-registries" : ["192.168.56.10:5000", "0.0.0.0/0"]
insecure-registries
是Docker中的一个配置选项,涉及容器镜像的安全拉取。
- 定义与用途:
指允许Docker从不安全(即未使用HTTPS协议的)注册中心拉取镜像的列表。默认情况下,Docker为了安全考虑,只允许从使用HTTPS的注册中心拉取镜像。 - 配置方式:
在Docker配置文件(如/etc/docker/daemon.json
)中,通过添加"insecure-registries"
字段并指定一个或多个注册中心地址来配置。例如,"insecure-registries" : ["my-registry.local:5000"]
允许从不安全的my-registry.local:5000
拉取镜像。
5.2. Jenkinsfile Pipeline 打包镜像缓慢解决办法
- 将所有使用的docker镜像推送至自己的私有仓库(防止拉取其他镜像缓慢)
DockerFile 文件
FROM openjdk:8-jdk-alpine as builder
# 执行工作目录
WORKDIR target
上述文件比下面的文件缓慢接近20多倍
FROM 192.168.56.10/lhm/openjdk:8-jdk-alpine
# 执行工作目录
WORKDIR target
5.3. unauthorized to access repository
[ERROR] Failed to execute goal com.spotify:dockerfile-maven-plugin:1.4.13:build (default) on project lhm-eureka: Could not build image: unauthorized: unauthorized to access repository: lhm/openjdk, action: pull: unauthorized to access repository: lhm/openjdk, action: pull -> [Help 1]
解决办法
docker login 192.168.56.10 admin Harbor12345
或者
新增 -v /root/.docker/config.json:/root/.docker/config.json
docker{
image '192.168.56.10/lhm/maven:3.8-adoptopenjdk-8-openj9'
reuseNode true
args '-v /root/.docker/config.json:/root/.docker/config.json -v /home/jenkins/repository:/root/.m2 -v /var/run/docker.sock:/var/run/docker.sock'
}
6. 打包增量
打包增量详细解释
6.1. docker打包增量例子
打包增量可以使用springcloud increment 分支
jar需要单独替换为下面文件中的jar
增强了功能
- 打印了一些日志
- 如果需要cachefrom 的镜像不存在就直接打包,忽略当前的缓存打包
https://docs.docker.com/reference/api/engine/version/v1.40/#tag/Image/operation/ImageBuild
修改了BuildMojo.java
@Nullable
static String buildImage(@Nonnull DockerClient dockerClient,
@Nonnull Log log,
boolean verbose,
@Nonnull Path contextDirectory,
@Nullable Path dockerfile,
@Nullable String repository,
@Nonnull String tag,
boolean pullNewerImage,
boolean noCache,
@Nullable Map<String,String> buildArgs,
@Nullable List<String> cacheFrom,
boolean squash)
throws MojoExecutionException, MojoFailureException {
log.info(MessageFormat.format("Building Docker context {0}", contextDirectory));
log.info(MessageFormat.format("Building Docker noCache {0}", noCache));
log.info(MessageFormat.format("Building Docker cacheFrom {0}", cacheFrom.toString()));
requireValidDockerFilePath(log, contextDirectory, dockerfile);
final ArrayList<DockerClient.BuildParam> buildParameters = new ArrayList<>();
if (dockerfile != null) {
buildParameters.add(DockerClient.BuildParam.dockerfile(
contextDirectory.relativize(dockerfile)));
}
final LoggingProgressHandler progressHandler = new LoggingProgressHandler(log, verbose);
if (pullNewerImage) {
buildParameters.add(DockerClient.BuildParam.pullNewerImage());
}
if (noCache) {
buildParameters.add(DockerClient.BuildParam.noCache());
}
if (buildArgs != null && !buildArgs.isEmpty()) {
buildParameters.add(new DockerClient.BuildParam("buildargs", encodeBuildParam(buildArgs)));
}
if (cacheFrom != null && !noCache) {
final List<String> cacheFromExistLocally = new ArrayList<>();
for (String image : cacheFrom) {
try {
if (pullNewerImage || !imageExistLocally(dockerClient, image)) {
dockerClient.pull(image);
}
log.info(MessageFormat.format("Build will use image {0} for cache-from", image));
cacheFromExistLocally.add(image);
} catch (Exception e) {
log.warn(MessageFormat.format(
"Image {0} not found, build will not use it for cache-from", image));
}
}
if (!cacheFromExistLocally.isEmpty()) {
buildParameters.add(new DockerClient.BuildParam("cachefrom",encodeBuildParam(cacheFromExistLocally)));
log.info("buildParameters.add for cachefrom "+ cacheFromExistLocally.toString());
}
}
if (squash) {
buildParameters.add(new DockerClient.BuildParam("squash", encodeBuildParam(squash)));
}
final DockerClient.BuildParam[] buildParametersArray =
buildParameters.toArray(new DockerClient.BuildParam[buildParameters.size()]);
log.info(""); // Spacing around build progress
try {
if (repository != null) {
if (!validateRepository(repository)) {
throw new MojoFailureException(
"Repo name \""
+ repository
+ "\" must contain only lowercase, numbers, '-', '_' or '.'.");
}
final String name = formatImageName(repository, tag);
log.info(MessageFormat.format("Image will be built as {0}", name));
log.info(""); // Spacing around build progress
dockerClient.build(contextDirectory, name, progressHandler, buildParametersArray);
} else {
log.info("Image will be built without a name");
log.info(""); // Spacing around build progress
dockerClient.build(contextDirectory, progressHandler, buildParametersArray);
}
} catch (DockerException | IOException | InterruptedException e) {
throw new MojoExecutionException("Could not build image", e);
}
log.info(""); // Spacing around build progress
return progressHandler.builtImageId();
}
打包有下面这个就代表使用了缓存
结果