jenkins pipeline打包流程

Jenkins Pipeline 是 Jenkins 提供的一种用于持续集成和持续交付(CI/CD)的脚本化流程工具。它允许你通过编写一个 Jenkinsfile 文件来定义整个构建、测试和部署的流程。本文介绍打包springcloud项目,react项目为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();
  }

在这里插入图片描述

在这里插入图片描述

打包有下面这个就代表使用了缓存
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liuhm~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值