手摸手教你玩 Jenkins,一次搞懂 CI/CD!(第二章:发布之sshPublisher)

简介: 本文详解如何用Jenkins通过SSH执行远程部署脚本实现项目发布:涵盖插件安装(Git Parameter、Publish Over SSH)、目标服务器配置(专用用户、免密sudo)、SSH凭据与Git凭据设置,并提供完整Jenkinsfile示例,支持参数化构建与多分支发布。(239字)

上一章节,我讲到了如何通过 docker 去安装 Jenkins,并且在 Jenkins 中安装了一些常用的插件用于做项目部署的前期准备,这一篇文章,咱们就直接开始进行项目发布。

看这篇文章之前,首先需要确定你已经安装了以下插件:

  • Git Parameter:拉取源代码分支
  • Publish Over SSH:将我们打包好的代码推送到目标服务器上
  • AnsiColor:向控制台输出添加 ANSI 颜色(也可以不安装)

如果不清楚如何安装的,建议先查看 《手摸手教你玩 Jenkins,一次搞懂 CI/CD!(第一章:部署)》这篇文章。

一些约定

在讲解如何通过 Jenkins 发布项目之前,我默认你已经有了自己的 Git 项目仓库。这里我为了演示,假定我的 Git 项目仓库地址为: http://127.0.0.1:8099/demo/demo-project.git (实际上根本不存在此 Git 仓库)

前期准备

需要明确的一点是:虽然我们可以直接通过在 Jenkins 中编译项目,然后将编译后的成品发布到目标服务器上进行部署,但是我的做法是不建议这么干。因为这么干,就太依赖于 Jenkins 了。

我建议的做法是:直接在目标服务器上写好发布脚本,然后通过 Jenkins 去直接执行目标服务器上的发布脚本就好了。

这样的做法好处是,就算是哪天我不用 Jenkins,我也可以手动去目标服务器上执行发布脚本进行发布即可,并且对于发布复杂的项目来说,也方便自己调试发布脚本。

发布脚本

我们假设在目标服务器上存在 deploy.sh 脚本,这里为了演示方便,我就简单写了一个时间输出。

我们假设只要在 Jenkins 中执行了这个脚本,就能够在目标服务器上成功发布项目。当然,实际情况应该要根据自己的项目来决定。我这里仅仅为了最简单的演示。

deploy.sh 脚本文件中

#!/bin/bash

version=`date +%Y%m%d.%H%M`

echo '当前时间:'$version

独立的登录用户

既然是需要执行脚本来发布项目,那么,是不是需要有登录用户去执行才行呢?是的,总不能用 root 用户去执行吧?

这里我们需要在服务器上创建一个新的登录用户,这个登录用户就专门用户通过 ssh 登录,然后再去执行 deploy.sh 脚本。

# 添加用户名为 jenkinsjob 的用户
sudo useradd -m -s /bin/bash jenkinsjob

# 修改 jenkinsjob 用户的密码
sudo passwd jenkinsjob
# 比如密码就为 123456

# 查看用户是否建成功
cat /etc/passwd
# 如果建错了,可以删除
sudo userdel jenkinsjob

并且,还有一个问题十分重要:如果有些服务需要临时用到 root 权限,该怎么办?

我们都知道是可以通过在命令前面添加 sudo ,但是你是否想过当我们在执行命令前面添加 sudo 之后,我们也是需要在交互界面输入当前用户的密码的。可是我们是通过 Jenkins 去执行的 deploy.sh 脚本,我们压根儿就无法进入交互界面,那么,我们该怎么处理呢?

答案是:通过为登录用户添加免密环境。

给登录用户添加免密环境

我们以 jenkinsjob 用户为例,给他设置免密环境。这样当 jenkinsjob 用户执行 sudo 命令时,就不需要再输入自己的账号密码了。

编辑 sudoers 文件:

# 默认是用的 nano 编辑器
sudo visudo

