Jenkins 实战:Jenkins + habor + docker 自动化部署

实现一个基于 docker 的自动化部署。

image

首先,程序员将本地代码,git push 到远程 GitLab 服务器。
然后,Jenkins git pull 代码到 Jenkins 服务器,使用 maven 帮我们打成 jar 包,并用 docker build 成镜像。
紧接着 Jenkins 帮我们把镜像 push 到远程 harbor 镜像仓库。
最后,测试服务器从 harbor 上 docker pull 拉取镜像,docker run 运行成容器。

一、 Jenkins CICD
1、将 Dockerfile 拷贝到项目根路径下,并提交到远程 GitLab 仓库

image

2、新建 Jenkins Pipeline 流水线任务

Dashboard > 新建 Item

image

3、配置流水线框架

Dashboard > first-pipeline >Configuration

选择 Pipeline script 脚本方式

image

可以选择的流水线定义有两种:

Pipeline script:自己在下面编辑框内编写流水线脚本。

Pipeline script from SCM:从版本管理系统(如:Git)中拉取已经写好的 Jenkinsfile,实现流水线配置的可版本化。

在右侧下拉选项中,选择 Hello World,以此为基础,编写流水线脚本。

image


 在流水线语法 Tab 页,选择插件或者命令,可以帮助我们以配置的方式,来生成流水线脚本。

image

4、拉取代码

示例步骤选择 git:Git,填入代码仓库地址,点击添加凭证

image

 

 填入 gitlab 相关信息,点击添加

image

 下拉选择凭证,点击生成流水线脚本

image

复制生成的代码,粘贴到流水线编写框中

image

 5、构建 jar 包

构建 jar 包需要使用 maven,而 maven 是一种工具(tools),它的配置在

Dashboard > Manage Jenkins > Tools > Maven 安装里

Maven 我们在  实战1 已经配置过了,所以流水线中我们可以直接拿它的名字 maven386 来使用。

image

添加 "构建 jar 包" 阶段

查看代码
 pipeline {
    agent any
    tools {
        maven 'maven386' // 引用全局配置的 Maven 名称(需提前在“全局工具配置”中设置)
    }
    stages {
        stage('拉取代码') {
            steps {
                git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
                echo '拉取成功'
            }
        }
        stage('构建 jar 包') {
            steps {
                sh 'mvn clean package -DskipTests=true'
                echo '构建 jar 包成功'
            }
        }
    }
}

 保存【配置】并【立即构建】,可以在控制台输出中查看构建日志

image

 

 

    制品

构建成功后,默认会在 /root/.jenkins/workspace/jenkins-study/target 目录下,帮我们生成一个 jar 包,说明构建 jar 包成功。

image

    移动 jar 包并重命名

为了方便我们后续 docker build,我们移动并重命名一下 jar 包,让它跟 Dockerfile 在一个路径下

pipeline {
    agent any
    tools {
        maven 'maven386' // 引用全局配置的 Maven 名称(需提前在“全局工具配置”中设置)
    }
    environment {
    	HARBOR_PROJECT = "jenkins-study"  // Harbor 中的项目名
    }
    stages {
        stage('拉取代码') {
            steps {
                git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
                echo '拉取成功'
            }
        }
        stage('构建 jar 包') {
            steps {
                sh """
                      mvn clean package -DskipTests=true
                      cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
                """
                echo '构建 jar 包成功'
            }
        }
    }
}

