- Published on
Jenkins 에이전트 오프라인·대기열 지옥 탈출
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
Jenkins를 운영하다 보면 어느 날 갑자기 빌드가 Queue 에 쌓이고, 에이전트는 offline 으로 떨어지며, 파이프라인은 끝없이 Waiting for next available executor 만 반복하는 순간이 옵니다. 이 상태가 길어지면 배포가 멈추고, 개발팀은 “Jenkins가 죽었다”는 결론을 내립니다.
하지만 대부분은 Jenkins 자체 장애가 아니라 에이전트 연결 경로, 라벨 매칭, 리소스 부족, 인증서/프록시, Kubernetes 스케줄링, 플러그인/버전 불일치 같은 운영 이슈가 겹쳐서 생깁니다. 이 글은 “대기열 지옥”을 재현 가능한 체크리스트로 쪼개고, 빠르게 정상화하는 방법을 정리합니다.
1) 증상부터 분류: 오프라인과 대기열은 원인이 다르다
먼저 화면에서 보이는 증상을 2가지로 분리합니다.
1-1. 에이전트가 오프라인인 경우
- 노드가
offline으로 표시됨 Launch agent실패 로그가 남음- 에이전트 프로세스는 떠 있는데도 Jenkins에서 끊김
핵심은 컨트롤러에서 에이전트로의 연결이 성립하지 않거나, 연결이 유지되지 않는 것입니다.
1-2. 에이전트는 온라인인데 빌드가 대기열에 쌓이는 경우
- 노드는
online이지만 executor가 0이거나 모두 바쁨 - 라벨이 맞지 않아 할당 불가
- 리소스/스케줄링 때문에 새 에이전트가 뜨지 않음
이 경우는 스케줄링/자원/라벨/동시성 정책 문제가 많습니다.
2) 가장 먼저 확인할 5가지: “즉시 효과” 체크리스트
아래는 현장에서 가장 빈도가 높은 순서입니다.
2-1. 라벨 불일치: 대기열의 1순위 범인
파이프라인이 특정 라벨을 요구하는데, 그 라벨을 가진 노드가 없거나 executor가 0이면 무한 대기가 발생합니다.
Jenkinsfile 예:
pipeline {
agent { label 'linux-docker' }
stages {
stage('Build') {
steps {
sh 'uname -a'
}
}
}
}
확인 포인트:
Manage Jenkins→Nodes에서 해당 라벨을 가진 노드가 실제로 존재하는가- 노드 설정에서
# of executors가 0으로 되어 있지 않은가 Restrict where this project can be run같은 제한이 걸려 있지 않은가
2-2. executor 고갈: “온라인인데도 못 도는” 상황
노드가 온라인이어도 executor가 꽉 차면 대기열이 쌓입니다.
대응:
- 단기: executor 수를 늘리거나, 병렬 빌드를 줄이기
- 중기: 빌드 시간을 줄이기(캐시, 아티팩트 재사용)
- 장기: 오토스케일 에이전트(예: Kubernetes 플러그인)로 확장
2-3. 에이전트 런처 방식 문제: SSH vs JNLP(WebSocket)
에이전트가 오프라인이면 런처부터 봐야 합니다.
- SSH 런처: 컨트롤러에서 에이전트로
22포트 접근 필요 - JNLP 인바운드: 에이전트가 컨트롤러로 붙음(방화벽에 유리)
- WebSocket: HTTP 기반이라 네트워크 제약이 적고 프록시 환경에서 유리
권장 방향:
- 사내망/보안망/클라우드 혼재 환경에서는 인바운드 + WebSocket 조합이 운영 난이도가 낮습니다.
2-4. DNS/HTTPS/프록시 문제: “연결은 되는데 작업 중 끊김”
에이전트가 온라인으로 보이다가도, 실제 빌드 단계에서 git clone 이나 패키지 다운로드가 실패하면서 작업이 꼬이고 재시도 루프가 생깁니다. 그 결과 executor가 장시간 점유되어 대기열이 늘어납니다.
특히 Kubernetes/EKS에서 흔한 패턴은 “DNS는 되는데 외부 HTTPS만 실패” 같은 네트워크 정책/라우팅/프록시 이슈입니다. 비슷한 증상은 아래 글의 점검 흐름이 그대로 도움이 됩니다.
2-5. 인증서/CA 문제: 에이전트가 TLS에서 죽는 케이스
사내 프록시나 MITM 장비가 있으면, 에이전트가 외부로 나갈 때 CERTIFICATE_VERIFY_FAILED 로 실패하고 빌드가 지속적으로 깨질 수 있습니다.
Python 기반 도구를 쓰는 빌드라면 아래 글의 해결법(사내 CA 주입, 신뢰 저장소 설정)이 그대로 적용됩니다.
3) 로그로 원인 확정: 컨트롤러 로그와 에이전트 로그를 같이 본다
대기열 지옥 탈출의 핵심은 “감”이 아니라 로그로 원인을 확정하는 것입니다.
3-1. 컨트롤러(마스터) 로그에서 보는 포인트
- 노드 연결 실패 메시지
- Remoting 채널 끊김
- 플러그인 예외
Docker로 Jenkins를 띄운 경우 예:
docker logs -f jenkins
Systemd 서비스라면 예:
journalctl -u jenkins -f
3-2. 에이전트 로그에서 보는 포인트
- JVM 버전 불일치
- Remoting jar 호환 문제
- 네트워크 끊김,
EOF류 메시지
인바운드 에이전트 실행 예:
java -jar agent.jar \
-url https://jenkins.example.com/ \
-secret YOUR_SECRET \
-name agent-01 \
-workDir /var/jenkins
주의: URL이나 토큰을 출력 로그에 그대로 남기지 않도록 마스킹 정책을 권장합니다.
4) Jenkins 대기열이 “영원히” 빠지지 않는 대표 시나리오 7가지
4-1. 노드가 실제로는 죽었는데 Jenkins가 “온라인”으로 착각
네트워크가 반쯤 죽으면 UI는 온라인처럼 보일 수 있습니다.
대응:
- 노드에
ping만 하지 말고, 실제 작업(예:git ls-remote)을 수행하는 헬스체크를 별도로 둡니다. - 일정 시간 이상 무응답이면 노드를 강제로
offline처리하고 새 노드를 띄우는 정책이 필요합니다.
4-2. Workspace 잠금/파일 락으로 스텝이 멈춤
동일 워크스페이스를 여러 빌드가 공유하면 락 경합이 생깁니다.
해결:
- 파이프라인에서
disableConcurrentBuilds()사용 - 혹은 빌드별 워크스페이스 분리
예:
options {
disableConcurrentBuilds()
}
4-3. Docker 데몬/소켓 권한 문제로 빌드가 무한 재시도
에이전트가 도커 빌드를 수행하는데 권한이 없으면, 스텝이 실패하고 재시도 로직이 executor를 오래 점유합니다.
확인:
id
ls -l /var/run/docker.sock
대응:
- 도커 그룹 권한 정리 또는 rootless Docker 고려
- Kubernetes라면 DinD 대신 Kaniko, BuildKit, podman 등 대안 검토
4-4. Kubernetes 에이전트가 Pending에서 못 뜸
Kubernetes 플러그인을 쓰면 “에이전트 오프라인”이 아니라 “에이전트가 생성되지 않음”이 원인일 때가 많습니다.
확인:
kubectl get pods -n jenkins -w
kubectl describe pod -n jenkins jenkins-agent-xxxx
자주 나오는 원인:
- CPU/메모리 requests가 커서 스케줄링 불가
- 이미지 풀 실패
- 노드 셀렉터/테인트/톨러레이션 불일치
- 서비스어카운트 권한 부족
권한 문제는 아래 글의 점검 흐름(토큰, RBAC, 인증 실패)을 참고하면 빠릅니다.
4-5. WebSocket/프록시 환경에서 연결이 자주 끊김
사내 프록시가 WebSocket 업그레이드를 막으면 에이전트가 간헐적으로 끊깁니다.
대응:
- 프록시에서 WebSocket 허용
- Jenkins URL과 에이전트 접속 경로를 단순화
- 가능하면 L4 수준에서 안정적인 경로 제공
4-6. Remoting/JDK 버전 불일치
컨트롤러는 최신인데 에이전트는 구형 JDK를 쓰면, 연결은 되다가 특정 클래스 로딩에서 터지기도 합니다.
권장:
- 컨트롤러와 에이전트의 최소 JDK 버전을 정책으로 고정
- 에이전트 이미지를 표준화
4-7. “대기열 폭발” 자체가 정상 동작인 경우
CI 트래픽이 급증했는데 에이전트 풀을 고정으로 운영하면 대기열이 늘어나는 건 정상입니다.
해결은 운영 정책입니다.
- 피크 시간대에만 자동 확장
- 우선순위 큐(중요 브랜치 우선)
- 빌드 단축(캐시, 병렬화 최적화)
5) 실전 복구 절차: 지금 당장 서비스 정상화
장애 상황에서 목표는 “완벽한 원인 분석”이 아니라 파이프라인을 다시 흐르게 만드는 것입니다.
5-1. stuck 빌드 정리: executor 점유 해제
- 오래된 빌드/멈춘 스텝을 중지
- 필요하면 노드를 일시적으로
offline후 다시online
Groovy 콘솔에서 오래된 실행을 찾는 예(환경에 맞게 수정 필요):
import jenkins.model.*
def now = System.currentTimeMillis()
Jenkins.instance.computers.each { c ->
c.executors.each { e ->
def exec = e.currentExecutable
if (exec != null) {
println("RUNNING on ${c.name}: ${exec}")
}
}
}
주의: 운영 환경에서 Groovy 콘솔은 강력한 권한이므로 접근 통제와 감사 로그가 필요합니다.
5-2. 에이전트 “재기동”이 아니라 “재생성”
VM 기반 고정 에이전트는 시간이 지날수록 드리프트가 생깁니다.
- 단기: 에이전트 서비스 재시작
- 중기: 골든 이미지로 재배포
- 장기: 불변 인프라(컨테이너 에이전트)로 전환
5-3. 네트워크/CA 문제는 빌드 단계에서 바로 검증
에이전트에 접속해서 아래를 실행해 외부 의존성이 정상인지 확인합니다.
# DNS
getent hosts github.com
# HTTPS
curl -I https://github.com
# Git
git ls-remote https://github.com/git/git
여기서 실패하면 Jenkins 문제가 아니라 에이전트 런타임 네트워크 문제일 확률이 큽니다.
6) 재발 방지: “대기열 지옥”을 구조적으로 없애는 설계
6-1. 에이전트 표준 이미지와 부트스트랩 스크립트
- JDK, Git, 빌드 툴, CA 번들, 프록시 설정을 표준화
- 빌드마다 셋업하지 말고 이미지에 넣어 시간을 줄임
Dockerfile 예:
FROM eclipse-temurin:17-jdk
RUN apt-get update \
&& apt-get install -y --no-install-recommends git curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# 사내 CA가 있다면 여기에 추가(파일명은 예시)
# COPY corp-ca.crt /usr/local/share/ca-certificates/corp-ca.crt
# RUN update-ca-certificates
WORKDIR /work
6-2. 오토스케일 전략: “큐 길이 기반”으로 확장
- 큐 길이, 평균 대기 시간, executor 사용률을 메트릭으로 수집
- 임계치 초과 시 에이전트 풀 확장
Kubernetes라면 HPA만으로는 부족할 수 있어, KEDA 같은 이벤트 기반 스케일링이나 Jenkins 플러그인 설정을 조합합니다.
6-3. 타임아웃과 재시도 정책을 파이프라인에 명시
무한 대기는 운영자를 지치게 합니다. 스텝별 타임아웃과 제한된 재시도로 “실패를 빨리 실패”하게 만들어야 합니다.
pipeline {
agent any
options {
timeout(time: 30, unit: 'MINUTES')
}
stages {
stage('Checkout') {
options {
timeout(time: 5, unit: 'MINUTES')
}
steps {
retry(2) {
checkout scm
}
}
}
}
}
6-4. 관측성: Jenkins 큐와 노드 상태를 대시보드화
- 큐 길이
- 노드 온라인 수
- executor 사용률
- 빌드 평균 시간
- 실패율(특히 네트워크 계열)
이 지표가 있어야 “느낌상 느려졌다”가 아니라 “어디가 병목인지”를 숫자로 말할 수 있습니다.
7) 결론: 오프라인과 대기열은 ‘운영 신호’다
Jenkins 에이전트 오프라인과 대기열 폭증은 단순 장애가 아니라, CI 시스템이 보내는 운영 신호입니다. 라벨/executor 같은 설정 문제부터 네트워크/인증서/스케줄링까지 원인을 체계적으로 분해하면, 대부분은 30분 이내에 정상화할 수 있습니다.
정리하면 다음 순서가 가장 효율적입니다.
- 대기열 항목에서 라벨 요구사항과 executor 상태 확인
- 에이전트 런처(SSH, JNLP, WebSocket)와 네트워크 경로 확인
- 에이전트에서 DNS, HTTPS, Git 같은 “실제 빌드 의존성” 검증
- Kubernetes라면
Pending원인을describe로 확정 - 재발 방지를 위해 표준 이미지, 타임아웃/재시도, 오토스케일, 관측성 구축
이 흐름으로 접근하면 “Jenkins가 또 멈췄다”가 아니라 “어느 레이어가 병목인지”를 빠르게 특정하고, 대기열 지옥에서 벗어날 수 있습니다.