3.1 Jenkins核心概念
项目(Job/Project)
定义: 项目是Jenkins中的基本工作单元,定义了一系列要执行的任务和配置。每个项目包含构建步骤、触发条件、参数设置等。
项目类型:
自由风格项目(Freestyle Project)
- 最基本的项目类型
- 通过GUI配置构建步骤
- 适合简单的构建任务
特点: ✓ 配置简单直观 ✓ 适合初学者 ✗ 复杂逻辑难以实现 ✗ 版本控制困难
Pipeline项目
- 使用代码定义构建流程
- 支持复杂的工作流
- 可版本控制
groovy // Jenkinsfile示例 pipeline { agent any stages { stage('Build') { steps { echo 'Building...' } } stage('Test') { steps { echo 'Testing...' } } } }
3. 多配置项目(Multi-configuration Project) - 在多个环境中执行相同任务 - 支持矩阵构建 - 适合跨平台测试 “`
配置轴示例: 操作系统: [Linux, Windows, macOS] JDK版本: [8, 11, 17] 浏览器: [Chrome, Firefox, Safari]
4. **文件夹(Folder)**
- 组织和管理项目
- 支持嵌套结构
- 可设置文件夹级别的权限
### 构建(Build)
**定义:**
构建是项目的一次执行实例,包含完整的执行过程和结果。
**构建生命周期:**
排队等待 → 开始执行 → 运行步骤 → 收集结果 → 完成/失败
**构建状态:**
- 🔵 **成功(Success)**:所有步骤正常完成
- 🔴 **失败(Failure)**:构建过程中出现错误
- ⚠️ **不稳定(Unstable)**:构建完成但有测试失败
- ⚫ **中止(Aborted)**:构建被手动或自动取消
- ⚪ **未构建(Not Built)**:构建被跳过
**构建编号:**
格式:#1, #2, #3… 特殊标识: - LAST_SUCCESSFUL_BUILD - LAST_STABLE_BUILD - LAST_FAILED_BUILD
### 工作空间(Workspace)
**定义:**
工作空间是Jenkins为每个项目分配的文件系统目录,用于存储源代码、构建产物和临时文件。
**工作空间结构:**
$JENKINS_HOME/workspace/ ├── project-name/ # 项目工作空间 │ ├── src/ # 源代码 │ ├── target/ # 构建产物 │ ├── .git/ # Git仓库信息 │ └── temp/ # 临时文件 ├── project-name@2/ # 并发构建工作空间 └── project-name@tmp/ # 临时工作空间
**工作空间管理:**
```groovy
// Pipeline中的工作空间操作
pipeline {
agent any
stages {
stage('Checkout') {
steps {
// 检出代码到工作空间
checkout scm
}
}
stage('Build') {
steps {
// 在工作空间中执行构建
sh 'make build'
}
}
stage('Archive') {
steps {
// 归档构建产物
archiveArtifacts artifacts: 'target/*.jar'
}
}
}
post {
always {
// 清理工作空间
cleanWs()
}
}
}
节点(Node)
定义: 节点是可以执行Jenkins任务的机器,包括控制器节点和代理节点。
节点类型:
控制器节点(Controller Node)
- Jenkins主服务器
- 管理整个Jenkins实例
- 默认标签:”master”
职责: - 调度构建任务 - 管理用户界面 - 存储配置和结果 - 插件管理
代理节点(Agent Node)
- 执行具体构建任务
- 可以是物理机、虚拟机或容器
- 通过各种协议连接到控制器
连接方式: - JNLP (Java Network Launch Protocol) - SSH - Windows服务 - Docker容器
节点配置示例:
// 节点配置
node('linux && docker') {
stage('Build on Linux') {
sh 'docker build -t myapp .'
}
}
node('windows') {
stage('Build on Windows') {
bat 'msbuild MyApp.sln'
}
}
标签(Label)
定义: 标签用于标识节点的特性和能力,帮助Jenkins选择合适的节点执行任务。
标签类型:
操作系统标签:linux, windows, macos
架构标签:x86, x64, arm64
工具标签:docker, maven, gradle, nodejs
环境标签:dev, test, prod
性能标签:high-memory, ssd, gpu
标签表达式:
// 单个标签
agent { label 'linux' }
// 逻辑与
agent { label 'linux && docker' }
// 逻辑或
agent { label 'linux || windows' }
// 逻辑非
agent { label '!windows' }
// 复杂表达式
agent { label '(linux || windows) && docker && !gpu' }
3.2 构建触发器
手动触发
立即构建: - 通过Web界面点击”立即构建” - 通过API调用触发 - 通过CLI命令触发
# CLI触发构建
java -jar jenkins-cli.jar -s http://jenkins-server:8080/ build job-name
# API触发构建
curl -X POST http://jenkins-server:8080/job/job-name/build \
--user username:token
定时触发
Cron表达式:
格式:分 时 日 月 周
示例:
H/15 * * * * # 每15分钟
0 2 * * * # 每天凌晨2点
0 0 * * 0 # 每周日午夜
0 0 1 * * # 每月1号午夜
H H(0-7) * * * # 每天0-7点之间的随机时间
配置示例:
// Pipeline中的定时触发
pipeline {
agent any
triggers {
cron('H 2 * * *') // 每天凌晨2点左右
}
stages {
stage('Nightly Build') {
steps {
echo 'Running nightly build...'
}
}
}
}
SCM轮询
轮询配置:
H/5 * * * * # 每5分钟检查一次代码变更
H H/4 * * * # 每4小时检查一次
优化建议:
// 使用Webhook替代轮询
pipeline {
agent any
triggers {
// 不推荐:频繁轮询
// pollSCM('H/2 * * * *')
// 推荐:使用Webhook
githubPush()
}
}
Webhook触发
GitHub Webhook配置:
{
"url": "http://jenkins-server:8080/github-webhook/",
"content_type": "application/json",
"events": ["push", "pull_request"]
}
GitLab Webhook配置:
URL: http://jenkins-server:8080/project/project-name
Secret Token: [生成的令牌]
Trigger: Push events, Tag push events
上游项目触发
依赖关系配置:
// 项目A完成后触发项目B
pipeline {
agent any
triggers {
upstream(upstreamProjects: 'project-a', threshold: hudson.model.Result.SUCCESS)
}
stages {
stage('Deploy') {
steps {
echo 'Deploying after successful build of project-a'
}
}
}
}
3.3 构建步骤
Shell/批处理命令
Linux/macOS Shell:
#!/bin/bash
set -e # 遇到错误立即退出
# 环境检查
echo "Current directory: $(pwd)"
echo "Java version: $(java -version)"
echo "Available memory: $(free -h)"
# 构建操作
mvn clean compile
mvn test
mvn package
# 结果检查
if [ -f target/myapp.jar ]; then
echo "Build successful: target/myapp.jar created"
else
echo "Build failed: JAR file not found"
exit 1
fi
Windows批处理:
@echo off
setlocal enabledelayedexpansion
REM 环境检查
echo Current directory: %CD%
java -version
REM 构建操作
call mvn clean compile
if !errorlevel! neq 0 (
echo Maven compile failed
exit /b 1
)
call mvn test
if !errorlevel! neq 0 (
echo Maven test failed
exit /b 1
)
call mvn package
if !errorlevel! neq 0 (
echo Maven package failed
exit /b 1
)
echo Build completed successfully
构建工具集成
Maven构建:
<!-- pom.xml配置 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<configuration>
<includes>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
</includes>
<reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory>
</configuration>
</plugin>
</plugins>
</build>
Gradle构建:
// build.gradle配置
apply plugin: 'java'
apply plugin: 'jacoco'
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
finalizedBy jacocoTestReport
}
jacocoTestReport {
reports {
xml.enabled true
html.enabled true
}
}
测试执行
JUnit测试配置:
// Pipeline中的测试步骤
stage('Test') {
steps {
sh 'mvn test'
}
post {
always {
// 发布测试结果
junit 'target/surefire-reports/*.xml'
// 发布覆盖率报告
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/site/jacoco',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
集成测试配置:
stage('Integration Tests') {
steps {
script {
// 启动测试环境
sh 'docker-compose -f docker-compose.test.yml up -d'
try {
// 等待服务启动
sh 'sleep 30'
// 执行集成测试
sh 'mvn verify -Pintegration-tests'
} finally {
// 清理测试环境
sh 'docker-compose -f docker-compose.test.yml down'
}
}
}
}
3.4 构建后操作
归档产物
产物归档配置:
stage('Archive') {
steps {
// 归档JAR文件
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
// 归档文档
archiveArtifacts artifacts: 'docs/**/*', allowEmptyArchive: true
// 归档测试报告
archiveArtifacts artifacts: 'target/surefire-reports/**/*'
}
}
产物管理策略:
// 项目配置中的产物保留策略
properties([
buildDiscarder(logRotator(
artifactDaysToKeepStr: '30',
artifactNumToKeepStr: '10',
daysToKeepStr: '90',
numToKeepStr: '50'
))
])
通知配置
邮件通知:
post {
success {
emailext (
subject: "✅ Build Success: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: """
<h2>Build Successful</h2>
<p><strong>Project:</strong> ${env.JOB_NAME}</p>
<p><strong>Build Number:</strong> ${env.BUILD_NUMBER}</p>
<p><strong>Build URL:</strong> <a href="${env.BUILD_URL}">${env.BUILD_URL}</a></p>
<p><strong>Duration:</strong> ${currentBuild.durationString}</p>
""",
mimeType: 'text/html',
to: '${DEFAULT_RECIPIENTS}'
)
}
failure {
emailext (
subject: "❌ Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: """
<h2>Build Failed</h2>
<p><strong>Project:</strong> ${env.JOB_NAME}</p>
<p><strong>Build Number:</strong> ${env.BUILD_NUMBER}</p>
<p><strong>Build URL:</strong> <a href="${env.BUILD_URL}">${env.BUILD_URL}</a></p>
<p><strong>Console Output:</strong> <a href="${env.BUILD_URL}console">${env.BUILD_URL}console</a></p>
""",
mimeType: 'text/html',
to: '${DEFAULT_RECIPIENTS}'
)
}
}
Slack通知:
post {
success {
slackSend (
channel: '#ci-cd',
color: 'good',
message: ":white_check_mark: Build Success: ${env.JOB_NAME} - ${env.BUILD_NUMBER} (<${env.BUILD_URL}|Open>)"
)
}
failure {
slackSend (
channel: '#ci-cd',
color: 'danger',
message: ":x: Build Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER} (<${env.BUILD_URL}console|Console>)"
)
}
}
部署操作
部署到测试环境:
stage('Deploy to Test') {
when {
branch 'develop'
}
steps {
script {
// 部署到测试环境
sh """
scp target/myapp.jar deploy@test-server:/opt/myapp/
ssh deploy@test-server 'sudo systemctl restart myapp'
# 健康检查
for i in {1..30}; do
if curl -f http://test-server:8080/health; then
echo "Application is healthy"
break
fi
echo "Waiting for application to start..."
sleep 10
done
"""
}
}
}
部署到生产环境:
stage('Deploy to Production') {
when {
branch 'main'
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
}
steps {
script {
// 需要人工确认
timeout(time: 5, unit: 'MINUTES') {
input message: 'Deploy to production?', ok: 'Deploy',
submitterParameter: 'DEPLOYER'
}
// 蓝绿部署
sh """
# 部署到绿色环境
ansible-playbook -i inventory/prod deploy.yml \
--extra-vars "target_env=green app_version=${env.BUILD_NUMBER}"
# 健康检查
ansible-playbook -i inventory/prod healthcheck.yml \
--extra-vars "target_env=green"
# 切换流量
ansible-playbook -i inventory/prod switch-traffic.yml \
--extra-vars "active_env=green"
"""
}
}
}
3.5 环境变量
内置环境变量
常用内置变量:
// 构建信息
env.BUILD_NUMBER // 构建编号
env.BUILD_ID // 构建ID
env.BUILD_URL // 构建URL
env.BUILD_TAG // 构建标签
// 项目信息
env.JOB_NAME // 项目名称
env.JOB_URL // 项目URL
env.WORKSPACE // 工作空间路径
// Jenkins信息
env.JENKINS_URL // Jenkins URL
env.JENKINS_HOME // Jenkins主目录
env.NODE_NAME // 节点名称
// Git信息(Git插件)
env.GIT_COMMIT // Git提交哈希
env.GIT_BRANCH // Git分支
env.GIT_URL // Git仓库URL
使用示例:
pipeline {
agent any
stages {
stage('Environment Info') {
steps {
script {
echo "Job Name: ${env.JOB_NAME}"
echo "Build Number: ${env.BUILD_NUMBER}"
echo "Workspace: ${env.WORKSPACE}"
echo "Git Commit: ${env.GIT_COMMIT}"
echo "Git Branch: ${env.GIT_BRANCH}"
}
}
}
}
}
自定义环境变量
全局环境变量:
// 在Manage Jenkins → Configure System中配置
// 或在Pipeline中定义
pipeline {
agent any
environment {
APP_NAME = 'myapp'
APP_VERSION = '1.0.0'
DEPLOY_ENV = 'production'
MAVEN_OPTS = '-Xmx1024m'
}
stages {
stage('Build') {
steps {
echo "Building ${APP_NAME} version ${APP_VERSION}"
sh 'mvn clean package -Dapp.version=${APP_VERSION}'
}
}
}
}
阶段级环境变量:
stage('Test') {
environment {
TEST_DATABASE_URL = 'jdbc:h2:mem:testdb'
TEST_PROFILE = 'test'
}
steps {
sh 'mvn test -Dspring.profiles.active=${TEST_PROFILE}'
}
}
动态环境变量:
stage('Set Version') {
steps {
script {
// 从Git标签获取版本
def version = sh(
script: 'git describe --tags --always',
returnStdout: true
).trim()
env.APP_VERSION = version
echo "Set APP_VERSION to ${env.APP_VERSION}"
}
}
}
3.6 参数化构建
参数类型
字符串参数:
parameters {
string(
name: 'BRANCH_NAME',
defaultValue: 'main',
description: 'Git branch to build'
)
string(
name: 'VERSION',
defaultValue: '1.0.0',
description: 'Application version'
)
}
选择参数:
parameters {
choice(
name: 'ENVIRONMENT',
choices: ['dev', 'test', 'staging', 'prod'],
description: 'Target environment'
)
choice(
name: 'LOG_LEVEL',
choices: ['DEBUG', 'INFO', 'WARN', 'ERROR'],
description: 'Log level'
)
}
布尔参数:
parameters {
booleanParam(
name: 'SKIP_TESTS',
defaultValue: false,
description: 'Skip unit tests'
)
booleanParam(
name: 'DEPLOY_TO_PROD',
defaultValue: false,
description: 'Deploy to production'
)
}
文件参数:
parameters {
file(
name: 'CONFIG_FILE',
description: 'Configuration file to upload'
)
}
参数使用
在Pipeline中使用参数:
pipeline {
agent any
parameters {
string(name: 'BRANCH_NAME', defaultValue: 'main')
choice(name: 'ENVIRONMENT', choices: ['dev', 'test', 'prod'])
booleanParam(name: 'SKIP_TESTS', defaultValue: false)
}
stages {
stage('Checkout') {
steps {
git branch: params.BRANCH_NAME, url: 'https://github.com/company/myapp.git'
}
}
stage('Test') {
when {
not { params.SKIP_TESTS }
}
steps {
sh 'mvn test'
}
}
stage('Deploy') {
steps {
script {
echo "Deploying to ${params.ENVIRONMENT}"
sh "ansible-playbook deploy.yml -e target_env=${params.ENVIRONMENT}"
}
}
}
}
}
参数验证:
stage('Validate Parameters') {
steps {
script {
if (!params.BRANCH_NAME) {
error('BRANCH_NAME parameter is required')
}
if (params.ENVIRONMENT == 'prod' && !params.DEPLOY_TO_PROD) {
error('DEPLOY_TO_PROD must be true for production deployment')
}
echo "Parameters validated successfully"
}
}
}
3.7 凭据管理
凭据类型
用户名密码:
// 使用凭据
withCredentials([usernamePassword(
credentialsId: 'database-credentials',
usernameVariable: 'DB_USER',
passwordVariable: 'DB_PASS'
)]) {
sh """
mysql -u ${DB_USER} -p${DB_PASS} -h db-server \
-e "CREATE DATABASE IF NOT EXISTS myapp;"
"""
}
SSH密钥:
withCredentials([sshUserPrivateKey(
credentialsId: 'deploy-ssh-key',
keyFileVariable: 'SSH_KEY',
usernameVariable: 'SSH_USER'
)]) {
sh """
ssh -i ${SSH_KEY} ${SSH_USER}@deploy-server \
'sudo systemctl restart myapp'
"""
}
API令牌:
withCredentials([string(
credentialsId: 'github-token',
variable: 'GITHUB_TOKEN'
)]) {
sh """
curl -H "Authorization: token ${GITHUB_TOKEN}" \
https://api.github.com/repos/company/myapp/releases
"""
}
证书文件:
withCredentials([certificate(
credentialsId: 'ssl-certificate',
keystoreVariable: 'KEYSTORE',
passwordVariable: 'KEYSTORE_PASSWORD'
)]) {
sh """
keytool -list -keystore ${KEYSTORE} \
-storepass ${KEYSTORE_PASSWORD}
"""
}
凭据最佳实践
安全原则:
// ✅ 正确:使用凭据管理
withCredentials([string(credentialsId: 'api-key', variable: 'API_KEY')]) {
sh 'curl -H "Authorization: Bearer ${API_KEY}" api.example.com'
}
// ❌ 错误:硬编码密码
sh 'curl -H "Authorization: Bearer abc123" api.example.com'
// ❌ 错误:在环境变量中暴露密码
environment {
API_KEY = 'abc123' // 不安全
}
凭据作用域:
Global: 所有项目可用
System: 仅系统级操作可用
Project: 仅特定项目可用
Folder: 仅文件夹内项目可用
3.8 本章小结
本章介绍了Jenkins的核心概念和术语:
关键概念: 1. 项目:Jenkins的基本工作单元 2. 构建:项目的执行实例 3. 工作空间:构建过程的文件系统 4. 节点:执行构建的机器 5. 标签:节点特性标识
重要机制: 1. 触发器:自动化构建启动 2. 构建步骤:具体执行操作 3. 环境变量:配置和信息传递 4. 参数化:灵活的构建配置 5. 凭据管理:安全的密钥管理
最佳实践: - 合理使用标签选择节点 - 优先使用Webhook而非轮询 - 妥善管理凭据和密钥 - 参数化提高构建灵活性 - 规范化环境变量使用
下一章预告: 下一章将介绍Jenkins的用户界面和基本操作,包括Web界面导航、项目管理、构建监控等实用技能。
3.9 练习与思考
理论练习
概念辨析
- 解释项目、构建、工作空间的关系
- 分析不同触发器的适用场景
- 比较各种参数类型的特点
架构设计
- 设计一个多环境部署的标签策略
- 规划项目的凭据管理方案
- 制定环境变量命名规范
实践练习
基础操作
- 创建不同类型的Jenkins项目
- 配置各种触发器
- 设置参数化构建
高级配置
- 实现复杂的标签表达式
- 配置多种凭据类型
- 设计环境变量体系
思考题
- 如何设计一个既安全又高效的凭据管理策略?
- 在微服务架构中,如何组织Jenkins项目结构?
- 如何平衡构建触发的及时性和系统负载?
- 参数化构建如何与GitOps实践相结合?