Published on

Jenkins 에이전트 Offline 원인과 해결 체크리스트

Authors

서버는 멀쩡한데 Jenkins 노드가 갑자기 Offline 으로 바뀌면, 파이프라인은 큐에 쌓이고 배포는 멈춥니다. 문제는 원인이 매우 다양하다는 점입니다. 네트워크 단절처럼 단순한 경우도 있지만, 에이전트 런타임(Java/SSH), 인증키/시크릿, 컨테이너 리소스, 워크스페이스 권한, WebSocket/인바운드 포트 차단 등 여러 축에서 발생합니다.

이 글은 “에이전트가 왜 Offline 이 되었는지”를 빠르게 좁히는 진단 순서와, 환경별(SSH, Inbound, Docker, Kubernetes) 실전 해결책을 체크리스트 형태로 제공합니다.

Offline 상태를 먼저 분류하기

Jenkins UI에서 노드 상세로 들어가면 보통 다음 중 하나의 힌트가 남습니다.

  • java.io.EOFException, ChannelClosedException: 연결이 끊김(네트워크/프록시/타임아웃)
  • Authentication failed: 키/토큰/시크릿 문제
  • Cannot contact ...: DNS, 라우팅, 방화벽, 포트 차단
  • Agent is running but cannot be reached: 역방향 연결(inbound) 관련
  • Disk full, No space left on device: 디스크/워크스페이스
  • OutOfMemoryError, Killed: 메모리 부족(OOMKill)

여기서 중요한 건 “에이전트 프로세스가 죽었는지”와 “살아 있는데 Jenkins가 못 보는지”를 먼저 가르는 것입니다.

1단계: Jenkins 컨트롤러 로그부터 확인

컨트롤러에서 에이전트 관련 로그는 가장 빠른 출발점입니다.

  • 시스템 로그(서비스)
  • Manage JenkinsSystem Log
  • 노드 상세 화면의 로그

리눅스에서 서비스 로그 예시:

# systemd 환경
sudo journalctl -u jenkins -n 300 --no-pager

# 컨테이너라면
docker logs --tail 300 jenkins

로그에서 다음 키워드를 찾습니다.

  • handshake failed
  • SSLHandshakeException
  • Connection timed out
  • No route to host
  • invalid agent secret
  • Rejected: ... (Remoting/JNLP 버전 불일치나 보안 정책)

2단계: 에이전트 실행 방식(SSH vs Inbound) 확인

Jenkins 에이전트는 크게 두 방식이 많습니다.

  • SSH 에이전트: 컨트롤러가 에이전트로 SSH 접속해서 java -jar agent.jar 를 실행
  • Inbound 에이전트(JNLP/WebSocket): 에이전트가 컨트롤러로 “역방향” 연결

Offline 원인과 해결이 완전히 달라지므로, 노드 설정에서 Launch method 를 먼저 확인하세요.

3단계: 네트워크/방화벽/프록시 점검(가장 흔함)

SSH 방식일 때

컨트롤러에서 에이전트로 다음을 확인합니다.

# 컨트롤러에서 실행
ssh -vvv user@agent-host "uname -a"

주요 실패 패턴:

  • Connection timed out: 보안그룹/방화벽/라우팅
  • Permission denied (publickey): 키/authorized_keys/권한
  • no matching host key type found: 구형 SSH 서버/클라이언트 알고리즘 불일치

Inbound(JNLP) 방식일 때

Inbound는 에이전트가 컨트롤러로 나가야 하므로 에이전트 측에서 점검합니다.

# 에이전트에서 실행
curl -vk https://jenkins.example.com/login

# DNS 확인
getent hosts jenkins.example.com

특히 사내 프록시가 있으면 HTTPS_PROXY 환경변수로 인해 JNLP/WebSocket이 프록시를 타며 끊기는 경우가 많습니다. 이때는 에이전트 실행 시 프록시 예외를 명시하거나, 해당 호스트를 NO_PROXY 에 추가하세요.

export NO_PROXY="jenkins.example.com,10.0.0.0/8,127.0.0.1,localhost"

