[배포 9시간 → 10분 #4] 결과와 회고 — 무엇이 바뀌었고 무엇이 남았나
[배포 9시간 → 10분 #4] 결과와 회고 — 무엇이 바뀌었고 무엇이 남았나
- 왜 배포가 9시간이나 걸렸나 — 문제와 병목 분석
- Jenkins on Kubernetes 재구축 — 죽지 않는 실행 기반 만들기
- 파이프라인 병렬화 — 2단 parallel과 “1회 선빌드” 캐시 전략
- 결과와 회고 — 무엇이 바뀌었고 무엇이 남았나 (이번 글)
1. 결과
전체 배포 9시간 → 약 10분. 이 숫자를 분해해 보면 개선의 구조가 보인다. 직렬 → 2단 병렬 전환이 46개 교과목의 대기 시간을 없앴고, “1회 선빌드 + 커밋 해시 재사용”이 46중복 빌드를 1회로 줄였다. 둘 중 하나만 했다면 이만큼 줄지 않았다. 병렬화만 했다면 캐시 미스 시 46개 빌드가 동시에 몰려 에이전트 자원을 잠식했을 것이고, 캐시만 했다면 배포 자체의 직렬 대기는 그대로였을 것이다. 병목이 여러 겹이면 해법도 여러 겹이어야 한다는 것이 1편의 분석이 실증된 지점이다.
Jenkins OOM 전면 다운 소멸. 월 5~6회 발생하던 “죽으면 재세팅”이 사라졌다. 빌드 부하가 일회용 에이전트 파드로 격리되고(numExecutors: 0, request=limit), 컨트롤러 상태가 PVC로 영속화된 결과다. 어떤 잡이 터져도 죽는 것은 그 파드 하나이고, 컨트롤러가 재시작돼도 상태는 볼륨에 남는다.
배포 후 수동 HPA 조정 제거. 처음엔 네임스페이스별 조정값을 담은 TSV 기준표를 읽어 일괄 kubectl patch하는 스크립트로 반자동화했고(3편 참고), 최종적으로 HPA·LimitRange가 배포 헬름 차트에 편입되면서 배포가 곧 완결된 상태 선언이 됐다. 사람이 클러스터에 들어가 파드 수를 만지는 절차와, 그 절차에 딸려 있던 “부하 시간대에 파드가 1개로 꺼져 있는 구간”이라는 리스크가 함께 사라졌다.
새벽 배포의 성격 변화. 배포가 10분으로 줄면서 “밤새 대기”가 “실행하고 확인”으로 바뀌었다. 피드백이 당일에 돌게 되면서 배포 요청이 새벽으로 밀리는 악순환의 고리 하나가 끊겼다.
그리고 정량 지표 밖의 변화가 있다. 개발팀·운영팀이 인프라팀의 배포를 신뢰하기 시작했다. 배포가 느리고 자주 죽던 시절에는 팀 간 커뮤니케이션에도 그 불신이 묻어 있었는데, 결과가 안정되자 관계 자체가 달라졌다. 이전처럼 우리 팀을 신뢰하지 않는 언행은 사라졌고, 이후로는 배포 관련 협의가 눈에 띄게 수월해졌다. 인프라 개선의 효과는 시스템 지표보다 팀 간 신뢰에서 먼저 체감된다는 것을 이때 배웠다.
2. 솔직한 한계
성과만 적으면 회고가 아니다. 남겨둘 것들을 남긴다.
스크립트 복잡성은 해소하지 못했다. 기존 if-else 덩어리의 복잡성이 병렬 구조 안으로 이사했을 뿐이다. 환경 분기(stg05/stg06/prod05/prod08/...)가 트리거와 서비스 잡 곳곳에 흩어져 있어, 새 환경이나 새 교과목 클러스터가 추가될 때마다 여러 파일을 고쳐야 한다. 환경-교과목 매핑을 스크립트 바깥의 설정(예: 설정 레포의 단일 매핑 파일)으로 빼는 것이 다음 리팩터링 대상이다. 같은 맥락에서 스크립트에 하드코딩된 토큰류는 Jenkins credentials(withCredentials)로 전량 이관해야 한다 — 스크립트는 Git에 남고, Git에 남은 시크릿은 언젠가 반드시 샌다.
커스텀 이미지 기반 구축의 대가. 에이전트·컨트롤러가 커스텀 이미지와 마운트 구조에 묶이면서, Jenkins 버전 업그레이드 한 번에 이미지 리빌드와 설정 재검증이 줄줄이 따라오는 번거로움이 생겼다. 이것이 Jenkins 자체의 한계인지, JCasC(Configuration as Code) 도입 등으로 완화 가능한 우리 구성의 한계인지는 계속 검토 중인 주제다. 속도를 얻는 대신 구축 복잡도를 얹은 트레이드오프였다는 점은 인정해야 한다.
배포 요청 구조는 그대로다. 개발 환경은 GitLab 웹훅 → Jenkins 자동 트리거로 개발팀 손에서 돌아가지만, 스테이징과 운영은 여전히 인프라팀에 요청해야 한다. 역설적으로 배포가 10분이 되니 스테이징 배포 요청 빈도가 늘었고, “배포 시간”이라는 부하가 “요청 처리”라는 부하로 형태를 바꿔 남았다. 배포 시간 단축은 이 문제의 해결이 아니라 전제 조건이었던 셈이다.
이 마지막 한계를 풀기 위해 이후 두 가지 작업을 별도로 진행했다. 스테이징 배포 권한을 이력 추적과 함께 개발팀에게 돌려주는 배포 API 서버 구축, 그리고 Jira 이슈 기반으로 배포가 트리거되는 자동 배포 연동이다. 이 시리즈의 범위(배포 시간 단축)를 넘는 주제라, 각각 별도의 글로 정리해 올릴 예정이다.
3. 회고 — 두 가지 교훈
초기 설계가 서비스의 규모와 특성을 담지 못하면, 그 비용은 운영 단계에서 이자까지 붙어 돌아온다. 저자별 교과목 46개 × 네임스페이스라는 곱셈 구조는 서비스 기획 단계부터 예정되어 있던 특성이다. 그 특성을 고려하지 않은 직렬 스크립트와 단일 인스턴스 Jenkins는 처음엔 굴러갔지만, 규모가 차오르자 9시간짜리 배포와 월 5~6회의 전면 장애로 청구서를 내밀었다. 인프라 설계는 지금의 규모가 아니라 구조가 만들어낼 규모를 봐야 한다.
성능 문제 앞에서 “더 큰 서버”가 아니라 병목의 구조 자체를 재설계하는 쪽이 훨씬 크게, 지속 가능하게 문제를 해결한다. 스케일 업은 세 겹의 병목(직렬+중복 빌드, 단일 인스턴스, 수동 작업) 중 어느 것도 건드리지 못했을 것이다. 실행 모델을 바꾸고(에이전트 파드), 산출물 공유 구조를 심고(레지스트리·공유 PVC), 수동 작업을 선언 안으로 편입시키자(HPA in Helm) 9시간이 10분이 됐다. 54배의 개선은 하드웨어에서 나온 것이 아니라 구조에서 나왔다.
배포는 여전히 매주 돌아가고 있고, 이 구조도 완성형이 아니라 계속 손보는 중이다. 다만 한 가지는 분명해졌다 — 이제 새벽 2시의 배포는 밤샘이 아니라 10분짜리 일이 됐다는 것.