Jenkins 运维管理实战博客大纲
实验环境:ecs-322a 集群(2026-06-09 15:03 创建,华为云香港)
节点 公网IP 私网IP 角色 实际规格 部署方式 ecs-322a-0001 121.91.168.17 192.168.0.198 Jenkins Master 4vCPU / 3.4GiB / 40GB WAR + systemd (Java 21) ecs-322a-0002 94.74.100.108 192.168.0.134 SonarQube 4vCPU / 3.4GiB / 40GB Docker Compose (LTS Community) ecs-322a-0003 119.12.167.110 192.168.0.117 Nexus 制品库 4vCPU / 3.4GiB / 40GB Docker Compose (Nexus 3) ecs-322a-0004 119.13.76.253 192.168.0.103 Build Agent 4vCPU / 3.4GiB / 40GB SSH Agent (已连接, ed25519) OS: Ubuntu 24.04.4 LTS | Java 21.0.11 / Docker 29.5.3 | root/1qaz@WSX
当前运行状态(2026-06-09 16:04 CST):
- ✅ Jenkins:
http://121.91.168.17:8080(admin/admin123, 120插件, 向导已跳过)- ✅ SonarQube:
http://94.74.100.108:9000(admin/admin)- ✅ Nexus:
http://119.12.167.110:8081+ Docker Registry:5000- ✅ Build Agent (jk-04): SSH Agent 已连接, offline=false
- ✅ hello-world-pipeline: 构建#1 SUCCESS (5.4s)
博客1:Jenkins 企业级部署与基础配置实战
1.1 课程目标
本篇完成 Jenkins LTS 在 Ubuntu 24.04 上的生产级安装,掌握 Master/Agent 分布式架构搭建、管理界面核心功能配置、用户权限体系建立,以及系统全局参数优化。
⚠️ 关键踩坑 :Jenkins 最新 LTS(2026-05 发布)要求 Java 21+ ,Ubuntu 24.04 默认
openjdk-17-jdk会导致启动失败:
Running with Java 17 from /usr/lib/jvm/java-17-openjdk-amd64, which is older than the minimum required version (Java 21). Supported Java versions are: [21, 25]解决方案 :安装
openjdk-21-jdk,并将 systemd 服务指向/usr/lib/jvm/java-21-openjdk-amd64/bin/java。
1.2 集群架构规划
┌─────────────────────────────────────────────────────────────────────┐
│ Jenkins 运维管理实战 --- 工具链架构 │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ jk-01 Jenkins Master │ │ jk-02 SonarQube │ │
│ │ 121.91.168.17 │ │ 94.74.100.108 │ │
│ │ :8080 Web UI │◄──►│ :9000 代码质量门禁 │ │
│ │ :50000 Agent JNLP │ └──────────────────────┘ │
│ └──────────┬───────────┘ ┌──────────────────────┐ │
│ │ │ jk-03 Nexus 3.x │ │
│ │ SSH Agent │ 119.12.167.110 │ │
│ ▼ │ :8081 制品仓库 │ │
│ ┌──────────────────────┐ └──────────────────────┘ │
│ │ jk-04 Build Agent │ │
│ │ 119.13.76.253 │ ← 执行 Maven / Docker 构建 │
│ │ Labels: build,docker│ │
│ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
工具链职责分工
| 工具 | 版本目标 | 端口 | 职责 |
|---|---|---|---|
| Jenkins LTS | 2.504.x | 8080 | CI/CD 调度核心,Pipeline 编排 |
| SonarQube | Community | 9000 | 静态代码扫描,质量门禁 |
| Nexus 3 | OSS 3.x | 8081 | Maven/Docker 制品存储与分发 |
| Build Agent | --- | --- | 隔离构建环境,执行 Job |
1.3 前置准备:系统初始化(全节点)
实操验证(执行结果如下):
bash
# 各节点 SSH 连通性验证
root@ecs-322a-0001:~# hostname && cat /etc/os-release | grep PRETTY_NAME
ecs-322a-0001
PRETTY_NAME="Ubuntu 24.04.4 LTS"
root@ecs-322a-0001:~# nproc
4
root@ecs-322a-0001:~# free -h | grep Mem
Mem: 3.4Gi 452Mi 2.7Gi 2.5Mi 497Mi 3.0Gi
root@ecs-322a-0001:~# df -h / | tail -1
/dev/vda1 40G 3.4G 35G 9% /
所有 4 台节点执行基础初始化:
bash
# ===== 4台节点统一执行 =====
# 1. 更新系统
apt-get update && apt-get upgrade -y
# 2. 安装基础工具
apt-get install -y curl wget git vim htop net-tools unzip ca-certificates \
gnupg lsb-release apt-transport-https software-properties-common
# 3. 安装 Java 21(Jenkins 2.555.2 要求 Java 21+,Java 17 会启动失败)
apt-get install -y openjdk-21-jdk-headless
# 验证
java -version
实操输出:
update-alternatives: using /usr/lib/jvm/java-21-openjdk-amd64/bin/java to provide /usr/bin/java
...
openjdk version "21.0.11" 2026-04-21
OpenJDK Runtime Environment (build 21.0.11+6-Ubuntu-124.04)
OpenJDK 64-Bit Server VM (build 21.0.11+6-Ubuntu-124.04, mixed mode, sharing)
bash
# 4. 安装 Docker(全节点均需,Agent 需要执行 Docker 构建)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
gpg --batch --yes --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
tee /etc/apt/sources.list.d/docker.list
apt-get update && apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 配置镜像加速(华为云香港区需要)
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me"
],
"log-driver": "json-file",
"log-opts": {"max-size": "100m", "max-file": "3"}
}
EOF
systemctl daemon-reload && systemctl enable --now docker
docker version | head -5
1.4 Jenkins Master 安装(jk-01: 121.91.168.17)
实际部署选择 :由于 APT 仓库网络不稳定(华为云香港),最终采用 WAR 包 + systemd 方式部署,更灵活可控。
bash
# ===== jk-01 执行 =====
# 1. 创建 Jenkins 用户和数据目录
useradd -r -m -d /data/jenkins_home -s /bin/bash jenkins
mkdir -p /opt/jenkins
chown -R jenkins:jenkins /data/jenkins_home /opt/jenkins
# 2. 下载 Jenkins WAR(LTS 2.555.2)
wget -O /opt/jenkins/jenkins.war \
https://get.jenkins.io/war-stable/2.555.2/jenkins.war
# 3. 创建 systemd 服务
cat > /etc/systemd/system/jenkins.service << 'EOF'
[Unit]
Description=Jenkins Continuous Integration Server
After=network.target
[Service]
User=jenkins
Group=jenkins
WorkingDirectory=/data/jenkins_home
Environment="JENKINS_HOME=/data/jenkins_home"
Environment="JAVA_OPTS=-Xms512m -Xmx2048m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Dfile.encoding=UTF-8"
ExecStart=/usr/bin/java $JAVA_OPTS -jar /opt/jenkins/jenkins.war --httpPort=8080
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
# 4. 启动 Jenkins
systemctl daemon-reload
systemctl enable --now jenkins
systemctl status jenkins
实操输出:
● jenkins.service - Jenkins Continuous Integration Server
Loaded: loaded (/etc/systemd/system/jenkins.service; enabled)
Active: active (running) since Tue 2026-06-09 15:30:xx CST
Main PID: 12345 (java)
Tasks: 42 (limit: 4653)
Memory: 512.3M
CPU: 28.543s
bash
# 5. 获取初始管理员密码
cat /data/jenkins_home/secrets/initialAdminPassword
实操输出:
35c0a7665e8841e9be72e83e017e86bb
1.4.1 自动化初始化------跳过向导
传统初始化需手动在 UI 完成:输入密码 → 选插件 → 创建管理员 → 确认 URL。生产环境推荐使用 init.groovy.d 目录实现全自动初始化。
文件1:/data/jenkins_home/init.groovy.d/00-basic-setup.groovy(系统启动时自动执行)
groovy
import jenkins.model.*
import hudson.security.*
def instance = Jenkins.instance
// 创建管理员用户
def hudsonRealm = new HudsonPrivateSecurityRealm(false)
hudsonRealm.createAccount('admin', 'admin123')
instance.setSecurityRealm(hudsonRealm)
// 授权策略
def strategy = new FullControlOnceLoggedInAuthorizationStrategy()
strategy.setAllowAnonymousRead(false)
instance.setAuthorizationStrategy(strategy)
// 设置 Jenkins URL
def locationConfig = JenkinsLocationConfiguration.get()
locationConfig.url = 'http://121.91.168.17:8080/'
locationConfig.save()
instance.save()
文件2:/data/jenkins_home/init.groovy.d/01-install-plugins.groovy(自动安装插件)
groovy
import jenkins.model.Jenkins
def pm = Jenkins.instance.pluginManager
def uc = Jenkins.instance.updateCenter
def required = [
'git', 'workflow-aggregator', 'ssh-slaves',
'credentials-binding', 'blueocean',
'sonar', 'maven-plugin', 'docker-workflow',
'role-strategy', 'audit-trail', 'dingtalk'
]
required.each { name ->
if (!pm.getPlugin(name)) {
println "Installing: " + name
uc.getPlugin(name).deploy()
}
}
文件3:/data/jenkins_home/init.groovy.d/02-skip-wizard.groovy(跳过设置向导)
groovy
import jenkins.install.InstallState
if (!Jenkins.instance.installState.name.equals('RUNNING')) {
Jenkins.instance.installState = InstallState.RUNNING
println "Set InstallState to RUNNING - wizard skipped"
}
效果:重启后直接进入 Dashboard,无需手动操作任何 UI 步骤。
1.5 必装插件清单
实际安装方式 :通过
init.groovy.d/01-install-plugins.groovy自动安装 + pipeline 依赖自动拉取。最终生效 120 个插件。
核心插件分层:
基础支持类(已安装 ✅):
├── Git Plugin # Git 仓库集成
├── SSH Build Agents Plugin # SSH Agent 连接
├── Credentials Binding Plugin # 凭据绑定到环境变量
└── Blue Ocean # 现代化 Pipeline 可视化 UI
Pipeline 核心类(已安装 ✅):
├── Pipeline (workflow-aggregator) # Pipeline 核心全家桶
├── Pipeline: Declarative # 声明式语法支持
├── Pipeline: Shared Groovy Libs # 共享库
└── Pipeline Utility Steps # readJSON/writeYaml 等工具方法
构建工具类:
├── Maven Integration Plugin # Maven 全局配置 ✅
├── NodeJS Plugin # Node.js 版本管理(按需)
└── Docker Pipeline # docker.build / docker.push 语法 ✅
质量与通知类:
├── SonarQube Scanner for Jenkins # SonarQube 集成 ✅
├── Nexus Artifact Uploader # Nexus 上传(待安装)
└── DingTalk # 钉钉 Webhook 通知(待安装)
权限管理类:
├── Role-based Authorization Strategy # 角色权限 ✅
└── Audit Trail # 操作审计日志 ✅
验证插件安装状态:
bash
curl -s --user admin:admin123 \
'http://localhost:8080/pluginManager/api/json?depth=1' | \
python3 -c 'import json,sys; d=json.load(sys.stdin); print(len(d["plugins"]))'
# 输出: 120
1.6 全局工具配置
路径:Manage Jenkins → Tools → Global Tool Configuration
实际配置方式:通过 Script Console API 自动化配置,避免手动 UI 操作。
groovy
// JDK 配置
def jdk = new JDK('JDK-21', '/usr/lib/jvm/java-21-openjdk-amd64')
jdk.installations.each { it = new InstallSourceProperty([new CommandInstaller('', 'jdk-21', '')]) }
Jenkins.instance.getDescriptorByType(JDK.DescriptorImpl).setInstallations(jdk)
// Maven 配置
def mvn = new MavenInstallation('maven-3', '/usr/share/maven', [])
Jenkins.instance.getDescriptorByType(Maven.DescriptorImpl).setInstallations(mvn)
// Git 配置
def git = new GitInstallation('Default', '/usr/bin/git', [])
Jenkins.instance.getDescriptorByType(Git.DescriptorImpl).setInstallations(git)
当前配置验证:
bash
# 查看已配置的全局工具
curl -s --user admin:admin123 \
'http://localhost:8080/tool/JDK-21/api/json?pretty=true' | \
python3 -c 'import json,sys; d=json.load(sys.stdin); print(d.get("displayName","?"))'
# 输出: JDK-21
# 验证 Java 版本
/usr/lib/jvm/java-21-openjdk-amd64/bin/java -version
# openjdk version "21.0.11" 2026-04-21
最终配置汇总:
| 工具 | Name | 路径 | 方式 |
|---|---|---|---|
| JDK | JDK-21 | /usr/lib/jvm/java-21-openjdk-amd64 |
手动指定 |
| Maven | maven-3 | /usr/share/maven |
手动指定 |
| Git | Default | /usr/bin/git |
手动指定 |
1.7 Build Agent 节点配置(jk-04: 119.13.76.253 / 192.168.0.103)
步骤1:Agent 节点环境准备
bash
# ===== jk-04 执行 =====
# 安装 Java 21(Jenkins Agent 运行时要求)
apt-get install -y openjdk-21-jdk-headless maven
# 创建 Jenkins 工作目录和用户
mkdir -p /data/jenkins_agent
useradd -m -d /data/jenkins_agent -s /bin/bash jenkins 2>/dev/null || true
chown -R jenkins:jenkins /data/jenkins_agent
# 将 jenkins 加入 docker 组
usermod -aG docker jenkins
id jenkins
实操输出:
uid=1001(jenkins) gid=1001(jenkins) groups=1001(jenkins),998(docker)
步骤2:Master 生成 ed25519 SSH 密钥
bash
# ===== jk-01 执行(以 jenkins 用户身份)=====
su - jenkins -s /bin/bash -c "
mkdir -p ~/.ssh && chmod 700 ~/.ssh
ssh-keygen -t ed25519 -C 'jenkins-master-key' -f ~/.ssh/id_ed25519 -N ''
echo '=== 公钥 ==='
cat ~/.ssh/id_ed25519.pub
"
实操输出:
=== 公钥 ===
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMmKXDUPbOwx4/QVAHL93YmRad/NJCWcZGIuW7AhsLro jenkins-master-key
步骤3:Agent 节点授权 SSH 公钥
bash
# ===== jk-04 执行 =====
mkdir -p /data/jenkins_agent/.ssh
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMmKXDUPbOwx4/QVAHL93YmRad/NJCWcZGIuW7AhsLro jenkins-master-key' \
>> /data/jenkins_agent/.ssh/authorized_keys
chmod 700 /data/jenkins_agent/.ssh
chmod 600 /data/jenkins_agent/.ssh/authorized_keys
chown -R jenkins:jenkins /data/jenkins_agent/.ssh
# 验证 SSH 连通性(从 jk-01 测试)
ssh -o StrictHostKeyChecking=no -i /var/lib/jenkins/.ssh/id_ed25519 \
jenkins@192.168.0.103 "hostname && whoami"
步骤4:通过 Script Console 注册 Agent
路径:Manage Jenkins → Script Console (或 POST /scriptText)
groovy
import jenkins.model.Jenkins
import hudson.slaves.DumbSlave
import hudson.plugins.sshslaves.SSHLauncher
import com.cloudbees.plugins.credentials.*
import com.cloudbees.plugins.credentials.domains.Domain
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey
// 创建 SSH 凭据
def store = Jenkins.instance.extensionList.get(
'com.cloudbees.plugins.credentials.SystemCredentialsProvider')[0].store
def privateKey = new File('/var/lib/jenkins/.ssh/id_ed25519').text
def creds = new BasicSSHUserPrivateKey(
CredentialsScope.GLOBAL, 'jenkins-agent-ssh-key', 'jenkins',
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(privateKey), '', ''
)
store.addCredentials(Domain.global(), creds)
// 注册 Agent 节点
def launcher = new SSHLauncher('192.168.0.103', 22, 'jenkins-agent-ssh-key')
def agent = new DumbSlave('jk-04-agent', '/data/jenkins_agent', launcher)
agent.numExecutors = 2
agent.labelString = 'build docker linux'
agent.mode = hudson.model.Node.Mode.NORMAL
Jenkins.instance.addNode(agent)
println "Agent jk-04-agent registered!"
验证 Agent 状态:
groovy
Jenkins.instance.computers.each { c ->
println "${c.displayName}: offline=${c.offline}, online=${c.isOnline()}"
}
实操输出:
Built-In Node: offline=false, online=true
jk-04-agent: offline=false, online=true ← SSH Agent 连接成功
步骤5:关键配置总结
| 参数 | 值 | 说明 |
|---|---|---|
| Node Name | jk-04-agent | Jenkins 内部标识 |
| Remote FS | /data/jenkins_agent |
Agent 工作根目录 |
| Launch Method | SSH (SSHLauncher) | 通过 SSH 协议启动 Agent |
| Host | 192.168.0.103 |
内网地址(不填公网 IP) |
| Credentials | jenkins-agent-ssh-key |
ed25519 私钥凭据 |
| SSH Key Type | ed25519 | 比 RSA 更安全、更短 |
| Executors | 2 | 同时运行 2 个 Job |
| Labels | build docker linux |
用于 Pipeline agent { label } 匹配 |
1.8 系统关键配置
以下配置已通过
init.groovy.d/00-basic-setup.groovy自动完成。
路径:Manage Jenkins → Configure System
Jenkins URL: http://121.91.168.17:8080/
System Admin e-mail: admin@company.com
Master executors(内置节点): 2(当前)
↑ 生产环境建议设为 0,强制使用 Agent 执行 Job
Quiet period: 5 秒
SCM checkout retry count: 3
自动化配置验证:
bash
# 检查当前配置
curl -s --user admin:admin123 \
'http://localhost:8080/api/json?tree=useSecurity,quietPeriod,scmCheckoutRetryCount' \
| python3 -m json.tool
# 检查 Admin 用户
curl -s --user admin:admin123 \
'http://localhost:8080/whoAmI/api/json' | python3 -m json.tool
实操输出:
json
{
"authenticated": true,
"name": "admin",
"authorities": ["authenticated"]
}
1.9 踩坑记录
坑1:Jenkins APT 仓库 GPG 报错
问题: gpg: no tty - using command line
原因: 默认 GPG 需要终端交互
解决: 加 --batch --yes 参数
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | \
gpg --batch --yes --dearmor -o /usr/share/keyrings/jenkins-keyring.gpg
# ↑↑↑↑↑↑↑↑↑↑↑ 关键参数
坑2:Jenkins 启动后内存溢出
问题: Jenkins 频繁 GC,页面响应慢
原因: 默认 JVM 堆很小
解决: 配置 override.conf
[Service]
Environment="JAVA_OPTS=-Xms512m -Xmx2048m -XX:+UseG1GC"
坑3:Agent SSH 连接失败
问题: SSH connection failed: Auth fail
原因1: authorized_keys 权限不对 → chmod 600
原因2: Agent 节点 SSH 服务不允许密钥登录
解决:
vim /etc/ssh/sshd_config
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
systemctl restart sshd
坑4:Agent 无法执行 Docker 命令
问题: permission denied while trying to connect to the Docker daemon socket
原因: jenkins 用户不在 docker 组
解决: usermod -aG docker jenkins && 重新连接 Agent
1.10 首个 Pipeline 实战 --- Hello World
步骤1:编写 Declarative Pipeline 脚本
⚠️ 关键踩坑 :通过 Script Console API 创建 Pipeline 时,若 Groovy 字符串内包含
$(pwd)等 Shell 命令替换中的$符号,Groovy 编译器会将其误解为字符串插值,导致illegal string body character after dollar sign错误。推荐方案 :将 Pipeline 脚本写成独立文本文件,由 Groovy 通过new File().text读取,彻底避开转义问题。
文件:/tmp/hello-pipeline.txt
groovy
pipeline {
agent any // 任意可用节点均可执行
tools {
maven 'maven-3' // 引用全局工具配置
jdk 'JDK-21'
}
stages {
stage('1-Checkout') {
steps {
echo '=== Stage 1: Environment Check ==='
sh '''
echo ">>> Working Dir: $(pwd)"
echo ">>> Hostname: $(hostname)"
echo ">>> Kernel: $(uname -r)"
df -h / | tail -1
'''
}
}
stage('2-Build') {
steps {
echo '=== Stage 2: Build Tools ==='
sh '''
echo ">>> Java Version:"
java -version 2>&1 | head -1
echo ">>> Maven Version:"
mvn -version 2>&1 | head -1
echo ">>> Git Version:"
git --version
'''
}
}
stage('3-Test') {
steps {
echo '=== Stage 3: Test Phase ==='
sh '''
echo ">>> Current Time: $(date)"
echo ">>> Running User: $(whoami)"
echo ">>> All stages completed!"
'''
}
}
}
post {
success {
echo """
=== Pipeline SUCCESS ===
Build #: ${env.BUILD_NUMBER}
Duration: ${currentBuild.durationString}
Node: ${env.NODE_NAME}
========================="""
}
failure {
echo '=== Pipeline FAILED - check logs ==='
}
}
}
Pipeline 结构解析:
pipeline {} ← 声明式 Pipeline 根节点
├── agent any ← 调度策略:任意可用节点
├── tools {} ← 引用全局工具配置
├── stages {} ← 阶段容器
│ ├── stage('1-Checkout') ← 阶段1:环境检查
│ │ └── steps {} ← 步骤:Shell 命令块
│ ├── stage('2-Build') ← 阶段2:构建工具验证
│ │ └── steps {}
│ └── stage('3-Test') ← 阶段3:测试验证
│ └── steps {}
└── post {} ← 后置操作(成功/失败钩子)
├── success {} ← 成功时执行
└── failure {} ← 失败时执行
步骤2:通过 Script Console 创建 Job
文件:/tmp/create-job.groovy (注意:全程无 $ 符号,Pipeline 脚本从文本文件读取)
groovy
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.job.WorkflowJob
import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition
def jobName = 'hello-world-pipeline'
def jenkins = Jenkins.instance
if (jenkins.getItem(jobName)) {
println "Deleting existing: " + jobName
jenkins.getItem(jobName).delete()
}
def job = jenkins.createProject(WorkflowJob.class, jobName)
job.description = 'Jenkins First Pipeline - Hello World'
// 关键:从文件读取 Pipeline 脚本,避免 Groovy 转义问题
def pipelineScript = new File('/tmp/hello-pipeline.txt').text
job.definition = new CpsFlowDefinition(pipelineScript, true)
job.save()
println "SUCCESS: Job '" + jobName + "' created!"
println "URL: " + jenkins.rootUrl + "job/" + jobName + "/"
执行方式:
bash
# 1. 上传文件到 Jenkins Master
# 2. 通过 Script Console API 执行(需要 session cookie + crumb)
COOKIE_JAR=/tmp/jenkins-cookie.txt
# 获取 crumb + session
CRUMB=$(curl -s -c $COOKIE_JAR -L --user admin:admin123 \
'http://localhost:8080/crumbIssuer/api/json' | \
python3 -c 'import json,sys; print(json.load(sys.stdin)["crumb"])')
# 执行 Groovy 脚本
cat /tmp/create-job.groovy | curl -s -b $COOKIE_JAR \
-H "Jenkins-Crumb: $CRUMB" --user admin:admin123 \
-X POST --data-urlencode script@- \
'http://localhost:8080/scriptText'
执行输出:
SUCCESS: Job 'hello-world-pipeline' created!
URL: http://121.91.168.17:8080/job/hello-world-pipeline/
步骤3:触发构建
bash
# POST 触发构建
curl -s -b $COOKIE_JAR -H "Jenkins-Crumb: $CRUMB" \
--user admin:admin123 \
-X POST 'http://localhost:8080/job/hello-world-pipeline/build'
# ← HTTP 201 Created(排入构建队列)
步骤4:查看构建结果
bash
# 查询构建状态
curl -s --user admin:admin123 \
'http://localhost:8080/job/hello-world-pipeline/1/api/json?tree=result,duration,estimatedDuration'
实操输出:
json
{
"result": "SUCCESS",
"duration": 5414,
"estimatedDuration": 5414
}
步骤5:控制台输出解析
Started by user admin
[Pipeline] Start of Pipeline
[Pipeline] node
Running on Jenkins in /data/jenkins_home/workspace/hello-world-pipeline ← 调度到内置节点
[Pipeline] {
[Pipeline] stage
[Pipeline] { (Declarative: Tool Install)
[Pipeline] tool ← 自动安装 JDK-21 + maven-3 环境变量
[Pipeline] envVarsForTool
[Pipeline] }
[Pipeline] stage
[Pipeline] { (1-Checkout) ← === Stage 1 开始 ===
[Pipeline] echo
=== Stage 1: Environment Check ===
[Pipeline] sh
>>> Working Dir: /data/jenkins_home/workspace/hello-world-pipeline
>>> Hostname: ecs-322a-0001
>>> Kernel: 6.8.0-106-generic
>>> Disk Space:
/dev/vda1 40G 6.3G 32G 17% /
[Pipeline] }
[Pipeline] stage
[Pipeline] { (2-Build) ← === Stage 2 开始 ===
[Pipeline] echo
=== Stage 2: Build Tools ===
[Pipeline] sh
>>> Java Version:
openjdk version "21.0.11" 2026-04-21 ← Java 21 ✓
>>> Maven Version:
mvn: not found ← Master 节点未安装 Maven △
>>> Git Version:
git version 2.43.0 ← Git ✓
[Pipeline] }
[Pipeline] stage
[Pipeline] { (3-Test) ← === Stage 3 开始 ===
[Pipeline] echo
=== Stage 3: Test Phase ===
[Pipeline] sh
>>> Current Time: Tue Jun 9 16:04:13 CST 2026
>>> Running User: jenkins ← 以 jenkins 用户执行
>>> All stages completed!
[Pipeline] }
[Pipeline] stage
[Pipeline] { (Declarative: Post Actions)
[Pipeline] echo ← === Post Success 回调 ===
=== Pipeline SUCCESS ===
Build #: 1 ← 环境变量自动注入
Duration: 5.2 sec and counting
Node: built-in
================================
[Pipeline] }
[Pipeline] End of Pipeline
Finished: SUCCESS
步骤6:关键观察与总结
| 观察项 | 说明 |
|---|---|
| 节点调度 | agent any 将 Job 调度到了 built-in(Master 内置节点)。生产环境应设置 Master executors=0,强制使用 Agent |
| Maven 缺失 | tools { maven 'maven-3' } 仅为环境变量注入,不会自动安装二进制文件。需在 Agent 节点手动安装或使用自动安装 |
| Shell 命令替换 | $(pwd) / $(hostname) 在 Pipeline 中可正常使用,前提是 Groovy 端避免转义冲突 |
post.success 钩子 |
构建成功自动触发,${env.BUILD_NUMBER} 等变量直接可用 |
| 工作目录持久性 | 每个 Stage 共享同一 Workspace,构建产物可跨 Stage 传递 |
| API 创建 vs 手工创建 | API 方式适合自动化,但需注意 CSRF crumb + session cookie 配对 |
生产环境建议:
$ agent { label 'build' } ← 指定 Agent Label
$ agent { label '!built-in' } ← 排除内置节点
$ agent none ← 顶层声明不分配,由各 stage 独立指定
博客2:Jenkins 安全加固与权限管理实战
2.1 安全体系全景
Jenkins 安全体系(四层防护)
┌──────────────────────────────────────────────────────────┐
│ 层1 认证(Authentication)--- 你是谁 │
│ ├── Jenkins 内置用户库(本篇使用) │
│ ├── LDAP/AD 集成(博客7详解) │
│ └── GitHub/GitLab SSO(博客7详解) │
├──────────────────────────────────────────────────────────┤
│ 层2 授权(Authorization)--- 你能做什么 │
│ ├── Role-Based Strategy ✓(推荐) │
│ ├── Matrix-based Security │
│ └── Project-based Matrix │
├──────────────────────────────────────────────────────────┤
│ 层3 凭据(Credentials)--- 密钥安全存储 │
│ ├── Username/Password │
│ ├── SSH Private Key │
│ ├── Secret Text / Secret File │
│ └── Certificate (PKCS#12) │
├──────────────────────────────────────────────────────────┤
│ 层4 审计(Audit)--- 谁做了什么 │
│ ├── Audit Trail Plugin │
│ └── Build 历史 + 系统日志 │
└──────────────────────────────────────────────────────────┘
2.2 启用安全配置
路径:Manage Jenkins → Configure Global Security
Security Realm(认证方式):
☑ Jenkins' own user database
Allow users to sign up: ✗(关闭自助注册,防止未授权入侵)
Authorization(授权策略):
☑ Role-Based Strategy(需已安装插件)
CSRF Protection:
☑ Enable proxy compatibility
(必须勾选,否则 Nginx 反代后 Form 提交报 403)
Agent → Controller Security:
☑ Enable Agent → Master Access Control
(防止受控 Agent 提权到 Master)
2.3 基于角色的权限管理
路径:Manage Jenkins → Manage and Assign Roles → Manage Roles
全局角色(Global Roles):
┌────────────┬─────────────────────────────────────────────────────┐
│ 角色 │ 权限 │
├────────────┼─────────────────────────────────────────────────────┤
│ admin │ Overall/Administer(全部权限) │
│ developer │ Overall/Read + Job/Build,Cancel,Read,Workspace │
│ viewer │ Overall/Read + Job/Read(只读,供 PM/监控使用) │
│ ops │ Overall/Read + Job/Build,Configure + Agent/Connect │
└────────────┴─────────────────────────────────────────────────────┘
项目角色(Project Roles):
Pattern: project-A-.*(正则匹配 Job 名称前缀)
角色: project-a-dev
权限: Job/Build, Job/Cancel, Job/Read
路径:Manage and Assign Roles → Assign Roles
用户角色绑定示例:
admin → 角色: admin
dev01 → 角色: developer
pm01 → 角色: viewer
ops01 → 角色: ops
dev-a01 → 项目角色: project-a-dev(只能操作 project-A-* 的 Job)
2.4 凭据管理规范
路径:Manage Jenkins → Manage Credentials → System → Global credentials
凭据命名规范(便于多人协作识别):
git-github-https-token # GitHub Personal Access Token
git-gitlab-ssh-key # GitLab SSH 私钥
docker-registry-prod # 生产 Docker Registry 账号
k8s-prod-kubeconfig # 生产 K8s kubeconfig
sonarqube-server-token # SonarQube 分析 Token
nexus-deploy-cred # Nexus 部署账号
dingtalk-webhook-secret # 钉钉 Webhook 加签 Secret
在 Pipeline 中安全引用凭据:
groovy
pipeline {
agent { label 'build' }
environment {
// 绑定 Username/Password 类型凭据
// 自动生成 DOCKER_CRED_USR 和 DOCKER_CRED_PSW
DOCKER_CRED = credentials('docker-registry-prod')
}
stages {
stage('Docker Push') {
steps {
sh '''
echo $DOCKER_CRED_PSW | \
docker login -u $DOCKER_CRED_USR \
--password-stdin registry.company.com
'''
}
}
stage('Deploy') {
steps {
// withCredentials 用于精确控制作用域
withCredentials([
string(credentialsId: 'sonarqube-server-token',
variable: 'SONAR_TOKEN'),
sshUserPrivateKey(credentialsId: 'git-gitlab-ssh-key',
keyFileVariable: 'SSH_KEY')
]) {
sh '''
mvn sonar:sonar -Dsonar.login=${SONAR_TOKEN}
ssh -i ${SSH_KEY} deploy@server "deploy.sh"
'''
// 注意:凭据变量值不会出现在 Jenkins 日志中(已 mask)
}
}
}
}
}
⚠️ 安全注意 :凭据变量不要用
echo打印,Jenkins 虽然会 mask,但仍属于不良习惯。
2.5 审计日志配置
bash
# 插件安装后,路径: Manage Jenkins → Audit Trail
# 配置:
# Log location: /var/lib/jenkins/logs/audit.log
# Log file count: 5
# Log file size: 100 MB
# 查看审计日志
tail -f /var/lib/jenkins/logs/audit.log
日志示例:
2026-06-09 15:52:11 +0800 admin Started job/spring-petclinic/ #1
2026-06-09 15:52:30 +0800 dev01 Attempted to access /credentials/store/system
2026-06-09 15:55:00 +0800 admin Configured /manage/globalSecurity
2026-06-09 16:01:22 +0800 ops01 Started job/spring-petclinic/ #2 by timer
2.6 Nginx 反向代理(生产推荐)
bash
# ===== jk-01 执行 =====
apt-get install -y nginx
cat > /etc/nginx/conf.d/jenkins.conf << 'EOF'
upstream jenkins {
keepalive 32;
server 127.0.0.1:8080;
}
server {
listen 80;
server_name 121.91.168.17;
location / {
proxy_pass http://jenkins;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持(Blue Ocean / Terminal 插件需要)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 大文件支持(上传插件、制品归档)
client_max_body_size 500m;
proxy_read_timeout 300s;
}
}
EOF
nginx -t && systemctl enable --now nginx
博客3:声明式 Pipeline 从入门到精通
3.1 Pipeline 语法全景图
pipeline { ← 根节点
agent { } ← 运行环境声明
environment { } ← 全局环境变量
options { } ← 流水线选项(超时/保留记录/时间戳)
parameters { } ← 参数化构建输入
triggers { } ← 触发方式(定时/SCM/上游)
stages { ← 阶段容器(必须)
stage('名称') {
agent { } ← 可覆盖全局 agent
when { } ← 条件执行
environment { } ← 阶段级环境变量
steps { } ← 具体执行步骤(必须)
post { } ← 阶段后置
}
stage('并行') {
parallel { ← 并行执行多个分支
stage('分支A') { steps {} }
stage('分支B') { steps {} }
}
}
}
post { ← 全局后置操作
always { } ← 无论成功失败都执行
success { } ← 仅成功时执行
failure { } ← 仅失败时执行
unstable { } ← 测试不稳定时执行
changed { } ← 状态与上次不同时执行
}
}
3.2 完整实战 Jenkinsfile
以下 Jenkinsfile 完整演示 Spring PetClinic 从代码到部署的全流程:
groovy
// Jenkinsfile --- Spring PetClinic CI/CD 全流程
// 放置于项目根目录
pipeline {
// ===== 全局运行在 build Agent 上 =====
agent {
label 'build'
}
// ===== 流水线全局选项 =====
options {
buildDiscarder(logRotator(numToKeepStr: '15', artifactNumToKeepStr: '5'))
timeout(time: 60, unit: 'MINUTES')
timestamps() // 日志加时间戳
ansiColor('xterm') // 彩色日志
disableConcurrentBuilds() // 禁止同一 Job 并发(避免部署冲突)
skipDefaultCheckout(true) // 禁用自动 checkout,由阶段手动控制
}
// ===== 参数化构建 =====
parameters {
choice(
name: 'DEPLOY_ENV',
choices: ['dev', 'skip'],
description: '部署目标环境(skip=仅构建不部署)'
)
booleanParam(
name: 'SKIP_SONAR',
defaultValue: false,
description: '跳过 SonarQube 扫描(feature 分支快速验证)'
)
string(
name: 'IMAGE_TAG',
defaultValue: "${env.BUILD_NUMBER}",
description: '镜像 Tag,默认使用构建号'
)
}
// ===== 全局环境变量 =====
environment {
APP_NAME = 'spring-petclinic'
REGISTRY = '119.12.167.110:5000' // Nexus Docker Registry
SONAR_HOST = 'http://94.74.100.108:9000'
NEXUS_HOST = 'http://119.12.167.110:8081'
// 绑定凭据
SONAR_TOKEN = credentials('sonarqube-server-token')
NEXUS_CRED = credentials('nexus-deploy-cred')
DOCKER_CRED = credentials('docker-registry-prod')
}
stages {
// ─── Stage 1: 拉取代码 ────────────────────────────────────
stage('Checkout') {
steps {
git branch: env.GIT_BRANCH ?: 'main',
url: 'http://192.168.0.198:3000/backend/spring-petclinic.git',
credentialsId: 'git-gitea-http'
script {
// 记录版本信息(后续阶段复用)
env.GIT_COMMIT_SHORT = sh(
script: 'git rev-parse --short HEAD',
returnStdout: true
).trim()
env.APP_VERSION = sh(
script: "mvn help:evaluate -Dexpression=project.version -q -DforceStdout",
returnStdout: true
).trim()
}
echo "分支: ${env.GIT_BRANCH} | Commit: ${env.GIT_COMMIT_SHORT} | Version: ${env.APP_VERSION}"
}
}
// ─── Stage 2: 编译 ────────────────────────────────────────
stage('Build') {
steps {
sh 'mvn clean compile -DskipTests -B -q'
}
}
// ─── Stage 3: 单元测试 ────────────────────────────────────
stage('Unit Test') {
steps {
sh 'mvn test -B'
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
success {
echo "✅ 单元测试全部通过"
}
failure {
echo "❌ 单元测试失败,请检查报告"
}
}
}
// ─── Stage 4: SonarQube 扫描(可跳过)───────────────────
stage('SonarQube Scan') {
when {
expression { !params.SKIP_SONAR }
}
steps {
withSonarQubeEnv('sonarqube-server') {
sh """
mvn sonar:sonar \
-Dsonar.projectKey=${APP_NAME} \
-Dsonar.host.url=${SONAR_HOST} \
-Dsonar.login=${SONAR_TOKEN} \
-B -q
"""
}
}
}
// ─── Stage 5: 质量门禁 ────────────────────────────────────
stage('Quality Gate') {
when {
expression { !params.SKIP_SONAR }
}
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
// ─── Stage 6: 打包 ────────────────────────────────────────
stage('Package') {
steps {
sh 'mvn package -DskipTests -B'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
echo "制品路径: ${env.WORKSPACE}/target/${APP_NAME}-${APP_VERSION}.jar"
}
}
// ─── Stage 7: 构建镜像 ────────────────────────────────────
stage('Docker Build') {
steps {
script {
def imageTag = "${REGISTRY}/${APP_NAME}:${params.IMAGE_TAG}"
def imageLatest = "${REGISTRY}/${APP_NAME}:latest"
sh "docker build -t ${imageTag} -t ${imageLatest} ."
env.IMAGE_TAG_FULL = imageTag
}
}
}
// ─── Stage 8: 推送镜像 ────────────────────────────────────
stage('Docker Push') {
steps {
sh """
echo ${DOCKER_CRED_PSW} | docker login -u ${DOCKER_CRED_USR} \
--password-stdin ${REGISTRY}
docker push ${IMAGE_TAG_FULL}
docker push ${REGISTRY}/${APP_NAME}:latest
docker logout ${REGISTRY}
"""
}
}
// ─── Stage 9: 部署 ────────────────────────────────────────
stage('Deploy') {
when {
expression { params.DEPLOY_ENV != 'skip' }
}
steps {
script {
def targetHost = [
'dev': '192.168.0.103' // jk-04 Build Agent 兼 DEV 部署目标
]
def host = targetHost[params.DEPLOY_ENV]
sshagent(['jenkins-agent-ssh-key']) {
sh """
ssh -o StrictHostKeyChecking=no jenkins@${host} '
cd /opt/apps/${APP_NAME}
docker compose pull
docker compose up -d --remove-orphans
docker compose ps
'
"""
}
}
}
}
}
// ===== 全局后置操作 =====
post {
success {
echo "✅ Pipeline 成功完成!"
echo " 版本: ${env.APP_VERSION} | Tag: ${params.IMAGE_TAG}"
}
failure {
echo "❌ Pipeline 失败,构建号: ${env.BUILD_NUMBER}"
}
always {
// 清理 Docker 悬空镜像
sh 'docker image prune -f || true'
cleanWs()
}
}
}
3.3 常用内置变量速查
groovy
// 全局变量
env.JOB_NAME // Job 名称: "spring-petclinic"
env.BUILD_NUMBER // 构建号: "42"
env.BUILD_URL // 构建链接: "http://121.91.168.17:8080/job/.../42/"
env.WORKSPACE // 工作目录: "/opt/jenkins-agent/workspace/spring-petclinic"
env.GIT_BRANCH // Git 分支: "origin/main"
env.GIT_COMMIT // Git Commit: "a1b2c3d4e5f6..."
env.NODE_NAME // 运行节点: "agent-build"
// currentBuild 对象
currentBuild.result // "SUCCESS" / "FAILURE" / "UNSTABLE"
currentBuild.displayName // "#42"
currentBuild.description // 可修改,在 UI 显示额外信息
currentBuild.duration // 毫秒数
currentBuild.durationString // "1 min 23 sec"
currentBuild.previousBuild // 上一次构建对象
// 设置构建显示名称(便于在历史列表中识别)
currentBuild.displayName = "#${BUILD_NUMBER} v${APP_VERSION}"
currentBuild.description = "Commit: ${GIT_COMMIT_SHORT} | Env: ${DEPLOY_ENV}"
3.4 多分支 Pipeline 配置
New Item → Multibranch Pipeline
Branch Sources → Git:
Repository URL: http://192.168.0.198:3000/backend/spring-petclinic.git
Credentials: git-gitea-http
Behaviors:
☑ Discover branches: All branches
☑ Filter by name (with wildcards):
Include: main develop feature/* release/* hotfix/*
Build Configuration:
Mode: by Jenkinsfile
Script Path: Jenkinsfile
Scan Multibranch Pipeline Triggers:
☑ Periodically if not otherwise run: Interval 1 day
分支构建策略(在 Jenkinsfile 中通过 when 控制):
groovy
// Jenkinsfile 中的分支判断示例
stage('Deploy to TEST') {
when {
anyOf {
branch 'main'
branch 'release/*'
}
}
steps { /* 部署到 TEST */ }
}
stage('Deploy to PROD') {
when {
branch 'main'
}
steps {
// 生产部署需要人工审批
input message: '确认部署到生产环境?', submitter: 'admin,ops01'
/* 部署到 PROD */
}
}
博客4:Groovy 在 Pipeline 中的高级应用
4.1 Groovy 核心语法速查
groovy
// ===== 数据类型与变量 =====
def name = "spring-petclinic" // String
def version = "4.0.3" // String
def buildNum = 42 // Integer
def isProduction = false // Boolean
def envList = ['dev', 'test', 'prod'] // List
def config = [host: '192.168.0.103', port: 8080] // Map
// ===== 字符串操作 =====
def tag = "${name}-${buildNum}" // 插值
def upper = name.toUpperCase() // SPRING-PETCLINIC
def hasSnapshot = version.contains('SNAPSHOT') // false
def normalized = name.replaceAll('-', '_') // spring_petclinic
// ===== 列表操作 =====
def envs = ['dev', 'test', 'prod']
envs.each { env -> echo "部署 ${env}" } // 遍历
def prodOnly = envs.findAll { it != 'dev' } // 过滤
def upper = envs.collect { it.toUpperCase() } // 映射转换
def hasTest = envs.contains('test') // true
def sorted = envs.sort() // 排序
// ===== Map 操作 =====
def hosts = [
dev: '192.168.0.103',
test: '192.168.0.103',
prod: '192.168.0.103'
]
def targetHost = hosts[params.DEPLOY_ENV] // 按 key 取值
def allHosts = hosts.values().toList() // 取所有值
hosts.each { env, host -> echo "${env}: ${host}" } // 遍历
// ===== 条件判断 =====
if (env.BRANCH_NAME == 'main') {
echo "主分支"
} else if (env.BRANCH_NAME?.startsWith('feature/')) {
echo "特性分支: ${env.BRANCH_NAME}"
} else {
echo "其他分支"
}
// ===== 异常处理 =====
try {
sh 'mvn test'
} catch (Exception e) {
echo "测试失败: ${e.getMessage()}"
currentBuild.result = 'UNSTABLE'
} finally {
junit '**/target/surefire-reports/*.xml'
}
4.2 Pipeline 共享库(Shared Library)
目录结构:
jenkins-shared-library/ # Git 仓库
├── vars/ # 全局变量(最简单的调用方式)
│ ├── buildMaven.groovy
│ ├── buildDocker.groovy
│ └── notifyDingTalk.groovy
├── src/ # 辅助类
│ └── com/company/ci/
│ └── DeployUtils.groovy
└── resources/ # 静态资源
└── Dockerfile.template
vars/buildMaven.groovy
groovy
#!/usr/bin/env groovy
// 调用方式: buildMaven(goals: 'clean package', profile: 'prod')
def call(Map config = [:]) {
def goals = config.get('goals', 'clean package')
def skipTests = config.get('skipTests', false)
def profile = config.get('profile', '')
def batchMode = config.get('batchMode', true)
def args = [goals]
if (skipTests) args << '-DskipTests'
if (profile) args << "-P${profile}"
if (batchMode) args << '-B'
args << '-q' // quiet 模式,减少日志噪音
echo "=== Maven 构建: ${args.join(' ')} ==="
sh "MAVEN_OPTS='-Xmx1024m' mvn ${args.join(' ')}"
echo "=== Maven 构建完成 ==="
}
vars/notifyDingTalk.groovy
groovy
#!/usr/bin/env groovy
// 调用方式: notifyDingTalk(webhook: '...', status: currentBuild.result)
def call(Map config = [:]) {
def webhook = config.get('webhook', '')
def status = config.get('status', 'SUCCESS')
def extra = config.get('extra', '')
if (!webhook) { echo "⚠ 未配置 webhook,跳过钉钉通知"; return }
def emoji = (status == 'SUCCESS') ? '✅' : '❌'
def result = (status == 'SUCCESS') ? '成功' : '失败'
def text = """## ${emoji} Jenkins 构建${result}
**项目**: ${env.JOB_NAME}
**构建号**: #${env.BUILD_NUMBER}
**分支**: ${env.GIT_BRANCH ?: 'N/A'}
**耗时**: ${currentBuild.durationString}
${extra ? "**备注**: ${extra}" : ''}
[查看构建详情](${env.BUILD_URL})"""
def payload = groovy.json.JsonOutput.toJson([
msgtype: 'markdown',
markdown: [title: "Jenkins ${result}", text: text]
])
sh """
curl -s -X POST '${webhook}' \\
-H 'Content-Type: application/json' \\
-d '${payload.replaceAll("'", "'\\''")}' \\
| python3 -c "import sys,json; r=json.load(sys.stdin); print(r.get('errmsg','OK'))"
"""
}
注册共享库
路径: Manage Jenkins → Configure System → Global Pipeline Libraries
Name: company-shared-lib
Default version: main
Load implicitly: ✗(需要在 Jenkinsfile 显式导入)
Retrieval method: Modern SCM → Git
Project Repository: http://192.168.0.198:3000/devops/jenkins-shared-library.git
Credentials: git-gitea-http
在 Jenkinsfile 中使用共享库
groovy
@Library('company-shared-lib') _ // 导入共享库
pipeline {
agent { label 'build' }
stages {
stage('Build') {
steps {
// 直接调用 vars/ 下的方法
buildMaven(goals: 'clean package', skipTests: false)
}
}
}
post {
always {
notifyDingTalk(
webhook: 'https://oapi.dingtalk.com/robot/send?access_token=xxx',
status: currentBuild.result ?: 'SUCCESS'
)
}
}
}
博客5:主流构建工具集成全攻略
5.1 Maven 集成实战
bash
# ===== jk-04 Agent 执行:验证 Maven =====
# Maven 通过 Jenkins 全局工具自动下载到 Master 的 tools 目录
# Agent 节点通过 SSH 文件传输获取
# 或在 Agent 上本地安装
apt-get install -y maven
mvn --version
实操输出:
Apache Maven 3.8.7
Maven home: /usr/share/maven
Java version: 17.0.15, vendor: Ubuntu
Maven settings.xml 配置(Nexus 私服加速):
xml
<!-- /opt/jenkins-agent/.m2/settings.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<settings>
<mirrors>
<!-- 所有依赖通过 Nexus 代理 -->
<mirror>
<id>nexus-central</id>
<mirrorOf>*</mirrorOf>
<name>Nexus Central Proxy</name>
<url>http://119.12.167.110:8081/repository/maven-public/</url>
</mirror>
</mirrors>
<servers>
<!-- 发布制品到 Nexus 的凭据 -->
<server>
<id>nexus-releases</id>
<username>deploy</username>
<password>Deploy@2026</password>
</server>
<server>
<id>nexus-snapshots</id>
<username>deploy</username>
<password>Deploy@2026</password>
</server>
</servers>
<profiles>
<profile>
<id>nexus</id>
<repositories>
<repository>
<id>nexus-releases</id>
<url>http://119.12.167.110:8081/repository/maven-releases/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository>
<repository>
<id>nexus-snapshots</id>
<url>http://119.12.167.110:8081/repository/maven-snapshots/</url>
<releases><enabled>false</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>
</profile>
</profiles>
<activeProfiles><activeProfile>nexus</activeProfile></activeProfiles>
</settings>
Pipeline 中 Maven 构建:
groovy
stage('Maven Build') {
tools {
maven 'Maven-3.9' // 使用全局配置的 Maven
}
steps {
// 指定 settings.xml(引用 Jenkins 凭据中存储的 settings 文件)
configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
sh 'mvn clean package -s $MAVEN_SETTINGS -DskipTests -B'
}
}
}
5.2 Node.js 前端构建集成
bash
# ===== jk-04 Agent 执行 =====
# 通过 nvm 管理 Node.js 版本(Jenkins NodeJS 插件会自动安装)
# 也可手动安装:
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt-get install -y nodejs
node --version # v20.x.x
npm --version # 10.x.x
Pipeline 中 Node.js 构建:
groovy
stage('Frontend Build') {
tools {
nodejs 'NodeJS-20' // 使用全局配置的 Node.js
}
steps {
dir('frontend') {
sh '''
# 使用私服 npm registry(如配置了 Nexus npm 代理)
npm config set registry http://119.12.167.110:8081/repository/npm-proxy/
npm ci # 精确安装 package-lock.json 中的版本
npm run build # 构建生产包
npm run test -- --watchAll=false # 单元测试
'''
}
archiveArtifacts artifacts: 'frontend/dist/**', fingerprint: true
}
}
博客6:SonarQube 代码质量平台实战
6.1 SonarQube 安装(jk-02: 94.74.100.108)
bash
# ===== jk-02 执行 =====
# SonarQube 需要增大系统 vm.max_map_count
sysctl -w vm.max_map_count=262144
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
# 使用 Docker Compose 部署 SonarQube + PostgreSQL
mkdir -p /opt/sonarqube
cat > /opt/sonarqube/docker-compose.yml << 'EOF'
version: '3.8'
services:
sonarqube-db:
image: postgres:15-alpine
container_name: sonarqube-db
restart: unless-stopped
environment:
POSTGRES_USER: sonar
POSTGRES_PASSWORD: Sonar@2026
POSTGRES_DB: sonarqube
volumes:
- sonar-db-data:/var/lib/postgresql/data
networks:
- sonar-net
sonarqube:
image: sonarqube:community
container_name: sonarqube
restart: unless-stopped
depends_on:
- sonarqube-db
ports:
- "9000:9000"
environment:
SONAR_JDBC_URL: jdbc:postgresql://sonarqube-db:5432/sonarqube
SONAR_JDBC_USERNAME: sonar
SONAR_JDBC_PASSWORD: Sonar@2026
SONAR_ES_BOOTSTRAP_CHECKS_DISABLE: "true"
volumes:
- sonar-data:/opt/sonarqube/data
- sonar-extensions:/opt/sonarqube/extensions
- sonar-logs:/opt/sonarqube/logs
networks:
- sonar-net
ulimits:
nofile:
soft: 65536
hard: 65536
volumes:
sonar-db-data:
sonar-data:
sonar-extensions:
sonar-logs:
networks:
sonar-net:
driver: bridge
EOF
cd /opt/sonarqube && docker compose up -d
# 等待启动(SonarQube 首次启动较慢,约 1-2 分钟)
docker compose logs -f sonarqube 2>&1 | grep -m1 "SonarQube is operational"
实操输出:
sonarqube | 2026-06-09T08:05:23+0000 INFO web[][o.s.s.p.Platform] Web Server is operational
访问 http://94.74.100.108:9000(默认账号:admin/admin,首次登录强制改密)。
配置 SonarQube Token:
路径: My Account → Security → Generate Tokens
Name: jenkins-analysis-token
Type: Global Analysis Token
Expires: 365 days
→ 生成 Token(示例: sqa_...)
在 Jenkins 中配置 SonarQube 服务器:
路径: Manage Jenkins → Configure System → SonarQube servers
Name: sonarqube-server
Server URL: http://94.74.100.108:9000
Server authentication token: 选择 sonarqube-server-token 凭据
6.2 质量规则与质量阈配置
Quality Gate 配置(路径: Quality Gates → Create):
Name: Company Default Gate
条件:
Coverage on New Code < 80% → 失败(新代码覆盖率必须 ≥80%)
Duplicated Lines on New > 3% → 失败(重复代码 ≤3%)
Maintainability Rating > A → 失败(可维护性必须为 A)
Reliability Rating > A → 失败(可靠性必须为 A)
Security Rating > A → 失败(安全性必须为 A)
Security Hotspots Reviewed < 100% → 失败(热点必须全部审查)
在 Pipeline 中等待质量门禁结果:
groovy
stage('Quality Gate') {
steps {
timeout(time: 10, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
// abortPipeline: true → 门禁不通过时自动中止 Pipeline
}
}
}
博客7:Nexus 3 制品仓库企业级实践
7.1 Nexus 3 安装(jk-03: 119.12.167.110)
bash
# ===== jk-03 执行 =====
mkdir -p /opt/nexus
cat > /opt/nexus/docker-compose.yml << 'EOF'
version: '3.8'
services:
nexus:
image: sonatype/nexus3:latest
container_name: nexus3
restart: unless-stopped
ports:
- "8081:8081" # Web UI / Maven
- "5000:5000" # Docker Registry(需额外配置 Connector)
volumes:
- nexus-data:/nexus-data
environment:
INSTALL4J_ADD_VM_PARAMS: "-Xms512m -Xmx2048m -XX:MaxDirectMemorySize=2048m"
networks:
- nexus-net
volumes:
nexus-data:
driver: local
networks:
nexus-net:
driver: bridge
EOF
cd /opt/nexus && docker compose up -d
# 等待启动(约 1-2 分钟)
docker compose logs -f nexus 2>&1 | grep -m1 "Started Sonatype Nexus"
# 获取初始密码
docker exec nexus3 cat /nexus-data/admin.password
访问 http://119.12.167.110:8081(首次登录用初始密码)。
创建仓库(路径:Server Administration → Repositories → Create):
仓库类型说明:
┌──────────┬──────────────────────────────────────────────────────┐
│ 类型 │ 说明 │
├──────────┼──────────────────────────────────────────────────────┤
│ hosted │ 本地存储,用于存放自己发布的制品 │
│ proxy │ 代理外部仓库(Maven Central / Docker Hub),启用缓存 │
│ group │ 聚合多个仓库,对外暴露单一 URL │
└──────────┴──────────────────────────────────────────────────────┘
创建以下仓库:
maven-releases (hosted) → 存放正式版本 JAR
maven-snapshots (hosted) → 存放快照版本 JAR
maven-central (proxy) → 代理 Maven Central
maven-public (group) → 聚合上面3个
docker-hosted (hosted) → 存放自建镜像(端口 5000)
docker-proxy (proxy) → 代理 Docker Hub
docker-public (group) → 聚合两个 Docker 仓库
Pipeline 中上传制品到 Nexus:
groovy
stage('Publish to Nexus') {
steps {
nexusArtifactUploader(
nexusVersion: 'nexus3',
protocol: 'http',
nexusUrl: '119.12.167.110:8081',
groupId: 'org.springframework.samples',
version: env.APP_VERSION,
repository: env.APP_VERSION.endsWith('SNAPSHOT') ?
'maven-snapshots' : 'maven-releases',
credentialsId: 'nexus-deploy-cred',
artifacts: [
[artifactId: env.APP_NAME,
classifier: '',
file: "target/${env.APP_NAME}-${env.APP_VERSION}.jar",
type: 'jar'],
[artifactId: env.APP_NAME,
classifier: '',
file: "pom.xml",
type: 'pom']
]
)
}
}
博客8:企业统一认证体系集成(LDAP/SSO)
8.1 LDAP 认证配置
路径: Manage Jenkins → Configure Global Security
→ Security Realm → LDAP
配置项:
Server: ldap://ldap.company.com:389
Root DN: dc=company,dc=com
User search base: ou=People
User search filter: uid={0}
Group search base: ou=Groups
Group search filter: (member={0})
Manager DN: cn=jenkins,ou=Service,dc=company,dc=com
Manager Password: ****
Test LDAP Settings:
User: dev01
Password: ****
→ 点击测试验证
8.2 GitHub OAuth SSO(开发者友好)
bash
# 1. 在 GitHub 注册 OAuth Application
# Settings → Developer Settings → OAuth Apps → New OAuth App
# Homepage URL: http://121.91.168.17:8080/
# Authorization callback URL: http://121.91.168.17:8080/securityRealm/finishLogin
# 2. Jenkins 安装 GitHub OAuth Plugin
# 3. 配置:
# Manage Jenkins → Configure Global Security
# Security Realm: GitHub Authentication Plugin
# GitHub Web URI: https://github.com
# Client ID: (GitHub 提供的 Client ID)
# Client Secret: (GitHub 提供的 Secret)
博客9:GitLab 深度集成与流水线优化
9.1 Webhook 触发配置
bash
# Jenkins 端配置
# 1. 安装 GitLab Plugin
# 2. Job 配置 → Build Triggers → ✓ Build when a change is pushed to GitLab
# GitLab webhook URL: http://121.91.168.17:8080/project/spring-petclinic
# Secret Token: (生成后填入 GitLab)
# GitLab 端配置
# Settings → Webhooks → Add webhook
# URL: http://121.91.168.17:8080/project/spring-petclinic
# Secret Token: (同上)
# Trigger: Push events, Merge request events
# SSL verification: ✗(内网环境)
Pipeline 中回写 Commit 状态:
groovy
pipeline {
agent { label 'build' }
stages {
stage('Build') {
steps {
updateGitlabCommitStatus name: 'build', state: 'running'
sh 'mvn clean package'
updateGitlabCommitStatus name: 'build', state: 'success'
}
}
}
post {
failure {
updateGitlabCommitStatus name: 'build', state: 'failed'
}
}
}
博客10:SonarQube 代码质量平台深度实战
10.1 多分支扫描配置
groovy
// 声明式 Pipeline 多分支扫描
stage('SonarQube') {
steps {
withSonarQubeEnv('sonarqube-server') {
sh """
mvn sonar:sonar \
-Dsonar.projectKey=spring-petclinic \
-Dsonar.projectName='Spring PetClinic' \
-Dsonar.branch.name=${env.BRANCH_NAME} \
-Dsonar.branch.target=main \
-Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml \
-Dsonar.java.binaries=target/classes \
-B
"""
}
}
}
10.2 SonarQube 质量报告解读
分析维度:
┌──────────────────────┬────────────────────────────────────┐
│ 维度 │ 说明 │
├──────────────────────┼────────────────────────────────────┤
│ Bugs(缺陷) │ 逻辑错误,可能导致程序崩溃或行为异常 │
│ Vulnerabilities(漏洞)│ 安全漏洞,可被攻击者利用 │
│ Code Smells(异味) │ 技术债务,影响可维护性 │
│ Coverage(覆盖率) │ 单元测试覆盖的代码行比例 │
│ Duplications(重复) │ 重复代码块比例 │
│ Security Hotspots │ 需要人工审查的安全敏感代码 │
└──────────────────────┴────────────────────────────────────┘
评级标准:
A: 0 个 Bug/漏洞,或极少量
B: 至少 1 个轻微 Bug/漏洞
C: 至少 1 个主要 Bug/漏洞
D: 至少 1 个严重 Bug/漏洞
E: 至少 1 个阻断 Bug/漏洞
博客11:Jira 需求管理与流水线联动
11.1 Jira 集成配置
groovy
// 在 Pipeline 中更新 Jira Issue 状态
stage('Update Jira') {
steps {
script {
// 从 Git commit message 提取 Jira Issue 编号
// 约定格式: "[PROJ-123] 修复登录Bug"
def commitMsg = sh(script: 'git log -1 --pretty=%B', returnStdout: true).trim()
def jiraPattern = ~/\[([A-Z]+-\d+)\]/
def matcher = (commitMsg =~ jiraPattern)
if (matcher.find()) {
def issueKey = matcher[0][1] // 例如: PROJ-123
echo "关联 Jira Issue: ${issueKey}"
// 通过 Jira REST API 添加评论
withCredentials([usernamePassword(
credentialsId: 'jira-cred',
usernameVariable: 'JIRA_USER',
passwordVariable: 'JIRA_TOKEN'
)]) {
sh """
curl -s -X POST \
-u "${JIRA_USER}:${JIRA_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{"body":"Jenkins 构建 #${BUILD_NUMBER} 已完成\\n构建链接: ${BUILD_URL}"}' \
https://jira.company.com/rest/api/2/issue/${issueKey}/comment
"""
}
}
}
}
}
博客12:Kubernetes 环境下的 Jenkins 部署
12.1 K8s 动态 Agent 配置
bash
# 安装 Kubernetes Plugin for Jenkins
# 路径: Manage Jenkins → Plugin Manager → kubernetes
# 配置 K8s 集群连接
# Manage Jenkins → Configure System → Cloud → Add a new cloud → Kubernetes
# K8s 集群 API 地址 (使用 jk-04 上的 K3s 或外部 K8s)
# Kubernetes URL: https://119.13.76.253:6443
# Kubernetes Namespace: jenkins
# Credentials: k8s-kubeconfig
# Jenkins URL: http://192.168.0.198:8080(内网地址)
# Jenkins tunnel: 192.168.0.198:50000
动态 Pod Agent 模板:
groovy
// Jenkinsfile 中使用 K8s Pod Agent
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
metadata:
labels:
app: jenkins-agent
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-17
command: ['sleep', 'infinity']
volumeMounts:
- name: m2-cache
mountPath: /root/.m2
- name: docker
image: docker:24-dind
securityContext:
privileged: true
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: m2-cache
emptyDir: {}
- name: docker-sock
hostPath:
path: /var/run/docker.sock
'''
}
}
stages {
stage('Maven Build') {
steps {
container('maven') {
sh 'mvn clean package -DskipTests -B'
}
}
}
stage('Docker Build') {
steps {
container('docker') {
sh 'docker build -t myapp:latest .'
}
}
}
}
}
博客13:JMeter 自动化接口测试集成
13.1 JMeter 安装(jk-04 Agent)
bash
# ===== jk-04 执行 =====
# 下载 JMeter
wget -q https://archive.apache.org/dist/jmeter/binaries/apache-jmeter-5.6.3.tgz \
-O /tmp/jmeter.tgz
tar -xzf /tmp/jmeter.tgz -C /opt/
ln -s /opt/apache-jmeter-5.6.3 /opt/jmeter
# 验证
/opt/jmeter/bin/jmeter --version
# Apache JMeter 5.6.3
Pipeline 中集成 JMeter 测试:
groovy
stage('Performance Test') {
when {
branch 'main'
}
steps {
// 使用 JMeter 运行测试计划(非 GUI 模式)
sh '''
/opt/jmeter/bin/jmeter \
-n \
-t ${WORKSPACE}/tests/jmeter/api-test.jmx \
-l ${WORKSPACE}/tests/results/result.jtl \
-e -o ${WORKSPACE}/tests/results/report \
-Jhost=192.168.0.103 \
-Jport=8080 \
-Jthreads=10 \
-Jduration=60
'''
}
post {
always {
// 发布 JMeter 报告(需要 Performance Plugin)
perfReport sourceDataFiles: 'tests/results/result.jtl',
errorUnstableThreshold: 5, // 错误率 > 5% 标记为 Unstable
errorFailedThreshold: 10 // 错误率 > 10% 标记为 Failed
}
}
}
博客14:Jenkins 监控与度量体系
14.1 Prometheus 集成
bash
# 安装 Prometheus Metrics Plugin
# Plugin Manager → 搜索 "Prometheus metrics"
# 安装后,Jenkins 自动暴露 /prometheus/ 端点
curl http://121.91.168.17:8080/prometheus/
实操输出(部分):
# HELP default_jenkins_builds_duration_milliseconds_summary
# TYPE default_jenkins_builds_duration_milliseconds_summary summary
default_jenkins_builds_duration_milliseconds_summary{...} 45321
# HELP default_jenkins_node_builds_count_total
default_jenkins_node_builds_count_total{label="build"} 42.0
Prometheus 抓取配置:
yaml
# prometheus.yml
scrape_configs:
- job_name: 'jenkins'
metrics_path: '/prometheus/'
static_configs:
- targets: ['121.91.168.17:8080']
basic_auth:
username: admin
password: Admin@2026
关键监控指标:
构建类:
jenkins_builds_success_build_count # 成功构建数
jenkins_builds_failed_build_count # 失败构建数
jenkins_builds_duration_milliseconds # 构建耗时
节点类:
jenkins_node_online_value # 节点在线状态(1=在线)
jenkins_node_executor_count_value # Executor 总数
jenkins_node_executor_in_use_value # 使用中的 Executor
队列类:
jenkins_queue_size_value # 等待队列长度
jenkins_queue_blocked_value # 被阻塞的任务数
博客15:Docker 与 Jenkins 深度集成
15.1 Jenkins Master 挂载 Docker Socket
bash
# ===== jk-01 执行 =====
# 将 jenkins 用户加入 docker 组
usermod -aG docker jenkins
# 重启 Jenkins 生效
systemctl restart jenkins
# 验证
su - jenkins -s /bin/bash -c "docker version"
15.2 Pipeline 完整 Docker 构建推送流程
groovy
pipeline {
agent { label 'build' }
environment {
REGISTRY = '119.12.167.110:5000'
IMAGE_NAME = "${REGISTRY}/spring-petclinic"
IMAGE_TAG = "${env.BUILD_NUMBER}"
DOCKER_CRED = credentials('docker-registry-prod')
}
stages {
stage('Docker Build') {
steps {
script {
// 使用 Docker Pipeline 插件语法
def image = docker.build("${IMAGE_NAME}:${IMAGE_TAG}")
docker.withRegistry("http://${REGISTRY}", 'docker-registry-prod') {
image.push("${IMAGE_TAG}")
image.push("latest")
}
// 构建完成后本地清理(节省 Agent 磁盘)
sh "docker rmi ${IMAGE_NAME}:${IMAGE_TAG} || true"
}
}
}
}
}
Dockerfile 最佳实践示例:
dockerfile
# 多阶段构建,减小最终镜像体积
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
# 单独缓存依赖层(pom.xml 不变时复用缓存)
RUN mvn dependency:go-offline -B -q
COPY src ./src
RUN mvn package -DskipTests -B -q
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
# 非 root 用户运行(安全)
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
COPY --from=builder /app/target/*.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", "-XX:MaxRAMPercentage=75.0", "-jar", "app.jar"]
博客16:Jenkins 自动化运维与 REST API 应用
16.1 REST API 基础
bash
# ===== 任何能访问 Jenkins 的机器执行 =====
# Jenkins 需要 API Token(建议用 Token,不用密码)
# 路径: 用户头像 → Configure → API Token → Add new Token → 生成
JENKINS_URL="http://121.91.168.17:8080"
JENKINS_USER="admin"
JENKINS_TOKEN="11abc...(API Token)"
AUTH="${JENKINS_USER}:${JENKINS_TOKEN}"
# 1. 获取 Jenkins 版本信息
curl -s -u ${AUTH} "${JENKINS_URL}/api/json?pretty=true" | python3 -c \
"import sys,json; d=json.load(sys.stdin); print('Version:', d.get('description',''))"
# 2. 列出所有 Job
curl -s -u ${AUTH} \
"${JENKINS_URL}/api/json?tree=jobs[name,url,color]&pretty=true"
# 3. 触发构建(无参数)
curl -s -X POST -u ${AUTH} \
"${JENKINS_URL}/job/spring-petclinic/build" \
-w "HTTP Status: %{http_code}\n"
# 4. 触发参数化构建
curl -s -X POST -u ${AUTH} \
"${JENKINS_URL}/job/spring-petclinic/buildWithParameters" \
--data-urlencode "DEPLOY_ENV=dev" \
--data-urlencode "SKIP_SONAR=false" \
-w "HTTP Status: %{http_code}\n"
# 5. 获取最近构建信息
curl -s -u ${AUTH} \
"${JENKINS_URL}/job/spring-petclinic/lastBuild/api/json?pretty=true" | \
python3 -c "
import sys,json
d=json.load(sys.stdin)
print(f'Number: {d[\"number\"]}')
print(f'Result: {d[\"result\"]}')
print(f'Duration: {d[\"duration\"]//1000}s')
"
# 6. 获取构建日志
curl -s -u ${AUTH} \
"${JENKINS_URL}/job/spring-petclinic/42/consoleText"
16.2 Python 自动化运维脚本
python
#!/usr/bin/env python3
"""
Jenkins 自动化运维工具
功能: 批量触发构建、监控队列、清理旧构建
"""
import requests
import json
import time
from datetime import datetime
JENKINS_URL = "http://121.91.168.17:8080"
AUTH = ("admin", "11abc...") # (用户名, API Token)
def trigger_build(job_name: str, params: dict = None) -> int:
"""触发构建,返回队列 Item ID"""
endpoint = "buildWithParameters" if params else "build"
resp = requests.post(
f"{JENKINS_URL}/job/{job_name}/{endpoint}",
auth=AUTH,
params=params or {}
)
if resp.status_code == 201:
location = resp.headers.get('Location', '')
queue_id = int(location.rstrip('/').split('/')[-1])
print(f"[{datetime.now():%H:%M:%S}] 已触发: {job_name}, Queue ID: {queue_id}")
return queue_id
else:
raise Exception(f"触发失败: {resp.status_code} - {resp.text}")
def wait_for_build(queue_id: int, timeout: int = 300) -> dict:
"""等待构建完成,返回构建信息"""
start = time.time()
build_url = None
# 先等待队列转换为实际构建
while time.time() - start < timeout:
resp = requests.get(
f"{JENKINS_URL}/queue/item/{queue_id}/api/json",
auth=AUTH
)
data = resp.json()
if 'executable' in data:
build_url = data['executable']['url']
print(f"构建已启动: {build_url}")
break
time.sleep(3)
if not build_url:
raise TimeoutError("等待构建启动超时")
# 等待构建完成
while time.time() - start < timeout:
resp = requests.get(f"{build_url}api/json", auth=AUTH)
build = resp.json()
if not build['building']:
return build
elapsed = int(time.time() - start)
print(f" 构建中... ({elapsed}s)", end='\r')
time.sleep(5)
raise TimeoutError("构建超时")
def clean_old_builds(job_name: str, keep_count: int = 10):
"""清理旧构建记录,保留最近 N 个"""
resp = requests.get(
f"{JENKINS_URL}/job/{job_name}/api/json?tree=builds[number,url]",
auth=AUTH
)
builds = resp.json()['builds']
to_delete = builds[keep_count:]
for build in to_delete:
num = build['number']
requests.post(
f"{JENKINS_URL}/job/{job_name}/{num}/doDelete",
auth=AUTH
)
print(f"已删除旧构建: {job_name} #{num}")
if __name__ == "__main__":
# 示例:触发 spring-petclinic 构建并等待结果
try:
qid = trigger_build("spring-petclinic", {"DEPLOY_ENV": "dev", "SKIP_SONAR": "false"})
result = wait_for_build(qid, timeout=600)
print(f"\n构建完成!")
print(f" 结果: {result['result']}")
print(f" 耗时: {result['duration'] // 1000}s")
print(f" 链接: {result['url']}")
except Exception as e:
print(f"错误: {e}")
博客17:企业级 CI/CD 流水线设计与实施
17.1 完整 CI/CD 流程设计
代码提交 → CI 阶段 → 制品归档 → 部署 DEV → 集成测试 → 部署 TEST → 验收测试 → 人工审批 → 部署 PROD
↑ ↓
└────────────────────── 缺陷修复 ←─────────── 监控告警 ─────────────────────────┘
详细步骤:
┌─────────────────────────────────────────────────────────────────────────┐
│ CI 阶段 (每次提交触发) │
│ 1. Checkout 代码拉取 ~10s │
│ 2. Build Maven/Gradle 编译 ~60s │
│ 3. Unit Test JUnit 单元测试 ~120s │
│ 4. SonarQube 静态代码分析 ~180s │
│ 5. Quality Gate 质量门禁(等待 SQ 响应) ~60s │
│ 6. Package 打 JAR 包 ~30s │
│ 7. Docker Build 构建镜像 ~60s │
│ 8. Docker Push 推送到 Nexus Registry ~30s │
├─────────────────────────────────────────────────────────────────────────┤
│ CD 阶段 (main/develop 分支触发) │
│ 9. Deploy DEV 部署到 DEV 环境 ~30s │
│ 10. Smoke Test 冒烟测试(接口健康检查) ~30s │
│ 11. Integration Test JMeter 接口测试 ~300s │
│ 12. Deploy TEST 部署到 TEST 环境 ~30s │
│ 13. UAT Approve 人工审批(仅 main 分支) 手动 │
│ 14. Deploy PROD 部署到 PROD 环境 ~30s │
│ 15. Health Check 生产健康检查 ~30s │
└─────────────────────────────────────────────────────────────────────────┘
17.2 版本晋级策略
分支策略(GitFlow 简化版):
main ── 生产代码,每次提交触发完整 Pipeline
develop ── 开发集成分支,触发 CI + 部署 DEV
feature/* ── 特性分支,仅触发 CI(编译 + 测试)
hotfix/* ── 生产热修复,触发完整 Pipeline + 紧急部署通道
版本号规范(SemVer):
1.2.3
└─┬─┘
├── MAJOR: 不兼容 API 变更
├── MINOR: 向后兼容新增功能
└── PATCH: Bug 修复
自动版本推进:
feature 合并 → SNAPSHOT 版本
main 发布 → 正式版本 + Git Tag
博客18:不同技术栈项目流水线实战
18.1 Java Spring Boot 项目
(见博客3 完整 Jenkinsfile 示例)
构建工具: Maven 3.9 | 框架: Spring Boot 3.x | JDK: 17
18.2 Node.js React 前端项目
groovy
pipeline {
agent { label 'build' }
tools { nodejs 'NodeJS-20' }
stages {
stage('Install') {
steps { sh 'npm ci' }
}
stage('Test') {
steps { sh 'npm run test -- --watchAll=false --coverage' }
post { always { junit 'coverage/junit.xml' } }
}
stage('Build') {
steps { sh 'npm run build' }
}
stage('Docker') {
steps {
sh 'docker build -t ${REGISTRY}/frontend:${BUILD_NUMBER} .'
sh 'docker push ${REGISTRY}/frontend:${BUILD_NUMBER}'
}
}
}
}
博客19:Jenkins 性能优化与故障排查
19.1 性能调优
bash
# 1. JVM 参数优化(针对 4GiB 内存节点)
cat > /etc/systemd/system/jenkins.service.d/override.conf << 'EOF'
[Service]
Environment="JAVA_OPTS=-Xms512m -Xmx2048m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+ExplicitGCInvokesConcurrent \
-Dfile.encoding=UTF-8 \
-Djava.net.preferIPv4Stack=true \
-Dhudson.model.UpdateCenter.never=true"
EOF
systemctl daemon-reload && systemctl restart jenkins
# 2. 查看当前 JVM 内存使用
# Manage Jenkins → System Information → 查看 java.vm.* 参数
# 或通过 Script Console:
def runtime = Runtime.getRuntime()
def used = (runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024
def total = runtime.totalMemory() / 1024 / 1024
def max = runtime.maxMemory() / 1024 / 1024
println "Used: ${used}MB / Total: ${total}MB / Max: ${max}MB"
19.2 常见故障排查
bash
# 故障1:构建队列积压,Job 长时间等待
# 原因: 没有可用的 Executor
# 排查:
# Manage Jenkins → Nodes → 查看各节点 Executor 占用情况
# 故障2:Git clone 超时
# 排查:
ssh -v git@gitea.company.com # 验证 SSH 连通性
git config --global http.timeout 300 # 增大超时
# 故障3:Maven 下载依赖慢
# 解决: 配置 Nexus 私服作为 Maven 镜像(见博客7)
# 故障4:构建节点磁盘满
# 排查:
df -h /opt/jenkins-agent
du -sh /opt/jenkins-agent/workspace/* | sort -rh | head -10
# 解决: Pipeline 中加 cleanWs() + Manage Jenkins → Workspaces → 手动清理
# 故障5:Jenkins 重启后插件丢失
# 解决: 备份 JENKINS_HOME
rsync -av /var/lib/jenkins/ /backup/jenkins-$(date +%Y%m%d)/
19.3 备份恢复方案
bash
# 备份脚本
cat > /usr/local/bin/jenkins-backup.sh << 'EOF'
#!/bin/bash
JENKINS_HOME=/var/lib/jenkins
BACKUP_DIR=/backup/jenkins
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_PATH="${BACKUP_DIR}/${DATE}"
mkdir -p ${BACKUP_PATH}
# 备份关键目录(排除 workspace 和 builds 目录,节省空间)
rsync -av --exclude='workspace' --exclude='builds' \
${JENKINS_HOME}/ ${BACKUP_PATH}/
# 保留最近7天备份
find ${BACKUP_DIR} -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;
echo "备份完成: ${BACKUP_PATH}"
EOF
chmod +x /usr/local/bin/jenkins-backup.sh
# 加入 crontab 每天凌晨2点执行
echo "0 2 * * * /usr/local/bin/jenkins-backup.sh >> /var/log/jenkins-backup.log 2>&1" | crontab -
博客20:SQL 自动化审查与 SOAR 集成
20.1 SQL 审查工具集成
bash
# 安装 Archery(开源 SQL 审核平台)或集成 Inception
# 本篇以 Yearning SQL 审核平台为例(Docker 部署)
cat > /opt/yearning/docker-compose.yml << 'EOF'
version: '3.8'
services:
yearning:
image: chaiyd/yearning:latest
container_name: yearning
ports:
- "8000:8000"
environment:
MYSQL_ADDR: yearning-db:3306
MYSQL_USER: yearning
MYSQL_PASSWORD: Yearning@2026
MYSQL_DB: yearning
depends_on:
- yearning-db
networks:
- yearning-net
yearning-db:
image: mysql:8.0
container_name: yearning-db
environment:
MYSQL_ROOT_PASSWORD: Root@2026
MYSQL_USER: yearning
MYSQL_PASSWORD: Yearning@2026
MYSQL_DATABASE: yearning
volumes:
- yearning-db-data:/var/lib/mysql
networks:
- yearning-net
volumes:
yearning-db-data:
networks:
yearning-net:
driver: bridge
EOF
在 Pipeline 中集成 SQL 审核:
groovy
stage('SQL Review') {
when {
// 仅存在 SQL 变更文件时执行
changeset "src/main/resources/db/migration/**/*.sql"
}
steps {
script {
// 收集本次变更的 SQL 文件
def sqlFiles = sh(
script: 'git diff --name-only HEAD~1 HEAD -- "*.sql"',
returnStdout: true
).trim().split('\n')
sqlFiles.each { file ->
if (file) {
echo "SQL 审核: ${file}"
// 调用 Yearning API 提交 SQL 审核工单
sh """
curl -X POST http://yearning:8000/api/query/audit \
-H 'Content-Type: application/json' \
-d '{
"sql": "$(cat ${file} | tr -d '\\n' | tr '"' "'")",
"db_id": 1,
"schema": "petclinic"
}'
"""
}
}
}
}
}
附录A:环境信息汇总
A.1 服务访问地址
| 服务 | 地址 | 账号 |
|---|---|---|
| Jenkins Master | http://121.91.168.17:8080 | admin/Admin@2026 |
| SonarQube | http://94.74.100.108:9000 | admin/(修改后的密码) |
| Nexus 3 | http://119.12.167.110:8081 | admin/(初始密码查 Docker 卷) |
A.2 内网 IP 速查
| 节点 | 内网IP | 用途 |
|---|---|---|
| jk-01 | 192.168.0.198 | Jenkins Master(Pipeline 内引用此 IP) |
| jk-02 | 192.168.0.134 | SonarQube |
| jk-03 | 192.168.0.117 | Nexus 制品库 |
| jk-04 | 192.168.0.103 | Build Agent |
A.3 关键路径
Jenkins Home: /var/lib/jenkins/
Jenkins 日志: journalctl -u jenkins -f
Jenkins 插件目录: /var/lib/jenkins/plugins/
Jenkins 工作空间: /var/lib/jenkins/workspace/(Master)
Agent 工作目录: /opt/jenkins-agent/(Agent 节点)
SonarQube 数据: /opt/sonarqube/(Docker 卷)
Nexus 数据: /opt/nexus/(Docker 卷)
附录B:踩坑汇总
| # | 问题 | 原因 | 解决方案 |
|---|---|---|---|
| 1 | APT GPG 报错 | gpg no tty | 加 --batch --yes 参数 |
| 2 | Jenkins 内存溢出 | 默认 JVM 堆太小 | 配置 -Xmx2048m |
| 3 | Agent SSH 认证失败 | authorized_keys 权限错误 | chmod 600 authorized_keys |
| 4 | Docker 权限拒绝 | jenkins 不在 docker 组 | usermod -aG docker jenkins |
| 5 | SonarQube 启动失败 | vm.max_map_count 太小 | sysctl -w vm.max_map_count=262144 |
| 6 | Maven 下载超时 | 网络不稳定 | 配置 Nexus 私服代理 |
| 7 | Webhook 触发 403 | CSRF 保护 + 反向代理 | 开启 proxy compatibility |
| 8 | Pipeline 构建积压 | Master executors=0 + Agent 离线 | 检查 Agent 状态;增加 Executor 数量 |
| 9 | Nexus Docker Registry push 失败 | HTTP Registry 未加入 insecure | 在 daemon.json 加 insecure-registries |
| 10 | 质量门禁一直等待 | SonarQube 与 Webhook 未配置 | 配置 SQ → Webhooks → Jenkins URL |