Kubernetes/EKS 환경이라면 네트워크 플러그인 이슈로 노드가 간헐적으로 외부 통신을 못 하는 경우도 있습니다. 이 유형은 Pod가 Running 인데도 통신이 깨지며 에이전트가 Offline 으로 보일 수 있습니다. 비슷한 네트워크 계열 트러블슈팅은 EKS Pod NotReady(NetworkPlugin cni) 10분 해결 글의 점검 흐름도 참고할 만합니다.

4단계: Remoting(Jenkins agent.jar) 버전 및 Java 호환성

에이전트는 Jenkins Remoting 프로토콜을 사용합니다. 컨트롤러 업그레이드 후 에이전트가 Offline 이 되는 대표 원인은 다음입니다.

  • 에이전트가 너무 오래된 agent.jar 를 사용
  • 에이전트 Java 버전이 Jenkins 요구사항과 불일치
  • TLS/인증서 체인 문제

해결: 에이전트 jar 갱신

일반적으로 Jenkins는 최신 agent.jar 다운로드 URL을 제공합니다.

# 에이전트에서 실행 (예시)
curl -fsSL -o agent.jar https://jenkins.example.com/jnlpJars/agent.jar
java -jar agent.jar -version

해결: Java 버전 정렬

Jenkins LTS는 시점에 따라 Java 최소 버전 요구사항이 바뀝니다. 컨트롤러를 Java 17 기반으로 올렸는데 에이전트가 Java 8/11에 묶여 있으면 런타임 에러 또는 핸드셰이크 실패가 발생할 수 있습니다.

java -version

권장 접근:

  • 컨트롤러/에이전트 모두 Jenkins 권장 Java 버전으로 정렬
  • 컨테이너 에이전트라면 베이스 이미지에서 Java 런타임을 명시적으로 고정

5단계: 인증/시크릿/키 문제(특히 Inbound)

Inbound 에이전트는 보통 secretagent name 조합으로 인증합니다. 다음 상황에서 자주 깨집니다.

  • 노드를 삭제 후 다시 만들었는데 예전 secret을 계속 사용
  • Kubernetes Secret이 롤링 중 잘못 주입
  • 환경변수/파일 마운트 경로가 바뀜

Inbound 실행 예시(시크릿 기반)

java -jar agent.jar \
  -url https://jenkins.example.com/ \
  -secret "$JENKINS_AGENT_SECRET" \
  -name "linux-agent-01" \
  -workDir "/home/jenkins"

체크 포인트:

  • 노드 설정 화면의 Secret 과 실제 값이 일치하는가
  • 에이전트 이름이 정확히 일치하는가(대소문자 포함)
  • workDir 에 쓰기 권한이 있는가

6단계: 디스크/워크스페이스/권한 문제

에이전트가 연결은 되지만 빌드 시작 직후 Offline 으로 떨어지거나, 반복 재접속을 하면 디스크/권한 문제가 흔합니다.

디스크 확인

df -h

du -sh /var/lib/jenkins /home/jenkins 2>/dev/null || true

워크스페이스 권한 확인

# 에이전트에서
id
ls -ld /home/jenkins
mkdir -p /home/jenkins/test && rm -rf /home/jenkins/test

해결 팁:

  • 워크스페이스를 ephemeral 하게 쓰는 컨테이너 에이전트면, 볼륨 용량 제한을 늘리거나 캐시 정책을 조정
  • 장기 운영 노드면 workspace cleanup 전략(빌드 후 정리, 오래된 작업 삭제) 수립

7단계: 리소스 부족(OOMKill/CPU Throttling)과 에이전트 프로세스 종료

에이전트가 Offline 으로 보이는 본질이 “프로세스가 죽었다”인 경우가 많습니다. 특히 Kubernetes에서는 OOMKill이 조용히 발생하기도 합니다.

Kubernetes에서 확인

kubectl get pod -n ci
kubectl describe pod -n ci jenkins-agent-xxxx
kubectl logs -n ci jenkins-agent-xxxx --previous

Last State: TerminatedReason: OOMKilled 가 보이면 메모리 리밋을 올리거나, 빌드 병렬도를 낮추거나, Gradle/Node 등 빌드 툴 메모리 옵션을 조정해야 합니다.

