手摸手教你玩 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 的发布流程的。创作不易,希望可以帮忙点赞关注一下,感谢。

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

相关文章
|
28天前
|
JSON 前端开发 编译器
别再乱用了!var arr []int 和 arr := []int{} 给整懵了?不就声明个空数组,项目居然崩了?
本文深入剖析Go中`var arr []int`与`arr := []int{}`的本质区别:前者为nil切片(JSON序列化为`null`),后者为空切片(序列化为`[]`)。结合内存结构、JSON接口坑点、append行为及实战选型,帮你避开前端报错、提升代码健壮性。
75 0
别再乱用了!var arr []int 和 arr := []int{} 给整懵了?不就声明个空数组,项目居然崩了?
|
1月前
|
人工智能 开发框架 .NET
2026阿里云轻量服务器38元1年、9.9元1个月、199元1年抢购:配置解析与适用场景攻略
2026年阿里云轻量应用服务器推出重磅特惠活动,新用户可每日10点、15点抢购2核2G配置,峰值200M带宽、40G ESSD云盘仅38元/年,日均成本约0.1元;2核4G配置可选9.9元/首月或199元/年,峰值200M带宽、50G ESSD云盘不限流量。产品预装WordPress、宝塔面板等24种主流镜像,还支持OpenClaw、Hermes等AI专属镜像,可一键部署个人博客、企业官网、AI助理等应用。它集成域名解析、HTTPS部署、防火墙管理等一站式功能,无需复杂运维,2核2G配置可轻松承载日均数千访客,是个人开发者、学生和小微企业低成本上云的高性价比选择。
|
3月前
|
存储 Rust NoSQL
一条命令迁移,帮你实现 OpenClaw 与 Hermes Agent 记忆互通!
本文是基于阿里云 Tablestore 的 Agent 记忆共享实战指南:一条命令迁移 OpenClaw 记忆至 Hermes,通过统一 Tablestore 实例、应用 ID 与租户 ID,实现跨Agent(如龙虾与马)记忆自动互通、实时同步与语义检索,支持 CLI 管理与对话中直接调用,安全可靠,开箱即用。
3705 122
|
3月前
|
Rust Linux 开发者
再见 pip!Rust 写的 uv 正在把 Python 包管理按在地上摩擦
Python开发者最头疼的依赖管理与环境配置难题,终于有解!uv——Rust编写的超快包管理器,安装、解析、虚拟环境创建速度达pip的10-100倍。支持一键装Python版本、运行脚本、编译依赖,正重塑Python开发工作流。(239字)
366 1
|
3月前
|
域名解析 网络协议 Ubuntu
改了DNS还是不生效?Ubuntu 24.04的这个坑我帮你踩过了
Ubuntu 24.04因`systemd-resolved`接管DNS,手动改`/etc/resolv.conf`无效,导致Docker拉镜像失败、`ping: Temporary failure in name resolution`。本文详解通过修改`/etc/systemd/resolved.conf`、重建软链接并重启服务,彻底修复DNS解析问题。(239字)
688 7
改了DNS还是不生效?Ubuntu 24.04的这个坑我帮你踩过了
|
4月前
|
SQL JSON 缓存
别再用过时的地区数据了!闸北区都消失了,教你一次性搞定省市区同步更新!(附实战源码)
本文记录了更新地区表至最新行政区划的完整实践:从权威数据源获取2024年省市区数据,通过Python脚本实现新旧表(省-市-区三级)精准比对,支持新增、软删除及层级关系维护,并附详细代码与分步缓存策略,兼顾准确性、安全性和可追溯性。(239字)
305 5
|
4月前
|
人工智能 小程序 JavaScript
AI开发实战6、抄作业吧!我优化了N遍的go-zero项目AI协作规范文件,一字不差全给你
本文是“AI开发完整项目”系列终篇,详解如何为go-zero微服务自动生成并规范编写CLAUDE.md文档,涵盖项目架构、API规范、代码生成流程及最佳实践,并开源「时光账记」小程序全栈代码供参考学习。(239字)
584 6
AI开发实战6、抄作业吧!我优化了N遍的go-zero项目AI协作规范文件,一字不差全给你
|
4月前
|
人工智能 小程序 API
AI开发实战5、手摸手教学:如何用AI+go-zero,从数据库设计开始构建API
本文是AI开发实战系列第5篇,聚焦用Claude 3.5/Gemini等模型高效构建Go微服务后端。以开源记账小程序「时光账记」为例,详解如何通过AI辅助完成数据库建模、API契约定义与业务代码生成,强调“框架自建+AI填空”模式,兼顾效率与代码一致性。(239字)
359 1
AI开发实战5、手摸手教学:如何用AI+go-zero,从数据库设计开始构建API
|
2月前
|
编解码 NoSQL 安全
Celery 太重了?这可能是你一直在找的 asyncio 任务队列
arq是Python原生异步任务队列,基于asyncio与Redis Streams构建,轻量(仅依赖redis-py)、高性能、开箱即用。专为FastAPI等异步框架设计,告别Celery的复杂配置,实现高并发I/O任务的优雅调度。(239字)
348 0
|
2月前
|
缓存 Rust Linux
Python 统一大业:uv 如何整合 Pip、Pyenv 和 Venv?
Python包管理长期饱受速度慢、依赖冲突之苦。Rust编写的`uv`横空出世——集Python版本管理、虚拟环境创建、依赖安装与锁定于一体,速度比pip快10–100倍,真正实现“All-in-One”极速开发体验。(239字)
490 0

热门文章

最新文章