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.BRANCHN?AME]],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