Published on

Jenkins 빌드가 갑자기 느려질 때 원인 7가지

Authors

서로 다른 팀(개발/인프라/보안)이 동시에 만지는 Jenkins는 “갑자기 느려짐”이 자주 발생합니다. 문제는 느려진 지점이 Jenkins 자체(컨트롤러/큐) 일 수도 있고, 에이전트 노드, Docker/쿠버네티스 런타임, 외부 네트워크(Git/Artifact/Registry), 스토리지(I/O), 플러그인/스크립트일 수도 있다는 점입니다.

이 글은 “원인 7가지”를 증상 → 확인 방법 → 즉시 조치 순으로 정리합니다. 가능하면 빌드 로그만 보고 추측하지 말고, 아래 커맨드로 병목을 수치로 잡는 것을 목표로 합니다.

> 전제: Pipeline/Declarative 기준 예시를 포함하며, 컨트롤러는 Jenkins, 실행 노드는 agent로 표기합니다.

1) 에이전트 노드 CPU/메모리 스로틀링(특히 컨테이너)

증상

  • 같은 커밋인데 단계별 시간이 전반적으로 늘어남(컴파일/테스트/압축 등 CPU 작업이 모두 느림)
  • docker build/npm ci/mvn test가 전체적으로 “느리고 고르게” 늘어남
  • Kubernetes 에이전트라면 Pod가 제한된 CPU quota로 스로틀링

확인 방법

  • 리눅스 에이전트에서 즉시 확인:
# CPU/메모리/Load
uptime
free -h
vmstat 1 5

# 컨테이너 환경이면 cgroup 제한 확인(일부 배포판 경로 상이)
cat /sys/fs/cgroup/cpu.max 2>/dev/null || true
cat /sys/fs/cgroup/memory.max 2>/dev/null || true
  • Kubernetes라면:
kubectl top pod -n jenkins
kubectl describe pod <agent-pod> -n jenkins | sed -n '/Limits:/,/Requests:/p'

즉시 조치

  • 에이전트 리소스 requests/limits 상향 또는 동시 실행 수 감소
  • JVM 기반 빌드(Gradle/Maven)라면 -Xmx가 너무 커서 OOM/스왑 유발하지 않는지 점검

2) 디스크 I/O 병목(워크스페이스/도커 레이어/캐시 디렉터리)

증상

  • checkout 이후부터 갑자기 느려짐(특히 테스트 리포트/아티팩트 생성 단계)
  • tar, zip, npm ci, pip install, gradle 캐시가 느려짐
  • 로그에 특별한 에러 없이 “그냥 오래 걸림”

확인 방법

  • 에이전트에서 디스크 사용량/IO wait 확인:
# 디스크 용량
 df -h

# IO wait/디스크 병목
 iostat -xz 1 5 2>/dev/null || true

# 상위 디렉터리 사용량(워크스페이스/캐시)
 du -h -d 1 $WORKSPACE | sort -h | tail -n 20
  • Docker 사용 시 레이어/빌드캐시가 비대해졌는지:
docker system df

즉시 조치

  • 워크스페이스 정리(단, 무조건 삭제는 금물: 캐시까지 날리면 더 느려질 수 있음)
  • 캐시 디렉터리(~/.m2, ~/.gradle, ~/.npm)를 별도 빠른 볼륨으로 분리
  • 도커 빌드가 많다면 BuildKit 캐시/registry cache 도입 검토

3) Git Checkout/Fetch가 느려짐(네트워크·서버·레포 크기)

증상

  • checkout scm 단계만 유독 느려짐
  • shallow clone을 쓰다가 어느 순간 full fetch로 바뀜(브랜치 전략/플러그인 설정 변경)
  • Git 서버(사내 GitLab/GHE) 부하 또는 네트워크 경로 문제

확인 방법

  • Jenkins Git 플러그인 로그에서 fetch 옵션 확인(깊이, refspec)
  • 에이전트에서 Git 네트워크/인증 왕복 확인:
# DNS/네트워크 기본 지표
getent hosts github.com || true
curl -I -L https://github.com 2>/dev/null | head

# Git 전송시간이 긴지 확인(진단용)
GIT_CURL_VERBOSE=1 GIT_TRACE=1 git ls-remote <repo-url> 2>&1 | tail -n 50

즉시 조치

  • CloneOption으로 shallow depth, no-tags, reference repo(미러) 적용
  • 레포가 너무 커졌다면 LFS/아티팩트 분리 검토
  • Kubernetes/EKS 환경에서 DNS가 느리면 Git/Artifact 모두 느려지는 연쇄가 생깁니다. Pod DNS만 느린 케이스는 아래 글의 ndots/search 튜닝이 힌트가 됩니다: EKS에서 Pod DNS만 느릴 때 ndots·search 튜닝

4) Docker/컨테이너 권한·스토리지 드라이버 이슈로 “재시도/대기” 증가

증상

  • docker build, docker login, docker run에서 간헐적으로 멈춘 듯 느려짐
  • 퍼미션 문제로 내부적으로 재시도/폴백이 발생하거나, 볼륨 마운트가 느림
  • overlay2 스토리지 드라이버/디스크 부족으로 성능 급락

확인 방법

# Docker 데몬 상태와 오류
systemctl status docker --no-pager || true
journalctl -u docker -n 200 --no-pager || true

# 스토리지 드라이버/루트 디렉터리 확인
docker info | sed -n '/Storage Driver/,+10p'

즉시 조치

  • 에이전트가 Docker socket을 쓰는 구조라면 권한/그룹 문제부터 정리(빌드가 느려질 뿐 아니라 실패로도 이어짐)
  • 관련해서 Docker 에이전트 권한 이슈 체크리스트는 아래 글이 빠릅니다: Jenkins Docker 에이전트 Permission denied 7가지 해결
  • 디스크가 임계치에 가까우면 overlay2 성능이 급락할 수 있으니 docker system prune는 신중히(공유 캐시 환경에서는 역효과)

5) 외부 의존성(레지스트리/패키지 저장소/아티팩트) 지연

증상

  • npm ci, pip install, go mod download, mvn dependency:resolve가 갑자기 느림
  • 도커 이미지 pull이 느리거나 ImagePullBackOff가 간헐적으로 발생
  • 사내 Nexus/Artifactory/Harbor가 느려짐 또는 egress 경로(NAT) 병목

확인 방법

  • 다운로드 구간을 타임스탬프로 쪼개서 확인(파이프라인에서 측정):
pipeline {
  agent any
  stages {
    stage('Deps') {
      steps {
        sh '''
          set -e
          date
          time npm ci
          date
        '''
      }
    }
  }
}

즉시 조치

  • 사내 프록시/캐시 레지스트리(예: Harbor proxy cache, Nexus Docker proxy) 도입
  • 패키지 매니저 캐시를 워크스페이스와 분리하고, 에이전트 재사용 전략(고정 노드/캐시 볼륨) 적용
  • NAT Gateway를 통한 egress가 병목이면 비용뿐 아니라 성능도 흔들립니다. 트래픽 급증 진단이 필요할 때: VPC NAT Gateway 비용 폭증 10분 진단·절감

6) Jenkins 컨트롤러/큐 병목(Executor, Throttle, Lock, SCM Polling)

증상

  • 빌드 자체는 짧은데 “대기 시간”이 길어짐(Queue에서 오래 머뭄)
  • 특정 라벨의 에이전트만 항상 밀림
  • lock()/throttle/milestone/input 같은 제어 스텝으로 직렬화

확인 방법

  • Jenkins UI에서 Build Queue, Manage Jenkins → System Information 확인
  • Pipeline에서 큐 대기/실행 시간을 로그로 남기기(간단한 방법):
