CI/CD 자동화를 구축해 놓으면 퇴근 후에도 마음이 편할 줄 알았습니다. 하지만 밤 11시에 슬랙으로 [빌드 실패] 빨간 알람이 울려 부랴부랴 랩탑을 열어보면, 내 코드의 버그가 원인이 아닙니다. "Github에서 소스코드를 다운로드하다가 DNS를 못 찾음", "NPM Registry 서버가 잠시 응답하지 않음", "Docker Hub Rate Limit(속도 제한)에 걸려 이미지 pull 실패" 등 인프라 엔지니어가 통제할 수 없는 '외부 요인(네트워크 지연 및 타임아웃)' 때문에 파이프라인이 터지는 억울한 경우가 실무 빌드 실패의 절반 이상을 차지합니다. 억울하게 실패한 빌드를 데브옵스 담당자가 젠킨스 화면에 접속해 수동으로 'Rebuild' 버튼을 눌러주면 언제 그랬냐는 듯 쌩쌩하게 잘 돌아갑니다.
이번 [Jenkins 실무 완벽 가이드] 시리즈에서는 인프라 담당자의 수면권을 보장하기 위해 이러한 일시적(Transient) 오류 발생 시 젠킨스가 스스로 기회를 다시 주는 'Retry(재시도) 블록의 강력한 회복 탄력성(Resilience) 기법'에 대하여 다뤄보겠습니다.

📶 1. 100% 신뢰할 수 없는 네트워크의 본질
마이크로서비스(MSA)와 클라우드 인프라의 첫 번째 원칙은 "네트워크는 언제나 실패할 수 있다(Network is not reliable)"는 것입니다. 여러분의 파이프라인이 curl로 외부 API를 찌르거나, apt-get install로 패키지를 받을 때 100번 중 1번은 패킷 로스나 BGP 라우팅 문제로 타임아웃이 발생합니다.
이때 단 한 번의 실패로 파이프라인 전체를 붉은색(Failure)으로 물들이고 슬랙에 에러 알람을 빵빵 울려대는 것은 매우 피곤한 일입니다. "한 번 실패했어? 그럼 3초만 쉬었다가 다시 한번 찔러봐. 그래도 안 되면 그때 실패 처리해." 이것이 바로 retry 지시어의 존재 이유입니다.
🔄 2. Retry 블록의 구조와 동작 원리
Declarative Pipeline에서는 실패하기 쉬운 불안정한 스텝들을 특정 횟수만큼 감싸주는 아주 우아한 문법을 제공합니다. steps 내부에서 retry(횟수) { ... } 블록을 사용합니다.
pipeline {
agent any
stages {
stage('Flaky Network Tasks') {
steps {
// 🌟 이 블록 안의 명령어가 실패(exit code 1 등)하면, 즉시 종료하지 않고 최대 3번까지 처음부터 다시 실행합니다.
retry(3) {
echo "외부 NPM 레지스트리에서 패키지 다운로드를 시도합니다..."
// 네트워크 순단으로 이 명령어가 실패할 확률이 5%라고 가정해봅시다.
// 3번 연속으로 실패(0.05 * 0.05 * 0.05)할 확률은 0.0125%로 극적으로 줄어듭니다.
sh 'npm install --no-optional'
}
echo "무사히 다운로드를 마쳤습니다!"
}
}
}
}
이렇게 retry(3)으로 감싸두면, 첫 번째 시도에서 npm install이 타임아웃으로 실패하더라도 젠킨스는 파이프라인을 멈추지 않고 즉시 두 번째 시도(Retrying...)를 시작합니다. 두 번째 시도에서 성공하면 파이프라인은 녹색 불을 켜고 다음 스테이지로 당당하게 넘어갑니다.
⏳ 3. Sleep과의 결합: 숨고르기 전략 (고급 실무)
retry를 쓸 때 주의할 점이 있습니다. 만약 상대방 API 서버가 순간적으로 트래픽이 몰려 잠시 죽어있는(503 Service Unavailable) 상태라고 칩시다. 이때 젠킨스가 재시도를 한답시고 0.1초 만에 미친 듯이 curl을 3번 연속으로 때려버리면 어떻게 될까요? 3번 모두 똑같이 503 에러를 맞고 장렬하게 전사하게 됩니다.
진정한 재시도는 '상대방이 회복할 수 있는 시간적 여유(Backoff)'를 주어야 합니다. 따라서 실무에서는 sleep 명령어와 결합하여 사용합니다.
stage('API Health Check') {
steps {
retry(5) { // 최대 5번 재시도
script {
try {
// API 서버가 살아있는지 핑(ping)을 때려봅니다.
sh 'curl -f http://my-unstable-api-server.com/health'
} catch (Exception e) {
// 실패했다면 즉시 재시도하지 말고, 10초(SECONDS) 동안 숨을 고른 뒤 다음 retry 루프로 넘어갑니다.
echo "API 서버가 불안정합니다. 10초 대기 후 재시도합니다..."
sleep(time: 10, unit: 'SECONDS')
error("강제로 에러를 발생시켜 retry 블록이 재시작되도록 유도")
}
}
}
}
}
🎯 4. 마무리 및 다음 단계
지금까지 통제할 수 없는 외부 네트워크의 불안정성 앞에서 파이프라인이 유리 멘탈처럼 깨지는 것을 막아주고, 인프라 엔지니어의 수면을 보장해 주는 강력한 오뚝이 스킬, 'Retry 블록과 Sleep 백오프 전략'에 대해 다루어 보았습니다. 단 두 단어(retry, sleep)만으로 여러분의 파이프라인 신뢰도(Reliability)는 수직 상승했습니다.
파이프라인이 튼튼해지니 하루에도 수백 번씩 배포가 성공하며 로그가 쌓여갑니다. 그런데 어느 날 젠킨스 서버에서 No space left on device라는 무서운 리눅스 디스크 풀(Disk Full) 에러가 떨어지며 서버가 완전히 먹통이 되었습니다! 원인을 찾아보니 젠킨스가 지난 3년간의 모든 빌드 기록, 로그 텍스트, 거대한 .jar 아티팩트 파일들을 쓰레기통에 비우지 않고 서버 하드디스크에 영구히 저장하고 있었던 것입니다. 이어지는 32단계 포스팅에서는 "30일 지난 과거의 영광(빌드 로그)은 미련 없이 불태워버려라!" 서버 용량 폭발을 막아주는 자동 청소부, 'Jenkins 로그 로테이션(Discard Old Builds)'에 대해 아주 뼈 때리는 생생한 경험담과 함께 파헤쳐 보겠습니다!
'CI.CD > Jenkins' 카테고리의 다른 글
| [Jenkins] DevSecOps의 완성: Trivy로 컨테이너 보안 취약점 원천 차단하기 (0) | 2026.07.22 |
|---|---|
| [Jenkins] Discard Old Builds: 디스크 용량 폭발을 막는 자동 청소부 (0) | 2026.07.22 |
| [Jenkins] Timestamps: 눈먼 로그에 생명력을 불어넣는 시간의 마법 (0) | 2026.07.22 |
| [Jenkins] Timeout의 미학: 좀비 파이프라인(무한 루프) 방어전 (0) | 2026.07.22 |
| [Jenkins] Jenkins 무중단 마이그레이션(Migration) 완벽 가이드 (0) | 2026.07.22 |