본문 바로가기

개발/Linux & DevOps

젠킨스 build.xml 에 대한 고찰

서론

배치 스케줄링을 담당하는 Pipeline 프로젝트에서 FreeStyle프로젝트를 호출하는 과정에서,
FreeStyle 프로젝트 빌드넘버가 갱신되는 에러가 발생했다.

조사결과 딱 Pipeline Proj -> Freestyle Proj 하는 과정에서 reload-configuration 을 실행했더니
이 사단이 났다.

요약하자면 build.xml 에 대해 알아야한다.

정확히 어떤 것이 문제가 됐는지 하나하나 배워보자.


1. Jenkins 내부 파헤치기

젠킨스는 오픈소스다.

내부 jar 를 뜯어서 그의 뽀얀 속살을 보자.

  1. Jenkins 빌드 번호 관리 메커니즘
// jenkins.model.Jenkins.java
public class Jenkins {
    private transient RunMap<Run> builds;

    // 빌드 정보는 builds.xml에 저장됨
    private void saveBuildNumbers() {
        // 빌드 번호와 타임스탬프 저장
    }
}

// hudson.model.Job.java
public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, RunT>> {
    protected transient RunMap<RunT> builds;

    // 다음 빌드 번호 결정
    private synchronized int assignBuildNumber() {
        int nextBuildNumber = getNextBuildNumber();
        builds.put(nextBuildNumber, build);
        return nextBuildNumber;
    }
}
  1. reload-configuration 발생 시 동작
// Jenkins reload-configuration 처리
public void reload() {
    // 모든 설정 리로드
    this.servletContext.setAttribute("app", new Jenkins(root, servletContext));

    // 빌드 정보 다시 로드
    builds = new RunMap<>();
    builds.load(); // builds.xml에서 정보 로드
}
  1. 빌드 번호가 달라지는 과정
상황:
1. 원래 빌드: myproj #4094 실행 중
2. reload-configuration 발생
3. 새 빌드: myproj #4095 시작

발생 순서:
1. Pipeline Job이 하위 프로젝트 호출
2. reload-configuration으로 Jenkins 재로드
3. builds.xml 재로드
4. 새로운 빌드 번호 할당

 


 

2. 대안 : reload-job 으로 reload-configuration 대체

젠킨스 조작에 사용된 jenkins-cli.jar 도 오픈소스다.

너도 예외는 없다. 낱낱히 발가벗겨 보자.

reload-job 명령의 Jenkins 내부 동작 분석:

  1. Jenkins CLI ReloadJobCommand
// jenkins.cli.ReloadJobCommand.java
public class ReloadJobCommand extends CLICommand {
    @Override
    protected int run() throws Exception {
        // 단일 Job만 찾아서 처리
        AbstractItem job = JobResolver.findSingleJob(jenkins, jobName);
        job.doReload();  // AbstractItem의 doReload 호출
        return 0;
    }
}
  1. AbstractItem의 doReload 구현
// hudson.model.AbstractItem.java
public abstract class AbstractItem extends Actionable implements Item {
    public synchronized void doReload() throws IOException {
        checkPermission(CONFIGURE);

        // 현재 설정 파일
        XmlFile cfg = getConfigFile();

        // 현재 트리거 상태 보존
        Map<TriggerDescriptor,Trigger<?>> oldTriggers = getTriggers();

        // 설정 다시 로드
        load(cfg);

        // 빌드 번호는 변경되지 않음
        // nextBuildNumber는 유지
    }

    protected void load(XmlFile file) throws IOException {
        // 설정만 다시 로드
        // 빌드 정보나 번호는 건드리지 않음
    }
}
  1. Job의 빌드 번호 관리
// hudson.model.Job.java
public abstract class Job<JobT extends Job<JobT, RunT>, 
        RunT extends Run<JobT, RunT>> 
        extends AbstractItem {

    /** 다음 빌드 번호 */
    private transient int nextBuildNumber = 1;

    /** builds.xml에서 관리되는 빌드 정보 */
    protected transient RunMap<RunT> builds;

    /** doReload()에서 이 부분은 건드리지 않음 */
    public synchronized int assignBuildNumber() throws IOException {
        int number = nextBuildNumber++;
        save();  // 빌드 번호 저장
        return number;
    }
}
  1. 실제 동작 분석
// 내부 동작 시퀀스
public class BuildSequence {
    void sequence() {
        // 1. reload-job 호출
        job.doReload();

        // 2. 설정 파일만 리로드
        // - builds.xml은 건드리지 않음
        // - nextBuildNumber 유지
        // - 실행 중인 빌드 정보 유지

        // 3. 트리거 상태 유지
        // - 기존 타이머 유지
        // - 스케줄링된 작업 유지
    }
}

결론: reload-job은 안전함

이유:

  1. 단일 Job의 설정만 리로드
  2. 빌드 번호 체계 유지
  3. 진행 중인 빌드에 영향 없음
  4. 트리거(타이머 등) 상태 보존

reload-jobreload-configuration보다 안전하며, 단일 Job의 설정만 리로드하므로 빌드 번호나 진행 중인 작업에 영향을 주지 않는다.


3. build.xml 상세 보기

Jenkins의 builds.xml 파일에 대해 상세히 설명:

  1. 위치
JENKINS_HOME/jobs/[JOB_NAME]/builds/builds.xml
# 예: /var/lib/jenkins/jobs/my-pipeline/builds/builds.xml
  1. 파일 구조
<?xml version='1.0' encoding='UTF-8'?>
<builds>
  <build>
    <number>123</number>
    <timestamp>1634567890123</timestamp>
  </build>
  <build>
    <number>124</number>
    <timestamp>1634567990123</timestamp>
  </build>
  <!-- 각 빌드별 정보 -->
</builds>
  1. 관련 Jenkins 코드
// hudson.model.RunMap.java
public class RunMap<R extends Run<?,R>> {
    private final File buildsDir;

    // builds.xml 로드
    public synchronized void load() throws IOException {
        XmlFile file = new XmlFile(new File(buildsDir, "builds.xml"));
        if(file.exists()) {
            builds.clear();
            builds.putAll(((Map) file.read()));
        }
    }

    // builds.xml 저장
    public synchronized void save() throws IOException {
        XmlFile file = new File(buildsDir, "builds.xml");
        file.write(builds);
    }
}

이러한 builds.xml은 Jenkins의 빌드 이력을 관리하는 핵심 파일로, 빌드 번호 관리, 이력 조회, 롤백 등 다양한 용도로 사용된다.

반응형