pipeline {
  agent { label 'linux' }
  options {
    timestamps()
  }
  stages {
    stage('Info') {
      steps {
        echo "NODE_NAME=${env.NODE_NAME} EXECUTOR_NUMBER=${env.EXECUTOR_NUMBER}"
      }
    }
  }
}
  • 특정 자원 잠금으로 느려지는지 Jenkinsfile 검색:
grep -R "lock(\|throttle\|milestone\|disableConcurrentBuilds" -n Jenkinsfile

즉시 조치

  • 라벨별 에이전트 풀을 확장하거나 executor 수 조정(컨트롤러 executor는 보통 0 권장)
  • disableConcurrentBuilds()가 불필요하게 전체 파이프라인을 직렬화하고 있지 않은지 재검토
  • lock(resource: 'docker-daemon')처럼 “큰 락”을 잡고 있으면 세분화

7) 플러그인/스크립트 변경으로 인한 성능 저하(특히 단계별 post/notify)

증상

  • 빌드 본 작업은 끝났는데 post { always { ... } }에서 오래 걸림
  • Slack/Jira/GitHub status update 같은 외부 호출이 느려짐
  • 최근 Jenkins/플러그인 업데이트 이후부터 체감

확인 방법

  • 빌드 로그에서 느린 구간이 항상 같은 스텝인지 확인
  • Scripted step에서 네트워크 호출 타임아웃/재시도 여부 점검
  • Jenkins 컨트롤러 로그에서 플러그인 예외/GC 증가 확인
# (컨트롤러 노드에서) 최근 로그 확인 경로는 설치 방식에 따라 다름
# systemd 기반이면:
journalctl -u jenkins -n 300 --no-pager || true

즉시 조치

  • 외부 API 호출은 타임아웃을 명시하고, 실패 시 빌드 전체를 막지 않도록 격리
  • 플러그인 업데이트는 한 번에 몰아서 하지 말고, 느려짐이 시작된 시점의 변경 이력을 역추적
  • 알림/리포팅은 가능하면 비동기(별도 job)로 분리

빠르게 결론 내는 10분 진단 루틴

아래 순서로 보면 “감”이 아니라 “근거”로 원인을 좁힐 수 있습니다.

  1. Queue 대기인가, 실행이 느린가부터 분리(Jenkins UI/타임스탬프)
  2. 실행이 느리면 에이전트에서 CPU/메모리/IO wait 확인(vmstat, iostat)
  3. 느린 단계가 checkout/deps downloadDNS/네트워크/레지스트리 확인
  4. Docker 사용 시 docker info, docker system df, 데몬 로그 확인
  5. 최근 변경(플러그인/에이전트 이미지/네트워크 정책/보안 정책)을 타임라인으로 정리

부록: 단계별 시간을 자동으로 남기는 Jenkinsfile 예시

“갑자기 느려짐”을 반복해서 겪는다면, 최소한 단계별 소요시간을 표준화해 두는 게 좋습니다.

pipeline {
  agent any
  options {
    timestamps()
    timeout(time: 60, unit: 'MINUTES')
  }
  stages {
    stage('Checkout') {
      steps {
        sh 'date; echo "== checkout start =="'
        checkout scm
        sh 'date; echo "== checkout end =="'
      }
    }
    stage('Build') {
      steps {
        sh '''
          set -e
          date; echo "== build start =="
          time make build
          date; echo "== build end =="
        '''
      }
    }
    stage('Test') {
      steps {
        sh '''
          set -e
          date; echo "== test start =="
          time make test
          date; echo "== test end =="
        '''
      }
    }
  }
  post {
    always {
      sh 'date; echo "== post always =="'
    }
  }
}

이 정도만 해도 “Checkout이 3배 느려짐”, “Deps download가 10배 느려짐”, “post 구간에서 5분 대기”처럼 병목이 선명해집니다. 그 다음부터는 위 7가지 원인 중 어디에 속하는지 매칭해서, 리소스/네트워크/캐시/플러그인/동시성 중 정확한 축으로 해결하면 됩니다.