# 或者通过 vim 编辑器(这里设置了临时通过 vim 编辑器去编辑)
sudo EDITOR=vim visudo

添加以下行

# jenkinsjob 为用户名
jenkinsjob ALL=(ALL) NOPASSWD: ALL

# 或者只为特定命令设置免密码
jenkinsjob ALL=(ALL) NOPASSWD: /usr/bin/apt, /bin/systemctl

编辑免密码环境之后,需要执行以下命令进行测试 sudoers 文件是否有错误

sudo visudo -c

开干

配置 ssh 登录用户

  1. 进入系统配置页面

系统配置

  1. 找到 Publish over SSH 下方的 SSH Servers 然后点击「新增」按钮

添加 ssh

然后设置 ssh 登录用户

  • Name:需要给新添加的 ssh server 设置一个名称。这个名称很重要,在 jenkinsfile 文件中会用到的
  • Hostname:目标服务器的 IP 地址
  • Username:登录目标服务器的账号名称
  • Remote Directory:登录目标服务器的操作目录

设置 ssh 登录用户

  1. 验证 ssh 连接是否生效

设置完了之后一定要点击 Test Configuration 进行验证,当出现了 Success 就表示已经设置成功了。

验证 ssh 生效

最后一定不要忘记点击 Save 按钮进行保存!

配置 Git 用户凭据

进入到凭据管理页面

凭据管理页面

我们会发现很难找到添加凭据的按钮

找不到添加凭据的按钮

其实添加凭据,一般都是通过 Jenkins UI 进行添加的,但是我演示的步骤不通过 Jenkins UI 来设置发布流程,我们通过编写 Jenkinsfile 来编写发布流程,所以需要通过直接访问下面的 URL Path 来进入添加凭据的页面。

添加凭据页面

可以直接通过访问 /manage/credentials/store/system/domain/_/ URL Path 来进入到 Add Credentials 页面。

添加新的凭据时,直接将用户名和密码就设置成自己 Git 服务器的账号和密码。

设置git账号凭据

设置凭据的时候,ID 选项,我们可以直接为空。

当我们设置好了账号和密码之后,点击「Create」

全局凭证页面

当我们进入到全局凭据页面后,我们会发现有一个 ID 项,请留意这里的 ID 这个在后面我们会用到的。

至此,我们所有的前期准备已经做好了,接下来,我们开始编写 jenkinsfile 文件。

编写 jenkinsfile

这里,我直接将我的 jenkinsfile 贴进来,以供各位看官老爷们参考:

#!/usr/bin/env groovy Jenkinsfile

pipeline {
   
    agent any

    options {
   
        ansiColor('xterm')
    }

    parameters {
   
        booleanParam(
            name: 'Select_demo_project',
            description: '可以自己填写一下备注,需要注意 name 字段中,最好不要出现横杠 -'
        )

        gitParameter(
            name: 'Git_Branch_demo_project',
            branch: '',
            branchFilter: 'demo_project/(.*)',
            defaultValue: 'master',
            quickFilterEnabled: true,
            selectedValue: 'NONE',
            sortMode: 'NONE',
            tagFilter: '*',
            type: 'PT_BRANCH_TAG',
            useRepository: 'demo-project',
            description: '需要注意 gitParameter.name 字段中,最好不要出现横杠 -'
        )
    }

    stages {
   

        stage('Step1:初始化,确认参数') {
   
            steps {
   
                catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
   
                    checkout([
                        $class: 'GitSCM',
                        branches: [[name: '*/master']],
                        extensions: [],
                        userRemoteConfigs: [[
                            credentialsId: '86b1ba1c-db25-47b2-b50e-e58516641574',
                            name: 'demo-project',
                            url: 'http://127.0.0.1:8099/demo/demo-project.git'
                        ]]
                    ])
                }
            }
        }

        stage('Step2:发布服务') {
   
            steps {
   
                script {
   
                    if (Select_demo_project == 'true') {
   
                        echo "----------------构建参数--------------------"
                        echo "git分支:${Git_Branch_demo_project} ---> Select_demo_project:${Select_demo_project}"

                        sshPublisher(
                            publishers: [
                                sshPublisherDesc(
                                    configName: 'target_ssh_jenkinsjob',
                                    transfers: [
                                        sshTransfer(
                                            cleanRemote: false,
                                            excludes: '',
                                            execCommand: '''
                                                cd /home/jenkinsjob/deploy_sample && \
                                                sudo ./deploy.sh
                                            ''',
                                            execTimeout: 1200000,
                                            flatten: false,
                                            makeEmptyDirs: false,
                                            noDefaultExcludes: false,
                                            patternSeparator: '[, ]+',
                                            remoteDirectory: '',
                                            remoteDirectorySDF: false,
                                            removePrefix: '',
                                            sourceFiles: ''
                                        )
                                    ],
                                    usePromotionTimestamp: false,
                                    useWorkspaceInPromotion: false,
                                    verbose: true
                                )
                            ]
                        )
                    }
                }
            }
        }

    }


}

