일반적인 Jenkins 성능 병목 현상과 해결 방법

느린 Jenkins 인스턴스로 고민하고 계신가요? 이 종합 가이드에서는 메모리 누수, 디스크 공간 문제, 과도한 로깅 등 일반적인 Jenkins 성능 병목 현상을 심층적으로 다룹니다. 증상 식별, 근본 원인 파악, JVM 튜닝, 지능형 빌드 기록 관리, 로그 최적화, 효율적인 파이프라인 코딩과 같은 실행 가능한 해결책을 배우게 됩니다. CI/CD 파이프라인을 원활하게 유지하여 더 빠른 빌드, 반응형 UI, 전반적으로 더 효율적인 소프트웨어 제공 프로세스를 보장하는 필수 모니터링 도구와 모범 사례를 알아보세요.

일반적인 Jenkins 성능 병목 현상과 해결 방법

느린 Jenkins 인스턴스는 일반적으로 단일 원인으로 발생하지 않습니다. UI가 무겁게 느껴지고, 빌드가 대기열에서 대기하며, 에이전트가 연결 해제되고, 로그를 열 때까지 오래 걸리며, 누군가 "Jenkins가 또 다운됐어"라고 말합니다. 이러한 불만의 이면에는 종종 컨트롤러 힙 압박, 느린 디스크, 과부하된 에이전트, 플러그인 문제, 잘못된 파이프라인 동작, 소스 제어 및 아티팩트 시스템으로의 네트워크 지연 등 몇 가지 일반적인 병목 현상 중 하나가 있습니다.

Jenkins 성능을 가장 빠르게 개선하는 방법은 컨트롤러 문제와 빌드 문제를 분리하는 것입니다. 빌드가 실행되지 않는 경우에도 Jenkins UI, 대기열 및 작업 페이지가 느리다면 컨트롤러부터 시작하십시오. UI는 괜찮지만 빌드 시간이 너무 오래 걸린다면 에이전트, 작업 공간, 캐시 및 외부 시스템부터 시작하십시오.

컨트롤러 메모리 및 가비지 컬렉션

Jenkins 컨트롤러는 Java 프로세스입니다. 작업 구성, 플러그인, 빌드 메타데이터, 대기열 상태 및 웹 요청을 위해 충분한 힙이 필요합니다. 힙이 너무 작으면 컨트롤러가 가비지 컬렉션에 너무 많은 시간을 소비합니다. 플러그인이 메모리를 누수하거나 메모리에 너무 많은 데이터를 저장하는 경우 힙을 늘리는 것은 다음 사고를 지연시킬 뿐입니다.

증상으로는 느린 UI, 긴 일시 중지, OutOfMemoryError, 빈번한 에이전트 연결 해제, 사용 가능한 실행기와 일치하지 않는 빌드 대기열 지연 등이 있습니다.

먼저 프로세스와 로그를 확인하십시오:

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

중간 규모 컨트롤러의 경우 2~4GB와 같은 힙이 충분할 수 있습니다. 사용량이 많은 설치에는 더 많은 용량이 필요할 수 있습니다. 힙을 시스템 RAM의 대부분으로 맹목적으로 설정하지 마십시오. OS는 여전히 파일 시스템 캐시, 프로세스 오버헤드 및 모니터링 에이전트를 위한 메모리가 필요합니다.

일반적인 서비스 옵션은 다음과 같습니다:

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

JVM 옵션을 변경한 후 유지 관리 기간 동안 Jenkins를 다시 시작하고 정상 부하에서의 동작을 관찰하십시오. 메모리가 며칠 동안 꾸준히 증가하고 안정화되지 않으면 힙 덤프를 수행하고 최근에 업데이트되거나 설치된 플러그인을 검토하십시오. 플러그인을 최신 상태로 유지하되, 롤백 계획 없이 대규모 플러그인 세트를 업데이트하지 마십시오.

디스크 공간 및 디스크 I/O

Jenkins는 지속적으로 디스크를 사용합니다. JENKINS_HOME은 작업 구성, 빌드 레코드, 지문, 플러그인 데이터, 비밀, 로그, 그리고 때로는 너무 많은 아티팩트를 저장합니다. 에이전트는 작업 공간, 종속성 캐시, Docker 레이어, 테스트 보고서 및 임시 파일에 디스크를 사용합니다.

