jenkins pipeline打包流程

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. 打包增量

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 {
        gitParameter(name: 'SERVER_BRANCH_NAME',type: 'branch', branchFilter: "origin/(.*)",defaultValue: 'master',selectedValue: "DEFAULT",useRepository: '${env.SERVER_GIT_URL}',description: '后端服务选择分支版本')
        gitParameter(name: 'WEB_BRANCH_NAME',type: 'branch', branchFilter: "origin/(.*)",defaultValue: 'master',selectedValue: "DEFAULT",useRepository: '${env.WEB_GIT_URL}',description: 'WEB服务选择分支版本')
		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("lhm/${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. 打包增量

打包增量详细解释

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/937706.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【LC】876. 链表的中间结点

题目描述&#xff1a; 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间结点…

WEB开发: Node.js路由之由浅入深- 即拿即用完整版

前面我们一起学习了Node.js路由之由浅入深&#xff0c;基本了解并熟悉了Node.js的路由实现。 现在我们来一个综合完整版&#xff0c;让这个路由模块即拿即用&#xff0c;也就是下载运行就可用&#xff0c;并可以轻松地自行增加路由&#xff0c;无需去繁琐地修改路由配置&#…

就业相关(硕士)

一、嵌入式 1.机器人行业 1.1 大致情况 要做机器人行业&#xff0c;主要技术栈是运动控制、深度学习、强化学习、具身智能等&#xff0c;主要求职方向有运动控制算法工程师和机器人算法工程师等等。大致薪资在30w到50w不等&#xff0c;主要看方向&#xff08;双211&#xff…

C++编程:使用树莓派Pico制作光控小夜灯

在智能家居系统中,光控设备通过环境光强度的变化自动调节设备的状态,具有广泛的应用。常见的应用场景包括自动开关灯、调节LED亮度等。本项目基于树莓派Pico开发板,通过光敏电阻检测环境光强度,并利用PWM调光控制LED亮度,实现一个简单的光控小夜灯。本文将深入解析光敏电阻…

安卓获取所有可用摄像头并指定预览

在Android设备中&#xff0c;做预览拍照的需求的时候&#xff0c;我们会指定 CameraSelector DEFAULT_FRONT_CAMERA前置 或者后置CameraSelector DEFAULT_BACK_CAMERA 如果你使用的是平板或者工业平板&#xff0c;那么就会遇到多摄像头以及外置摄像头问题&#xff0c;简单的指…

回归任务与分类任务应用及评价指标

能源系统中的回归任务与分类任务应用及评价指标 一、回归任务应用1.1 能源系统中的回归任务应用1.1.1 能源消耗预测1.1.2 负荷预测1.1.3 电池健康状态估计&#xff08;SOH预测&#xff09;1.1.4 太阳能发电量预测1.1.5 风能发电量预测 1.2 回归任务中的评价指标1.2.1 RMSE&…

shilei定标算法,测试的时候为什么有多解

设定P&#xff0c;找C12和C13时&#xff0c;如果找的是实数&#xff0c;则求解的P只需要保证是实数就能满足螺旋度 0方程

【echarts】数据过多时可以左右滑动查看(可鼠标可滚动条)

1. 鼠标左右拖动 在和 series 同级的地方配置 dataZoom&#xff1a; dataZoom: [{type: inside, // inside 鼠标左右拖图表&#xff0c;滚轮缩放&#xff1b; slider 使用滑动条start: 0, // 左边的滑块位置&#xff0c;表示从 0 开始显示end: 60, // 右边的滑块位置&#xf…

redis集群 服务器更换ip,怎么办,怎么更换redis集群的ip

redis集群 服务器更换ip&#xff0c;怎么办&#xff0c;怎么更换redis集群的ip 1、安装redis三主三从集群2、正常状态的redis集群3、更改redis集群服务器的ip 重启服务器 集群会down4、更改redis集群服务器的ip 重启服务器 集群down的原因5、更改redis集群服务器的ip后&#xf…

计算机网络知识点全梳理(一.TCP/IP网络模型)

目录 TCP/IP网络模型概述 应用层 什么是应用层 应用层功能 应用层协议 传输层 什么是传输层 传输层功能 传输层协议 网络层 什么是网络层 网络层功能 网络层协议 数据链路层 什么是数据链路层 数据链路层功能 物理层 物理层的概念和功能 写在前面 本系列文…

【Python爬虫实战】深入解析 Scrapy 管道:数据清洗、验证与存储的实战指南

&#x1f308;个人主页&#xff1a;易辰君-CSDN博客 &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html ​ 目录 前言 一、了解 Scrapy Shell 二、配置文件 settings.py &#xff08;一&#xff09;为什么需要配置文件 &…

PHPstudy中的数据库启动不了

法一 netstat -ano |findstr "3306" 查看占用该端口的进程号 taskkill /f /pid 6720 杀死进程 法二 sc delete mysql

Hu矩原理 | cv2中基于Hu矩计算图像轮廓相似度差异的函数cv2.matchShapes【小白记笔记】

Hu 矩&#xff08;Hu Moments&#xff09; 是一种用于描述轮廓形状的 不变特征。它基于图像的矩提取&#xff0c;经过数学变换得到 7 个不变矩&#xff0c;这些不变矩在图像 平移、旋转和缩放等几何变换下保持不变&#xff0c;适合用来衡量轮廓或形状的相似度差异。 1、图像矩…

Ilya Sutskever发表了对AI未来发展的颠覆性看法

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

网络层IP协议(TCP)

IP协议&#xff1a; 在了解IP协议之前&#xff0c;我们市面上看到的"路由器"其实就是工作在网络层。如下图&#xff1a; 那么网络层中的IP协议究竟是如何发送数据包的呢&#xff1f; IP报头&#xff1a; IP协议的报头是比较复杂的&#xff0c;作为程序猿只需要我们重…

【MySQL】InnoDB引擎中的Compact行格式

目录 1、背景2、数据示例3、Compact解释【1】组成【2】头部信息【3】隐藏列【4】数据列 4、总结 1、背景 mysql中数据存储是存储引擎干的事&#xff0c;InnoDB存储引擎以页为单位存储数据&#xff0c;每个页的大小为16KB&#xff0c;平时我们操作数据库都是以行为单位进行增删…

Visual Studio 玩转 IntelliCode AI辅助开发

&#x1f380;&#x1f380;&#x1f380;【AI辅助编程系列】&#x1f380;&#x1f380;&#x1f380; Visual Studio 使用 GitHub Copilot 与 IntelliCode 辅助编码Visual Studio 安装和管理 GitHub CopilotVisual Studio 使用 GitHub Copilot 扩展Visual Studio 使用 GitHu…

【LDAP】LDAP概念和原理介绍

目录 一、前言 二、什么是LDAP&#xff1f; 2.1 什么是目录服务&#xff1f; 2.2 LDAP的介绍 2.3 为什么要使用LDAP 三、LDAP的主要产品线 四、LDAP的基本模型 4.1 目录树概念 4.2 LDAP常用关键字列表 4.3 objectClass介绍 五、JXplorer工具使用 一、前言 对于许多的…

用ue5打开网址链接

需要用到 Launch URL 这个函数 字面意思就是打开填写的链接网页 这里填写的是百度&#xff0c;按下Tab键后就会打开百度的网页

在ESP32使用AT指令集与服务器进行TCP/IP通信时,<link ID> 解释

在ESP32使用AT指令集与服务器进行TCP/IP通信时&#xff0c;<link ID> 是一个非常重要的参数。它用于标识不同的连接实例&#xff0c;特别是在多连接场景下&#xff08;如同时建立多个TCP或UDP连接&#xff09;。每个连接都有唯一的<link ID>&#xff0c;通过这个ID…