通过 jenkinsfile 来添加 job

进入首页,添加 job

Create a job

然后输入任务名称,再点击流水线 (pipeline)

通过 Pipeline script 来添加任务

直接将页面滑到最下面,选择 Pipeline script 然后将 jenkinsfile 中的内容直接贴进来(其他的选项都保持默认就好了,仅仅粘贴 jenkinsfile 内容就好)

pipeline script

保存之后,点击立即构建

立即构建

当我们直接构建之后,可以看到构建日志可能报错了

查看构建日志

查看控制台输出内容

查看构建日志内容

其实,这是正常的。因为我提供的 jenkinsfile 中是需要传参数的,我们直接点击立即构建没有给脚本传参数,所以脚本无法执行,就报错了。

当我们再次回到构建页面时,我们可以看到立即构建变成了Build with Parameters

Build with Parameters

之所以在 jenkinsfile 中设定了需要传参,那是因为在实际开发中,我们一次性可能不止需要发某一个项目,可能我们需要同时多个项目一起发布,并且我们还需要发布不同的项目分支。

灵活的构建项目

当我们选择了我们需要发布的项目和分支之后,我们再次点击「Build」

选择需要发布的项目和分支

然后我们再查看发布日志,我们就可以看到已经登录到目标服务器上,并且已经执行了 deploy.sh 脚本

执行了 deploy.sh 脚本

至此,我们通过 sshPublisher 的方式去发布项目已经顺利成功了!

本篇文章为了方便小白用户更好的理解流程,截图较多。只要你跟着步骤一步一步的来,相信你也会很快掌握 Jenkins 的发布流程的。创作不易,希望可以帮忙点赞关注一下,感谢。

下一篇,我会继续讲解通过代理节点登录目标服务器,然后发布项目的步骤。敬请期待~

