"어? 어제 퇴근할 때 걸어놓은 배포가 오늘 아침까지 안 끝나고 계속 돌아가고 있네?"
CI/CD 인프라를 운영하다 보면 누구나 한 번쯤 마주치게 되는 소름 돋는 상황입니다. 쉘 스크립트 안에 잘못 작성된 while(true) 무한 루프 버그, 혹은 외부 API 서버가 죽어서 응답을 주지 않아 무한 대기(Hang)에 빠지는 경우 등 원인은 다양합니다. 문제는 이렇게 '에러를 뿜으며 실패(Failure)하지도 않고, 그렇다고 성공(Success)하지도 않은 채 영원히 멈춰있는 좀비(Zombie) 빌드'들이 젠킨스의 가장 큰 적이라는 점입니다.
이번 글에서는 소중한 젠킨스의 워커(Worker) 자원을 갉아먹는 좀비 빌드들을 제거하는
'Timeout(타임아웃) 제어를 통한 파이프라인 무한 대기 방어 기술'에 대하여 다뤄보겠습니다.
1. 좀비 빌드의 심각성 (리소스 고갈의 공포)
젠킨스는 기본적으로 작업을 수행하기 위해 실행기(Executor)라는 일꾼 쓰레드를 사용합니다. 만약 젠킨스 설정에서 일꾼을 5명(Executor=5)으로 세팅해 두었다면, 동시에 5개의 파이프라인만 돌 수 있습니다. 그런데 앞서 말한 좀비 빌드가 하나 생기면, 일꾼 1명이 그 작업에 매달린 채 영원히 퇴근하지 못하게 됩니다.
이런 좀비 빌드가 5개가 쌓이면 어떻게 될까요? 가용할 수 있는 일꾼 5명이 모두 멈춰버리는(Deadlock) 최악의 사태가 발생합니다. 다른 개발자들이 아무리 배포 버튼을 눌러도 "대기열(Queue)에서 실행기를 기다리는 중입니다..."라는 메시지만 뜨고 전사의 모든 CI/CD가 셧다운(Shutdown) 됩니다. 인프라 관리자가 수동으로 빨간색 X 버튼을 눌러 죽이기 전까지 시스템은 복구되지 않습니다. 자동화 시스템이 수동 감시를 요구하는 모순이 발생하는 것입니다.
2. 전역 타임아웃 (Global Timeout) 설정하기
이 끔찍한 현상을 원천 차단하는 가장 쉽고 확실한 방법은, 파이프라인 전체 생존 시간에 유통기한을 강제로 부여하는 것입니다. Declarative Pipeline 최상단의 options 블록에 timeout 지시어를 선언합니다.
pipeline {
agent any
// 파이프라인 전체를 감싸는 절대 방어막 (전역 타임아웃)
options {
// 이 파이프라인이 시작된 지 1시간(HOURS)이 지나면? 묻지도 따지지도 않고 강제 종료(Aborted)!
timeout(time: 1, unit: 'HOURS')
}
stages {
stage('Build') {
steps { echo "빌드를 시작합니다..." }
}
// ... 수많은 스테이지들 ...
}
}
이렇게 options에 타임아웃을 걸어두면, 빌드가 어떤 이유로든 1시간을 초과하는 순간 젠킨스 데몬이 개입하여 "Timeout has been exceeded"라는 붉은 로그를 남기며 해당 파이프라인의 숨통을 강제로 끊어버립니다(Aborted). 그리고 일꾼(Executor)을 회수하여 대기열에 있던 다음 작업에 정상적으로 투입합니다.
3. 지역 타임아웃 (Stage-level Timeout)의 정교한 통제
전역 타임아웃만으로도 훌륭하지만, 실무에서는 더 정교한 통제가 필요할 때가 많습니다. 전체 빌드는 1시간이 걸려도 괜찮지만, "외부 서버로 API를 쏘고 응답을 기다리는 특정 스테이지는 무조건 3분 안에 끝나야 한다(안 그러면 API 서버가 죽은 것임)"라는 조건이 있을 수 있습니다. 이때는 특정 steps 내부를 timeout 블록으로 감싸주면 됩니다.
pipeline {
agent any
stages {
stage('Fragile External API Call') {
steps {
echo "불안정한 외부 결제망 API와 연동 테스트를 시작합니다..."
// 특정 로직에만 씌우는 정밀 방어막 (지역 타임아웃)
// 이 블록 안의 작업이 5분을 초과하면, 이 스테이지에서 즉시 에러를 발생시키며 중단됩니다.
timeout(time: 5, unit: 'MINUTES') {
// 서버 응답이 없으면 curl이 무한 대기(Hang)에 빠질 수 있는 위험한 스크립트
sh 'curl http://very-slow-and-unstable-api.com/sync'
}
echo "API 연동 테스트가 무사히 5분 안에 끝났습니다!"
}
}
}
}
이 기법은 에서 배웠던 Input Step(수동 승인 팝업)과 결합할 때 최고의 시너지를 냅니다. 팀장님이 퇴근하고 승인 버튼을 안 눌러줘도, timeout으로 감싸두면 30분 뒤에 알아서 팝업이 사라지고 깔끔하게 취소 처리됩니다.
4. 마무리 및 주요 발전 방향
지금까지 젠킨스 자원을 고갈시키는 가장 무서운 적, '좀비 빌드(무한 루프/무한 대기)'의 원인을 짚어보고, 전역(Global)과 지역(Stage) 레벨에서 Timeout 방어막을 쳐서 인프라의 안정성을 극강으로 끌어올리는 비법에 대해 3,000자의 밀도 높은 내용으로 살펴보았습니다. 이제 여러분의 서버는 그 어떤 결함 있는 코드나 외부 장애 앞에서도 스스로를 방어하며 굳건히 돌아갈 것입니다.
타임아웃 덕분에 멈춰있던 빌드가 취소(Aborted)로 끝났습니다. 이제 개발자는 왜 실패했는지 에러 로그(Console Output)를 열어볼 것입니다. 그런데 10만 줄의 텍스트가 전부 흑백이고, 이 에러가 정확히 몇 시 몇 분에 발생했는지 시간(Time)조차 안 찍혀 있다면? 원인 분석에 엄청난 시간을 허비하게 됩니다. 본 설명에서는 밋밋하고 읽기 힘든 젠킨스 로그에 생명력을 불어넣고 디버깅 시간을 10배 단축시켜 주는 구세주, 'Timestamps 적용과 빌드 로그 가독성 혁신 기법'에 대해 아주 치밀하게 파헤쳐 보겠습니다!
파이프라인이 실패하여 원인을 찾기 위해 젠킨스의 Console Output(콘솔 출력) 화면을 열었습니다. 까만 바탕에 흰 글씨로 찍힌 10만 줄의 로그를 스크롤 하다가 Error: Connection timeout 이라는 문구를 발견했습니다. 그런데 이 에러가 도대체 몇 시 몇 분에 발생한 것인지, 그리고 이 앞의 npm install 스텝에서 도대체 몇 분 동안이나 시간을 끌다가 에러가 난 것인지 알 턱이 없습니다. 기본 젠킨스 로그는 그저 명령어의 결과 텍스트만 차가운 콘솔에 주르륵 뱉어낼 뿐, 어떠한 시간 정보(Time Context)도 제공하지 않기 때문입니다.
본 글에서는 인프라 엔지니어의 디버깅 시간을 10배 단축시키고, 파이프라인의 병목(Bottleneck) 구간을 직관적으로 찾아내게 해 주는 'Timestamps 플러그인의 완벽한 적용과 빌드 로그 가독성 혁신 기법'에 대하여 다뤄보겠습니다.
盲 1. 시간 없는 로그의 치명적인 한계
파이프라인 전체 실행 시간이 평소 5분에서 오늘 갑자기 30분으로 늘어났다고 가정해 봅시다. 어디서 25분이 더 걸린 걸까요? 타임스탬프가 없다면 로 이루어진 파이프라인 중 어느 구간이 범인인지 절대 알 수 없습니다.
또한, 새벽에 발생한 간헐적인 데이터베이스 커넥션 타임아웃 에러를 추적할 때도, 로그에 찍힌 에러가 "새벽 3시 정각(DB 백업이 도는 시간)"에 발생한 것인지, "새벽 3시 15분"에 발생한 것인지 시간을 정확히 매칭할 수 없다면 원인 분석(Root Cause Analysis) 자체가 불가능해집니다. 시간은 디버깅의 가장 중요한 단서입니다.
2. Timestamps 플러그인과 옵션 선언
이러한 답답함을 단 한 줄의 코드로 완벽하게 해결해 주는 것이 바로 Timestamps 플러그인입니다. 젠킨스 플러그인 관리에서 Timestamper를 설치하고 나면, 파이프라인의 options 블록에 마법의 주문을 적어 넣을 수 있습니다.
pipeline {
agent any
// 이 한 줄이 콘솔 로그의 가독성을 180도 바꿔놓습니다.
options {
timestamps()
}
stages {
stage('Install Dependencies') {
steps {
// 이전 로그: npm install 시작...
// 변경 로그: [2024-03-15T04:22:10.123Z] npm install 시작...
echo "npm install 시작..."
sh 'npm install'
// 이전 로그: npm install 완료!
// 변경 로그: [2024-03-15T04:25:40.555Z] npm install 완료!
// (관리자 생각: 아~ 여기서 정확히 3분 30초나 걸렸구나!)
echo "npm install 완료!"
}
}
}
}
3. 실무 활용 노하우: 경과 시간(Elapsed Time) 분석
위와 같이 timestamps()를 선언하면 콘솔 화면 왼쪽 끝에 항상 날짜와 시간이 찍힙니다. 하지만 실무에서 우리가 진짜 궁금한 것은 "지금 몇 시인가?"보다 "앞의 명령어와 뒤의 명령어 사이의 '시간 차이(경과 시간)'가 얼마나 되나?"입니다.
젠킨스 클래식 UI의 콘솔 출력(Console Output) 화면 우측 상단을 자세히 보시면 [Timestamps 설정 톱니바퀴 아이콘]이 숨어있습니다. 이 아이콘을 클릭하여 'Elapsed time' (경과 시간) 모드로 변경해 보십시오. 그러면 로그 좌측의 텍스트가 절대 시간(시:분:초)에서, 빌드가 시작된 지 "00:03:15 소요", "00:08:42 소요" 같은 스톱워치(Stopwatch) 형태의 누적 시간으로 싹 바뀝니다. 이를 통해 "아, 특정 sh 스크립트 하나가 5분이나 잡아먹었네?" 하고 병목 구간(Bottleneck)을 1초 만에 시각적으로 찾아낼 수 있습니다.
4. 마무리 및 주요 발전 방향
지금까지 그동안 까막눈처럼 바라보던 텍스트 덩어리 로그에 '시간'이라는 축을 부여하여 직관적인 프로파일링 도구로 변모시키는 'Timestamps()' 옵션의 위대함에 대해 다루어 보았습니다. 단 한 줄의 코드이지만, 이 옵션이 없는 파이프라인은 실무에서 디버깅이 불가능한 반쪽짜리 자동화일 뿐입니다.
시간을 찍어보니 어떤 명령어가 오래 걸리는지 알게 되었습니다. 그런데 로그를 분석하다 보니 억울한 실패가 너무 많습니다. 사내 프라이빗 레지스트리의 네트워크가 딱 1초 끊겨서 npm install이 타임아웃 났고 그 탓에 30분짜리 전체 빌드가 허무하게 실패(Failure) 처리된 것입니다.
"아, 그냥 한번 껐다 켜면(다시 실행하면) 되는 건데 이걸 수동으로 다시 눌러야 해?"
다음글에서는 일시적인 네트워크 지연이나 외부 API 장애 앞에서 무너지지 않고 다시 회복 하는 'Retry(재시도) 블록의 완벽한 통제 기법'에 대해 다뤄보겠습니다.
'CI. CD > Jenkins' 카테고리의 다른 글
| [Jenkins] Trivy로 컨테이너 보안 취약점 차단하기 (0) | 2026.10.14 |
|---|---|
| [Jenkins] Retry 블록 네트워크 장애를 극복하는 파이프라인 (0) | 2026.10.14 |
| [Jenkins] Maven Gradle 빌드 캐시 최적화 빌드 시간 10배 단축의 비밀 (0) | 2026.10.08 |
| [Jenkins] Jenkins 백업 스크립트 복구 (0) | 2026.10.07 |
| [Jenkins] 쿠버네티스(KubernetesK8s) 완벽 연동 무중단 배포 자동화 (0) | 2026.10.07 |