디스크가 가득 차면 명백합니다. 느린 디스크는 더 성가신데, 아무것도 고장난 것처럼 보이지 않지만 모든 것이 대기하기 때문입니다.

용량과 지연 시간을 모두 확인하십시오:

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

빌드 중 %util 및 대기 시간이 높으면 디스크가 병목 현상입니다. 일반적인 해결 방법은 작업 공간을 더 빠른 스토리지로 이동하고, 오래된 Docker 레이어를 정리하고, 아티팩트 보존을 줄이고, 보고서나 패키지만 필요한 경우 전체 디렉토리를 보관하는 작업을 중지하는 것입니다.

작업에 빌드 폐기 정책을 설정하십시오:

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

JENKINS_HOME에서 수동 정리에 주의하십시오. Jenkins가 실행되는 동안 임의의 XML 파일이나 플러그인 디렉토리를 삭제하지 마십시오. Jenkins 보존 설정, 플러그인별 정리 도구 및 백업을 사용하십시오.

컨트롤러에서 너무 많은 작업 수행

가장 해로운 구성 중 하나는 컨트롤러에서 빌드를 실행하는 것입니다. 컨트롤러는 컴파일, 테스트 실행, Docker 이미지 빌드 또는 대규모 체크아웃을 수행해서는 안 됩니다.

대부분의 설치에서 컨트롤러 실행기를 0으로 설정하십시오. 빌드를 에이전트에 배치하십시오. 이미 컨트롤러 빌드를 실행 중인 경우 로컬 도구 경로, 자격 증명 바인딩 또는 컨트롤러에만 존재하는 파일과 같은 숨겨진 가정을 주시하면서 점진적으로 이동하십시오.

또한 컨트롤러에 부담을 주는 Groovy 코드가 있는지 파이프라인을 확인하십시오. sh와 같은 파이프라인 단계는 에이전트에서 실행되지만 Jenkinsfile의 Groovy 로직은 컨트롤러에서 실행될 수 있습니다. 대용량 파일을 Groovy 변수로 읽거나, 거대한 맵을 구축하거나, 파이프라인 스크립트에서 대규모 JSON 처리를 수행하지 마십시오. 대용량 데이터 작업에는 에이전트에서 셸, Python, jq 또는 빌드 도구를 사용하십시오.

에이전트 과부하 또는 부적합

특정 레이블의 대기열 시간이 긴 경우 일반 실행기를 추가해도 도움이 되지 않습니다. linux && docker && large-memory가 필요한 작업에는 정확히 해당 용량이 필요합니다.

대기열 이유와 레이블 사용량을 확인하십시오. 그런 다음 에이전트 OS를 확인하십시오:

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

에이전트가 스와핑 중이면 실행기를 줄이거나 메모리를 늘리십시오. 사용량이 많은 시간에 CPU 사용률이 높고 빌드 기간이 증가하면 동시성을 줄이거나 에이전트를 추가하십시오. I/O 대기가 높으면 캐시와 작업 공간을 더 빠른 스토리지로 이동하거나 해당 노드의 동시 작업 수를 줄이십시오.

Kubernetes 에이전트의 경우 리소스 요청이 Jenkins 실행기 수만큼 중요합니다. 너무 적은 CPU나 메모리를 요청하는 Pod는 이미 사용량이 많은 노드에 예약될 수 있으며, Jenkins는 준비된 에이전트를 보지만 빌드는 느리게 진행됩니다. 일회용 Pod의 경우 Pod당 하나의 실행기가 일반적으로 동일한 컨테이너를 공유하는 여러 실행기보다 추론하기 쉽습니다.

플러그인 문제

플러그인은 Jenkins의 강점 중 하나이지만 성능 문제의 일반적인 원인이기도 합니다. 플러그인은 페이지 렌더링 비용을 추가하고, 작업 로딩을 느리게 하며, 너무 많은 빌드 기록을 보유하거나, 일반 UI 작업 중에 외부 호출을 할 수 있습니다.