相关文章
|
4月前
|
运维 Java Nacos
Spring Cloud Alibaba + MSE Nacos:微服务注册发现与配置中心实战
Nacos 是 Spring Cloud Alibaba 生态的核心组件,承担服务注册发现与配置中心双重职责。阿里云 MSE(微服务引擎)提供了 Nacos 的全托管版本,免运维、高可用、与企业版功能增强。本文从电商微服务场景出发,实战演示 Nacos 自建集群 vs MSE Nacos 托管两种方案:服务注册发现、配置中心热更新、命名空间隔离、灰度发布,并给出详细的成本对比和选型建议。
Spring Cloud Alibaba + MSE Nacos:微服务注册发现与配置中心实战
|
3月前
|
运维 数据可视化 机器人
WhatsApp、LINE、Telegram 怎么选?2026 出海企业沟通指南
WhatsApp、LINE、Telegram虽同为聊天工具,但定位迥异:WhatsApp是全球企业消息基础设施,LINE深耕日台泰官方账号运营,Telegram则强于内容分发与大型社区管理。选型关键在目标市场、用户关系模式、内容类型及合规成本。
728 0
|
存储 关系型数据库 MySQL
Docker(五)进阶:Docker卷(volumes)
数据卷:设计用来持久化数据的,它的生命周期独立于容器,不会因为容器被删除后自动删除,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷。
2209 0
Docker(五)进阶:Docker卷(volumes)
|
2月前
|
缓存 运维 架构师
基于 RAG + LangChain + FastAPI 搭建生产级私有知识库问答系统(完整可运行)
本文以十年架构师视角,详解如何用RAG解决大模型落地三大痛点:知识滞后、私有文档不可读、幻觉。提供一套生产级、可运行、可扩展的FastAPI+LangChain+Chroma后端方案,含文档解析、智能分块、混合检索、重排、缓存与评估,代码经实测,开箱即用。(239字)
370 4
|
2月前
|
存储 监控 API
基于 RAG + LangChain 搭建企业级私有知识库问答系统(2026 实战版)
本文是作者基于多个企业RAG知识库落地经验的实战总结,提供完整可运行代码与十年避坑指南。涵盖文档解析、混合检索、向量存储、DeepSeek接入、结果重排、拒答机制及效果评估,助你构建本地可运行、生产可扩展的企业级私有知识库系统。(239字)
731 1
|
3月前
|
jenkins 持续交付 开发工具
手摸手教你玩 Jenkins,一次搞懂 CI/CD!(第一章:部署)
Jenkins 是开源CI/CD自动化工具,支持代码拉取、构建、测试、部署全流程。本文详解Docker一键部署(含国内镜像加速)、初始配置及必备插件(Git Parameter、Publish Over SSH),助开发者告别手动运维,实现高效持续集成与交付。(239字)
377 1
手摸手教你玩 Jenkins,一次搞懂 CI/CD!(第一章:部署)
|
2月前
|
存储 运维 监控
无人自助洗车平台:系统架构搭建与交易资金结算合规实践
无人自助洗车属于典型物联网撮合类业务,平台对接大量线下自助洗车设备网点,面向 C 端用户提供扫码洗车、套餐充值、次卡购买服务。业务链路涉及设备硬件、小程序前端、物联网网关、后端业务系统、多方资金结算。 平台营收来源包含单次洗车消费、套餐充值、会员次卡;资金需要拆分给设备加盟商、场地物业方、设备运维服务商以及平台自身佣金。很多初创团队优先完成设备联网、扫码启动、状态上报等业务功能,却忽略交易结算体系的设计。 早期业务规模小的时候,很多平台采用资金先进入平台账户,后期财务人工转账结算合作方。随着网点扩张、设备数量上涨,订单量爆发,就会暴露出二清风险、对账工作量巨大、结算错漏、资金流向不可追溯等问题
188 2
|
3月前
|
存储 人工智能 关系型数据库
千问背后的记忆引擎:PolarDB-X Mem0
阿里云PolarDB-X分布式数据库,作为夸克与千问APP“长记忆”核心底座,支撑流量暴涨100倍、峰值20万TPS,具备海量弹性扩展、统一向量+结构化存储、强一致高可用及全球低延迟能力。其推出的全托管服务Mem0,一体化融合语义与业务记忆,降本50%,助力AI实现长期、安全、有温度的连续交互。
356 0
千问背后的记忆引擎:PolarDB-X Mem0
|
3月前
|
JSON 前端开发 编译器
别再乱用了!var arr []int 和 arr := []int{} 给整懵了?不就声明个空数组,项目居然崩了?
本文深入剖析Go中`var arr []int`与`arr := []int{}`的本质区别:前者为nil切片(JSON序列化为`null`),后者为空切片(序列化为`[]`)。结合内存结构、JSON接口坑点、append行为及实战选型,帮你避开前端报错、提升代码健壮性。
118 0
别再乱用了!var arr []int 和 arr := []int{} 给整懵了?不就声明个空数组,项目居然崩了?