Colli di bottiglia comuni delle prestazioni di Jenkins e come risolverli

Hai problemi con un'istanza Jenkins lenta? Questa guida completa analizza i colli di bottiglia comuni delle prestazioni di Jenkins, inclusi memory leak, problemi di spazio su disco e logging eccessivo. Impara a identificare i sintomi, comprendere le cause profonde e implementare soluzioni pratiche come la messa a punto JVM, la gestione intelligente della cronologia delle build, l'ottimizzazione dei log e la codifica efficiente delle pipeline. Scopri gli strumenti di monitoraggio essenziali e le best practice per mantenere le tue pipeline CI/CD fluide, garantendo build più veloci, un'interfaccia utente reattiva e un processo di delivery del software complessivamente più efficiente.

Colli di bottiglia comuni delle prestazioni di Jenkins e come risolverli

Un'istanza Jenkins lenta di solito non ha una sola causa. L'interfaccia utente è pesante, le build aspettano in coda, gli agenti si disconnettono, i log impiegano un'eternità ad aprirsi e qualcuno dice: "Jenkins è di nuovo giù". Sotto questa lamentela, il problema è spesso uno dei pochi colli di bottiglia ordinari: pressione heap del controller, disco lento, agenti sovraccarichi, problemi con i plugin, comportamento scorretto delle pipeline o ritardi di rete verso i sistemi di controllo versione e artefatti.

Il modo più rapido per risolvere le prestazioni di Jenkins è separare i problemi del controller dai problemi di build. Se l'interfaccia utente di Jenkins, la coda e le pagine dei job sono lente anche quando non sono in esecuzione build, inizia dal controller. Se l'interfaccia utente è ok ma le build richiedono troppo tempo, inizia da agenti, workspace, cache e sistemi esterni.

Memoria del controller e garbage collection

Il controller Jenkins è un processo Java. Necessita di heap sufficiente per la configurazione dei job, i plugin, i metadati delle build, lo stato della coda e le richieste web. Quando l'heap è troppo piccolo, il controller trascorre troppo tempo nella garbage collection. Quando un plugin perde memoria o memorizza troppi dati in memoria, aumentare l'heap potrebbe solo ritardare l'incidente successivo.

I sintomi includono un'interfaccia utente lenta, pause lunghe, OutOfMemoryError, frequenti disconnessioni degli agenti o ritardi nella coda delle build che non corrispondono agli executor disponibili.

Controlla prima il processo e i log:

ps -o pid,rss,vsz,etime,cmd -C java
journalctl -u jenkins --since "2 hours ago" | grep -Ei 'OutOfMemory|GC overhead|heap|killed'

Per un controller moderato, un heap come 2-4 GB potrebbe essere sufficiente. Installazioni molto utilizzate potrebbero averne bisogno di più. Non impostare ciecamente l'heap alla maggior parte della RAM della macchina. Il sistema operativo necessita ancora di memoria per la cache del filesystem, l'overhead dei processi e gli agenti di monitoraggio.

Le opzioni di servizio tipiche sono simili a queste:

JENKINS_JAVA_OPTS="-Xms1g -Xmx4g -XX:+UseG1GC"

Dopo aver modificato le opzioni JVM, riavvia Jenkins durante una finestra di manutenzione e osserva il comportamento sotto carico normale. Se la memoria aumenta costantemente per giorni e non si stabilizza mai, esegui un heap dump e verifica i plugin installati o aggiornati di recente. Mantieni i plugin aggiornati, ma evita di aggiornare un set di plugin di grandi dimensioni senza un piano di rollback.

Spazio su disco e I/O del disco

Jenkins utilizza il disco costantemente. JENKINS_HOME memorizza la configurazione dei job, i record delle build, le impronte digitali, i dati dei plugin, i segreti, i log e talvolta troppi artefatti. Gli agenti utilizzano il disco per workspace, cache delle dipendenze, layer Docker, report di test e file temporanei.

Un disco pieno è ovvio. Un disco lento è più fastidioso perché nulla sembra rotto; tutto aspetta e basta.

Controlla sia la capacità che la latenza:

