一、环境说明
- 已部署组件 :
- GitLab CE 16.11.5
- JDK 17(Jenkins 依赖,已安装并配置环境变量)
- 目标:实现 "GitLab 代码提交 → Jenkins 自动构建 → 输出构建产物" 的完整流程
二、安装 Jenkins(基于 JDK 17)
Jenkins 需依赖 Java 运行,已部署 JDK 17 可直接使用。以下以 CentOS 7/8 为例:
1. 添加 Jenkins 官方仓库
bash
# 下载 Jenkins 仓库配置文件(适用于 RHEL/CentOS 系统)
sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo
# 导入仓库签名密钥(验证包完整性)
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
# 更新 YUM 缓存(使新仓库生效)
sudo yum clean all && sudo yum makecache fast
- 命令解释 :
wget
下载仓库配置,rpm --import
导入密钥确保包未被篡改,yum makecache
刷新本地缓存。
2. 安装 Jenkins及修改端口
bash
# 安装 Jenkins(自动处理依赖)
sudo yum install -y jenkins fontconfig
# 编辑 Jenkins 系统配置文件:
sudo vi /etc/sysconfig/jenkins
# 添加或修改如下参数
JENKINS_PORT="8090"
# 保存并退出
# Jenkins 的 systemd 服务文件可能硬编码了端口参数,需验证并修改
sudo cat /usr/lib/systemd/system/jenkins.service
# 找到 ExecStart 行,检查是否包含 --httpPort=8080,修改为8090
# 找到Environment行修改为8090
Environment="JENKINS_PORT=8090"
fontconfig
是 Jenkins 图形界面依赖的字体配置工具,必须安装否则可能出现界面乱码。- Jenkins默认8080,为防止其他默认8080端口冲突,指定其他端口,如8090
3. 启动 Jenkins 并配置开机自启
bash
# 启动 Jenkins 服务
sudo systemctl start jenkins
# 设置开机自启(避免服务器重启后需手动启动)
sudo systemctl enable jenkins
# 检查服务状态(确保状态为 active (running))
sudo systemctl status jenkins
启动jenkins服务时,若发生如下报错,则表示找不到有效的 Java 安装(failed to find a valid Java installation)
1.查找 Java 安装目录(以 OpenJDK 17 为例,通常路径为/usr/lib/jvm/java-17-openjdk
2.编辑Jenkins 的 systemd 服务文件:sudo vi /usr/lib/systemd/system/jenkins.service
bash
# 编辑 Jenkins 的 systemd 服务配置
sudo vi /usr/lib/systemd/system/jenkins.service
[Service]
# 新增以下两行,指定 JAVA_HOME 和 Java 可执行文件路径
Environment="JAVA_HOME=/usr/lib/jvm/jdk-17.0.12"
Environment="JAVA=/usr/lib/jvm/jdk-17.0.12/bin/java"
# 保存后退出
# 查看 JDK 目录权限(需确保 jenkins 用户有读/执行权限)
ls -ld /usr/lib/jvm/jdk-17.0.12
ls -l /usr/lib/jvm/jdk-17.0.12/bin/java
# 若权限不足,调整目录权限(示例:允许所有用户读/执行)
sudo chmod -R 755 /usr/lib/jvm/jdk-17.0.12
# 注册 JDK 17 到系统默认 Java 备选
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-17.0.12/bin/java 20000
sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk-17.0.12/bin/javac 20000
# 选择默认 Java(按提示输入 JDK 17 对应的序号,如1)
sudo update-alternatives --config java
# 重新加载 systemd 配置
sudo systemctl daemon-reload
# 重启 Jenkins 并检查状态
sudo systemctl restart jenkins

- 关键 :若状态为
failed
,查看日志排查:sudo journalctl -u jenkins -f
(常见原因:JDK 版本不兼容,需确认 JDK 17 已正确配置)。
4. 开放 Jenkins 端口(默认 8080)
bash
# 若启用防火墙,开放 8080 端口(Jenkins 默认端口)
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
# 重新加载防火墙规则(使配置生效)
sudo firewall-cmd --reload
# 验证端口是否开放
sudo firewall-cmd --list-ports | grep 8080
- 说明:若服务器使用云厂商防火墙(如阿里云安全组),需在控制台额外开放 8080 端口。
三、初始化 Jenkins 并安装核心插件
1. 首次登录 Jenkins
- 浏览器访问:
http://<服务器IP>:8080
(如http://192.168.1.100:8080
)。 - 获取初始管理员密码( Jenkins 自动生成,用于首次登录):
bash
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
2. 安装必备插件
登录后,选择 "安装推荐的插件"(基础插件),等待安装完成后,额外安装以下插件:
- 进入 Jenkins 首页 → 左侧 "Manage Jenkins" → "Plugins" → "Available plugins"。
- 搜索并勾选以下插件,点击 "Install without restart" :
GitLab Plugin
:核心插件,实现 GitLab 与 Jenkins 通信(如触发构建、获取代码)。Maven Integration
:若项目使用 Maven 构建(如 Spring Boot),用于执行mvn
命令。Generic Webhook Trigger
:灵活处理 GitLab Webhook 触发(兼容各种版本的 GitLab)。- Blue Ocean:用于简化持续交付管道(Pipeline)的创建、编辑和监控,提供可视化操作界面以提升团队生产力
- Generic Webhook Trigger Plugin:自动化构建触发、灵活的事件处理、减少手动干预、集成第三方服务等
- SSH Agent:通过SSH密钥实现远程服务器的身份验证和自动化操作,主要用于Pipeline脚本中批量执行SSH命令
四、配置 GitLab 访问凭据(Jenkins 拉取代码的权限)
Jenkins 需要通过凭据访问 GitLab 仓库,配置步骤:
- 进入 Jenkins 首页 → 右侧 "Manage Jenkins" → "Credentials" → "System" → "Global credentials" → "Add Credentials"。
- 选择凭据类型:"Username with password" ,填写:
- Username :GitLab 登录用户名(如
root
或有权限访问项目的用户)。 - Password:对应用户的 GitLab 密码。
- ID :自定义标识(如
gitlab-credentials
,后续配置会引用此 ID)。 - Description:可选描述(如 "GitLab 访问权限")。
- Username :GitLab 登录用户名(如
- 点击 "Create" 保存。
- 作用:此凭据用于 Jenkins 克隆 GitLab 仓库代码、获取分支信息等操作。
五、配置 GitLab 与 Jenkins 联动
- 进入 Jenkins 首页 → 右侧 "Manage Jenkins" → "System" →"GitLab",按照如图填写
- 往下继续找到"增加GitLab服务",点击增加,填写gitlab中生成的Access Tokens(勾选api)


六、创建多分支 Pipeline 任务(关联 GitLab 分支)
多分支 Pipeline 能自动识别 GitLab 中的 dev
/qa
/uat
/prod
分支,并为每个分支生成独立构建流水线,是多环境隔离的高效方案。
1. 新建多分支 Pipeline 任务
- 进入 Jenkins 主页 → 点击 **"新建任务"** → 输入任务名(如
multi-env-ci
)→ 选择 **"多分支 Pipeline"** → 点击 **"确定"**。
2. 配置 "分支源"(关联 GitLab 仓库与分支)
- 在 "Branch Sources" 区域,点击 **"Add source"** → 选择 **"GitLab Project"**,填写并保存:
- 服务器:会自动选择第五步中配置的Gitlab server
- 代码检查凭据:选择第四步中配置的凭据
- 所有者:与凭据的用户名一致
- **项目列表:**完成前面三步后,会显示下拉框选择gitlab服务中的代码项目,选择你要构建的项目
- 行为列表:"Branches to always include"中填写要构建的分支,多个用"|"分隔,如:dev|qa|uat|prod
七、配置 Jenkins 识别 Maven
Jenkins 需要明确 "使用哪个 Maven 安装",需在**全局工具配置(Tools)**中指定:
- 进入 Jenkins 网页端 → 点击左侧 "管理 Jenkins" → "全局工具配置"。
- 找到 "Maven" 区域 → 点击 "新增 Maven"。
- 配置 Maven 安装:
- 名称 :自定义(如
Maven-3.9.11
,后续 Pipeline 中需引用此名称)。 - MAVEN_HOME :填写服务器上 Maven 的安装路径(如
/usr/local/apache-maven-3.9.11
)。 - (可选)若要 Jenkins 自动安装,可选择 "自动安装" 并指定版本。
- 名称 :自定义(如
- 点击 "保存"。
后续在 pipeline
中通过 tools
指令指定 Maven:
Groovy
pipeline {
agent any
tools {
// 此处名称需与"全局工具配置"中设置的 Maven 名称一致
maven 'Maven-3.9.11'
}
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
}
}
插件或任务配置修改后,需重启 Jenkins 加载变更:
bash
# 系统服务方式(CentOS/Ubuntu)
sudo systemctl restart jenkins
# Docker 容器方式
docker restart <jenkins-container-name>
八、编写 Jenkinsfile
(定义多环境构建流程)
在 GitLab 项目根目录创建 Jenkinsfile
,通过分支名判断自动切换环境配置(示例为 Maven 项目 + SSH 部署):
步骤 1:为 jenkins 用户生成 SSH 密钥对
步骤 2:在 Jenkins 中添加 "各环境 SSH 凭据"
- 登录 Jenkins,进入凭证管理界面 :
- 点击左侧菜单 → "凭证" → "系统" → "全局凭证(unrestricted)"。
- 点击 **"添加凭证"**,选择凭据类型为 "SSH Username with private key"(生产部署常通过 SSH 连接服务器)。
- 填写配置项:
- Scope :选
Global
(表示凭据对所有 Jenkins 任务全局可用)。 - Username :服务器的登录用户名(如
root
或业务专用用户)。 - Private Key :选择
Enter directly
,然后粘贴各自环境服务器的 SSH 私钥内容 (需提前生成 SSH 密钥对,并将公钥放入生产服务器的~/.ssh/authorized_keys
文件中)。 - ID :自定义唯一 ID(如
prod-server-ssh(后续的
Jenkinsfile需要五个不同名凭据匹配各环境,详见
Jenkinsfile)
,后续 Jenkinsfile 需通过此 ID 引用)。 - Description:填写描述(如 "生产环境服务器 SSH 凭据",方便后续识别)。
- Scope :选
- 点击 **"确定"**,完成凭据添加。
步骤 3:在 Jenkinsfile 中引用生产环境凭据
假设需通过 SSH 将构建产物部署到生产服务器,Jenkinsfile 示例如下:
Groovy
pipeline {
agent any // 可指定特定节点(如 label: 'maven')
tools {
maven 'maven3.9.11'
}
environment {
// 环境变量:不同环境的服务器地址、部署路径(敏感信息用 Jenkins 凭据管理)
DEV_SERVER = "dev-server-ip" // 开发环境服务器IP,格式xxx.xxx.xxx.xxx
DEV_PORT = "22"
DEV_PATH = "/data/api/" // 开发环境部署路径
QA_SERVER = "qa-server-ip" // 测试环境服务器IP
QA_PORT = "22"
QA_PATH = "/data/api-qa/" // 测试环境部署路径
UAT_SERVER = "qa-server-ip" // UAT环境服务器IP
UAT_PORT = "22"
UAT_PATH = "/data/api-uat/" // UAT环境部署路径
// 1. 生产服务器主机名(直接指定,如 IP 或域名)
PROD_SERVER = "qa-server-ip"
// 2. 生产环境 SSH 凭据(存储私钥,用于免密登录)
PROD_SSH_CRED = credentials('prod-server-ssh')
PROD_PORT = "22"
PROD_PATH = "/data/api-prod/" // 生产环境部署路径
}
stages {
stage('拉取代码') {
steps {
checkout scm // 自动拉取当前分支代码
}
}
stage('编译打包') {
steps {
// 1. 调试:查看当前目录结构(确认代码拉取成功)
sh 'ls -l'
// 2. 执行 mvn 打包,保留详细日志(便于排查打包失败原因)
sh 'mvn clean package -DskipTests -X' // -X 开启调试日志,定位依赖/编译错误
// 3. 调试:确认 target 目录下是否有 jar 包
sh 'ls -l target/*.jar || echo "target 目录无 jar 包,打包失败"'
}
post {
success {
// 仅当 steps 全部成功(mvn 打包成功且有 jar 包),才执行 stash
stash includes: 'target/*.jar', name: 'app-jar'
echo "=== stash 成功:已保存 target/*.jar 到 'app-jar' ==="
}
failure {
// 若 steps 失败,打印提示(便于定位问题)
echo "=== 编译打包阶段失败,未执行 stash!请查看 mvn 日志 ==="
}
}
}
// 开发环境部署:仅 dev 分支触发
stage('部署到开发环境') {
when { branch 'dev' }
steps {
echo "部署分支 ${env.BRANCH_NAME} 到开发环境"
unstash 'app-jar' // 恢复构建产物
// SSH 部署(需在 Jenkins 配置 SSH 凭据,ID 为 dev-ssh-cred)
sshagent(['dev-ssh-cred']) {
sh """
whoami # 打印当前执行用户
echo "=== 调试:服务器=${DEV_SERVER},端口=${DEV_PORT},路径=${DEV_PATH} ==="
echo "当前 HOME 目录: \$HOME"
# 复制时重命名jar包,添加dev后缀
scp -P ${DEV_PORT} target/*.jar root@${DEV_SERVER}:${DEV_PATH}/project-name-dev.jar
# 执行deploy.sh时,指定jar包名
ssh -p ${DEV_PORT} root@${DEV_SERVER} 'cd ${DEV_PATH} && ./deploy.sh restart project-name-dev.jar'
"""
}
}
}
// 测试环境部署:仅 qa 分支触发
stage('部署到测试环境') {
when { branch 'qa' }
steps {
echo "部署分支 ${env.BRANCH_NAME} 到测试环境"
unstash 'app-jar'
// SSH 部署(需在 Jenkins 配置 SSH 凭据,ID 为 qa-ssh-cred)
sshagent(['qa-ssh-cred']) {
sh """
scp -P ${QA_PORT} target/*.jar root@${QA_SERVER}:${QA_PATH}/project-name-qa.jar
ssh -p ${QA_PORT} root@${QA_SERVER} 'cd ${QA_PATH} && ./deploy.sh restart project-name-qa.jar'
"""
}
}
}
// UAT 环境部署:仅 uat 分支触发
stage('部署到 UAT 环境') {
when { branch 'uat' }
steps {
echo "部署分支 ${env.BRANCH_NAME} 到 UAT 环境"
unstash 'app-jar'
// SSH 部署(需在 Jenkins 配置 SSH 凭据,ID 为 uat-ssh-cred)
sshagent(['uat-ssh-cred']) {
sh """
scp -P ${UAT_PORT} target/*.jar root@${UAT_SERVER}:${UAT_PATH}/project-name-uat.jar
ssh -p ${UAT_PORT} root@${UAT_SERVER} 'cd ${UAT_PATH} && ./deploy.sh restart project-name-uat.jar'
"""
}
}
}
// 生产环境部署:仅 prod 分支触发,需手动确认
stage('部署到生产环境') {
when { branch 'prod' }
steps {
input message: '确认部署到生产环境?', ok: '确认部署' // 手动确认步骤(降低风险)
echo "部署分支 ${env.BRANCH_NAME} 到生产环境"
unstash 'app-jar'
// SSH 部署(需在 Jenkins 配置 SSH 凭据,ID 为 prod-ssh-cred)
sshagent(['prod-ssh-cred']) {
// 第一行:生产部署前备份旧版本(可选)ssh root@${PROD_SERVER} 'cd ${PROD_PATH} && cp *.jar backup/$(date +%F)_app.jar'
sh """
# 先创建 backup 目录(-p 确保目录不存在时自动创建)
ssh -p ${PROD_PORT} root@${PROD_SERVER} 'cd ${PROD_PATH} && mkdir -p backup && cp *.jar backup/\$(date +%F)_app.jar'
scp -P ${PROD_PORT} target/*.jar root@${PROD_SERVER}:${PROD_PATH}
ssh -p ${PROD_PORT} root@${PROD_SERVER} 'cd ${PROD_PATH} && ./deploy.sh restart'
"""
}
}
}
}
post {
// 构建结果反馈到 GitLab(显示"成功/失败"状态徽章)
success {
updateGitlabCommitStatus name: env.BRANCH_NAME, state: 'success'
}
failure {
updateGitlabCommitStatus name: env.BRANCH_NAME, state: 'failed'
}
}
}
scp
与 ssh
的端口参数大小写完全不同,需严格区分(这是高频混淆点):
工具 | 端口参数 | 作用 | 错误用法 | 正确用法 |
---|---|---|---|---|
scp |
大写 -P |
指定 SSH 端口 | scp -p 22 源 目标 (小写 -p 被当作 "保留属性" 参数) |
scp -P 22 源 目标 |
ssh |
小写 -p |
指定 SSH 端口 | ssh -P 22 用户@主机 (大写 -P 是无效参数) |
ssh -p 22 用户@主机 |
九、触发 Jenkins 多分支项目的 "分支扫描"
Jenkins 不会实时检测 Git 仓库的分支变化,需要 ** 手动触发 "扫描"** 来发现新分支(如 qa
)。
- 进入 Jenkins 的多分支 Pipeline 项目页面
- 点击左侧菜单的 "Scan Multibranch Pipeline Now"(立即扫描多分支 Pipeline),触发 Jenkins 扫描远程 Git 仓库的分支。
- 在blue ocean中点击分支的运行

总结
通过多分支 Pipeline + Jenkinsfile
+ GitLab Plugin 的组合,可实现 dev
/qa
/uat
/prod
分支与对应环境的自动化持续集成。核心是利用分支过滤 、环境变量隔离、** 手动确认(生产环境)** 等机制,既提升开发效率,又降低生产部署风险。