Skip to content
SON BLOG
Go back

Jenkins executor 수 최적화: 6개 서비스 동시 빌드를 위한 성능 튜닝

Edit page

배경

XGEN 2.0 서비스는 6개다: xgen-model, xgen-core, xgen-workflow, xgen-frontend, xgen-documents, xgen-backend-gateway. 각 서비스는 Jenkins Job을 통해 빌드되고 ArgoCD로 배포된다.

배포 시 6개 서비스를 동시에 업데이트하는 경우가 있다. 전체 인프라 변경이나 주요 버전 업그레이드 때다. 이때 Jenkins executor가 2개뿐이면 빌드 큐가 4개씩 쌓이고, 빌드 완료까지 30~40분이 걸렸다.

문제: executor 2개의 한계

[빌드 큐 상황 (executor 2개)]

실행 중: xgen-model (10분), xgen-frontend (5분)
대기:   xgen-core, xgen-workflow, xgen-documents, xgen-backend-gateway

xgen-model 완료 → xgen-core 시작
xgen-frontend 완료 → xgen-workflow 시작
... 총 소요 시간: 35분 (직렬 처리)

특히 xgen-model은 PyTorch 이미지를 포함해 Docker 빌드에 10~15분이 걸린다. 이 Job이 executor를 점유하는 동안 나머지 서비스들이 줄줄이 대기한다.

numExecutors 설정

# values-override.yaml
# # 커밋: Jenkins numExecutors 2 → 6으로 증가 (6개 서비스 동시 빌드 지원)
# # 날짜: 2024-09-10
controller:
  numExecutors: 6

JCasC 설정도 함께 업데이트했다.

# casc-config.yaml
# # 커밋: JCasC numExecutors 6으로 업데이트 (값 일관성 유지)
# # 날짜: 2024-09-10
jenkins:
  numExecutors: 6
  systemMessage: "XGEN 2.0 Jenkins"

두 곳을 모두 업데이트해야 한다. values.yamlnumExecutors는 초기 설치 시에만 적용되고, JCasC의 값이 Jenkins 재시작 후 덮어쓰기 때문이다. 값이 다르면 어떤 것이 우선인지 헷갈린다.

리소스 계획

executor 수를 늘리기 전에 K3s 노드의 리소스를 확인해야 한다. 빌드 Pod는 Docker DinD(Docker in Docker)를 사용하므로 CPU/메모리를 꽤 쓴다.

# Jenkins Kubernetes agent Pod 리소스 설정
clouds:
  - kubernetes:
      templates:
        - name: "default"
          containers:
            - name: "docker"
              image: "docker:24-dind"
              resourceRequestCpu: "500m"
              resourceLimitCpu: "2"
              resourceRequestMemory: "512Mi"
              resourceLimitMemory: "4Gi"

executor 1개 = 빌드 Pod 1개 = 최대 2 CPU, 4GB RAM.

executor 6개 동시 실행 = 최대 12 CPU, 24GB RAM.

서버 스펙(32 CPU, 128GB RAM)에서는 충분히 수용 가능하다. 하지만 xgen-model 빌드는 PyTorch 이미지 레이어가 크고 I/O가 많아서, 실제로는 6개 동시 빌드 시 디스크 I/O가 병목이 됐다.

서비스별 빌드 시간 분류

6개 서비스의 빌드 시간이 제각각이다. 이를 파악하고 빌드 순서를 고려해야 한다.

서비스평균 빌드 시간이유
xgen-model12~15분PyTorch, CUDA 관련 패키지
xgen-core4~6분FastAPI, SQLAlchemy
xgen-workflow4~5분유사한 의존성
xgen-frontend8~10분Next.js 빌드, node_modules
xgen-documents6~8분임베딩 모델 의존성
xgen-backend-gateway3~5분Rust 빌드 (캐시 있을 때)

executor 6개로 모두 동시 시작하면 이론적으로 가장 오래 걸리는 xgen-model의 빌드 시간인 15분에 수렴한다. 실제로는 디스크 I/O 경합으로 20분 전후로 완료됐지만, 기존 35분에서 크게 개선됐다.

