일반적인 Jenkins 파이프라인 오류 문제 해결

실패한 Jenkins 파이프라인으로 어려움을 겪고 계신가요? 이 전문 가이드는 기본적인 Groovy 구문 오류와 환경 설정 오류부터 복잡한 보안 및 자격 증명 관리 실패에 이르기까지 가장 일반적인 오류에 대한 실용적인 해결책을 자세히 설명합니다. 콘솔 출력을 효과적으로 활용하는 방법, `withCredentials`를 사용하여 비밀을 안전하게 관리하는 방법, '명령을 찾을 수 없음' 오류를 해결하여 CI/CD 프로세스를 안정적이고 안전하며 신뢰할 수 있게 유지하는 방법을 알아보세요.

일반적인 Jenkins 파이프라인 오류 문제 해결

Jenkins 파이프라인은 빌드 및 배포 단계를 코드로 변환합니다. 이는 작은 구문 실수, 누락된 자격 증명 또는 에이전트 차이가 전체 실행을 중단시킬 때까지 유용합니다. 가장 빠른 수정은 모든 빌드 실패를 동일한 종류의 문제로 취급하지 않고 실패를 계층적으로 읽는 것에서 비롯됩니다.

먼저 Jenkins가 파이프라인을 해석하지 못했는지, 에이전트가 예상 환경을 제공하지 못했는지, 아니면 파이프라인 내부의 명령이 자체적으로 실패했는지 결정하는 것부터 시작하세요. 이렇게 구분하면 조사가 체계적으로 진행됩니다.

초기 진단: 어디서부터 시작해야 할까요?

특정 오류 코드를 살펴보기 전에 문제 해결의 기본 단계는 효과적인 진단입니다. 항상 컨텍스트를 수집하는 것부터 시작하세요.

1. 콘솔 출력 분석

콘솔 출력은 기본 디버깅 도구입니다. 파이프라인 단계가 실패하면 Jenkins는 스택 추적, 오류 메시지 및 일반적으로 실행이 중단된 Groovy 스크립트의 특정 줄을 출력합니다.

실행 가능한 팁: 실패 지점에서 위로 스크롤하세요. 마지막으로 성공한 단계를 찾으면 문제를 후속 단계 또는 환경 변경으로 격리하는 데 도움이 됩니다.

2. 파이프라인 단계 재생 기능 사용

사소한 구문 변경이 있거나 변수 문제가 의심되는 경우 전체 SCM 체크아웃을 트리거하고 즉시 빌드하지 마세요. Jenkins에서는 재생 기능을 사용하여 실패한 파이프라인 실행을 수정하고 다시 실행할 수 있습니다. 이는 빌드 기록을 복잡하게 만들지 않고 빠르게 반복하고 수정 사항을 테스트하는 데 매우 중요합니다.

3. 환경 변수 검사

많은 문제는 실행 에이전트의 잘못된 환경 설정으로 인해 발생합니다. 특정 단계에서 사용 가능한 환경 변수를 출력하여 경로, 도구 설치 및 정의된 변수를 확인할 수 있습니다.

stage('디버그 환경') {
    steps {
        sh 'printenv'
        // 또는 특정 확인을 위해:
        sh 'echo "Java Home: $JAVA_HOME"'
    }
}

카테고리 1: 구문, 스크립팅 및 Groovy 오류

Groovy는 Jenkins 파이프라인을 작성하는 데 사용되는 도메인 특화 언어(DSL)입니다. 구문 오류는 가장 일반적인 초기 장애물입니다.

오류 1.1: 누락된 속성 또는 메서드

일반적으로 다음과 같이 나타납니다: groovy.lang.MissingPropertyException: 클래스에 대한 속성 variableName이 없습니다...

원인: 정의되지 않은 변수를 참조하거나, 단계 이름을 잘못 입력했거나, 선언적 파이프라인 블록 내에서 스크립트 파이프라인 기능을 사용하려고 시도했거나(또는 그 반대)합니다.

해결책:

  1. 철자 확인: 변수 이름이나 단계 이름의 철자가 정확하고 대소문자가 일치하는지 확인하세요(Groovy는 대소문자를 구분합니다).
  2. 범위 확인: 변수가 이전 script {} 블록에서 정의된 경우, 특히 단계 간에 데이터를 이동할 때 올바른 범위에서 정의되었는지 확인하세요.
  3. 스니펫 생성기 사용: 기본 제공 단계(sh, git, archive 등)의 경우 Jenkins 파이프라인 구문 / 스니펫 생성기 도구를 사용하세요. 이 도구는 제공한 단계 매개변수에 대해 올바른 Groovy 코드를 생성합니다.