성능이 갑자기 변경되면 최근에 변경된 사항을 확인하십시오:

  • Jenkins 코어 업그레이드.
  • 플러그인 업그레이드.
  • 새 플러그인 설치.
  • 새 전역 구성.
  • 새 파이프라인 라이브러리 버전.

"Manage Jenkins" 상태 정보, 로그 및 플러그인 변경 로그를 사용하십시오. 플러그인 업데이트 후 UI가 느려진 경우 스테이징 컨트롤러(있는 경우)에서 롤백을 테스트하십시오. 대규모 업그레이드 전에 JENKINS_HOME 및 플러그인 버전을 백업하십시오.

"만약을 대비해" 플러그인을 유지하지 마십시오. 설치된 각 플러그인은 유지 관리 표면을 추가합니다. 작업 종속성을 확인한 후 사용하지 않는 플러그인을 제거하십시오.

SCM 및 아티팩트 저장소 지연

많은 "Jenkins가 느리다"는 보고는 실제로 Git, 패키지 레지스트리, 컨테이너 레지스트리 또는 아티팩트 저장소 문제입니다.

반복되는 느린 단계가 있는지 빌드 로그를 확인하십시오:

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

모든 작업이 종속성 다운로드를 기다리는 경우 가까운 프록시 또는 캐시를 추가하십시오. git fetch가 느린 경우 저장소 크기, 브랜치 검색, 얕은 클론 설정 및 에이전트에서 Git 서버까지의 네트워크 경로를 확인하십시오. 임시 에이전트에서 Docker 풀이 느린 경우 레지스트리 미러 또는 BuildKit 레지스트리 캐시를 사용하십시오.

진단을 정직하게 유지하십시오: Jenkins는 작업을 예약하지만 먼 패키지 레지스트리를 빠르게 만들 수는 없습니다.

로그 및 빌드 기록 비대

대용량 콘솔 로그는 페이지 렌더링을 느리게 하고 스토리지를 차지합니다. 모든 테스트 픽스처, 모든 HTTP 응답 또는 일반 빌드 중 전체 디버그 로그를 출력하는 작업은 결국 Jenkins 사용을 불편하게 만듭니다.

먼저 작업을 수정하십시오. 일반 로그 상세도를 줄이고 필요한 경우에만 압축된 아티팩트로 상세 로그를 보관하십시오. 콘솔 출력을 진행 상황 및 실패 컨텍스트에 집중하십시오.

그런 다음 보존을 설정하십시오:

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

규정 준수가 중요한 환경의 경우 장기 아티팩트와 로그를 보존, 검색 및 수명 주기 정책을 위해 설계된 외부 스토리지 시스템으로 이동하십시오.

실용적인 사고 대응 경로

Jenkins가 지금 느린 경우 다음 순서를 사용하십시오:

  1. 컨트롤러 UI가 느린지 확인하십시오.
  2. 컨트롤러 CPU, 메모리, GC 증상 및 디스크를 확인하십시오.
  3. 대기열 이유와 대기 중인 레이블을 확인하십시오.
  4. 가장 바쁜 에이전트의 CPU, 메모리, 디스크 및 작업 공간 증가를 확인하십시오.
  5. 최근 플러그인, 작업 및 공유 라이브러리 변경 사항을 비교하십시오.
  6. 느린 빌드 로그 하나를 읽고 반복되는 비용이 많이 드는 단계를 식별하십시오.

이 경로는 무작위 튜닝을 방지합니다. 힙을 늘려도 포화된 Docker 에이전트는 해결되지 않습니다. 실행기를 추가해도 가득 찬 디스크는 해결되지 않습니다. 작업 공간을 정리해도 컨트롤러 일시 중지를 유발하는 플러그인은 해결되지 않습니다.

Jenkins 유지 관리 가능하게 유지

건강한 Jenkins 설치는 지루한 습관을 가지고 있습니다: 컨트롤러 실행기가 0으로 설정되고, 에이전트가 워크로드에 맞게 크기가 조정되며, 빌드 보존이 구성되고, 종속성 캐시가 의도적이며, 플러그인 업데이트가 추적되고, 기본 메트릭이 Prometheus, Grafana, CloudWatch 또는 팀이 이미 사용하는 모니터링 시스템으로 내보내집니다.