빌드 큐 모니터링

Jenkins에서 executor 사용량을 확인하는 방법이다.

# Jenkins CLI로 executor 상태 확인
java -jar jenkins-cli.jar -s http://jenkins.x2bee.com/ \
    -auth admin:${JENKINS_TOKEN} \
    list-jobs

# Jenkins API로 큐 상태 확인
curl -u admin:${JENKINS_TOKEN} \
    http://jenkins.x2bee.com/queue/api/json?pretty=true

# 실행 중인 빌드 확인
curl -u admin:${JENKINS_TOKEN} \
    "http://jenkins.x2bee.com/api/json?tree=jobs[name,builds[number,building,duration]]&pretty=true"

Kubernetes 빌드 에이전트 주의사항

Jenkins가 K8s에서 실행될 때 빌드 Pod가 동적으로 생성/삭제된다. executor 수가 빌드 Pod 생성 한계를 결정하는 것이 아니라, K8s 클러스터의 가용 리소스가 실제 한계다.

# jenkins-kubernetes-cloud.yaml
kubernetes:
  maxRequestsPerHost: 64
  containerCapStr: "10"  # 최대 동시 빌드 Pod 수

containerCapStr은 Kubernetes 에이전트의 전체 Pod 수 제한이다. executor 6개에 맞게 최소 6 이상으로 설정해야 한다.

빌드 병렬화 전략

단순히 executor 수를 늘리는 것 외에, 파이프라인 내부에서도 병렬화를 적용할 수 있다.

// 멀티 서비스를 하나의 파이프라인에서 병렬 빌드
pipeline {
    stages {
        stage('Build All Services') {
            parallel {
                stage('xgen-model') {
                    steps { build job: 'xgen-model/dev' }
                }
                stage('xgen-core') {
                    steps { build job: 'xgen-core/dev' }
                }
                stage('xgen-workflow') {
                    steps { build job: 'xgen-workflow/dev' }
                }
                stage('xgen-frontend') {
                    steps { build job: 'xgen-frontend/dev' }
                }
                stage('xgen-documents') {
                    steps { build job: 'xgen-documents/dev' }
                }
                stage('xgen-backend-gateway') {
                    steps { build job: 'xgen-backend-gateway/dev' }
                }
            }
        }
    }
}

parallel 블록을 사용하면 여러 Job을 하나의 파이프라인에서 동시에 트리거할 수 있다. 각 Job이 별도 executor를 사용하므로 executor 6개가 모두 활용된다.

디스크 I/O 병목 대응

6개 동시 빌드 시 Docker 이미지 레이어 pull/push가 동시에 발생해 디스크 I/O가 포화 상태가 됐다. 특히 레지스트리 push 단계에서 네트워크 + 디스크가 동시에 한계에 달했다.

해결책 1: 빌드 순서 조정

// xgen-model을 먼저 트리거 (빌드 시간이 가장 김)
// 나머지는 xgen-model 시작 후 1~2분 지연해서 트리거
stage('Staggered Build') {
    steps {
        build job: 'xgen-model/dev', wait: false
        sleep(time: 2, unit: 'MINUTES')
        parallel {
            stage('core') { steps { build job: 'xgen-core/dev' } }
            stage('workflow') { steps { build job: 'xgen-workflow/dev' } }
            // ...
        }
    }
}

해결책 2: 로컬 레지스트리 캐시 활용

Harbor 레지스트리에 캐시 설정을 추가해 같은 베이스 이미지 레이어를 반복 다운로드하지 않도록 했다.

결과

Jenkins executor 수는 “서비스 개수에 맞춰라”가 직관적인 기준이다. executor 수보다 실제 빌드 리소스(CPU, 메모리, 디스크 I/O)가 진짜 병목인 경우가 많으니, 리소스 모니터링과 함께 튜닝해야 한다.


Edit page
Share this post:

Previous Post
Istio Gateway HTTPS 설정과 TLS 인증서 관리
Next Post
Jenkins JCasC로 6개 서비스 빌드 Job 자동 생성하기