오류 1.2: 잘못된 선언적 구문

선언적 파이프라인은 엄격한 구조를 요구합니다. 오류에는 종종 중괄호를 잘못 배치하거나 예약된 키워드를 잘못 사용하는 것이 포함됩니다.

예: steps { ... }를 사용하지 않고 최상위 stage 블록 내에 직접 steps 블록을 배치합니다.

해결책:

  • 유효성 검사: API를 통해 액세스할 수 있는 Jenkins 내장 파이프라인 린터를 사용하세요: JENKINS_URL/pipeline-model-converter/validate.
  • 다시 시작 확인: 지속적이고 혼란스러운 구문 오류의 일반적인 원인은 Jenkins 컨트롤러에서 파이프라인 스크립트를 직접 편집하고 작업을 제대로 새로 고치지 않기 때문입니다. 항상 디버깅 중인 스크립트가 실행 중인 스크립트인지 확인하세요.

카테고리 2: 환경 및 도구 실패

이러한 오류는 실행 에이전트에 파이프라인에 필요한 소프트웨어 또는 구성이 없을 때 발생합니다.

오류 2.1: 도구를 찾을 수 없음(명령을 찾을 수 없음)

mvn, npm 또는 docker와 같은 명령을 실행할 때 발생하는 일반적인 실패입니다.

원인: 도구가 실행 에이전트에 설치되지 않았거나, 더 자주는 도구의 바이너리 위치가 에이전트 시스템 PATH에서 사용 가능하지 않습니다.

해결책:

  1. Jenkins 도구 자동 설치 사용: Jenkins 관리 > 전역 도구 구성에서 도구를 정의합니다. 그런 다음 파이프라인에서 tool 지시문을 사용하여 참조하면 올바른 경로가 환경에 자동으로 주입됩니다.

    pipeline {
        agent any
        tools {
            maven 'Maven 3.8.4'
        }
        stages {
            stage('Build') {
                steps {
                    sh 'mvn clean install'
                }
            }
        }
    }
    
  2. 에이전트 레이블 확인: 파이프라인이 필요한 도구가 실제로 설치된 노드의 레이블과 일치하는 agent를 지정하는지 확인하세요.

    agent { label 'docker-enabled-node' }
    

오류 2.2: 에이전트 연결 거부 또는 오프라인

파이프라인이 단계를 시작하기 전에 즉시 실패하면 에이전트를 사용할 수 없을 수 있습니다.

원인: Jenkins 컨트롤러와 에이전트 간의 연결(일반적으로 JNLP 또는 SSH를 통해)이 실패했거나 에이전트가 과부하되었거나 오프라인 상태입니다.

해결책:

  • 에이전트 상태 확인: Jenkins 관리 > 노드로 이동하여 영향을 받은 에이전트의 상태를 확인하세요. 연결 로그 또는 오류 메시지(예: java.io.EOFException은 네트워크 연결 끊김을 나타냄)를 찾아보세요.
  • 리소스 확인: 에이전트 시스템에 충분한 메모리와 CPU 리소스가 있는지 확인하세요.

카테고리 3: 보안, 자격 증명 및 권한 부여

자격 증명 오류는 파이프라인이 Git 리포지토리, Docker 레지스트리 또는 클라우드 서비스와 같은 외부 리소스에 액세스하지 못하게 합니다.

오류 3.1: SCM 체크아웃 중 액세스 거부

소스 코드 체크아웃 시 파이프라인이 즉시 실패하면 Jenkins Git 플러그인에 일반적으로 필요한 자격 증명이 없습니다.

원인: Git 리포지토리에 구성되지 않았거나 작업과 연결되지 않은 SSH 키 또는 사용자 이름/비밀번호가 필요합니다.

해결책:

  1. 자격 증명 구성: 필요한 자격 증명(예: 사용자 이름 및 비밀번호, SSH 사용자 이름 및 개인 키)이 Jenkins 관리 > 자격 증명에 저장되어 있는지 확인하세요.
  2. 작업과 연결: 선언적 파이프라인에서 SCM 블록을 사용하는 경우 credentialsId 속성이 올바르게 설정되었는지 확인하세요.

오류 3.2: 저장된 비밀에 잘못 액세스

Jenkinsfile에 비밀을 하드코딩하지 마세요. 자격 증명은 withCredentials 단계를 사용하여 환경에 안전하게 주입되어야 합니다.

원인: 자격 증명 ID를 환경 변수로 직접 참조하거나 보호된 블록 외부에서 비밀에 액세스하려고 시도합니다.

