mingming

Jenkins Helm Chart 기동 구조 분석 본문

카테고리 없음

Jenkins Helm Chart 기동 구조 분석

mingming_96 2026. 3. 22. 23:21

Jenkins를 Helm으로 배포하고 운영하다 보면 한 번쯤 이런 의문이 생깁니다. Pod가 재시작될 때 플러그인은 어디서 오는 걸까? ConfigMap은 어떻게 Jenkins에 반영되는 걸까?

이 글에서는 jenkinsci/jenkins Helm Chart를 기반으로 Jenkins Pod가 기동될 때의 전체 흐름을 단계별로 분석합니다.


1. 전체 Pod 구성

Jenkins Helm Chart는 하나의 Pod 안에 아래와 같은 컨테이너 구조로 구성됩니다.

Pod: jenkins-0 (StatefulSet) ├── initContainer: copy-default-config ← 기동 전 준비 작업 ├── container: jenkins ← 메인 Jenkins 프로세스 └── container: config-reload (선택) ← JCasC 자동 리로드 사이드카

StatefulSet으로 배포되기 때문에 Pod 이름은 항상 <release>-0으로 고정됩니다. initContainer가 완료되어야만 메인 컨테이너가 기동됩니다.


2. 생성되는 Kubernetes 리소스

helm install 시 생성되는 주요 리소스 목록입니다.

리소스 이름 용도
StatefulSet<release>Jenkins Controller Pod 관리
ConfigMap<release>apply_config.sh, plugins.txt, jvm-options 등
ConfigMap<release>-jenkins-jcasc-configJCasC 기본 보안/권한 설정
ConfigMap<release>-jenkins-config-<key>configScripts 항목별 JCasC yaml
Secret<release>admin 계정 username / password
ServiceAccount<release>Jenkins RBAC 권한
PVC<release>jenkins_home 영구 저장소
Service<release>8080 (UI), 50000 (Agent 연결)

3. 볼륨 구성

initContainer와 메인 컨테이너가 공유하는 볼륨 구조입니다. 어떤 볼륨이 재시작 후에도 살아남는지 파악하는 것이 중요합니다.

볼륨명 타입 initContainer 마운트 메인 컨테이너 마운트 데이터 유지
jenkins-homePVC/var/jenkins_home/var/jenkins_home✅ 영구
jenkins-configConfigMap (읽기전용)/var/jenkins_config✅ ConfigMap 기준
plugin-diremptyDir/var/jenkins_plugins/usr/share/jenkins/ref/plugins❌ 재시작 시 초기화
secrets-diremptyDir/usr/share/jenkins/ref/secrets/usr/share/jenkins/ref/secrets❌ 재시작 시 초기화
sc-config-volumeemptyDir/var/jenkins_home/casc_configs❌ 재시작 시 초기화

plugin-dir은 emptyDir입니다. Pod가 재시작될 때마다 비워지기 때문에 매 기동 시 initContainer가 plugins.txt를 다시 읽어 플러그인을 전부 새로 설치합니다.


4. initContainer 상세 분석

4.1 기본 정보

initContainer는 메인 컨테이너와 동일한 이미지를 사용하며, ConfigMap에 저장된 쉘 스크립트를 실행합니다.

name: copy-default-config image: jenkins/jenkins:lts command: ["sh", "/var/jenkins_config/apply_config.sh"]

4.2 주입되는 환경 변수

env: # Secret에서 주입 - name: ADMIN_PASSWORD valueFrom: secretKeyRef: name: <release> key: jenkins-admin-password - name: ADMIN_USER valueFrom: secretKeyRef: name: <release> key: jenkins-admin-user # values.yaml에서 주입 - name: JENKINS_VERSION # Setup Wizard 비활성화에 사용 value: "2.x.x" - name: JAVA_OPTS # controller.javaOpts value: "-Xmx512m ..." - name: JENKINS_OPTS # controller.jenkinsOpts value: "..."

4.3 apply_config.sh 실행 흐름

Step 1 — Setup Wizard 비활성화

JCasC 환경에서 최초 기동 시 나타나는 Setup Wizard를 건너뛰기 위해 버전 파일을 미리 생성합니다.

echo $JENKINS_VERSION > /var/jenkins_home/jenkins.install.UpgradeWizard.state echo $JENKINS_VERSION > /var/jenkins_home/jenkins.install.InstallUtil.lastExecVersion

Step 2 — plugins.txt 복사 (ConfigMap → PVC)

ConfigMap에 있는 plugins.txt를 PVC로 복사합니다. 이전 실행에서 남은 .lock 파일도 함께 제거합니다.

