Jenkins
Jenkins 是一款开源的持续集成 / 持续交付(CI/CD)自动化服务器,用 Java 编写,通过插件扩展可以对接几乎任何构建、测试、部署工具。它是目前使用最广泛的 CI/CD 平台之一。
核心概念:
| 概念 | 说明 |
|---|---|
| Job / Project | 一个可执行的任务单元,定义了要做什么 |
| Build | Job 的一次执行实例 |
| Pipeline | 用代码描述的完整 CI/CD 流程(推荐方式) |
| Stage | Pipeline 中的一个阶段,如 Build、Test、Deploy |
| Step | Stage 内的单个操作 |
| Node / Agent | 执行构建任务的机器(Master 或 Worker) |
| Executor | Node 上并发执行构建的槽位数量 |
| Workspace | 构建时的工作目录,存放源码和产物 |
| Plugin | 插件,Jenkins 功能的主要扩展方式(目前 1800+) |
| Jenkinsfile | 用代码定义 Pipeline 的文件,存放在代码仓库中 |
| Credential | 安全存储的凭据(密码、Token、SSH Key 等) |
| Artifact | 构建产物,如 JAR、APK、Docker 镜像 |
| Trigger | 触发构建的条件,如 Push、定时、上游 Job 完成 |
安装
方式一:Docker 安装(推荐)
# 创建数据卷目录
mkdir -p /var/jenkins_home
chown -R 1000:1000 /var/jenkins_home
# 启动 Jenkins(含 Docker-in-Docker 支持)
docker run -d \
--name jenkins \
--restart unless-stopped \
-p 8080:8080 \
-p 50000:50000 \
-v /var/jenkins_home:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
jenkins/jenkins:lts-jdk17
# 查看初始管理员密码
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
端口说明:
8080为 Web UI,50000为 Agent(Inbound)连接端口。
方式二:Docker Compose
# docker-compose.yml
version: '3.8'
services:
jenkins:
image: jenkins/jenkins:lts-jdk17
container_name: jenkins
restart: unless-stopped
ports:
- "8080:8080"
- "50000:50000"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
environment:
- JAVA_OPTS=-Duser.timezone=Asia/Shanghai
volumes:
jenkins_home:
docker compose up -d
docker compose logs -f jenkins
方式三:Linux 原生安装(Ubuntu/Debian)
# 添加 Jenkins 仓库
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key \
| sudo tee /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
https://pkg.jenkins.io/debian-stable binary/" \
| sudo tee /etc/apt/sources.list.d/jenkins.list > /dev/null
# 安装 Java 和 Jenkins
sudo apt update
sudo apt install -y fontconfig openjdk-17-jre jenkins
# 启动服务
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins
# 查看初始密码
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
方式四:Linux 原生安装(CentOS/RHEL)
sudo wget -O /etc/yum.repos.d/jenkins.repo \
https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
sudo dnf install -y java-17-openjdk jenkins
sudo systemctl enable --now jenkins
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
初始化向导
- 浏览器访问
http://localhost:8080 - 输入初始管理员密码
- 选择 Install suggested plugins(安装推荐插件)
- 创建第一个管理员账号
- 配置 Jenkins URL(保持默认即可)
基础配置
全局工具配置
路径:Manage Jenkins → Tools
# 配置 JDK
Name: JDK17
JAVA_HOME: /usr/lib/jvm/java-17-openjdk
# 配置 Maven
Name: Maven3
Install automatically: 勾选
Version: 3.9.x
# 配置 Node.js(需安装 NodeJS 插件)
Name: NodeJS20
Install automatically: 勾选
Version: 20.x
系统配置
路径:Manage Jenkins → System
# 重要配置项
Jenkins URL: http://your-jenkins-host:8080/
System Admin e-mail: admin@example.com
# of executors: 4(建议设为 CPU 核心数)
管理凭据
路径:Manage Jenkins → Credentials → System → Global credentials
| 凭据类型 | 用途 |
|---|---|
| Username with password | Git 账号、Maven 仓库等 |
| SSH Username with private key | SSH 登录服务器、Git SSH |
| Secret text | API Token、密码字符串 |
| Secret file | 配置文件、证书文件 |
| Certificate | PKCS#12 证书 |
# 添加 Git SSH Key
Kind: SSH Username with private key
ID: github-ssh-key ← 在 Pipeline 中通过此 ID 引用
Username: git
Private Key: 粘贴私钥内容
配置 Git
# 在 Jenkins 服务器上配置全局 Git 用户信息
git config --global user.name "Jenkins"
git config --global user.email "jenkins@example.com"
Pipeline 基础
Pipeline 是 Jenkins 推荐的任务定义方式,将整个 CI/CD 流程写成代码(Pipeline as Code),存储在项目根目录的 Jenkinsfile 中,随代码版本管理。
两种语法风格
| 风格 | 特点 | 推荐程度 |
|---|---|---|
| Declarative(声明式) | 结构固定、可读性强、有语法校验 | ★★★★★ 推荐 |
| Scripted(脚本式) | 完整 Groovy 语法、灵活但复杂 | 复杂场景使用 |
Declarative Pipeline 详解
基本结构
pipeline {
agent any // 在任意可用 Agent 上运行
environment { // 环境变量
APP_NAME = 'my-app'
VERSION = '1.0.0'
}
options { // 全局选项
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(logRotator(numToKeepStr: '10'))
}
triggers { // 触发器
cron('H 2 * * 1-5') // 工作日凌晨 2 点
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/*.xml'
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh './deploy.sh'
}
}
}
post { // 构建后动作
success {
echo '构建成功!'
}
failure {
mail to: 'team@example.com', subject: '构建失败', body: "${env.BUILD_URL}"
}
always {
cleanWs() // 清理工作目录
}
}
}
agent 指令
// 在任意可用节点运行
agent any
// 不分配 Agent(常用于顶层,各 Stage 单独指定)
agent none
// 指定标签的节点
agent { label 'linux && docker' }
// 在 Docker 容器中运行
agent {
docker {
image 'node:20-alpine'
args '-v /tmp:/tmp'
reuseNode true // 复用上层节点(节省切换开销)
}
}
// 使用 Dockerfile 构建镜像
agent {
dockerfile {
filename 'ci/Dockerfile.build'
dir 'ci'
args '--build-arg ENV=ci'
}
}
environment 指令
environment {
// 普通变量
APP_ENV = 'production'
// 引用凭据(自动注入为环境变量)
GIT_TOKEN = credentials('github-token') // Secret text → 单变量
DOCKER_CREDS = credentials('dockerhub-creds') // user/pass → 生成 _USR / _PSW 两个变量
SSH_KEY = credentials('deploy-ssh-key') // SSH Key → 文件路径
}
steps {
sh 'echo $APP_ENV'
sh 'echo $DOCKER_CREDS_USR' // dockerhub 用户名
sh 'echo $DOCKER_CREDS_PSW' // dockerhub 密码(日志中自动打码)
sh 'docker login -u $DOCKER_CREDS_USR -p $DOCKER_CREDS_PSW'
}
when 条件
stage('Deploy to Prod') {
when {
// 单个条件
branch 'main'
// 多个条件(AND 关系)
allOf {
branch 'main'
not { changeRequest() } // 不是 PR
environment name: 'DEPLOY', value: 'true'
}
// 任一条件(OR 关系)
anyOf {
branch 'main'
branch 'release/*'
}
// 表达式条件
expression { return params.DEPLOY_ENABLED == true }
// 文件变更
changeset 'src/backend/**'
// Tag
tag 'v*'
}
steps { ... }
}
并行 Stage
stage('Parallel Tests') {
parallel {
stage('Unit Tests') {
steps {
sh 'mvn test -pl unit-tests'
}
}
stage('Integration Tests') {
steps {
sh 'mvn test -pl integration-tests'
}
}
stage('E2E Tests') {
agent { label 'selenium' }
steps {
sh 'npm run e2e'
}
}
}
}
参数化构建
parameters {
string(name: 'BRANCH', defaultValue: 'main', description: '分支名')
booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '跳过测试')
choice(name: 'ENV',
choices: ['dev', 'staging', 'prod'], description: '部署环境')
password(name: 'DB_PASS', defaultValue: '', description: '数据库密码')
text(name: 'RELEASE_NOTES',defaultValue: '', description: '发布说明')
file(name: 'CONFIG_FILE', description: '上传配置文件')
}
// 使用参数
steps {
sh "git checkout ${params.BRANCH}"
script {
if (!params.SKIP_TESTS) {
sh 'mvn test'
}
}
}
post 动作
post {
always { echo '始终执行' }
success { echo '构建成功时执行' }
failure { echo '构建失败时执行' }
unstable { echo '测试不稳定时执行(测试有失败但不是错误)' }
aborted { echo '构建被中止时执行' }
changed { echo '构建结果与上次不同时执行' }
fixed { echo '从失败变为成功时执行' }
regression{ echo '从成功变为失败时执行' }
cleanup { echo '在所有 post 动作之后执行(用于清理)' }
}
options 选项
options {
// 超时控制
timeout(time: 1, unit: 'HOURS')
// 并发控制
disableConcurrentBuilds(abortPrevious: true) // 新构建触发时取消旧构建
// 构建历史保留
buildDiscarder(logRotator(
numToKeepStr: '20', // 保留最近 20 次
artifactNumToKeepStr: '5' // 产物只保留最近 5 次
))
// 跳过默认 Checkout(手动控制检出时机)
skipDefaultCheckout()
// 时间戳
timestamps()
// ANSI 彩色输出(需 AnsiColor 插件)
ansiColor('xterm')
// 重试(适用于 Stage 级别)
retry(3)
}
Scripted Pipeline
适合需要复杂逻辑、循环、动态 Stage 的场景。
node('linux') {
def mvnHome = tool 'Maven3'
def javaHome = tool 'JDK17'
withEnv(["PATH+MVN=${mvnHome}/bin", "JAVA_HOME=${javaHome}"]) {
try {
stage('Checkout') {
checkout scm
}
stage('Build') {
sh 'mvn clean package -DskipTests'
}
// 动态生成 Stage(Declarative 不支持)
def services = ['auth-service', 'user-service', 'order-service']
stage('Deploy Services') {
for (svc in services) {
sh "kubectl set image deployment/${svc} ${svc}=${IMAGE}:${BUILD_NUMBER}"
}
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
throw e
} finally {
cleanWs()
}
}
}
常用内置变量
| 变量 | 说明 |
|---|---|
env.BUILD_NUMBER |
当前构建编号 |
env.BUILD_ID |
构建 ID(同 BUILD_NUMBER) |
env.BUILD_URL |
构建的完整 URL |
env.JOB_NAME |
Job 名称 |
env.JOB_BASE_NAME |
Job 名称(不含路径) |
env.WORKSPACE |
工作目录路径 |
env.BRANCH_NAME |
分支名(Multibranch Pipeline) |
env.TAG_NAME |
Tag 名(Tag 触发时) |
env.GIT_COMMIT |
当前 Git commit SHA |
env.GIT_BRANCH |
Git 分支 |
env.CHANGE_ID |
PR/MR 编号 |
currentBuild.result |
当前构建结果(SUCCESS/FAILURE/UNSTABLE) |
currentBuild.currentResult |
实时构建结果 |
currentBuild.duration |
构建耗时(毫秒) |
currentBuild.displayName |
构建显示名 |
常用 Steps
文件与目录
// 创建目录
sh 'mkdir -p target/reports'
// 写文件
writeFile file: 'version.txt', text: "1.0.${BUILD_NUMBER}"
// 读文件
def content = readFile 'config.properties'
// 读 JSON
def data = readJSON file: 'package.json'
echo "Version: ${data.version}"
// 读 YAML
def cfg = readYaml file: 'values.yaml'
// 文件存在检查
if (fileExists('dist/index.html')) {
archiveArtifacts artifacts: 'dist/**'
}
// 复制文件
sh 'cp -r dist/ /var/www/html/'
归档与测试报告
// 归档构建产物
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
// JUnit 测试报告
junit 'target/surefire-reports/**/*.xml'
// HTML 报告(需 HTML Publisher 插件)
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/site/jacoco',
reportFiles: 'index.html',
reportName: 'Code Coverage'
])
// 代码覆盖率(需 JaCoCo 插件)
jacoco execPattern: 'target/jacoco.exec'
通知
// 邮件(需配置 SMTP)
mail(
to: 'dev@example.com',
subject: "Build ${currentBuild.result}: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "详情:${env.BUILD_URL}"
)
// 企业微信(需 Qy Wechat Notification 插件)
qyWechatNotification(
webhookUrl: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx',
message: "Jenkins 构建 ${currentBuild.result}"
)
// 钉钉(需 DingTalk 插件)
dingtalk(
robot: 'dingtalk-robot-id',
type: 'MARKDOWN',
title: "Jenkins 构建通知",
text: ["# ${env.JOB_NAME} 构建${currentBuild.result == 'SUCCESS' ? '成功' : '失败'}"]
)
用户输入
stage('Confirm Deploy to Prod') {
steps {
script {
// 等待用户确认(超时自动终止)
def result = input(
message: '确认部署到生产环境?',
ok: '确认部署',
submitter: 'admin,ops-team', // 只允许这些用户操作
parameters: [
choice(name: 'ROLLBACK_VERSION', choices: ['1.0', '0.9', '0.8'], description: '回滚版本(取消时用)')
]
)
echo "用户选择:${result}"
}
}
}
触发器配置
SCM 轮询
triggers {
// 每 5 分钟检查一次 SCM 变更
pollSCM('H/5 * * * *')
}
定时构建(Cron 语法)
triggers {
// 每天凌晨 2 点
cron('0 2 * * *')
// 工作日每小时的第 30 分钟(H 表示哈希随机,避免同时触发)
cron('H 30 * * 1-5')
}
| Cron 表达式 | 说明 |
|---|---|
H * * * * |
每小时(随机分钟) |
H/15 * * * * |
每 15 分钟 |
H 8-17 * * 1-5 |
工作日 8-17 点每小时 |
@daily |
每天 |
@weekly |
每周 |
Webhook 触发(GitLab/GitHub)
// 需安装对应插件(Generic Webhook Trigger / GitLab / GitHub)
triggers {
gitlab(triggerOnPush: true, triggerOnMergeRequest: true, branchFilterType: 'All')
}
GitLab 配置步骤:
1. Jenkins 安装 GitLab 插件
2. GitLab 项目 → Settings → Webhooks
3. URL 填 http://jenkins:8080/project/your-job
4. 勾选 Push events、Merge request events
5. 添加 Secret Token(对应 Jenkins 中的配置)
多分支 Pipeline(Multibranch Pipeline)
Multibranch Pipeline 会自动扫描代码仓库的所有分支和 PR,为每个分支单独创建 Job,对应分支根目录下的 Jenkinsfile。
创建步骤: 1. New Item → Multibranch Pipeline 2. Branch Sources → 选择 Git/GitHub/GitLab 3. 配置仓库 URL 和凭据 4. Scan Multibranch Pipeline Triggers → 设置扫描间隔
典型 Jenkinsfile 分支策略:
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('Test') {
steps {
sh 'mvn test'
}
}
stage('Deploy to Dev') {
when { branch 'develop' }
steps {
sh './deploy.sh dev'
}
}
stage('Deploy to Staging') {
when { branch 'release/*' }
steps {
sh './deploy.sh staging'
}
}
stage('Deploy to Prod') {
when {
allOf {
branch 'main'
not { changeRequest() }
}
}
steps {
sh './deploy.sh prod'
}
}
}
}
Jenkins + Docker 集成
在 Pipeline 中构建 Docker 镜像
pipeline {
agent any
environment {
REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
IMAGE_NAME = "${REGISTRY}/my-org/my-app"
IMAGE_TAG = "${env.BRANCH_NAME}-${env.BUILD_NUMBER}"
}
stages {
stage('Build Image') {
steps {
script {
docker.build("${IMAGE_NAME}:${IMAGE_TAG}", "--build-arg ENV=prod .")
}
}
}
stage('Push Image') {
steps {
script {
docker.withRegistry("https://${REGISTRY}", 'aliyun-registry-creds') {
docker.image("${IMAGE_NAME}:${IMAGE_TAG}").push()
docker.image("${IMAGE_NAME}:${IMAGE_TAG}").push('latest')
}
}
}
}
stage('Deploy') {
steps {
sh """
docker pull ${IMAGE_NAME}:${IMAGE_TAG}
docker stop my-app || true
docker rm my-app || true
docker run -d \\
--name my-app \\
--restart unless-stopped \\
-p 8080:8080 \\
${IMAGE_NAME}:${IMAGE_TAG}
"""
}
}
}
post {
always {
sh "docker rmi ${IMAGE_NAME}:${IMAGE_TAG} || true"
}
}
}
使用 Docker Agent 运行构建
pipeline {
agent none // 顶层不分配 Agent
stages {
stage('Build Java') {
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v $HOME/.m2:/root/.m2' // 挂载 Maven 缓存
}
}
steps {
sh 'mvn clean package'
}
}
stage('Build Frontend') {
agent {
docker {
image 'node:20-alpine'
args '-v $HOME/.npm:/root/.npm'
}
}
steps {
sh 'npm ci && npm run build'
}
}
}
}
Jenkins + Kubernetes 集成
使用 Kubernetes 插件在 K8s 集群中动态创建 Pod 作为构建 Agent,构建完成后自动销毁。
pipeline {
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: maven
image: maven:3.9-eclipse-temurin-17
command: ["sleep", "infinity"]
volumeMounts:
- name: maven-cache
mountPath: /root/.m2
- name: docker
image: docker:dind
securityContext:
privileged: true
volumes:
- name: maven-cache
persistentVolumeClaim:
claimName: maven-cache-pvc
'''
}
}
stages {
stage('Build') {
steps {
container('maven') {
sh 'mvn clean package'
}
}
}
stage('Build Image') {
steps {
container('docker') {
sh 'docker build -t my-app:latest .'
}
}
}
}
}
常用插件推荐
| 分类 | 插件名 | 说明 |
|---|---|---|
| SCM | Git, GitHub, GitLab | 代码仓库集成 |
| Pipeline | Pipeline, Multibranch Pipeline | Pipeline 核心 |
| UI | Blue Ocean | 现代化 Pipeline 可视化界面 |
| 构建工具 | Maven Integration, NodeJS, Gradle | 构建工具集成 |
| Docker | Docker, Docker Pipeline | Docker 构建和发布 |
| K8s | Kubernetes | K8s 动态 Agent |
| 代码质量 | SonarQube Scanner | 代码静态分析 |
| 测试报告 | JUnit, HTML Publisher, JaCoCo | 测试和覆盖率报告 |
| 制品 | Nexus Artifact Uploader, Artifactory | 制品管理 |
| 通知 | Email Extension, DingTalk, Qy Wechat | 消息通知 |
| 凭据 | Credentials Binding | 安全注入凭据 |
| 安全 | Role-based Authorization Strategy | 基于角色的权限控制 |
| 时间戳 | Timestamper | 构建日志添加时间戳 |
| 工作空间 | Workspace Cleanup | 构建后清理工作目录 |
| 参数 | Extended Choice Parameter | 扩展参数类型 |
分布式构建(Master-Agent 架构)
Jenkins Master 负责调度,构建任务在 Agent(Worker)上执行。
添加 Agent(SSH 方式)
- Manage Jenkins → Nodes → New Node
- 填写节点名、Remote root directory(如
/home/jenkins) - Labels:用于 Pipeline 中的
agent { label '...' } - Launch method:Launch agents via SSH
- 填写 Agent 机器的 IP、SSH 凭据
Agent 机器需要预先安装 Java:
sudo apt install -y openjdk-17-jre
sudo useradd -m -s /bin/bash jenkins
添加 Agent(JNLP/Inbound 方式,适合容器)
# 在 Agent 机器上执行(从 Jenkins UI 获取命令)
java -jar agent.jar \
-url http://jenkins-master:8080/ \
-secret <secret-token> \
-name "docker-agent" \
-workDir "/home/jenkins/workspace"
节点标签策略
// 只在有 docker 标签的节点运行
agent { label 'docker' }
// 在有 linux 和 high-memory 标签的节点运行
agent { label 'linux && high-memory' }
// 在 linux 或 mac 节点上运行
agent { label 'linux || mac' }
共享库(Shared Library)
共享库允许跨 Job 复用 Pipeline 代码,将公共逻辑提取为库函数。
目录结构
jenkins-shared-library/
├── vars/ # 全局变量/函数(在 Pipeline 中直接调用)
│ ├── buildMaven.groovy
│ ├── deployToK8s.groovy
│ └── notifyDingTalk.groovy
├── src/ # 辅助类(Groovy 类)
│ └── org/example/
│ └── Utils.groovy
└── resources/ # 静态资源
└── templates/
└── deploy.yaml
编写共享函数(vars/buildMaven.groovy)
def call(Map config = [:]) {
def goals = config.get('goals', 'clean package')
def skipTests = config.get('skipTests', false)
sh "mvn ${goals}${skipTests ? ' -DskipTests' : ''}"
}
注册共享库
Manage Jenkins → System → Global Pipeline Libraries
Name: shared-lib
Default version: main
Retrieval method: Modern SCM → Git
Project Repository: http://gitlab.example.com/devops/shared-lib.git
Credentials: gitlab-creds
在 Pipeline 中使用
@Library('shared-lib') _ // 引入共享库
pipeline {
agent any
stages {
stage('Build') {
steps {
buildMaven goals: 'clean package', skipTests: true
}
}
stage('Deploy') {
steps {
deployToK8s namespace: 'production', image: "myapp:${BUILD_NUMBER}"
}
}
}
post {
failure {
notifyDingTalk message: "构建失败:${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
}
}
安全配置
基于角色的权限控制(Role Strategy 插件)
- 安装 Role-based Authorization Strategy 插件
- Manage Jenkins → Security → Authorization → 选择 Role-Based Strategy
- Manage Jenkins → Manage and Assign Roles
| 角色 | 权限 |
|---|---|
| admin | 所有权限 |
| developer | 构建、查看、取消 |
| viewer | 只读查看 |
| deployer | 仅允许触发特定 Job |
CSRF 保护
Jenkins 默认启用 CSRF 保护,API 调用需携带 Crumb Token:
# 获取 Crumb
CRUMB=$(curl -s -u admin:token \
"http://jenkins:8080/crumbIssuer/api/json" \
| jq -r '.crumb')
# 触发构建
curl -s -X POST \
-u admin:token \
-H "Jenkins-Crumb: ${CRUMB}" \
"http://jenkins:8080/job/my-job/build"
Jenkins API 使用
REST API 常用操作
# 获取 Job 列表
curl -s -u admin:token http://jenkins:8080/api/json | jq '.jobs[].name'
# 触发构建(无参数)
curl -X POST -u admin:token \
http://jenkins:8080/job/my-job/build
# 触发参数化构建
curl -X POST -u admin:token \
"http://jenkins:8080/job/my-job/buildWithParameters?BRANCH=main&ENV=prod"
# 查询构建状态
curl -s -u admin:token \
http://jenkins:8080/job/my-job/lastBuild/api/json | jq '.result'
# 获取构建日志
curl -s -u admin:token \
http://jenkins:8080/job/my-job/lastBuild/consoleText
# 停止构建
curl -X POST -u admin:token \
http://jenkins:8080/job/my-job/lastBuild/stop
使用 API Token
- 用户头像 → Configure → API Token → Add new Token
- 保存 Token(只显示一次)
- 用于 Basic Auth:
curl -u username:token
常见完整示例
Java Maven 项目 CI/CD
pipeline {
agent any
tools {
maven 'Maven3'
jdk 'JDK17'
}
environment {
NEXUS_URL = 'http://nexus.example.com'
SONAR_URL = 'http://sonar.example.com'
DOCKER_REGISTRY = 'registry.example.com'
}
stages {
stage('Checkout') {
steps {
checkout scm
script {
env.APP_VERSION = sh(script: "mvn help:evaluate -Dexpression=project.version -q -DforceStdout", returnStdout: true).trim()
echo "Version: ${env.APP_VERSION}"
}
}
}
stage('Build') {
steps {
sh 'mvn clean compile'
}
}
stage('Unit Test') {
steps {
sh 'mvn test'
}
post {
always {
junit 'target/surefire-reports/**/*.xml'
jacoco execPattern: 'target/jacoco.exec',
classPattern: 'target/classes',
sourcePattern: 'src/main/java'
}
}
}
stage('Code Analysis') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
stage('Package') {
steps {
sh 'mvn package -DskipTests'
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
}
}
stage('Build & Push Docker Image') {
when { anyOf { branch 'main'; branch 'develop' } }
steps {
script {
def image = docker.build("${DOCKER_REGISTRY}/my-app:${APP_VERSION}-${BUILD_NUMBER}")
docker.withRegistry("https://${DOCKER_REGISTRY}", 'registry-creds') {
image.push()
image.push('latest')
}
}
}
}
stage('Deploy to Dev') {
when { branch 'develop' }
steps {
sh "kubectl set image deployment/my-app my-app=${DOCKER_REGISTRY}/my-app:${APP_VERSION}-${BUILD_NUMBER} -n dev"
sh "kubectl rollout status deployment/my-app -n dev --timeout=120s"
}
}
stage('Deploy to Prod') {
when { branch 'main' }
steps {
input message: "确认部署 v${APP_VERSION} 到生产环境?", ok: '确认'
sh "kubectl set image deployment/my-app my-app=${DOCKER_REGISTRY}/my-app:${APP_VERSION}-${BUILD_NUMBER} -n prod"
sh "kubectl rollout status deployment/my-app -n prod --timeout=300s"
}
}
}
post {
success {
slackSend(color: 'good', message: "✅ 构建成功:${JOB_NAME} #${BUILD_NUMBER} - ${APP_VERSION}")
}
failure {
slackSend(color: 'danger', message: "❌ 构建失败:${JOB_NAME} #${BUILD_NUMBER}")
}
always {
cleanWs()
}
}
}
Node.js 前端项目 CI/CD
pipeline {
agent {
docker {
image 'node:20-alpine'
args '-v $HOME/.npm:/root/.npm'
}
}
environment {
CI = 'true'
}
stages {
stage('Install') {
steps {
sh 'npm ci --prefer-offline'
}
}
stage('Lint') {
steps {
sh 'npm run lint'
}
}
stage('Test') {
steps {
sh 'npm run test -- --coverage --watchAll=false'
}
post {
always {
publishHTML([
reportDir: 'coverage/lcov-report',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
stage('Build') {
steps {
sh 'npm run build'
archiveArtifacts artifacts: 'dist/**', fingerprint: true
}
}
stage('Deploy') {
when { branch 'main' }
steps {
sh 'aws s3 sync dist/ s3://my-bucket/ --delete'
sh 'aws cloudfront create-invalidation --distribution-id XXXXX --paths "/*"'
}
}
}
}
备份与维护
备份 Jenkins 数据
# 备份 JENKINS_HOME(排除构建日志和临时文件)
tar -czf jenkins-backup-$(date +%Y%m%d).tar.gz \
--exclude='jobs/*/builds' \
--exclude='workspace' \
--exclude='war' \
--exclude='.gradle' \
/var/jenkins_home
# 使用 ThinBackup 插件(推荐)
# Manage Jenkins → ThinBackup → 配置备份路径和计划
关键配置文件:
$JENKINS_HOME/
├── config.xml # 主配置
├── credentials.xml # 凭据
├── jobs/ # Job 配置
│ └── my-job/
│ └── config.xml
├── plugins/ # 插件
└── secrets/ # 加密密钥(务必备份)
升级 Jenkins
# Docker 方式升级
docker pull jenkins/jenkins:lts-jdk17
docker stop jenkins
docker rm jenkins
# 重新运行(数据卷不变,数据保留)
docker run -d --name jenkins ... jenkins/jenkins:lts-jdk17
清理构建历史
// 在 Job 的 options 中配置
options {
buildDiscarder(logRotator(
daysToKeepStr: '30', // 保留 30 天
numToKeepStr: '100', // 最多保留 100 次
artifactDaysToKeepStr:'7', // 产物保留 7 天
artifactNumToKeepStr: '5' // 产物最多 5 次
))
}
常见问题排查
构建卡住不动
# 查看 Jenkins 线程 dump
curl -u admin:token http://jenkins:8080/threadDump
# 检查 Executor 是否被占用
# Manage Jenkins → Nodes → 查看各节点 Executor 状态
OutOfMemoryError
# 增大 JVM 堆内存
# Docker 方式:
docker run -e JAVA_OPTS="-Xmx2g -Xms512m" jenkins/jenkins:lts-jdk17
# systemd 方式:
# /etc/default/jenkins
JAVA_ARGS="-Xmx2g -Xms512m -XX:+UseG1GC"
插件更新失败
# 手动下载插件(.hpi 文件)从 https://plugins.jenkins.io
# 上传:Manage Jenkins → Plugins → Advanced → Deploy Plugin
工作空间权限问题
# 修复 Jenkins 工作目录权限
chown -R jenkins:jenkins /var/lib/jenkins/workspace/
chmod -R 755 /var/lib/jenkins/workspace/
Pipeline 语法验证
Jenkins 内置语法校验工具:
- http://jenkins:8080/pipeline-syntax/ — Pipeline Snippet Generator(代码片段生成器)
- http://jenkins:8080/directive-generator/ — Declarative Directive Generator
- 在 Jenkinsfile 编辑器中使用 Validate 按钮
最佳实践
- Pipeline as Code:所有 Pipeline 定义写在
Jenkinsfile中并提交到代码仓库,而不是在 UI 中配置。 - 最小权限原则:为 Agent 和服务账号配置最小必要权限,避免使用 root。
- 凭据管理:所有密码、Token、密钥通过 Jenkins Credentials 管理,严禁硬编码到 Jenkinsfile。
- 构建隔离:每次构建开始前清理工作空间(
cleanWs()),避免上次构建残留干扰。 - 超时设置:为每个 Pipeline 和 Stage 设置合理的
timeout,防止僵尸构建占用资源。 - 并发控制:使用
disableConcurrentBuilds()防止同一 Job 并发运行导致资源竞争。 - 制品版本化:镜像和构建产物使用
版本号-构建号作为 Tag,保证可追溯。 - 共享库:将公共构建逻辑提取到 Shared Library,避免在多个 Jenkinsfile 中重复代码。
- 蓝绿/金丝雀发布:生产部署配合
input步骤做人工确认,重要版本分阶段发布。 - 监控告警:接入 Prometheus + Grafana 监控 Jenkins 指标(构建队列、构建时长、失败率)。