해결책: withCredentials 도우미 함수를 사용하세요. 이 함수는 저장된 자격 증명 ID를 블록 기간 동안에만 보안 환경 변수에 매핑합니다.

stage('Deploy') {
    steps {
        withCredentials([usernamePassword(credentialsId: 'my-docker-registry-secret',
                                          passwordVariable: 'DOCKER_PASSWORD',
                                          usernameVariable: 'DOCKER_USER')]) {
            sh "echo 'Logging in with user: $DOCKER_USER'"
            sh "docker login -u $DOCKER_USER -p $DOCKER_PASSWORD myregistry.com"
        }
    }
}

보안 경고: withCredentials에 정의된 변수(예: DOCKER_PASSWORD)는 콘솔 출력에서 자동으로 마스킹되지만 사용 범위를 제한해야 합니다.


카테고리 4: 파이프라인 흐름 및 리소스 오류

이러한 문제는 파이프라인이 진행되는 방식이나 실행 제한을 처리하는 방식과 관련이 있습니다.

오류 4.1: 예기치 않은 빌드 실패 또는 중단

파이프라인이 무작위로 실패하거나 마지막 단계에서 FATAL: 명령 실행 실패를 보고하는 경우, 이는 종종 외부 원인 또는 리소스 제한을 나타냅니다.

잠재적 원인:

  • 프로세스 시간 초과: 단계 또는 단계가 할당된 시간 제한을 초과했습니다(options { timeout(...) }을 통해 구성된 경우).
  • OOM(메모리 부족): 에이전트의 메모리가 부족하여 운영 체제가 Jenkins 작업자 프로세스를 종료했습니다.
  • 디스크 공간: 디스크 공간 부족으로 아티팩트를 저장하거나 대규모 리포지토리를 복제할 수 없습니다.

해결책:

  1. 에이전트 로그 확인: 에이전트 시스템에서 시스템 로그(Linux의 경우 dmesg)를 확인하여 OOM 킬러 경고를 찾으세요.
  2. 시간 초과 구성: 단계가 실제로 오래 실행되는 경우 timeout 값을 늘리세요. 그렇지 않은 경우 비효율적인 단계를 최적화하세요.
  3. 작업 공간 정리: ws 단계를 사용하거나 정리 단계를 추가하여 작업 공간이 무한정 커져 디스크 공간을 소모하지 않도록 하세요.

오류 4.2: 병렬 단계 교착 상태 또는 불일치

parallel 단계를 사용할 때 스레드 간에 공유되는 변수나 리소스로 인해 예측할 수 없는 실패나 교착 상태가 발생할 수 있습니다.

모범 사례: 병렬 분기 내에서 전역 환경 변수를 수정하지 마세요. 특정 parallel 단계 내에 정의된 지역화된 변수를 사용하거나 공유 리소스(예: 특정 시스템 또는 외부 서비스)에 대한 액세스를 직렬화해야 하는 경우 lock 단계 플러그인을 활용하세요.

// 잠금 플러그인을 사용한 직렬화 예
stage('공유 리소스 액세스') {
    steps {
        lock('DatabaseMigrationLock') {
            // 한 번에 하나의 파이프라인 인스턴스만 이 단계를 실행할 수 있습니다.
            sh 'run_migration_script'
        }
    }
}

파이프라인을 안정적으로 유지하는 습관

사전 예방적 조치를 채택하면 파이프라인 실패 빈도가 크게 줄어듭니다:

  1. 선언적 구문 사용: 대부분의 프로젝트에서 선언적 파이프라인에 의해 적용되는 구조는 스크립트 파이프라인보다 스크립팅 오류가 덜 발생합니다.
  2. 실행 격리: 가능하면 컨테이너화된 에이전트(Docker/Kubernetes)를 사용하여 모든 빌드에 대해 깨끗하고 재현 가능한 실행 환경을 보장하여 많은 도구 경로 문제를 제거합니다.
  3. 환경을 명시적으로 정의: 에이전트의 시스템 기본값에만 의존하지 말고 environment 지시문을 사용하여 파이프라인 내에서 중요한 경로와 변수를 명확하게 설정하세요.
  4. 에이전트 상태 정기적으로 검토: 모든 전용 빌드 에이전트의 메모리, CPU 및 디스크 사용량을 모니터링하여 리소스 고갈 실패를 사전에 방지하세요.

오류는 일반적으로 빨간색 선보다 더 일찍 발생합니다.