cp /var/jenkins_config/plugins.txt /var/jenkins_home rm -rf /usr/share/jenkins/ref/plugins/*.lock

Step 3 — 플러그인 설치 (jenkins-plugin-cli)

jenkins-plugin-cli 버전에 따라 두 가지 방식으로 분기합니다.

# jenkins-plugin-cli v2.1.1 이상 (신버전) jenkins-plugin-cli \ --war "/usr/share/jenkins/jenkins.war" \ --plugin-file "/var/jenkins_home/plugins.txt" \ --latest true # v2.1.1 미만 (구버전 폴백) /usr/local/bin/install-plugins.sh $(cat /var/jenkins_home/plugins.txt)

Update Center를 참조하여 의존성을 재귀적으로 해결하며, 버전 충돌 시 높은 버전으로 자동 대체됩니다.

Step 4 — plugin-dir(emptyDir)로 플러그인 복사

설치된 플러그인을 메인 컨테이너가 읽을 수 있는 공유 볼륨으로 복사합니다.

yes n | cp -i /usr/share/jenkins/ref/plugins/* /var/jenkins_plugins/

5. 메인 컨테이너 기동 순서

initContainer가 exit 0으로 종료된 후 메인 Jenkins 컨테이너가 기동됩니다.

순서 작업 비고
1JVM 기동JAVA_OPTS, JENKINS_OPTS 적용
2플러그인 로드/usr/share/jenkins/ref/plugins/
3JCasC 설정 적용/var/jenkins_home/casc_configs/ 내 yaml 전체 읽기
4Probe 통과HTTP GET /login → 200 OK 확인 후 트래픽 허용

플러그인 의존성 충돌은 2번 단계에서 Jenkins가 죽습니다. kubectl logs <pod> -c jenkins에서 오류를 확인하세요.


6. JCasC(Jenkins Configuration as Code) 적용 구조

6.1 ConfigMap 생성 구조

values.yamlcontroller.JCasC.configScripts 항목들은 각각 독립적인 ConfigMap으로 생성됩니다.

controller: JCasC: configScripts: welcome-message: | jenkins: systemMessage: "Hello Jenkins" credentials: | credentials: system: ...

6.2 Pod 내 마운트 경로

/var/jenkins_home/casc_configs/ ├── welcome-message.yaml ← ConfigMap에서 마운트 ├── credentials.yaml ← ConfigMap에서 마운트 └── jcasc-default-config.yaml ← 기본 보안/권한 설정

6.3 configAutoReload 사이드카

controller.sidecars.configAutoReload.enabled: true로 설정하면 사이드카 컨테이너가 추가됩니다.

사이드카는 ConfigMap 변경을 감지하면 yaml을 casc_configs/에 복사하고 리로드를 요청합니다. Jenkins 재기동 없이 설정 변경이 반영됩니다.


7. Secret 및 Credential 주입 구조

7.1 Admin 계정 Secret

controller: adminUser: "admin" adminPassword: "password"

7.2 추가 Secret 마운트

controller: additionalSecrets: - name: github-token value: "ghp_xxx" additionalExistingSecrets: - name: my-k8s-secret keyName: db-password

Pod의 /run/secrets/ 경로에 마운트되며, JCasC에서 ${github-token} 형식으로 참조할 수 있습니다.


8. 기동 실패 주요 원인

원인 증상 확인 방법
플러그인 의존성 충돌initContainer CrashLoopBackOffkubectl logs <pod> -c copy-default-config
PVC 마운트 실패Pod Pending 상태 지속kubectl describe pod <pod> Events 확인
Update Center 연결 실패플러그인 다운로드 타임아웃initContainer 로그에서 connection refused 확인
JCasC yaml 문법 오류Jenkins 기동 후 즉시 재시작kubectl logs <pod> -c jenkins
Secret 누락initContainer Error 즉시 종료kubectl describe pod Events 확인

주요 확인 명령어

# initContainer 로그 kubectl logs <pod> -c copy-default-config -n <namespace> # 메인 컨테이너 로그 kubectl logs <pod> -c jenkins -n <namespace> # 사이드카 로그 kubectl logs <pod> -c config-reload -n <namespace> # Pod 전체 이벤트 kubectl describe pod <pod> -n <namespace> # rollout 상태 확인 kubectl rollout status statefulset/jenkins -n <namespace>

9. 전체 기동 흐름 요약

지금까지 살펴본 내용을 단계별로 정리하면 다음과 같습니다.

Phase 1 — 리소스 준비

단계 내용
Helm install/upgradeChart 렌더링 후 K8s 리소스 apply
ConfigMap 생성/업데이트apply_config.sh, plugins.txt, JCasC yaml
Secret 생성/업데이트admin 계정, 추가 credentials
PVC 바인딩jenkins_home 영구 볼륨 확보

Phase 2 — initContainer (copy-default-config)

순서 작업 관련 볼륨
1Setup Wizard 비활성화 파일 생성jenkins-home (PVC)
2ConfigMap → PVC로 plugins.txt 복사jenkins-config, jenkins-home
3jenkins-plugin-cli로 플러그인 다운로드Update Center 네트워크 통신
4설치된 플러그인을 plugin-dir에 복사plugin-dir (emptyDir)

Phase 3 — 메인 컨테이너 (jenkins)

순서 작업 관련 볼륨
5JVM 기동 (JAVA_OPTS, JENKINS_OPTS 적용)
6plugin-dir에서 플러그인 로드plugin-dir (emptyDir)
7casc_configs/ 읽어 JCasC 설정 적용sc-config-volume (emptyDir)
8Liveness / Readiness Probe 통과

Phase 4 — 사이드카 (config-reload, 선택)

순서 작업
9Kubernetes Watch API로 ConfigMap 변경 감시 시작
10변경 감지 시 yaml을 casc_configs/에 복사 후 Jenkins에 리로드 요청