| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 | 31 |
- Kubernetes
- EFS CSI Driver
- 그라파나 대시보드
- 로드밸런서 컨트롤러
- 딥레이서
- SAA 합격 후기
- AWS 딥레이서
- 솔데스크
- 메탈LB
- kubernetes 동작 원리
- Prometheus install
- jenkins
- terraform
- grafana on kubernetes
- 쿠버네티스 컴포넌트
- blue-green
- 그라파나 시각화
- LoadBalancer Controller
- Solution Architecture
- headless service
- livenessPorbe
- helm
- EKS 클러스터
- Aurora cluster
- github action 사용법
- 깃허브 액션
- 딥레이서 보상함수
- 쿠버네티스
- Firelens
- Kubernets on Jenkins
mingming
Jenkins Helm Chart 기동 구조 분석 본문
Jenkins를 Helm으로 배포하고 운영하다 보면 한 번쯤 이런 의문이 생깁니다. Pod가 재시작될 때 플러그인은 어디서 오는 걸까? ConfigMap은 어떻게 Jenkins에 반영되는 걸까?
이 글에서는 jenkinsci/jenkins Helm Chart를 기반으로 Jenkins Pod가 기동될 때의 전체 흐름을 단계별로 분석합니다.
1. 전체 Pod 구성
Jenkins Helm Chart는 하나의 Pod 안에 아래와 같은 컨테이너 구조로 구성됩니다.
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-config | JCasC 기본 보안/권한 설정 |
| 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-home | PVC | /var/jenkins_home | /var/jenkins_home | ✅ 영구 |
jenkins-config | ConfigMap (읽기전용) | /var/jenkins_config | — | ✅ ConfigMap 기준 |
plugin-dir | emptyDir | /var/jenkins_plugins | /usr/share/jenkins/ref/plugins | ❌ 재시작 시 초기화 |
secrets-dir | emptyDir | /usr/share/jenkins/ref/secrets | /usr/share/jenkins/ref/secrets | ❌ 재시작 시 초기화 |
sc-config-volume | emptyDir | — | /var/jenkins_home/casc_configs | ❌ 재시작 시 초기화 |
plugin-dir은 emptyDir입니다. Pod가 재시작될 때마다 비워지기 때문에 매 기동 시 initContainer가 plugins.txt를 다시 읽어 플러그인을 전부 새로 설치합니다.
4. initContainer 상세 분석
4.1 기본 정보
initContainer는 메인 컨테이너와 동일한 이미지를 사용하며, ConfigMap에 저장된 쉘 스크립트를 실행합니다.
4.2 주입되는 환경 변수
4.3 apply_config.sh 실행 흐름
Step 1 — Setup Wizard 비활성화
JCasC 환경에서 최초 기동 시 나타나는 Setup Wizard를 건너뛰기 위해 버전 파일을 미리 생성합니다.
Step 2 — plugins.txt 복사 (ConfigMap → PVC)
ConfigMap에 있는 plugins.txt를 PVC로 복사합니다. 이전 실행에서 남은 .lock 파일도 함께 제거합니다.
Step 3 — 플러그인 설치 (jenkins-plugin-cli)
jenkins-plugin-cli 버전에 따라 두 가지 방식으로 분기합니다.
Update Center를 참조하여 의존성을 재귀적으로 해결하며, 버전 충돌 시 높은 버전으로 자동 대체됩니다.
Step 4 — plugin-dir(emptyDir)로 플러그인 복사
설치된 플러그인을 메인 컨테이너가 읽을 수 있는 공유 볼륨으로 복사합니다.
5. 메인 컨테이너 기동 순서
initContainer가 exit 0으로 종료된 후 메인 Jenkins 컨테이너가 기동됩니다.
| 순서 | 작업 | 비고 |
|---|---|---|
| 1 | JVM 기동 | JAVA_OPTS, JENKINS_OPTS 적용 |
| 2 | 플러그인 로드 | /usr/share/jenkins/ref/plugins/ |
| 3 | JCasC 설정 적용 | /var/jenkins_home/casc_configs/ 내 yaml 전체 읽기 |
| 4 | Probe 통과 | HTTP GET /login → 200 OK 확인 후 트래픽 허용 |
플러그인 의존성 충돌은 2번 단계에서 Jenkins가 죽습니다. kubectl logs <pod> -c jenkins에서 오류를 확인하세요.
6. JCasC(Jenkins Configuration as Code) 적용 구조
6.1 ConfigMap 생성 구조
values.yaml의 controller.JCasC.configScripts 항목들은 각각 독립적인 ConfigMap으로 생성됩니다.
6.2 Pod 내 마운트 경로
6.3 configAutoReload 사이드카
controller.sidecars.configAutoReload.enabled: true로 설정하면 사이드카 컨테이너가 추가됩니다.
사이드카는 ConfigMap 변경을 감지하면 yaml을 casc_configs/에 복사하고 리로드를 요청합니다. Jenkins 재기동 없이 설정 변경이 반영됩니다.
7. Secret 및 Credential 주입 구조
7.1 Admin 계정 Secret
7.2 추가 Secret 마운트
Pod의 /run/secrets/ 경로에 마운트되며, JCasC에서 ${github-token} 형식으로 참조할 수 있습니다.
8. 기동 실패 주요 원인
| 원인 | 증상 | 확인 방법 |
|---|---|---|
| 플러그인 의존성 충돌 | initContainer CrashLoopBackOff | kubectl 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 확인 |
주요 확인 명령어
9. 전체 기동 흐름 요약
지금까지 살펴본 내용을 단계별로 정리하면 다음과 같습니다.
Phase 1 — 리소스 준비
| 단계 | 내용 |
|---|---|
| Helm install/upgrade | Chart 렌더링 후 K8s 리소스 apply |
| ConfigMap 생성/업데이트 | apply_config.sh, plugins.txt, JCasC yaml |
| Secret 생성/업데이트 | admin 계정, 추가 credentials |
| PVC 바인딩 | jenkins_home 영구 볼륨 확보 |
Phase 2 — initContainer (copy-default-config)
| 순서 | 작업 | 관련 볼륨 |
|---|---|---|
| 1 | Setup Wizard 비활성화 파일 생성 | jenkins-home (PVC) |
| 2 | ConfigMap → PVC로 plugins.txt 복사 | jenkins-config, jenkins-home |
| 3 | jenkins-plugin-cli로 플러그인 다운로드 | Update Center 네트워크 통신 |
| 4 | 설치된 플러그인을 plugin-dir에 복사 | plugin-dir (emptyDir) |
Phase 3 — 메인 컨테이너 (jenkins)
| 순서 | 작업 | 관련 볼륨 |
|---|---|---|
| 5 | JVM 기동 (JAVA_OPTS, JENKINS_OPTS 적용) | — |
| 6 | plugin-dir에서 플러그인 로드 | plugin-dir (emptyDir) |
| 7 | casc_configs/ 읽어 JCasC 설정 적용 | sc-config-volume (emptyDir) |
| 8 | Liveness / Readiness Probe 통과 | — |
Phase 4 — 사이드카 (config-reload, 선택)
| 순서 | 작업 |
|---|---|
| 9 | Kubernetes Watch API로 ConfigMap 변경 감시 시작 |
| 10 | 변경 감지 시 yaml을 casc_configs/에 복사 후 Jenkins에 리로드 요청 |