Jenkins는 종종 마지막 실패 단계를 빨간색으로 표시하지만 유용한 단서는 일반적으로 더 일찍 있습니다. 셸 명령은 20줄 위의 종속성 설치가 실패했기 때문에 종료 코드 1로 종료될 수 있습니다. 배포 단계는 이전 단계에서 빈 아티팩트를 작성했기 때문에 실패할 수 있습니다. Groovy 스택 추적은 실제 실수가 하나의 잘못 입력된 변수임에도 불구하고 화면을 가득 채울 수 있습니다.

실패한 파이프라인을 열면 아래에서 위로 검색하여 첫 번째 예상치 못한 메시지를 찾으세요. 저는 ERROR, Exception, Permission denied, not found, No such file, 401, 403, timeout 및 첫 번째 0이 아닌 종료 코드를 찾는 경향이 있습니다. 그런 다음 해당 줄을 단계 이름과 비교합니다. 목표는 한 가지 간단한 질문에 답하는 것입니다: Jenkins가 파이프라인을 실행하지 못했습니까, 아니면 파이프라인 내부의 명령이 실패했습니까?

이 구분은 중요합니다. Jenkins에서 No such DSL method라고 표시되면 파이프라인 구문 또는 플러그인 가용성을 디버깅하는 것입니다. mvn test가 테스트 실패로 종료되면 Jenkins는 제 역할을 한 것이고 빌드 도구가 프로젝트 문제를 보고하는 것입니다. 둘 다 "Jenkins가 고장났다"고 처리하면 임시방편 수정으로 이어집니다.

보기보다 이상해 보이는 선언적 파이프라인 오류

선언적 파이프라인은 구조에 대해 엄격합니다. agent, environment, stages, stage, steps, postwhen과 같은 블록은 올바른 위치에 있어야 합니다. 중괄호가 누락되면 Jenkins가 파일의 완벽하게 유효한 줄에 대해 불평할 수 있습니다.

오류에 Expected a step, Undefined section 또는 Multiple occurrences of the stage section이 언급된 경우 Jenkinsfile을 가장 작은 중단된 블록으로 줄이세요. 내장 린터는 전체 실행 전에 모델의 유효성을 검사하기 때문에 유용합니다:

curl -X POST -F "jenkinsfile=<Jenkinsfile" \
  https://jenkins.example.com/pipeline-model-converter/validate

익명 액세스가 비활성화된 경우 Jenkins 인스턴스에 올바른 인증 방법을 사용하세요. 요점은 정확한 명령이 아닙니다. 요점은 체크아웃, 종속성 설치 및 테스트 설정을 기다리기 전에 구문의 유효성을 검사하는 것입니다.

한 가지 일반적인 실수는 script 블록 없이 스크립트된 Groovy를 steps 내에 직접 넣는 것입니다. 선언적 파이프라인은 거기에 일반 단계를 허용하지만 더 복잡한 Groovy 논리는 script { ... } 내부에 속합니다:

stage('대상 선택') {
    steps {
        script {
            def target = env.BRANCH_NAME == 'main' ? 'prod' : 'dev'
            echo "Deploying to ${target}"
        }
    }
}

오류를 없애기 위해 모든 것을 script 안에 넣지 마세요. 선언적 파이프라인을 읽기 쉽게 만드는 일부 안전 장치를 잃게 됩니다.

자격 증명 오류: ID, 범위 및 사용 위치 확인

자격 증명 실패는 종종 Git, Docker, 클라우드 또는 셸 실패처럼 보입니다. 파이프라인에 Authentication failed, 403 Forbidden, repository not found, denied: requested access to the resource is denied 또는 클라우드 공급자의 액세스 오류가 표시될 수 있습니다. 코드를 변경하기 전에 Jenkinsfile의 자격 증명 ID가 기존 자격 증명과 정확히 일치하는지 확인하세요.

또한 자격 증명 범위를 확인하세요. 폴더 수준 자격 증명은 한 작업에는 표시되지만 다른 작업에는 표시되지 않을 수 있습니다. 멀티브랜치 작업은 리포지토리 스캔에 하나의 자격 증명을 사용하고 Jenkinsfile 내부에 다른 자격 증명을 사용할 수 있습니다. 이는 브랜치 검색은 작동하지만 체크아웃 또는 배포가 나중에 실패하기 때문에 사람들을 혼란스럽게 할 수 있습니다.

셸 명령에 필요한 비밀에는 withCredentials를 사용하고 로그에서 비밀을 유지하세요:

withCredentials([string(credentialsId: 'npm-token', variable: 'NPM_TOKEN')]) {
    sh '''
      set +x
      npm config set //registry.npmjs.org/:_authToken "$NPM_TOKEN"
      npm ci
    '''
}

디버깅을 위해 비밀을 에코하지 마세요. 변수가 존재한다는 것을 증명해야 하는 경우 길이 또는 무해한 마커를 출력하세요:

