13.1 性能优化概述
性能问题识别
常见性能瓶颈:
Jenkins性能问题分类:1. 系统资源瓶颈- CPU使用率过高- 内存不足或泄漏- 磁盘I/O瓶颈- 网络带宽限制2. 应用层面问题- JVM配置不当- 垃圾回收频繁- 线程池配置问题- 数据库连接池不足3. 架构设计问题- 单点瓶颈- 负载分布不均- 缓存策略不当- 同步操作过多4. 配置和使用问题- 插件冲突或性能差- 构建配置不合理- 并发设置不当- 日志级别过详细
性能监控指标:
关键性能指标(KPI):1. 响应时间指标- 页面加载时间- API响应时间- 构建启动延迟- 队列等待时间2. 吞吐量指标- 并发构建数量- 每分钟构建数- 用户并发数- 请求处理速率3. 资源利用率- CPU使用率- 内存使用率- 磁盘使用率- 网络使用率4. 错误率指标- 构建失败率- 系统错误率- 超时错误率- 连接失败率
性能测试方法:
性能测试策略:1. 基准测试- 建立性能基线- 定期性能回归测试- 版本间性能对比- 配置变更影响评估2. 负载测试- 模拟正常负载- 测试系统稳定性- 验证性能指标- 识别性能拐点3. 压力测试- 测试系统极限- 识别瓶颈点- 验证故障恢复- 评估扩展需求4. 容量规划- 预测增长需求- 评估硬件需求- 规划扩展策略- 成本效益分析
性能优化策略
分层优化方法:
优化层次结构:┌─────────────────────────────────────┐
│ 应用层优化 │
│ - 代码优化 │
│ - 算法优化 │
│ - 缓存策略 │
│ - 异步处理 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 中间件优化 │
│ - JVM调优 │
│ - 数据库优化 │
│ - 网络优化 │
│ - 负载均衡 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ 系统层优化 │
│ - 操作系统调优 │
│ - 硬件配置 │
│ - 存储优化 │
│ - 网络配置 │
└─────────────────────────────────────┘优化原则:
1. 先测量,后优化
2. 优化最大瓶颈
3. 平衡各项指标
4. 持续监控验证
13.2 JVM调优
内存配置优化
堆内存配置:
# Jenkins启动脚本优化
#!/bin/bash# 基础内存配置(适用于中等规模Jenkins)
JAVA_OPTS="-Xms4g # 初始堆大小-Xmx8g # 最大堆大小-XX:NewRatio=1 # 新生代与老年代比例-XX:SurvivorRatio=8 # Eden与Survivor比例-XX:MaxMetaspaceSize=512m # 元空间最大大小-XX:CompressedClassSpaceSize=128m
"# 垃圾回收器配置(推荐G1GC)
GC_OPTS="-XX:+UseG1GC # 使用G1垃圾回收器-XX:MaxGCPauseMillis=200 # 最大GC暂停时间-XX:G1HeapRegionSize=16m # G1堆区域大小-XX:G1NewSizePercent=30 # 新生代初始占比-XX:G1MaxNewSizePercent=40 # 新生代最大占比-XX:G1MixedGCCountTarget=8 # 混合GC目标次数-XX:InitiatingHeapOccupancyPercent=45 # 并发标记触发阈值
"# 大规模Jenkins配置(高并发场景)
LARGE_SCALE_OPTS="-Xms16g-Xmx32g-XX:NewRatio=1-XX:SurvivorRatio=6-XX:MaxMetaspaceSize=1g-XX:+UseG1GC-XX:MaxGCPauseMillis=100-XX:G1HeapRegionSize=32m-XX:ParallelGCThreads=16-XX:ConcGCThreads=4
"# 性能监控和调试选项
MONITORING_OPTS="-XX:+PrintGC # 打印GC信息-XX:+PrintGCDetails # 详细GC信息-XX:+PrintGCTimeStamps # GC时间戳-XX:+PrintGCApplicationStoppedTime # 应用暂停时间-Xloggc:/var/log/jenkins/gc.log # GC日志文件-XX:+UseGCLogFileRotation # GC日志轮转-XX:NumberOfGCLogFiles=10 # GC日志文件数量-XX:GCLogFileSize=100M # GC日志文件大小-XX:+HeapDumpOnOutOfMemoryError # OOM时生成堆转储-XX:HeapDumpPath=/var/log/jenkins/heapdump.hprof
"# JIT编译器优化
JIT_OPTS="-XX:+TieredCompilation # 分层编译-XX:TieredStopAtLevel=4 # 编译级别-XX:CompileThreshold=10000 # 编译阈值-XX:+UseCodeCacheFlushing # 代码缓存清理-XX:ReservedCodeCacheSize=256m # 代码缓存大小
"# 网络和I/O优化
NETWORK_OPTS="-Djava.net.preferIPv4Stack=true-Djava.awt.headless=true-Dfile.encoding=UTF-8-Dsun.jnu.encoding=UTF-8-Dhudson.model.DirectoryBrowserSupport.CSP=-Djenkins.install.runSetupWizard=false
"# 组合所有选项
export JAVA_OPTS="$JAVA_OPTS $GC_OPTS $MONITORING_OPTS $JIT_OPTS $NETWORK_OPTS"# 启动Jenkins
java $JAVA_OPTS -jar jenkins.war --httpPort=8080
内存分析脚本:
#!/bin/bash
# jenkins_memory_analysis.shJENKINS_PID=$(pgrep -f jenkins.war)if [ -z "$JENKINS_PID" ]; thenecho "Jenkins进程未找到"exit 1
fiecho "=== Jenkins内存分析报告 ==="
echo "时间: $(date)"
echo "PID: $JENKINS_PID"
echo# 基本内存信息
echo "=== 基本内存信息 ==="
jcmd $JENKINS_PID VM.info | grep -E "(heap|metaspace|code cache)"
echo# 堆内存使用情况
echo "=== 堆内存使用情况 ==="
jcmd $JENKINS_PID GC.run_finalization
jcmd $JENKINS_PID VM.memory
echo# GC统计信息
echo "=== GC统计信息 ==="
jstat -gc $JENKINS_PID
echo# 类加载统计
echo "=== 类加载统计 ==="
jstat -class $JENKINS_PID
echo# 编译统计
echo "=== JIT编译统计 ==="
jstat -compiler $JENKINS_PID
echo# 生成堆转储(可选)
read -p "是否生成堆转储文件?(y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; thenDUMP_FILE="/tmp/jenkins_heapdump_$(date +%Y%m%d_%H%M%S).hprof"echo "生成堆转储文件: $DUMP_FILE"jcmd $JENKINS_PID GC.run_finalizationjcmd $JENKINS_PID VM.memoryjhsdb jmap --heap --pid $JENKINS_PID
fi# 内存使用趋势分析
echo "=== 内存使用趋势(最近10次采样) ==="
for i in {1..10}; doecho "采样 $i:"jstat -gc $JENKINS_PID | tail -1sleep 5
done
垃圾回收优化
G1GC调优配置:
# G1GC详细配置
G1_TUNING_OPTS="# 基础G1配置-XX:+UseG1GC-XX:MaxGCPauseMillis=200# 堆区域配置-XX:G1HeapRegionSize=16m-XX:G1NewSizePercent=20-XX:G1MaxNewSizePercent=30# 并发标记配置-XX:InitiatingHeapOccupancyPercent=45-XX:G1MixedGCLiveThresholdPercent=85-XX:G1HeapWastePercent=5# 混合GC配置-XX:G1MixedGCCountTarget=8-XX:G1OldCSetRegionThreshold=10# 并发线程配置-XX:ConcGCThreads=4-XX:ParallelGCThreads=16# 字符串去重(Java 8u20+)-XX:+UseStringDeduplication# 大对象处理-XX:G1ReservePercent=10
"# GC日志详细配置
GC_LOGGING_OPTS="-Xloggc:/var/log/jenkins/gc-%t.log-XX:+UseGCLogFileRotation-XX:NumberOfGCLogFiles=10-XX:GCLogFileSize=100M-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintGCDateStamps-XX:+PrintGCApplicationStoppedTime-XX:+PrintGCApplicationConcurrentTime-XX:+PrintStringDeduplicationStatistics
"
GC分析脚本:
#!/usr/bin/env python3
# gc_analysis.pyimport re
import sys
from datetime import datetime
from collections import defaultdictclass GCAnalyzer:def __init__(self, log_file):self.log_file = log_fileself.gc_events = []self.pause_times = []self.heap_usage = []def parse_gc_log(self):"""解析GC日志文件"""with open(self.log_file, 'r') as f:for line in f:self._parse_line(line.strip())def _parse_line(self, line):"""解析单行GC日志"""# 解析G1GC暂停时间pause_pattern = r'\[GC pause.*?([0-9.]+) secs\]'pause_match = re.search(pause_pattern, line)if pause_match:pause_time = float(pause_match.group(1)) * 1000 # 转换为毫秒self.pause_times.append(pause_time)# 解析堆使用情况heap_pattern = r'(\d+)M->(\d+)M\((\d+)M\)'heap_match = re.search(heap_pattern, line)if heap_match:before = int(heap_match.group(1))after = int(heap_match.group(2))total = int(heap_match.group(3))self.heap_usage.append({'before': before,'after': after,'total': total,'utilization': (after / total) * 100})def analyze(self):"""分析GC性能"""if not self.pause_times:print("未找到GC暂停时间数据")return# 暂停时间统计avg_pause = sum(self.pause_times) / len(self.pause_times)max_pause = max(self.pause_times)min_pause = min(self.pause_times)# 计算百分位数sorted_pauses = sorted(self.pause_times)p95_pause = sorted_pauses[int(len(sorted_pauses) * 0.95)]p99_pause = sorted_pauses[int(len(sorted_pauses) * 0.99)]print("=== GC性能分析报告 ===")print(f"总GC次数: {len(self.pause_times)}")print(f"平均暂停时间: {avg_pause:.2f}ms")print(f"最大暂停时间: {max_pause:.2f}ms")print(f"最小暂停时间: {min_pause:.2f}ms")print(f"95%暂停时间: {p95_pause:.2f}ms")print(f"99%暂停时间: {p99_pause:.2f}ms")# 堆使用情况分析if self.heap_usage:avg_utilization = sum(h['utilization'] for h in self.heap_usage) / len(self.heap_usage)max_utilization = max(h['utilization'] for h in self.heap_usage)print(f"\n=== 堆使用情况 ===")print(f"平均堆使用率: {avg_utilization:.2f}%")print(f"最大堆使用率: {max_utilization:.2f}%")# 性能建议self._provide_recommendations(avg_pause, max_pause, p95_pause)def _provide_recommendations(self, avg_pause, max_pause, p95_pause):"""提供优化建议"""print("\n=== 优化建议 ===")if avg_pause > 200:print("- 平均暂停时间过长,建议减小MaxGCPauseMillis目标")if max_pause > 1000:print("- 最大暂停时间过长,建议增加堆大小或调整G1参数")if p95_pause > 500:print("- 95%暂停时间过长,建议优化应用代码减少对象分配")if len(self.pause_times) > 1000:print("- GC频率过高,建议增加堆大小")if __name__ == "__main__":if len(sys.argv) != 2:print("用法: python3 gc_analysis.py <gc_log_file>")sys.exit(1)analyzer = GCAnalyzer(sys.argv[1])analyzer.parse_gc_log()analyzer.analyze()
13.3 系统级优化
操作系统调优
Linux系统优化:
#!/bin/bash
# jenkins_system_tuning.shecho "=== Jenkins系统优化脚本 ==="# 1. 内核参数优化
echo "配置内核参数..."
cat >> /etc/sysctl.conf << EOF
# Jenkins系统优化参数# 网络优化
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_rmem = 4096 65536 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216
net.core.rmem_default = 262144
net.core.rmem_max = 16777216
net.core.wmem_default = 262144
net.core.wmem_max = 16777216# 内存管理
vm.swappiness = 1
vm.dirty_ratio = 15
vm.dirty_background_ratio = 5
vm.vfs_cache_pressure = 50
vm.min_free_kbytes = 65536# 文件系统
fs.file-max = 2097152
fs.nr_open = 2097152# 进程限制
kernel.pid_max = 4194304
kernel.threads-max = 4194304
EOF# 应用内核参数
sysctl -p# 2. 文件描述符限制
echo "配置文件描述符限制..."
cat >> /etc/security/limits.conf << EOF
# Jenkins用户限制
jenkins soft nofile 65535
jenkins hard nofile 65535
jenkins soft nproc 32768
jenkins hard nproc 32768
jenkins soft memlock unlimited
jenkins hard memlock unlimited# 所有用户默认限制
* soft nofile 65535
* hard nofile 65535
EOF# 3. systemd服务限制
echo "配置systemd服务限制..."
mkdir -p /etc/systemd/system/jenkins.service.d
cat > /etc/systemd/system/jenkins.service.d/limits.conf << EOF
[Service]
LimitNOFILE=65535
LimitNPROC=32768
LimitMEMLOCK=infinity
EOF# 4. 磁盘I/O优化
echo "优化磁盘I/O..."
# 设置I/O调度器为deadline(适合SSD)
echo deadline > /sys/block/sda/queue/scheduler# 禁用透明大页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag# 5. CPU优化
echo "优化CPU设置..."
# 设置CPU调度器
echo performance > /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor# 6. 创建优化的挂载选项
echo "优化文件系统挂载选项..."
cat >> /etc/fstab << EOF
# Jenkins工作目录优化挂载
/dev/sdb1 /var/lib/jenkins ext4 defaults,noatime,nodiratime,barrier=0 0 2
EOFecho "系统优化完成,建议重启系统使所有设置生效"
性能监控脚本:
#!/bin/bash
# jenkins_performance_monitor.shLOG_FILE="/var/log/jenkins/performance.log"
INTERVAL=60 # 监控间隔(秒)# 创建日志目录
mkdir -p $(dirname $LOG_FILE)echo "Jenkins性能监控启动,日志文件: $LOG_FILE"
echo "监控间隔: ${INTERVAL}秒"while true; doTIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')# 获取Jenkins进程信息JENKINS_PID=$(pgrep -f jenkins.war)if [ -n "$JENKINS_PID" ]; then# CPU使用率CPU_USAGE=$(ps -p $JENKINS_PID -o %cpu --no-headers)# 内存使用情况MEMORY_INFO=$(ps -p $JENKINS_PID -o %mem,vsz,rss --no-headers)MEM_PERCENT=$(echo $MEMORY_INFO | awk '{print $1}')VSZ=$(echo $MEMORY_INFO | awk '{print $2}')RSS=$(echo $MEMORY_INFO | awk '{print $3}')# 文件描述符使用情况FD_COUNT=$(lsof -p $JENKINS_PID 2>/dev/null | wc -l)# 线程数量THREAD_COUNT=$(ps -p $JENKINS_PID -o nlwp --no-headers)# 系统负载LOAD_AVG=$(uptime | awk -F'load average:' '{print $2}' | sed 's/^[ \t]*//')# 磁盘使用情况DISK_USAGE=$(df -h /var/lib/jenkins | tail -1 | awk '{print $5}' | sed 's/%//')# JVM堆内存使用情况(如果jstat可用)if command -v jstat >/dev/null 2>&1; thenHEAP_INFO=$(jstat -gc $JENKINS_PID | tail -1)HEAP_USED=$(echo $HEAP_INFO | awk '{print ($3+$4+$6+$8)/1024}' | bc -l 2>/dev/null || echo "N/A")HEAP_TOTAL=$(echo $HEAP_INFO | awk '{print ($1+$2+$5+$7)/1024}' | bc -l 2>/dev/null || echo "N/A")elseHEAP_USED="N/A"HEAP_TOTAL="N/A"fi# 记录性能数据echo "$TIMESTAMP,CPU:${CPU_USAGE}%,MEM:${MEM_PERCENT}%,VSZ:${VSZ}KB,RSS:${RSS}KB,FD:${FD_COUNT},THREADS:${THREAD_COUNT},LOAD:${LOAD_AVG},DISK:${DISK_USAGE}%,HEAP_USED:${HEAP_USED}MB,HEAP_TOTAL:${HEAP_TOTAL}MB" >> $LOG_FILE# 检查性能阈值并告警if (( $(echo "$CPU_USAGE > 80" | bc -l) )); thenecho "[$TIMESTAMP] 警告: CPU使用率过高 ${CPU_USAGE}%" | tee -a $LOG_FILEfiif (( $(echo "$MEM_PERCENT > 85" | bc -l) )); thenecho "[$TIMESTAMP] 警告: 内存使用率过高 ${MEM_PERCENT}%" | tee -a $LOG_FILEfiif [ "$FD_COUNT" -gt 50000 ]; thenecho "[$TIMESTAMP] 警告: 文件描述符使用过多 $FD_COUNT" | tee -a $LOG_FILEfiif [ "$DISK_USAGE" -gt 85 ]; thenecho "[$TIMESTAMP] 警告: 磁盘使用率过高 ${DISK_USAGE}%" | tee -a $LOG_FILEfielseecho "[$TIMESTAMP] Jenkins进程未运行" >> $LOG_FILEfisleep $INTERVAL
done
存储优化
磁盘配置优化:
#!/bin/bash
# jenkins_storage_optimization.shecho "=== Jenkins存储优化 ==="# 1. 创建优化的文件系统结构
echo "创建优化的目录结构..."# Jenkins主目录
JENKINS_HOME="/var/lib/jenkins"# 分离不同类型的数据
mkdir -p $JENKINS_HOME/{jobs,workspace,logs,plugins,tools,secrets}
mkdir -p /var/cache/jenkins/{builds,artifacts}
mkdir -p /tmp/jenkins/{workspace,builds}# 2. 配置tmpfs用于临时文件
echo "配置tmpfs..."
cat >> /etc/fstab << EOF
# Jenkins临时文件系统
tmpfs /tmp/jenkins tmpfs defaults,size=4G,mode=1777 0 0
EOF# 3. 设置合适的文件权限
echo "设置文件权限..."
chown -R jenkins:jenkins $JENKINS_HOME
chown -R jenkins:jenkins /var/cache/jenkins
chown -R jenkins:jenkins /tmp/jenkins# 4. 配置日志轮转
echo "配置日志轮转..."
cat > /etc/logrotate.d/jenkins << EOF
/var/lib/jenkins/logs/*.log {dailymissingokrotate 30compressdelaycompressnotifemptycopytruncatesu jenkins jenkins
}/var/log/jenkins/*.log {dailymissingokrotate 30compressdelaycompressnotifemptycopytruncatesu jenkins jenkins
}
EOF# 5. 创建清理脚本
cat > /usr/local/bin/jenkins_cleanup.sh << 'EOF'
#!/bin/bash
# Jenkins存储清理脚本JENKINS_HOME="/var/lib/jenkins"
RETENTION_DAYS=30
WORKSPACE_RETENTION_DAYS=7echo "开始Jenkins存储清理..."# 清理旧的构建日志
echo "清理构建日志..."
find $JENKINS_HOME/jobs/*/builds/*/log -type f -mtime +$RETENTION_DAYS -delete# 清理旧的工作空间
echo "清理工作空间..."
find $JENKINS_HOME/workspace/* -type d -mtime +$WORKSPACE_RETENTION_DAYS -exec rm -rf {} + 2>/dev/null# 清理临时文件
echo "清理临时文件..."
find /tmp/jenkins -type f -mtime +1 -delete
find /var/cache/jenkins -type f -mtime +$RETENTION_DAYS -delete# 清理旧的插件缓存
echo "清理插件缓存..."
find $JENKINS_HOME/plugins -name "*.tmp" -delete
find $JENKINS_HOME/plugins -name "*.bak" -mtime +7 -delete# 压缩旧的构建产物
echo "压缩构建产物..."
find $JENKINS_HOME/jobs/*/builds/*/archive -type f -name "*.jar" -mtime +7 ! -name "*.gz" -exec gzip {} \;# 统计清理结果
echo "清理完成,当前磁盘使用情况:"
df -h $JENKINS_HOMEecho "Jenkins目录大小:"
du -sh $JENKINS_HOME
EOFchmod +x /usr/local/bin/jenkins_cleanup.sh# 6. 设置定时清理任务
echo "设置定时清理任务..."
cat > /etc/cron.d/jenkins-cleanup << EOF
# Jenkins存储清理任务
0 2 * * * jenkins /usr/local/bin/jenkins_cleanup.sh >> /var/log/jenkins/cleanup.log 2>&1
EOFecho "存储优化配置完成"
存储监控脚本:
#!/usr/bin/env python3
# jenkins_storage_monitor.pyimport os
import sys
import json
import time
from datetime import datetime
from pathlib import Pathclass StorageMonitor:def __init__(self, jenkins_home='/var/lib/jenkins'):self.jenkins_home = Path(jenkins_home)self.report_file = '/var/log/jenkins/storage_report.json'def get_directory_size(self, path):"""获取目录大小"""total_size = 0try:for dirpath, dirnames, filenames in os.walk(path):for filename in filenames:filepath = os.path.join(dirpath, filename)try:total_size += os.path.getsize(filepath)except (OSError, IOError):continueexcept (OSError, IOError):passreturn total_sizedef get_disk_usage(self, path):"""获取磁盘使用情况"""try:statvfs = os.statvfs(path)total = statvfs.f_frsize * statvfs.f_blocksfree = statvfs.f_frsize * statvfs.f_availableused = total - freereturn {'total': total,'used': used,'free': free,'usage_percent': (used / total) * 100 if total > 0 else 0}except OSError:return Nonedef analyze_jenkins_storage(self):"""分析Jenkins存储使用情况"""report = {'timestamp': datetime.now().isoformat(),'jenkins_home': str(self.jenkins_home),'directories': {},'disk_usage': {},'recommendations': []}# 分析各个目录的大小directories_to_check = ['jobs','workspace','plugins','logs','tools','secrets','userContent','war']total_jenkins_size = 0for dir_name in directories_to_check:dir_path = self.jenkins_home / dir_nameif dir_path.exists():size = self.get_directory_size(dir_path)total_jenkins_size += sizereport['directories'][dir_name] = {'size_bytes': size,'size_mb': size / (1024 * 1024),'size_gb': size / (1024 * 1024 * 1024)}report['total_jenkins_size'] = {'size_bytes': total_jenkins_size,'size_mb': total_jenkins_size / (1024 * 1024),'size_gb': total_jenkins_size / (1024 * 1024 * 1024)}# 获取磁盘使用情况disk_usage = self.get_disk_usage(self.jenkins_home)if disk_usage:report['disk_usage'] = {'total_gb': disk_usage['total'] / (1024 * 1024 * 1024),'used_gb': disk_usage['used'] / (1024 * 1024 * 1024),'free_gb': disk_usage['free'] / (1024 * 1024 * 1024),'usage_percent': disk_usage['usage_percent']}# 生成建议self._generate_recommendations(report)return reportdef _generate_recommendations(self, report):"""生成优化建议"""recommendations = []# 检查磁盘使用率if 'disk_usage' in report and report['disk_usage']['usage_percent'] > 85:recommendations.append({'type': 'critical','message': f"磁盘使用率过高 ({report['disk_usage']['usage_percent']:.1f}%),需要立即清理"})# 检查各目录大小if 'directories' in report:# 检查workspace目录if 'workspace' in report['directories']:workspace_size_gb = report['directories']['workspace']['size_gb']if workspace_size_gb > 10:recommendations.append({'type': 'warning','message': f"workspace目录过大 ({workspace_size_gb:.1f}GB),建议清理旧的工作空间"})# 检查jobs目录if 'jobs' in report['directories']:jobs_size_gb = report['directories']['jobs']['size_gb']if jobs_size_gb > 20:recommendations.append({'type': 'warning','message': f"jobs目录过大 ({jobs_size_gb:.1f}GB),建议清理旧的构建记录"})# 检查logs目录if 'logs' in report['directories']:logs_size_gb = report['directories']['logs']['size_gb']if logs_size_gb > 5:recommendations.append({'type': 'info','message': f"logs目录较大 ({logs_size_gb:.1f}GB),建议配置日志轮转"})report['recommendations'] = recommendationsdef save_report(self, report):"""保存报告到文件"""os.makedirs(os.path.dirname(self.report_file), exist_ok=True)with open(self.report_file, 'w') as f:json.dump(report, f, indent=2)def print_report(self, report):"""打印报告"""print("=== Jenkins存储分析报告 ===")print(f"时间: {report['timestamp']}")print(f"Jenkins主目录: {report['jenkins_home']}")print()# 总体使用情况if 'total_jenkins_size' in report:total_size = report['total_jenkins_size']print(f"Jenkins总大小: {total_size['size_gb']:.2f} GB")if 'disk_usage' in report:disk = report['disk_usage']print(f"磁盘使用情况: {disk['used_gb']:.1f}GB / {disk['total_gb']:.1f}GB ({disk['usage_percent']:.1f}%)")print()# 目录详情print("=== 目录大小详情 ===")if 'directories' in report:for dir_name, info in sorted(report['directories'].items(), key=lambda x: x[1]['size_gb'], reverse=True):print(f"{dir_name:15}: {info['size_gb']:8.2f} GB")print()# 建议if 'recommendations' in report and report['recommendations']:print("=== 优化建议 ===")for rec in report['recommendations']:icon = {'critical': '🚨', 'warning': '⚠️', 'info': 'ℹ️'}.get(rec['type'], '')print(f"{icon} {rec['message']}")else:print("✅ 存储使用情况良好,无需特别优化")def run(self):"""运行存储监控"""report = self.analyze_jenkins_storage()self.save_report(report)self.print_report(report)return reportif __name__ == '__main__':jenkins_home = sys.argv[1] if len(sys.argv) > 1 else '/var/lib/jenkins'monitor = StorageMonitor(jenkins_home)monitor.run()
13.4 构建优化
Pipeline性能优化
并行化优化策略:
// 高性能Pipeline示例
pipeline {agent noneoptions {// 构建保留策略buildDiscarder(logRotator(numToKeepStr: '10',daysToKeepStr: '30',artifactNumToKeepStr: '5'))// 超时设置timeout(time: 30, unit: 'MINUTES')// 禁用并发构建disableConcurrentBuilds()// 跳过默认检出skipDefaultCheckout()}environment {// 优化环境变量MAVEN_OPTS = '-Xmx2g -XX:+UseG1GC -Dmaven.repo.local=/var/cache/maven'GRADLE_OPTS = '-Xmx2g -XX:+UseG1GC -Dorg.gradle.daemon=false'DOCKER_BUILDKIT = '1'}stages {stage('Checkout') {agent { label 'fast-ssd' }steps {sh 'mvn clean compile -T 4'}}}
}
资源池管理:
// 资源池管理脚本
class ResourcePoolManager {def jenkins = Jenkins.instancedef pools = [:]def initializePools() {pools['build'] = [maxConcurrent: 10,current: 0,queue: [],nodes: ['build-1', 'build-2', 'build-3']]pools['test'] = [maxConcurrent: 5,current: 0,queue: [],nodes: ['test-1', 'test-2']]pools['deploy'] = [maxConcurrent: 2,current: 0,queue: [],nodes: ['deploy-1']]}def requestResource(String poolName, Closure task) {def pool = pools[poolName]if (pool.current < pool.maxConcurrent) {pool.current++try {task()} finally {pool.current--processQueue(poolName)}} else {pool.queue.add(task)echo "任务已加入${poolName}队列,当前队列长度: ${pool.queue.size()}"}}def processQueue(String poolName) {def pool = pools[poolName]if (pool.queue.size() > 0 && pool.current < pool.maxConcurrent) {def nextTask = pool.queue.remove(0)pool.current++// 异步执行下一个任务Thread.start {try {nextTask()} finally {pool.current--processQueue(poolName)}}}}
}// 使用示例
def resourceManager = new ResourcePoolManager()
resourceManager.initializePools()pipeline {agent nonestages {stage('Build') {steps {script {resourceManager.requestResource('build') {node('build') {sh 'mvn clean package'}}}}}}
}
13.5 网络优化
带宽优化
网络配置优化:
#!/bin/bash
# jenkins_network_optimization.shecho "=== Jenkins网络优化配置 ==="# 1. TCP优化
echo "配置TCP参数..."
cat >> /etc/sysctl.conf << EOF
# Jenkins网络优化
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_no_metrics_save = 1
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq# 连接跟踪优化
net.netfilter.nf_conntrack_max = 1048576
net.netfilter.nf_conntrack_tcp_timeout_established = 7200
EOFsysctl -p# 2. 配置Jenkins反向代理
echo "配置Nginx反向代理..."
cat > /etc/nginx/sites-available/jenkins << 'EOF'
upstream jenkins {server 127.0.0.1:8080 fail_timeout=0;
}server {listen 80;server_name jenkins.company.com;return 301 https://$server_name$request_uri;
}server {listen 443 ssl http2;server_name jenkins.company.com;# SSL配置ssl_certificate /etc/ssl/certs/jenkins.crt;ssl_certificate_key /etc/ssl/private/jenkins.key;ssl_protocols TLSv1.2 TLSv1.3;ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;ssl_prefer_server_ciphers off;ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;# 性能优化client_max_body_size 100M;client_body_timeout 60s;client_header_timeout 60s;keepalive_timeout 65s;send_timeout 60s;# 压缩配置gzip on;gzip_vary on;gzip_min_length 1024;gzip_proxied any;gzip_comp_level 6;gzip_typestext/plaintext/csstext/xmltext/javascriptapplication/jsonapplication/javascriptapplication/xml+rssapplication/atom+xmlimage/svg+xml;# 缓存配置location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {expires 1y;add_header Cache-Control "public, immutable";access_log off;}# Jenkins代理配置location / {proxy_set_header Host $http_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;proxy_set_header X-Forwarded-Port $server_port;proxy_pass http://jenkins;proxy_read_timeout 90s;proxy_redirect http://jenkins https://jenkins.company.com;# WebSocket支持proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "upgrade";# 缓冲优化proxy_buffering on;proxy_buffer_size 128k;proxy_buffers 4 256k;proxy_busy_buffers_size 256k;}# 健康检查location /health {access_log off;return 200 "healthy\n";add_header Content-Type text/plain;}
}
EOF# 启用站点
ln -sf /etc/nginx/sites-available/jenkins /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginxecho "网络优化配置完成"
CDN配置:
# cloudfront-jenkins.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Jenkins CDN配置'Parameters:JenkinsOrigin:Type: StringDefault: 'jenkins.company.com'Description: 'Jenkins服务器域名'Resources:JenkinsCDN:Type: AWS::CloudFront::DistributionProperties:DistributionConfig:Enabled: trueComment: 'Jenkins CDN Distribution'Origins:- Id: jenkins-originDomainName: !Ref JenkinsOriginCustomOriginConfig:HTTPPort: 443HTTPSPort: 443OriginProtocolPolicy: https-onlyOriginSSLProtocols:- TLSv1.2DefaultCacheBehavior:TargetOriginId: jenkins-originViewerProtocolPolicy: redirect-to-httpsAllowedMethods:- GET- HEAD- OPTIONS- PUT- POST- PATCH- DELETECachedMethods:- GET- HEAD- OPTIONSCompress: trueForwardedValues:QueryString: trueHeaders:- Authorization- Host- X-Forwarded-For- X-Forwarded-ProtoCookies:Forward: allDefaultTTL: 0MaxTTL: 31536000MinTTL: 0CacheBehaviors:# 静态资源缓存- PathPattern: '*.css'TargetOriginId: jenkins-originViewerProtocolPolicy: redirect-to-httpsAllowedMethods: [GET, HEAD]CachedMethods: [GET, HEAD]Compress: trueForwardedValues:QueryString: falseHeaders: []DefaultTTL: 86400MaxTTL: 31536000MinTTL: 0- PathPattern: '*.js'TargetOriginId: jenkins-originViewerProtocolPolicy: redirect-to-httpsAllowedMethods: [GET, HEAD]CachedMethods: [GET, HEAD]Compress: trueForwardedValues:QueryString: falseHeaders: []DefaultTTL: 86400MaxTTL: 31536000MinTTL: 0- PathPattern: '*.png'TargetOriginId: jenkins-originViewerProtocolPolicy: redirect-to-httpsAllowedMethods: [GET, HEAD]CachedMethods: [GET, HEAD]Compress: falseForwardedValues:QueryString: falseHeaders: []DefaultTTL: 2592000MaxTTL: 31536000MinTTL: 0PriceClass: PriceClass_100ViewerCertificate:AcmCertificateArn: !Ref SSLCertificateSslSupportMethod: sni-onlyMinimumProtocolVersion: TLSv1.2_2021SSLCertificate:Type: AWS::CertificateManager::CertificateProperties:DomainName: !Sub 'cdn.${JenkinsOrigin}'ValidationMethod: DNSOutputs:CDNDomainName:Description: 'CloudFront域名'Value: !GetAtt JenkinsCDN.DomainNameExport:Name: !Sub '${AWS::StackName}-CDN-Domain'
连接优化
连接池配置:
// Jenkins系统配置脚本
import jenkins.model.Jenkins
import org.jenkinsci.plugins.workflow.libs.GlobalLibraries
import org.jenkinsci.plugins.workflow.libs.LibraryConfiguration
import org.jenkinsci.plugins.workflow.libs.SCMSourceRetriever
import jenkins.plugins.git.GitSCMSource// HTTP连接池优化
System.setProperty('hudson.model.ParametersAction.keepUndefinedParameters', 'true')
System.setProperty('hudson.model.DirectoryBrowserSupport.CSP', '')
System.setProperty('jenkins.model.Jenkins.slaveAgentPort', '50000')
System.setProperty('jenkins.model.Jenkins.slaveAgentPortEnforce', 'true')// 网络超时设置
System.setProperty('hudson.remoting.Launcher.pingIntervalSec', '300')
System.setProperty('hudson.remoting.Launcher.pingTimeoutSec', '60')
System.setProperty('hudson.slaves.ChannelPinger.pingInterval', '5')
System.setProperty('hudson.slaves.ChannelPinger.pingTimeout', '10')// Git连接优化
System.setProperty('org.jenkinsci.plugins.gitclient.Git.timeOut', '30')
System.setProperty('hudson.plugins.git.GitSCM.ALLOW_LOCAL_CHECKOUT', 'true')// HTTP客户端优化
System.setProperty('hudson.ProxyConfiguration.DEFAULT_CONNECT_TIMEOUT_MILLIS', '20000')
System.setProperty('hudson.ProxyConfiguration.DEFAULT_READ_TIMEOUT_MILLIS', '60000')println "网络连接优化配置完成"
13.6 监控和调优工具
性能分析工具
JProfiler集成脚本:
#!/bin/bash
# jenkins_profiling.shJPROFILER_HOME="/opt/jprofiler"
JENKINS_PID=$(pgrep -f jenkins.war)if [ -z "$JENKINS_PID" ]; thenecho "Jenkins进程未找到"exit 1
fiecho "=== Jenkins性能分析 ==="
echo "Jenkins PID: $JENKINS_PID"# 1. 启动JProfiler代理
echo "启动JProfiler代理..."
$JPROFILER_HOME/bin/jpenable --pid=$JENKINS_PID --port=8849# 2. 生成堆转储
echo "生成堆转储..."
HEAP_DUMP_FILE="/tmp/jenkins_heap_$(date +%Y%m%d_%H%M%S).hprof"
jcmd $JENKINS_PID GC.run_finalization
jhsdb jmap --heap --pid $JENKINS_PID > "${HEAP_DUMP_FILE}.txt"
jcmd $JENKINS_PID VM.memory >> "${HEAP_DUMP_FILE}.txt"# 3. 线程转储
echo "生成线程转储..."
THREAD_DUMP_FILE="/tmp/jenkins_threads_$(date +%Y%m%d_%H%M%S).txt"
jstack $JENKINS_PID > $THREAD_DUMP_FILE# 4. GC分析
echo "分析GC日志..."
GC_LOG_FILE="/var/log/jenkins/gc.log"
if [ -f "$GC_LOG_FILE" ]; then# 使用GCViewer分析GC日志java -jar $JPROFILER_HOME/lib/gcviewer.jar $GC_LOG_FILE
fi# 5. 性能报告
echo "生成性能报告..."
cat > "/tmp/jenkins_performance_report_$(date +%Y%m%d_%H%M%S).txt" << EOF
Jenkins性能分析报告
生成时间: $(date)
Jenkins PID: $JENKINS_PID=== 系统信息 ===
$(uname -a)=== CPU信息 ===
$(lscpu)=== 内存信息 ===
$(free -h)=== 磁盘信息 ===
$(df -h)=== 网络连接 ===
$(netstat -an | grep :8080)=== Java进程信息 ===
$(ps -p $JENKINS_PID -o pid,ppid,cmd,%mem,%cpu,etime)=== JVM信息 ===
$(jcmd $JENKINS_PID VM.info)=== 类加载统计 ===
$(jstat -class $JENKINS_PID)=== 编译统计 ===
$(jstat -compiler $JENKINS_PID)=== GC统计 ===
$(jstat -gc $JENKINS_PID)
EOFecho "性能分析完成,文件保存在 /tmp/ 目录"
ls -la /tmp/jenkins_*
自动化调优脚本:
#!/usr/bin/env python3
# jenkins_auto_tuning.pyimport os
import re
import json
import subprocess
from datetime import datetime, timedeltaclass JenkinsAutoTuner:def __init__(self):self.jenkins_home = '/var/lib/jenkins'self.config_file = '/etc/jenkins/tuning.json'self.metrics = {}def collect_metrics(self):"""收集性能指标"""# 获取Jenkins进程信息jenkins_pid = self._get_jenkins_pid()if not jenkins_pid:return False# CPU使用率cpu_usage = self._get_cpu_usage(jenkins_pid)# 内存使用情况memory_info = self._get_memory_info(jenkins_pid)# GC信息gc_info = self._get_gc_info(jenkins_pid)# 响应时间response_time = self._get_response_time()# 构建队列长度queue_length = self._get_queue_length()self.metrics = {'timestamp': datetime.now().isoformat(),'cpu_usage': cpu_usage,'memory': memory_info,'gc': gc_info,'response_time': response_time,'queue_length': queue_length}return Truedef analyze_performance(self):"""分析性能并生成调优建议"""recommendations = []# CPU分析if self.metrics['cpu_usage'] > 80:recommendations.append({'type': 'cpu','severity': 'high','message': 'CPU使用率过高,建议增加执行器或优化构建脚本','action': 'increase_executors'})# 内存分析heap_usage = self.metrics['memory']['heap_usage_percent']if heap_usage > 85:recommendations.append({'type': 'memory','severity': 'high','message': '堆内存使用率过高,建议增加堆大小','action': 'increase_heap_size'})# GC分析gc_time_percent = self.metrics['gc']['time_percent']if gc_time_percent > 5:recommendations.append({'type': 'gc','severity': 'medium','message': 'GC时间占比过高,建议调整GC参数','action': 'tune_gc_parameters'})# 响应时间分析if self.metrics['response_time'] > 5000: # 5秒recommendations.append({'type': 'response','severity': 'medium','message': '响应时间过长,建议优化插件或增加资源','action': 'optimize_plugins'})# 队列分析if self.metrics['queue_length'] > 20:recommendations.append({'type': 'queue','severity': 'medium','message': '构建队列过长,建议增加构建节点','action': 'add_build_nodes'})return recommendationsdef apply_tuning(self, recommendations):"""应用调优建议"""applied_changes = []for rec in recommendations:if rec['action'] == 'increase_heap_size':if self._increase_heap_size():applied_changes.append('增加堆内存大小')elif rec['action'] == 'tune_gc_parameters':if self._tune_gc_parameters():applied_changes.append('优化GC参数')elif rec['action'] == 'increase_executors':if self._increase_executors():applied_changes.append('增加执行器数量')return applied_changesdef _get_jenkins_pid(self):"""获取Jenkins进程ID"""try:result = subprocess.run(['pgrep', '-f', 'jenkins.war'], capture_output=True, text=True)return result.stdout.strip() if result.returncode == 0 else Noneexcept:return Nonedef _get_cpu_usage(self, pid):"""获取CPU使用率"""try:result = subprocess.run(['ps', '-p', pid, '-o', '%cpu', '--no-headers'],capture_output=True, text=True)return float(result.stdout.strip()) if result.returncode == 0 else 0except:return 0def _get_memory_info(self, pid):"""获取内存信息"""try:# 获取进程内存使用ps_result = subprocess.run(['ps', '-p', pid, '-o', '%mem,vsz,rss', '--no-headers'],capture_output=True, text=True)mem_percent, vsz, rss = ps_result.stdout.strip().split()# 获取JVM堆信息jstat_result = subprocess.run(['jstat', '-gc', pid],capture_output=True, text=True)gc_data = jstat_result.stdout.strip().split('\n')[-1].split()heap_used = float(gc_data[2]) + float(gc_data[3]) + float(gc_data[5]) + float(gc_data[7])heap_total = float(gc_data[0]) + float(gc_data[1]) + float(gc_data[4]) + float(gc_data[6])return {'mem_percent': float(mem_percent),'vsz_kb': int(vsz),'rss_kb': int(rss),'heap_used_kb': heap_used,'heap_total_kb': heap_total,'heap_usage_percent': (heap_used / heap_total) * 100 if heap_total > 0 else 0}except:return {}def _get_gc_info(self, pid):"""获取GC信息"""try:result = subprocess.run(['jstat', '-gc', pid],capture_output=True, text=True)lines = result.stdout.strip().split('\n')if len(lines) >= 2:headers = lines[0].split()values = lines[1].split()gc_data = dict(zip(headers, values))# 计算GC时间占比gc_time = float(gc_data.get('GCT', 0))uptime_result = subprocess.run(['ps', '-p', pid, '-o', 'etime', '--no-headers'],capture_output=True, text=True)uptime_str = uptime_result.stdout.strip()uptime_seconds = self._parse_uptime(uptime_str)time_percent = (gc_time / uptime_seconds) * 100 if uptime_seconds > 0 else 0return {'total_time': gc_time,'time_percent': time_percent,'young_gc_count': int(gc_data.get('YGC', 0)),'full_gc_count': int(gc_data.get('FGC', 0))}except:passreturn {}def _parse_uptime(self, uptime_str):"""解析进程运行时间"""# 格式: [[DD-]HH:]MM:SSparts = uptime_str.split(':')seconds = 0if len(parts) == 2: # MM:SSseconds = int(parts[0]) * 60 + int(parts[1])elif len(parts) == 3: # HH:MM:SSseconds = int(parts[0]) * 3600 + int(parts[1]) * 60 + int(parts[2])elif '-' in uptime_str: # DD-HH:MM:SSday_part, time_part = uptime_str.split('-')days = int(day_part)time_parts = time_part.split(':')seconds = days * 86400 + int(time_parts[0]) * 3600 + int(time_parts[1]) * 60 + int(time_parts[2])return secondsdef _get_response_time(self):"""获取响应时间"""try:import timeimport urllib.requeststart_time = time.time()urllib.request.urlopen('http://localhost:8080/api/json', timeout=10)end_time = time.time()return (end_time - start_time) * 1000 # 转换为毫秒except:return 0def _get_queue_length(self):"""获取构建队列长度"""try:import urllib.requestimport jsonresponse = urllib.request.urlopen('http://localhost:8080/queue/api/json', timeout=5)data = json.loads(response.read().decode())return len(data.get('items', []))except:return 0def _increase_heap_size(self):"""增加堆内存大小"""# 这里应该修改Jenkins启动脚本# 实际实现需要根据具体的部署方式print("建议增加堆内存大小到当前的1.5倍")return Truedef _tune_gc_parameters(self):"""调整GC参数"""print("建议调整GC参数以减少GC时间")return Truedef _increase_executors(self):"""增加执行器数量"""print("建议增加执行器数量以提高并发处理能力")return Truedef run(self):"""运行自动调优"""print("=== Jenkins自动调优开始 ===")# 收集指标if not self.collect_metrics():print("无法收集性能指标")returnprint(f"当前性能指标:")print(f" CPU使用率: {self.metrics['cpu_usage']:.1f}%")print(f" 内存使用率: {self.metrics['memory'].get('heap_usage_percent', 0):.1f}%")print(f" GC时间占比: {self.metrics['gc'].get('time_percent', 0):.1f}%")print(f" 响应时间: {self.metrics['response_time']:.0f}ms")print(f" 队列长度: {self.metrics['queue_length']}")# 分析性能recommendations = self.analyze_performance()if not recommendations:print("✅ 系统性能良好,无需调优")returnprint(f"\n发现 {len(recommendations)} 个优化建议:")for i, rec in enumerate(recommendations, 1):print(f" {i}. [{rec['severity'].upper()}] {rec['message']}")# 应用调优applied_changes = self.apply_tuning(recommendations)if applied_changes:print(f"\n已应用以下优化:")for change in applied_changes:print(f" ✓ {change}")print("\n=== 自动调优完成 ===")if __name__ == '__main__':tuner = JenkinsAutoTuner()tuner.run()
本章小结
本章详细介绍了Jenkins的性能优化:
- 性能优化概述:了解性能问题识别和优化策略
- JVM调优:掌握内存配置和垃圾回收优化
- 系统级优化:学习操作系统和存储优化
- 构建优化:实现Pipeline和资源管理优化
- 网络优化:配置带宽和连接优化
- 监控调优工具:使用性能分析和自动调优工具
通过系统性的性能优化,可以显著提升Jenkins的运行效率和用户体验。
下一章预告
下一章我们将学习Jenkins的故障排除,包括常见问题诊断、日志分析和恢复策略。
练习与思考
理论练习
-
性能分析:
- 分析不同类型的性能瓶颈
- 设计性能监控方案
- 制定性能优化计划
-
调优策略:
- 比较不同JVM垃圾回收器的特点
- 设计资源分配策略
- 规划网络优化方案
实践练习
-
JVM调优:
- 配置G1GC参数
- 分析GC日志
- 优化内存配置
-
系统优化:
- 实施操作系统调优
- 配置存储优化
- 部署监控工具
思考题
-
优化平衡:
- 如何在性能和稳定性之间找到平衡?
- 如何评估优化效果?
- 如何避免过度优化?
-
持续改进:
-
如何建立性能优化的持续改进机制?
-
如何处理性能回归问题?
-
如何在团队中推广性能优化最佳实践? {
script {
// 优化的检出策略
checkout([
class:′GitSCM′,branches:[[name:env.BRANCHNAME]],doGenerateSubmoduleConfigurations:false,extensions:[[class: 'GitSCM',branches: [[name: env.BRANCH_NAME]],doGenerateSubmoduleConfigurations: false,extensions: [[class:′GitSCM′,branches:[[name:env.BRANCHNAME]],doGenerateSubmoduleConfigurations:false,extensions:[[class: ‘CloneOption’,
depth: 1,
noTags: true,
shallow: true],
[$class: ‘CheckoutOption’, timeout: 10]
],
userRemoteConfigs: [[url: env.GIT_URL]]
])
}// 缓存依赖stash includes: '**', name: 'source-code'}
}
stage(‘Parallel Build & Test’) {
parallel {
stage(‘Unit Tests’) {
agent { label ‘test-runner’ }
steps {
unstash ‘source-code’// 使用缓存的依赖script {if (fileExists('pom.xml')) {sh '''# Maven并行构建mvn clean test \-T 4 \-Dmaven.test.failure.ignore=true \-Dmaven.repo.local=/var/cache/maven \-Dparallel=methods \-DthreadCount=4'''} else if (fileExists('build.gradle')) {sh '''# Gradle并行构建./gradlew test \--parallel \--max-workers=4 \--build-cache \--gradle-user-home=/var/cache/gradle'''}}}post {always {publishTestResults(testResultsPattern: '**/target/surefire-reports/*.xml,**/build/test-results/**/*.xml',allowEmptyResults: true)}}}stage('Code Quality') {agent { label 'sonar-scanner' }steps {unstash 'source-code'script {// 并行代码质量检查parallel(['SonarQube': {sh '''sonar-scanner \-Dsonar.projectKey=${JOB_NAME} \-Dsonar.sources=src \-Dsonar.host.url=${SONAR_URL} \-Dsonar.login=${SONAR_TOKEN}'''},'Security Scan': {sh '''# OWASP依赖检查dependency-check.sh \--project ${JOB_NAME} \--scan . \--format XML \--out dependency-check-report.xml'''}])}}}stage('Build Artifacts') {agent { label 'build-server' }steps {unstash 'source-code'script {if (fileExists('pom.xml')) {sh '''# Maven优化构建mvn clean package \-T 4 \-DskipTests \-Dmaven.repo.local=/var/cache/maven \-Dmaven.compile.fork=true \-Dmaven.compiler.maxmem=1024m'''} else if (fileExists('Dockerfile')) {sh '''# Docker多阶段构建docker build \--build-arg BUILDKIT_INLINE_CACHE=1 \--cache-from ${IMAGE_NAME}:cache \-t ${IMAGE_NAME}:${BUILD_NUMBER} \-t ${IMAGE_NAME}:latest .'''}}// 存储构建产物stash includes: '**/target/*.jar,**/build/libs/*.jar', name: 'artifacts'}}}
}
stage(‘Integration Tests’) {
agent { label ‘integration-test’ }
when {
anyOf {
branch ‘main’
branch ‘develop’
changeRequest()
}
}
steps {
unstash ‘source-code’
unstash ‘artifacts’script {// 并行集成测试def testStages = [:]['api-tests', 'ui-tests', 'performance-tests'].each { testType ->testStages[testType] = {sh "./run-${testType}.sh"}}parallel testStages}}
}
stage(‘Deploy’) {
agent { label ‘deployment’ }
when {
branch ‘main’
}
steps {
unstash ‘artifacts’script {// 蓝绿部署sh '''# 部署到蓝绿环境./deploy.sh --strategy=blue-green --timeout=300'''}}
}
}
post {
always {
script {
// 清理工作空间
cleanWs(
cleanWhenAborted: true,
cleanWhenFailure: true,
cleanWhenNotBuilt: true,
cleanWhenSuccess: true,
cleanWhenUnstable: true,
deleteDirs: true
)
}
}success {script {// 成功通知if (env.BRANCH_NAME == 'main') {slackSend(channel: '#deployments',color: 'good',message: "✅ 部署成功: ${env.JOB_NAME} #${env.BUILD_NUMBER}")}}}failure {script {// 失败通知和分析emailext(subject: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",body: '''构建失败详情:项目: ${env.JOB_NAME}构建号: ${env.BUILD_NUMBER}分支: ${env.BRANCH_NAME}提交: ${env.GIT_COMMIT}查看详情: ${env.BUILD_URL}''',to: '${DEFAULT_RECIPIENTS}')}}
}
} -
**构建缓存优化:**
```groovy
// 共享库中的缓存管理
@Library('jenkins-shared-library') _def buildWithCache(Map config) {def cacheKey = generateCacheKey(config)def cacheHit = falsestage('Cache Check') {script {// 检查缓存是否存在cacheHit = checkCache(cacheKey)if (cacheHit) {echo "缓存命中: ${cacheKey}"restoreCache(cacheKey)} else {echo "缓存未命中,开始构建"}}}if (!cacheHit) {stage('Build') {script {// 执行构建config.buildSteps()// 保存缓存saveCache(cacheKey, config.cachePatterns)}}}
}def generateCacheKey(Map config) {// 基于文件内容生成缓存键def checksums = []config.cacheFiles.each { file ->if (fileExists(file)) {def checksum = sh(script: "sha256sum ${file} | cut -d' ' -f1",returnStdout: true).trim()checksums.add(checksum)}}def combinedChecksum = sh(script: "echo '${checksums.join(',')}' | sha256sum | cut -d' ' -f1",returnStdout: true).trim()return "${config.projectName}-${combinedChecksum}"
}def checkCache(String cacheKey) {// 检查S3或其他缓存存储def exitCode = sh(script: "aws s3 ls s3://jenkins-cache/${cacheKey}.tar.gz",returnStatus: true)return exitCode == 0
}def restoreCache(String cacheKey) {sh """aws s3 cp s3://jenkins-cache/${cacheKey}.tar.gz cache.tar.gztar -xzf cache.tar.gzrm cache.tar.gz"""
}def saveCache(String cacheKey, List patterns) {def files = patterns.join(' ')sh """tar -czf cache.tar.gz ${files}aws s3 cp cache.tar.gz s3://jenkins-cache/${cacheKey}.tar.gzrm cache.tar.gz"""
}// 使用示例
pipeline {agent anystages {stage('Build with Cache') {steps {script {buildWithCache([projectName: 'my-app',cacheFiles: ['pom.xml', 'package.json', 'requirements.txt'],cachePatterns: ['~/.m2/repository', 'node_modules', '.venv'],buildSteps: {sh 'mvn clean package'sh 'npm install'sh 'pip install -r requirements.txt'}])}}}}
}
资源管理优化
动态节点管理:
// 智能节点分配脚本
@Library('jenkins-shared-library') _def allocateOptimalNode(Map requirements) {def availableNodes = getAvailableNodes()def optimalNode = selectOptimalNode(availableNodes, requirements)if (optimalNode) {return optimalNode} else {// 动态创建节点return createDynamicNode(requirements)}
}def getAvailableNodes() {def nodes = []Jenkins.instance.computers.each { computer ->if (computer.isOnline() && !computer.isTemporarilyOffline()) {def node = computer.getNode()def executor = computer.getExecutors().find { !it.isBusy() }if (executor) {nodes.add([name: node.getNodeName(),labels: node.getLabelString().split(' '),cpu: getNodeCpuUsage(computer),memory: getNodeMemoryUsage(computer),disk: getNodeDiskUsage(computer),load: getNodeLoad(computer)])}}}return nodes
}def selectOptimalNode(List nodes, Map requirements) {// 过滤满足标签要求的节点def candidateNodes = nodes.findAll { node ->requirements.labels.every { label ->node.labels.contains(label)}}if (candidateNodes.isEmpty()) {return null}// 计算节点得分def scoredNodes = candidateNodes.collect { node ->def score = calculateNodeScore(node, requirements)[node: node, score: score]}// 选择得分最高的节点def bestNode = scoredNodes.max { it.score }return bestNode.node
}def calculateNodeScore(Map node, Map requirements) {def score = 0// CPU得分(使用率越低得分越高)score += (100 - node.cpu) * 0.3// 内存得分score += (100 - node.memory) * 0.3// 磁盘得分score += (100 - node.disk) * 0.2// 负载得分score += Math.max(0, 100 - node.load * 20) * 0.2// 特殊要求加分if (requirements.preferSSD && node.labels.contains('ssd')) {score += 10}if (requirements.preferHighCpu && node.labels.contains('high-cpu')) {score += 10}return score
}def createDynamicNode(Map requirements) {// 基于需求创建云节点def nodeTemplate = selectNodeTemplate(requirements)def cloudName = nodeTemplate.cloud// 触发节点创建def cloud = Jenkins.instance.getCloud(cloudName)def provisionedNode = cloud.provision(nodeTemplate, 1)// 等待节点上线waitForNodeOnline(provisionedNode.name, 300) // 5分钟超时return provisionedNode
}def selectNodeTemplate(Map requirements) {def templates = [[name: 'small-node',cloud: 'aws-ec2',instanceType: 't3.medium',labels: ['linux', 'docker'],cpu: 2,memory: 4],[name: 'medium-node',cloud: 'aws-ec2',instanceType: 't3.large',labels: ['linux', 'docker', 'maven'],cpu: 2,memory: 8],[name: 'large-node',cloud: 'aws-ec2',instanceType: 't3.xlarge',labels: ['linux', 'docker', 'high-cpu'],cpu: 4,memory: 16]]// 选择满足需求的最小模板def suitableTemplates = templates.findAll { template ->template.cpu >= requirements.minCpu &&template.memory >= requirements.minMemory &&requirements.labels.every { label ->template.labels.contains(label)}}return suitableTemplates.min { it.cpu + it.memory }
}// 使用示例
pipeline {agent nonestages {stage('Lightweight Tasks') {agent {label allocateOptimalNode([labels: ['linux', 'docker'],minCpu: 1,minMemory: 2,preferSSD: false]).name}steps {sh 'echo "Running on optimized node"'}}stage('Heavy Compilation') {agent {label allocateOptimalNode([labels: ['linux', 'maven', 'high-cpu'],minCpu: 4,minMemory: 8,preferSSD: true,preferHighCpu: true]).name}steps