Jenkins 运维管理实战博客大纲

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
相关推荐
X1A0RAN2 小时前
解决jenkins(本机部署或容器部署)安全机制【CSP】问题
jenkins·allure报告
艾莉丝努力练剑2 小时前
【Linux网络】网络层IP协议(三):网段划分(下)
linux·运维·服务器·网络·tcp/ip
尔染君子2 小时前
嵌入式Linux驱动开发(基于树莓派rasberrypi 5的LED驱动开发)
linux·运维·驱动开发
烧饼Fighting2 小时前
Jenkins自动化编译部署Spring Boot项目
spring boot·自动化·jenkins
上海云盾安全满满2 小时前
服务器CPU跑满的原因及解决办法
运维·服务器
tobias.b2 小时前
供电不稳定、无UPS/无双电源环境下服务器高可用完整方案
运维·服务器
serve the people2 小时前
Elasticsearch(3) show me some examples
大数据·elasticsearch·jenkins
团象科技2 小时前
从出海业务落地视角观察 海外服务器跑开源软件的实操逻辑演变
运维·服务器·开源软件
加成BUFF2 小时前
第七天 ROS《 参数服务器与Launch文件》
运维·ros·参数服务器