가장 좋은 해결책은 종종 작고 구체적입니다. Docker 빌드를 전용 에이전트로 이동하십시오. 시끄러운 작업의 로그 출력을 줄이십시오. Maven 프록시를 추가하십시오. 스와핑 노드의 실행기를 줄이십시오. 사용하지 않는 플러그인을 제거하십시오. 수년간 모든 빌드를 유지한 작업에 보존을 설정하십시오.

Jenkins 성능은 하나의 블랙박스로 취급하는 것을 중단하고 대기열에서 컨트롤러, 에이전트, 파일 시스템, 네트워크 종속성, 그리고 다시 빌드 로그로 작업을 따라가기 시작할 때 향상됩니다.

예시: 빌드는 느리지만 Jenkins는 괜찮음

개발자가 PR 확인에 25분이 걸린다고 보고한다고 가정해 보겠습니다. Jenkins UI는 반응이 빠릅니다. 대기열은 짧습니다. 에이전트는 온라인 상태입니다. 느린 로그는 다음을 보여줍니다:

git fetch: 20초
npm ci: 9분
단위 테스트: 4분
docker build: 10분
아티팩트 보관: 1분

이는 주로 Jenkins 컨트롤러 문제가 아닙니다. 가능한 해결책은 패키지 캐싱, Dockerfile 레이어 순서, BuildKit 캐시, 그리고 아마도 테스트 분할입니다. 컨트롤러 힙을 늘려도 아무것도 변경되지 않습니다.

예시: 모든 것이 대기하지만 에이전트는 유휴 상태

에이전트가 유휴 상태로 보이는 동안 작업이 대기열에 있는 경우 대기열 이유를 읽으십시오. 작업에 linux && docker가 필요한데 유휴 에이전트에는 linux만 있을 수 있습니다. 또는 작업이 disableConcurrentBuilds, 잠금 가능 리소스 또는 일치하는 에이전트 프로비저닝에 실패하는 클라우드 플러그인에 의해 차단될 수 있습니다.

이러한 병목 현상은 원시 용량이 아닌 구성입니다. 일치하지 않는 에이전트 두 개를 추가해도 도움이 되지 않습니다.

예시: 컨트롤러가 매일 오후에 느려짐

UI가 매일 같은 시간에 저하되는 경우 예약된 작업(브랜치 인덱싱, 백업, 대규모 아티팩트 정리, 취약점 스캔 또는 너무 일찍 시작되는 야간 파이프라인)을 찾으십시오. 해당 시간대에 컨트롤러 CPU, 힙 및 디스크 I/O를 확인하십시오. 또한 0 2 * * *와 같은 cron 표현식으로 인해 많은 작업이 정확히 같은 분에 시작되는지 확인하십시오.

Jenkins 일정에서는 가능한 경우 해시된 타이밍을 선호하십시오:

H 2 * * *

이렇게 하면 모든 작업이 정시에 시작되는 대신 분산됩니다.

좋은 모니터링이 답해야 할 사항

최소한 모니터링은 서버에 로그인하지 않고도 다음 질문에 답할 수 있어야 합니다:

  • 컨트롤러 프로세스가 살아 있고 응답하는가?
  • 힙이 얼마나 사용되고 있으며 가비지 컬렉션이 얼마나 자주 실행되고 있는가?
  • 레이블별로 작업이 대기열에서 얼마나 오래 대기하고 있는가?
  • 어떤 에이전트가 오프라인이거나 반복적으로 재연결되고 있는가?
  • 컨트롤러 및 에이전트 디스크가 거의 가득 찼는가?
  • 동일한 작업에 대해 빌드 시간이 점점 느려지고 있는가?

완벽한 대시보드가 필요하지 않습니다. 디스크, 힙, 대기열 길이 및 에이전트 가용성에 대한 몇 가지 메트릭과 알림만으로도 개발자가 보고하기 전에 많은 장애를 포착할 수 있습니다.