test -n "$NPM_TOKEN" && echo "NPM 토큰이 있습니다"

샌드박스 및 승인 오류

Groovy 샌드박스가 메서드를 차단하면 Jenkins에 Scripts not permitted to use method...가 표시되거나 스크립트가 프로세스 내 스크립트 승인으로 전송될 수 있습니다. 이는 일반적인 빌드 실패가 아닙니다. Jenkins가 승인되지 않은 Groovy가 컨트롤러 수준 액세스 권한으로 실행되는 것을 방지하고 있습니다.

공유 파이프라인의 경우 더 나은 수정은 종종 안전하지 않은 논리를 관리자가 유지 관리하는 신뢰할 수 있는 공유 라이브러리로 이동하거나 사용자 지정 Groovy를 지원되는 파이프라인 단계로 대체하는 것입니다. 하나의 빌드를 차단 해제하기 위해 임의의 메서드를 승인하면 Jenkinsfile을 편집할 수 있는 모든 작업에 대한 위험이 증가할 수 있습니다.

플러그인 업데이트 후 스크립트 승인이 나타나면 주의 깊게 읽으세요. 때로는 플러그인이 구현을 변경하여 승인이 필요한 메서드를 호출합니다. 때로는 Jenkinsfile 변경으로 위험한 호출이 도입되었습니다. 대응은 달라야 합니다.

파이프라인 내부의 셸 오류

셸 단계는 잘못된 작업 디렉터리, 누락된 실행 비트, 다른 셸, 누락된 환경 변수 또는 비대화형 모드에서 다르게 동작하는 명령과 같은 간단한 이유로 실패합니다.

실패하는 명령 앞에 작은 검사를 추가하세요:

sh '''
  pwd
  ls -la
  command -v node
  node --version
  ./scripts/build.sh
'''

스크립트가 노트북에서는 작동하지만 Jenkins에서는 실패하는 경우 shebang과 줄 끝을 확인하세요. Windows 줄 끝이 있는 파일은 bad interpreter와 같은 혼란스러운 메시지와 함께 실패할 수 있습니다. #!/bin/bash로 시작하는 스크립트는 /bin/sh만 있는 에이전트 이미지에서 실패합니다. 필요한 셸을 설치하거나 실제로 가지고 있는 셸용 스크립트를 작성하세요.

set -euo pipefail을 주의해서 사용하세요. Bash 스크립트에서 실패를 표시하므로 유용하지만 의도적으로 실패하는 명령을 테스트하는 스크립트를 중단시킬 수도 있습니다. 엄격한 셸 플래그를 추가한 후 파이프라인이 실패하기 시작한 경우 설계상 0이 아닌 상태를 반환할 수 있는 각 명령을 검사하세요.

병렬 파이프라인 및 공유 상태

병렬 단계는 "무작위" 파이프라인 오류의 일반적인 원인입니다. 두 분기가 동일한 작업 공간 경로를 사용하고, 동일한 보고서 파일을 작성하고, 동일한 Docker 태그를 푸시하거나, 동일한 테스트 환경에 배포할 수 있습니다. 실패는 타이밍에 따라 달라지므로 간헐적으로 보입니다.

각 병렬 분기에 자체 디렉터리를 제공하세요:

parallel(
  unit: {
    dir('work-unit') {
      sh './gradlew test'
    }
  },
  integration: {
    dir('work-integration') {
      sh './gradlew integrationTest'
    }
  }
)

분기가 동일한 외부 리소스를 건드려야 하는 경우 해당 작업에 대해서만 잠금을 사용하세요. 전체 빌드를 직렬화하려는 경우가 아니라면 전체 빌드를 잠그지 마세요.

재생이 도움이 되는 경우와 그렇지 않은 경우

재생은 실패한 실행에서 작은 Jenkinsfile 변경 사항을 테스트하는 데 탁월합니다. 수정 사항을 커밋하는 것을 대체하지는 않습니다. 재생이 문제를 증명하면 소스 제어에서 Jenkinsfile을 업데이트하고 작업을 정상적으로 실행하세요.

재생은 변경된 외부 상태(누락된 Docker 이미지, 만료된 토큰, 삭제된 브랜치 또는 불안정한 서비스)로 인한 실패에는 덜 유용합니다. 이러한 경우 동일한 파이프라인을 다시 실행하면 아무것도 설명하지 않고 통과할 수 있습니다. 실패한 실행이 사라지기 전에 충분한 증거(콘솔 로그, 단계 타이밍, 에이전트 이름, 커밋 SHA, 자격 증명 ID 및 외부 요청 ID)를 캡처하세요.