리소스가 부족해 Pending 에 걸려 아예 에이전트가 뜨지 않는 경우도 있는데, 이때는 스케줄링 이벤트를 보면 빠르게 원인을 찾을 수 있습니다. 관련 흐름은 EKS Pod Pending(Insufficient cpu) 원인과 해결 도 참고하세요.

8단계: Docker-in-Docker, 소켓 권한, 컨테이너 런타임 이슈

컨테이너 에이전트에서 Docker 빌드를 하면 다음으로 Offline 처럼 보이는 장애가 생깁니다.

  • docker 명령이 hang 또는 권한 에러로 빌드가 멈춤
  • 빌드 시간이 길어져 에이전트가 타임아웃/재시작
  • /var/run/docker.sock 권한 문제

체크:

# 에이전트 컨테이너 내부
docker version
ls -l /var/run/docker.sock
id

해결:

  • 가능하면 Docker-in-Docker 대신 Kaniko/BuildKit 등으로 전환
  • 소켓 마운트 시 사용자/그룹 권한 정렬
  • 빌드 타임아웃을 Jenkins Job 레벨에서 명시

9단계: WebSocket 모드와 L7 프록시/로드밸런서 호환성

최근 Jenkins는 인바운드 에이전트를 WebSocket으로 붙이는 구성이 많습니다. 이때 L7 프록시(Nginx, ALB, 사내 WAF)가 WebSocket 업그레이드를 제대로 처리하지 못하면 연결이 불안정해져 Offline 이 됩니다.

증상:

  • 처음엔 붙었다가 몇 분 후 끊김
  • Unexpected EOF 혹은 Read timed out

점검:

  • 프록시에서 WebSocket Upgrade 헤더 전달 여부
  • 타임아웃(Idle timeout)이 너무 짧지 않은지

Nginx/리다이렉트/프록시 계열 문제는 원인 패턴이 반복되는 편입니다. 프록시 환경에서 인증/리다이렉트가 꼬일 때의 사고방식은 Nginx 뒤 OAuth 콜백 302 무한리다이렉트 원인 글도 같이 보면 도움이 됩니다.

10단계: 재현 가능한 “표준 복구 절차” 만들기

장애가 한 번 해결돼도 재발하면 운영 비용이 커집니다. 팀에서 다음을 표준화하면 Offline 대응 시간이 확 줄어듭니다.

1) 에이전트 헬스체크 스크립트

#!/usr/bin/env bash
set -euo pipefail

echo "[1] java"
java -version

echo "[2] disk"
df -h

echo "[3] jenkins reachability"
curl -fsS https://jenkins.example.com/login >/dev/null

echo "OK"

2) 에이전트 실행 파라미터를 코드로 관리

  • systemd unit 파일
  • Kubernetes manifest(Deployment/StatefulSet)
  • Helm values

핵심은 agent.jar 다운로드, secret 주입, workDir, 프록시 예외, 리소스 요청/제한을 모두 선언적으로 관리하는 것입니다.

3) Jenkins 노드 설정의 불변성 유지

  • 노드 이름 규칙 고정
  • 노드 삭제/재생성 시 secret 교체가 반드시 필요하다는 운영 규칙 문서화

빠른 결론: 가장 많이 맞는 원인 Top 5

  1. 네트워크/방화벽/프록시로 컨트롤러와 에이전트 간 연결이 끊김
  2. 컨트롤러 업그레이드 후 agent.jar 또는 Java 버전 불일치
  3. Inbound secret 불일치(노드 재생성, Secret 주입 오류)
  4. 디스크 부족 또는 워크스페이스 권한 문제
  5. Kubernetes OOMKill/리소스 부족으로 에이전트 프로세스가 종료

Offline 은 “증상”이고, 실제 원인은 연결·인증·런타임·리소스·프록시 중 하나로 귀결되는 경우가 대부분입니다. 위 순서대로 좁혀가면, 불필요한 재시작이나 감에 의존한 설정 변경 없이도 원인을 빠르게 특정할 수 있습니다.