df -h
du -sh /var/lib/jenkins/* 2>/dev/null | sort -h | tail
iostat -xz 1

Se %util e i tempi di attesa sono alti durante le build, il disco è un collo di bottiglia. Le soluzioni comuni sono spostare i workspace su storage più veloce, eliminare i vecchi layer Docker, ridurre la conservazione degli artefatti e impedire ai job di archiviare intere directory quando sono necessari solo report o pacchetti.

Imposta le politiche di scarto delle build sui job:

options {
  buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '10'))
}

Fai attenzione con la pulizia manuale in JENKINS_HOME. Non eliminare file XML casuali o directory di plugin mentre Jenkins è in esecuzione. Utilizza le impostazioni di conservazione di Jenkins, gli strumenti di pulizia specifici dei plugin e i backup.

Troppo lavoro sul controller

Una delle configurazioni più dannose è eseguire le build sul controller. Il controller non dovrebbe compilare, eseguire test, creare immagini Docker o fare checkout di grandi dimensioni.

Imposta gli executor del controller a 0 nella maggior parte delle installazioni. Metti le build sugli agenti. Se già esegui build sul controller, spostale gradualmente e fai attenzione a presupposti nascosti come percorsi di strumenti locali, binding di credenziali o file che esistono solo sul controller.

Controlla anche le pipeline per codice Groovy pesante sul controller. I passaggi della pipeline come sh vengono eseguiti sugli agenti, ma la logica Groovy nel Jenkinsfile può essere eseguita sul controller. Evita di leggere file di grandi dimensioni in variabili Groovy, costruire mappe enormi o fare elaborazione JSON di grandi dimensioni nello script della pipeline. Usa shell, Python, jq o il tuo strumento di build sull'agente per lavori pesanti sui dati.

Agenti sovraccarichi o non corrispondenti

Se il tempo di coda è alto per una label, aggiungere executor generici non aiuterà. Un job che richiede linux && docker && large-memory necessita di quella capacità esatta.

Guarda le ragioni della coda e l'uso delle label. Poi controlla il sistema operativo dell'agente:

uptime
free -h
mpstat 1
iostat -xz 1
docker system df

Se un agente sta facendo swapping, riduci gli executor o aumenta la memoria. Se la CPU è al massimo e la durata delle build aumenta durante i periodi di punta, riduci la concorrenza o aggiungi agenti. Se l'attesa I/O è alta, sposta cache e workspace su storage più veloce o riduci il numero di job concorrenti su quel nodo.

Per gli agenti Kubernetes, le richieste di risorse contano tanto quanto il conteggio degli executor di Jenkins. Un pod che richiede troppa poca CPU o memoria potrebbe essere schedulato su un nodo già occupato, quindi Jenkins vede un agente pronto mentre la build procede a rilento. Per pod usa e getta, un executor per pod è solitamente più facile da gestire rispetto a più executor che condividono lo stesso container.

Problemi con i plugin

I plugin sono uno dei punti di forza di Jenkins e anche una fonte comune di problemi di prestazioni. Un plugin può aggiungere costo di rendering della pagina, rallentare il caricamento dei job, conservare troppa cronologia delle build o effettuare chiamate esterne durante le normali azioni dell'interfaccia utente.

Quando le prestazioni cambiano improvvisamente, chiedi cosa è cambiato di recente:

  • Aggiornamento del core di Jenkins.
  • Aggiornamento di un plugin.
  • Installazione di un nuovo plugin.
  • Nuova configurazione globale.
  • Nuova versione della libreria di pipeline.

Usa le informazioni sullo stato di "Gestisci Jenkins", i log e i changelog dei plugin. Se l'interfaccia utente è diventata lenta dopo un aggiornamento di un plugin, testa il rollback in un controller di staging se ne hai uno. Mantieni un backup di JENKINS_HOME e delle versioni dei plugin prima di aggiornamenti importanti.

Non tenere plugin "per ogni evenienza." Ogni plugin installato aggiunge superficie di manutenzione. Rimuovi i plugin inutilizzati dopo aver verificato le dipendenze dei job.

Ritardi del repository SCM e degli artefatti

Molte segnalazioni di "Jenkins è lento" sono in realtà problemi di Git, registry di pacchetti, registry di container o repository di artefatti.

Controlla i log delle build per passaggi lenti ripetuti:

git fetch
mvn dependency:resolve
npm ci
docker pull
docker push
archiveArtifacts

Se ogni job aspetta i download delle dipendenze, aggiungi un proxy o una cache nelle vicinanze. Se git fetch è lento, controlla la dimensione del repository, la scoperta dei branch, le impostazioni di shallow clone e il percorso di rete dagli agenti al server Git. Se i pull Docker sono lenti su agenti effimeri, usa un mirror del registry o una cache del registry BuildKit.

Mantieni onesta la diagnosi: Jenkins pianifica il lavoro, ma non può rendere veloce un registry di pacchetti distante.

Log e cronologia delle build gonfiati

Log di console grandi rallentano il rendering delle pagine e occupano spazio di archiviazione. I job che stampano ogni fixture di test, ogni risposta HTTP o log di debug completi durante le build normali rendono Jenkins doloroso da usare.

Prima sistema il job. Riduci la verbosità normale dei log e archivia i log dettagliati come artefatti compressi solo quando necessario. Mantieni l'output della console concentrato sui progressi e sul contesto degli errori.

Poi imposta la conservazione:

options {
  buildDiscarder(logRotator(daysToKeepStr: '30', numToKeepStr: '50'))
}

Per ambienti con forti requisiti di conformità, sposta artefatti e log a lungo termine su un sistema di archiviazione esterno progettato per conservazione, ricerca e politiche del ciclo di vita.

Un percorso pratico per gli incidenti

Quando Jenkins è lento in questo momento, usa questo ordine:

  1. Controlla se l'interfaccia utente del controller è lenta.
  2. Controlla CPU, memoria, sintomi GC e disco del controller.
  3. Controlla le ragioni della coda e quali label sono in attesa.
  4. Controlla gli agenti più occupati per CPU, memoria, disco e crescita del workspace.
  5. Confronta le modifiche recenti a plugin, job e librerie condivise.
  6. Leggi un log di build lento e identifica il passaggio costoso ripetuto.

Questo percorso previene la messa a punto casuale. Aumentare l'heap non risolverà un agente Docker saturo. Aggiungere executor non risolverà un disco pieno. Potare i workspace non risolverà un plugin che causa pause del controller.

Mantieni Jenkins manutenibile

Le installazioni sane di Jenkins hanno abitudini noiose: executor del controller impostati a zero, agenti dimensionati per il loro carico di lavoro, conservazione delle build configurata, cache delle dipendenze intenzionali, aggiornamenti dei plugin tracciati e metriche di base esportate verso Prometheus, Grafana, CloudWatch o qualsiasi sistema di monitoraggio il team già utilizzi.

La soluzione migliore è spesso piccola e specifica. Sposta le build Docker su agenti dedicati. Riduci l'output di log di un job rumoroso. Aggiungi un proxy Maven. Riduci gli executor su un nodo che fa swapping. Rimuovi un plugin inutilizzato. Imposta la conservazione su un job che ha tenuto ogni build per anni.

Le prestazioni di Jenkins migliorano quando smetti di trattarlo come una scatola nera e inizi a seguire il lavoro: dalla coda, al controller, all'agente, al filesystem, alla dipendenza di rete e di nuovo al log di build.

Esempio: la build è lenta ma Jenkins è ok

Supponiamo che gli sviluppatori segnalino che i controlli delle pull request richiedono 25 minuti. L'interfaccia utente di Jenkins è reattiva. La coda è breve. Gli agenti sono online. Il log lento mostra:

git fetch: 20 secondi
npm ci: 9 minuti
test unitari: 4 minuti
docker build: 10 minuti
archivia artefatti: 1 minuto

Questo non è principalmente un problema del controller Jenkins. Le soluzioni probabili sono la memorizzazione nella cache dei pacchetti, l'ordinamento dei layer Dockerfile, la cache BuildKit e forse la suddivisione dei test. Aumentare l'heap del controller non cambierebbe nulla.

Esempio: tutto aspetta ma gli agenti sono inattivi

Se i job sono in coda mentre gli agenti sembrano inattivi, leggi il motivo della coda. Potresti scoprire che i job richiedono linux && docker, mentre gli agenti inattivi hanno solo linux. O i job potrebbero essere bloccati da disableConcurrentBuilds, una risorsa bloccabile o un plugin cloud che non riesce a fornire agenti corrispondenti.

Questo tipo di collo di bottiglia è di configurazione, non di capacità grezza. Aggiungere due agenti non corrispondenti non aiuterà.

Esempio: il controller rallenta ogni pomeriggio

Se l'interfaccia utente degrada alla stessa ora ogni giorno, cerca job pianificati: indicizzazione dei branch, backup, pulizia di grandi artefatti, scansioni di vulnerabilità o pipeline notturne che iniziano troppo presto. Controlla CPU, heap e I/O del disco del controller durante quella finestra. Controlla anche se molti job iniziano esattamente allo stesso minuto a causa di espressioni cron come 0 2 * * *.

Nelle pianificazioni di Jenkins, preferisci la temporizzazione hash dove possibile:

H 2 * * *

Questo distribuisce i job invece di farli iniziare tutti all'inizio dell'ora.

Cosa dovrebbe rispondere un buon monitoraggio

Come minimo, il monitoraggio dovrebbe rispondere a queste domande senza accedere al server:

  • Il processo del controller è vivo e reattivo?
  • Quanto heap viene utilizzato e con quale frequenza viene eseguita la garbage collection?
  • Quanto tempo aspettano i job in coda per label?
  • Quali agenti sono offline o si riconnettono ripetutamente?
  • I dischi del controller e degli agenti sono quasi pieni?
  • Le build stanno diventando più lente nel tempo per gli stessi job?

Non hai bisogno di una dashboard perfetta per iniziare. Anche poche metriche e avvisi per disco, heap, lunghezza della coda e disponibilità degli agenti cattureranno molti guasti prima che gli sviluppatori li segnalino.