Jenkins executor 수 최적화: 6개 서비스 동시 빌드를 위한 성능 튜닝
배경¶
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.yaml의 numExecutors는 초기 설치 시에만 적용되고, 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-model | 12~15분 | PyTorch, CUDA 관련 패키지 |
| xgen-core | 4~6분 | FastAPI, SQLAlchemy |
| xgen-workflow | 4~5분 | 유사한 의존성 |
| xgen-frontend | 8~10분 | Next.js 빌드, node_modules |
| xgen-documents | 6~8분 | 임베딩 모델 의존성 |
| xgen-backend-gateway | 3~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 레지스트리에 캐시 설정을 추가해 같은 베이스 이미지 레이어를 반복 다운로드하지 않도록 했다.
결과¶
- executor 2 → 6으로 증가
- 전체 6개 서비스 동시 배포 시간: 35분 → 20분
- JCasC와 values.yaml 양쪽 값 일관성 유지 필요
- K8s 동적 에이전트 덕분에 idle 시 리소스 낭비 없음
Jenkins executor 수는 "서비스 개수에 맞춰라"가 직관적인 기준이다. executor 수보다 실제 빌드 리소스(CPU, 메모리, 디스크 I/O)가 진짜 병목인 경우가 많으니, 리소스 모니터링과 함께 튜닝해야 한다.
관련 글
- Jenkins JCasC로 6개 서비스 빌드 Job 자동 생성하기
CI/CDDevOpsGroovy - XGEN K3s 인프라 완전 해부 (4) — CI/CD 파이프라인: Jenkins 빌드에서 ArgoCD 배포까지
App of AppsArgoCDCI/CD - Docker BuildKit 캐시 전략과 NO_CACHE 옵션
BuildKitCI/CDDevOps - XGEN K3s 인프라 완전 해부 (2) — Kubernetes 핵심 오브젝트와 스케일링 전략
DeploymentDevOpsHPA - XGEN K3s 인프라 완전 해부 (3) — Helm 차트 설계: 하나의 Chart로 6개 서비스 배포
ConfigMapDevOpsGitOps