3.1 Jenkins核心概念

项目(Job/Project)

定义: 项目是Jenkins中的基本工作单元,定义了一系列要执行的任务和配置。每个项目包含构建步骤、触发条件、参数设置等。

项目类型:

  1. 自由风格项目(Freestyle Project)

    • 最基本的项目类型
    • 通过GUI配置构建步骤
    • 适合简单的构建任务 特点: ✓ 配置简单直观 ✓ 适合初学者 ✗ 复杂逻辑难以实现 ✗ 版本控制困难
  2. 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任务的机器,包括控制器节点和代理节点。

节点类型:

  1. 控制器节点(Controller Node)

    • Jenkins主服务器
    • 管理整个Jenkins实例
    • 默认标签:”master” 职责: - 调度构建任务 - 管理用户界面 - 存储配置和结果 - 插件管理
  2. 代理节点(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 练习与思考

理论练习

  1. 概念辨析

    • 解释项目、构建、工作空间的关系
    • 分析不同触发器的适用场景
    • 比较各种参数类型的特点
  2. 架构设计

    • 设计一个多环境部署的标签策略
    • 规划项目的凭据管理方案
    • 制定环境变量命名规范

实践练习

  1. 基础操作

    • 创建不同类型的Jenkins项目
    • 配置各种触发器
    • 设置参数化构建
  2. 高级配置

    • 实现复杂的标签表达式
    • 配置多种凭据类型
    • 设计环境变量体系

思考题

  1. 如何设计一个既安全又高效的凭据管理策略?
  2. 在微服务架构中,如何组织Jenkins项目结构?
  3. 如何平衡构建触发的及时性和系统负载?
  4. 参数化构建如何与GitOps实践相结合?