`

environment:定义环境变量,environment 后续的模块就可以使用 ${KEY} 来获取 VALUE

""" xxxxx """:定义多行 Shell 命令,不同行 Shell 命令之间具有上下文关系

cp ./target/${HARBOR_PROJECT}*.jar ./app.jar:将 target 目录下的 jar 包拷贝到当前目录并重命名为 app.jar

 

 

 

保存【配置】并【立即构建】后,app.jar 跟 Dockerfile 在同一个路径下了:

image

6、构建镜像

pipeline {
    agent any
    tools {
        maven 'maven386' // 引用全局配置的 Maven 名称(需提前在“全局工具配置”中设置)
    }
    environment {
    	HARBOR_DOMAIN = "192.168.40.100"  // Harbor 域名/IP
        HARBOR_PROJECT = "jenkins-study"  // Harbor 中的项目名
	    IMAGE_NAME = "my-app"  // 镜像名称
        IMAGE_TAG = "${BUILD_NUMBER}"  // 镜像标签(用 Jenkins 构建号,确保唯一)
        FULL_IMAGE = "${HARBOR_DOMAIN}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}"  // 完整镜像地址
    }
    stages {
        stage('拉取代码') {
            steps {
                git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
                echo '拉取成功'
            }
        }
        stage('构建 jar 包') {
            steps {
                sh """
                      mvn clean package -DskipTests=true
                      cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
                """
                echo '构建 jar 包成功'
            }
        }
        stage('构建 docker 镜像') {
            steps {
                 // 假设项目根目录有 Dockerfile,执行构建
                sh """
                      echo "开始构建镜像:${FULL_IMAGE}"
                      docker build -t ${FULL_IMAGE} .
                """
                echo '构建 docker 镜像成功'
            }
        }
    }
}

 保存【配置】并【立即构建】后,在 jenkins98 服务器查看镜像:

image

7、推送镜像到 Harbor

要推送镜像到 Harbor,就要登录 Harbor,就需要 用户名、密码;用户名密码在 Jenkins 里面叫 凭据。

打开流水线语法 Tab 页, http://192.168.40.98:8080/job/jenkins-study/pipeline-syntax/ 示例步骤选择 withCredentials: Bind credentials to variables,新增

 image

新增一个用户名密码分开的绑定

image

定义用户名变量、密码变量、添加 Harbor 凭据

image

填入 Harbor 相关信息后点击添加

image

选择新建的Harbor 凭据,生成流水线脚本

 image

拷贝流水线脚本到我们的流水线代码中

pipeline {
    agent any
    tools {
        maven 'maven386' // 引用全局配置的 Maven 名称(需提前在“全局工具配置”中设置)
    }
    environment {
    	HARBOR_DOMAIN = "192.168.40.100"  // Harbor 域名/IP
        HARBOR_PROJECT = "jenkins-study"  // Harbor 中的项目名
	    IMAGE_NAME = "my-app"  // 镜像名称
        IMAGE_TAG = "${BUILD_NUMBER}"  // 镜像标签(用 Jenkins 构建号,确保唯一)
        FULL_IMAGE = "${HARBOR_DOMAIN}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}"  // 完整镜像地址
        CONTAINER_NAME = "jenkins-study"  // 部署的容器名
    }
    stages {
        stage('拉取代码') {
            steps {
                git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
                echo '拉取成功'
            }
        }
        stage('构建 jar 包') {
            steps {
                sh """
                      mvn clean package -DskipTests=true
                      cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
                """
                echo '构建 jar 包成功'
            }
        }
        stage('构建 docker 镜像') {
            steps {
                 // 假设项目根目录有 Dockerfile,执行构建
                sh """
                      echo "开始构建镜像:${FULL_IMAGE}"
                      docker build -t ${FULL_IMAGE} .
                """
                echo '构建 docker 镜像成功'
            }
        }
        stage('推送镜像到 Harbor') {
            steps {
                script {
                    // 使用 Harbor 凭证登录并推送
                    withCredentials([usernamePassword(
                        credentialsId: 'harbor-creds',
                        passwordVariable: 'HARBOR_PWD',
                        usernameVariable: 'HARBOR_USER'
                    )]) {
                        sh """
                            # 登录 Harbor(若为 HTTP,需配置 Docker  insecure-registries)
                            docker login ${HARBOR_DOMAIN} -u ${HARBOR_USER} -p ${HARBOR_PWD}
                            # 推送镜像
                            docker push ${FULL_IMAGE}
                            # 登出(可选)
                            docker logout ${HARBOR_DOMAIN}
                        """
                    }
                }
            }
        }
    }
}

 

Jenkins 流水线语法 script 脚本的作用:

    在 Jenkins 流水线(Pipeline)中,script 块是一个非常重要的语法元素,主要作用是在声明式流水线(Declarative Pipeline)中嵌入脚本式流水线(Scripted Pipeline)的语法,从而增强流水线的灵活性和可编程性。
    声明式流水线(以 pipeline { ... } 为标志)语法简洁、结构固定,适合定义标准化的流程(如 agent、stages、post 等固定结构),但对复杂逻辑(如循环、条件判断嵌套、动态生成步骤等)支持有限。
    script 块允许在声明式流水线中插入脚本式语法(类似 Groovy 脚本),实现复杂的流程控制。
    script 块可以在声明式流水线的 steps、post、environment 等部分中使用,用于在固定结构中插入自定义逻辑。
    script 块是声明式和脚本式流水线的 “桥梁”,但应避免过度使用(否则会失去声明式流水线的简洁性)。
    脚本式语法更灵活,但也更易出错(需自己处理异常、流程控制等),建议优先使用声明式的内置语法(如 when 条件),仅在必要时用 script 块扩展。
    上述代码其实也可以不使用 script 块,这里只是提供一种补充,让大家知道有这种语法功能。

保存【配置】并【立即构建】后,在 Harbor 服务器查看镜像:

image

8、部署到 Docker 服务器

我们要想在测试服务器运行 docker 命令,需要安装 SSH Publisher 插件。
1)安装 SSH Publisher 插件

Dashboard > Manage Jenkins > 插件管理 > Available plugins

image

安装完,返回首页

image

 

 

2)配置测试服务器信息

Dashboard > Manage Jenkins > System,新增测试服务器

image

 

填上测试服务器信息并保存

image

Test Configuration 测试连接

image

3)生成流水线脚本

在流水线语法 Tab 页

image

 拷贝流水线脚本到我们的流水线代码中

pipeline {
    agent any
    tools {
        maven 'maven386' // 引用全局配置的 Maven 名称(需提前在“全局工具配置”中设置)
    }
    environment {
    	HARBOR_DOMAIN = "192.168.40.100"  // Harbor 域名/IP
        HARBOR_PROJECT = "jenkins-study"  // Harbor 中的项目名
	    IMAGE_NAME = "my-app"  // 镜像名称
        IMAGE_TAG = "${BUILD_NUMBER}"  // 镜像标签(用 Jenkins 构建号,确保唯一)
        FULL_IMAGE = "${HARBOR_DOMAIN}/${HARBOR_PROJECT}/${IMAGE_NAME}:${IMAGE_TAG}"  // 完整镜像地址
        CONTAINER_NAME = "jenkins-study"  // 部署的容器名
    }
    stages {
        stage('拉取代码') {
            steps {
                git credentialsId: 'gitlab', url: 'http://192.168.40.99/study/jenkins-study.git'
                echo '拉取成功'
            }
        }
        stage('构建 jar 包') {
            steps {
                sh """
                      mvn clean package -DskipTests=true
                      cp ./target/${HARBOR_PROJECT}*.jar ./app.jar
                """
                echo '构建 jar 包成功'
            }
        }
        stage('构建 docker 镜像') {
            steps {
                 // 假设项目根目录有 Dockerfile,执行构建
                sh """
                      echo "开始构建镜像:${FULL_IMAGE}"
                      docker build -t ${FULL_IMAGE} .
                """
                echo '构建 docker 镜像成功'
            }
        }
        stage('推送镜像到 Harbor') {
            steps {
                script {
                    // 使用 Harbor 凭证登录并推送
                    withCredentials([usernamePassword(
                        credentialsId: 'harbor-creds',
                        passwordVariable: 'HARBOR_PWD',
                        usernameVariable: 'HARBOR_USER'
                    )]) {
                        sh """
                            # 登录 Harbor(若为 HTTP,需配置 Docker  insecure-registries)
                            docker login ${HARBOR_DOMAIN} -u ${HARBOR_USER} -p ${HARBOR_PWD}
                            # 推送镜像
                            docker push ${FULL_IMAGE}
                            # 登出(可选)
                            docker logout ${HARBOR_DOMAIN}
                        """
                    }
                }
            }
        }
        stage('部署到 Docker 服务器') {
            steps {
                script {
                    withCredentials([
                        usernamePassword(
                            credentialsId: 'harbor-creds',
                            passwordVariable: 'HARBOR_PWD',
                            usernameVariable: 'HARBOR_USER'
                        )
                    ]) {
                        // 通过 SSH 连接远程服务器,执行部署命令
                        sshPublisher(
                            publishers: [
                                sshPublisherDesc(
                                    configName: "test97",  // 对应 Publish Over SSH 配置的服务器名
                                    transfers: [
                                        sshTransfer(
                                            execCommand: """
                                                # 远程服务器登录 Harbor
                                                docker login ${HARBOR_DOMAIN} -u ${HARBOR_USER} -p ${HARBOR_PWD}
                                                # 停止并删除旧容器(若存在)
                                                if [ \$(docker ps -a -q -f name=${CONTAINER_NAME}) ]; then
                                                    docker stop ${CONTAINER_NAME} && docker rm ${CONTAINER_NAME}
                                                fi
                                                # 拉取最新镜像
                                                docker pull ${FULL_IMAGE}
                                                # 启动新容器(根据需求调整端口、环境变量等)
                                                docker run -d --name ${CONTAINER_NAME} -p 8088:8088 ${FULL_IMAGE}
                                                # 登出 Harbor
                                                docker logout ${HARBOR_DOMAIN}
                                            """,
                                            execTimeout: 120000  // 超时时间 2 分钟
                                        )
                                    ]
                                )
                            ]
                        )
                    }
                }
            }
        }
    }
}

 

docker ps -a -q -f name=${CONTAINER_NAME}) :

    docker ps:显示容器信息
    -a:all,显示所有容器信息,包括正在运行的和已停止的
    -q:quiet,只显示容器ID
    -f name=${CONTAINER_NAME}):filter 过滤容器名称=jenkins-study 的容器

整条命令的意思,是过滤出容器名称=jenkins-study 的容器,只返回容器ID

""" if [ \$() ] """ 中 \ 的作用:

    \$ 中的 \ 是转义字符,作用是保留 $ 符号本身,防止其被外层的解析器提前解析。
    在多行字符串内部,如果直接写 $(...),外层的解析器(比如 Python 解释器,或生成 shell 脚本的程序)可能会把 $ 当作变量或命令替换的标记,尝试提前解析 $(...) 的内容,导致不符合预期的结果。
    加上 \ 转义后,\$ 会被外层解析器当作普通的 $ 符号处理,最终生成的字符串中会保留 $(...) 结构。当这段字符串被作为 shell 脚本执行时,shell 会正常解析 $(...) 作为命令替换(即执行 docker ps ... 并获取结果)。

保存【配置】并【立即构建】后,在 test97 服务器查看:

[root@test97 ~]# docker images
REPOSITORY                            TAG       IMAGE ID       CREATED        SIZE
192.168.40.100/jenkins-study/my-app   5         3e575c84958d   16 hours ago   489MB
[root@test97 ~]# docker ps
CONTAINER ID   IMAGE                                    COMMAND               CREATED          STATUS          PORTS                                       NAMES
8afe1f6f176b   192.168.40.100/jenkins-study/my-app:5   "java -jar app.jar"   17 seconds ago   Up 16 seconds   0.0.0.0:8088->8088/tcp, :::8088->8088/tcp   jenkins-study

 

posted @ 2026-01-30 14:01  追梦$少年  阅读(3)  评论(0)    收藏  举报