Helm Deployment and Rollback Scenarios
Hooks Live Outside the Release
한국어 원문으로 표시합니다.
한 줄 요약
훅 리소스는 릴리스가 관리하지 않는다. 그래서 롤백해도 사라지지 않고, 삭제 정책을 안 걸면 클러스터에 쌓인다.
왜 이게 필요했나
DB 마이그레이션을 배포에 넣는 가장 흔한 방법이 pre-upgrade 훅 Job 이다. 그런데 이 Job 은 릴리스의 일부가 아니다. Helm 은 훅을 적용하고, 완료를 기다리고, 그다음 본 매니페스트를 적용한다. 훅으로 만든 Job 오브젝트는 릴리스 시크릿에 들어가지 않는다.
결과는 둘이다.
- 롤백해도 훅이 한 일은 남는다. 마이그레이션이 바꾼 스키마는 그대로다.
- Job 오브젝트가 계속 쌓인다.
helm.sh/hook-delete-policy를 안 걸면 배포할 때마다 하나씩 늘어난다.
어떻게 동작하나
metadata:
annotations:
"helm.sh/hook": pre-upgrade,pre-install
"helm.sh/hook-weight": "-5" # 작을수록 먼저
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
hook-delete-policy 값 세 가지의 뜻이 다르다.
| 값 | 언제 지우나 |
|---|---|
before-hook-creation |
다음 배포에서 같은 훅을 만들기 직전 (실패한 Job 을 남겨 조사 가능) |
hook-succeeded |
성공하면 즉시 |
hook-failed |
실패하면 즉시 (로그가 사라진다 — 대개 나쁜 선택) |
실무 기본형은 before-hook-creation,hook-succeeded 다. 성공한 건 치우고, 실패한 건 다음 배포까지 남겨 로그를 볼 수 있게 한다.
흔한 착각
훅이 실패하면 롤백된다는 착각. pre-upgrade 훅이 실패하면 업그레이드가 중단되고 릴리스는 failed 로 남는다. --atomic 이 있으면 롤백이 걸리지만, 이미 실행된 마이그레이션은 되돌아오지 않는다.
훅으로 하지 말아야 할 것
훅은 편리해서 온갖 것을 넣게 됩니다. 넣지 말아야 할 것이 셋 있습니다.
오래 걸리는 데이터 이관. 수백만 행을 옮기는 작업은 배포를 몇십 분 붙잡습니다.
그동안 릴리스는 pending-upgrade 로 잠겨 다른 배포가 막힙니다. 이런 일은 배포와
분리해 별도 Job 으로 돌리고, 애플리케이션이 두 스키마를 모두 읽을 수 있게 만듭니다.
외부 시스템에 알리는 일. post-install 훅으로 슬랙에 알리는 것은 흔한데,
훅이 실패하면 배포 전체가 실패로 표시됩니다. 알림이 안 갔다고 배포를 실패로
볼 이유는 없습니다. 이런 것은 CI 쪽에 둡니다.
테스트. helm test 는 별도 명령이고 helm.sh/hook: test 를 씁니다. 배포
과정에 섞어 넣으면 테스트가 흔들릴 때마다 배포가 막힙니다.
훅이 걸리면 배포가 멈춰 선다
훅 Job 이 끝나지 않으면 helm upgrade 는 계속 기다립니다. --timeout 기본값은
5분이고, 그 뒤에는 실패로 처리합니다. 그런데 타임아웃이 나도 Job 은 클러스터에서
계속 돕니다. Helm 은 기다리기를 그만둘 뿐 Job 을 죽이지 않습니다.
여기서 위험한 상황이 생깁니다. 실패로 판단하고 다시 배포하면, 앞의 마이그레이션이 아직 돌고 있는데 두 번째가 시작됩니다. 같은 테이블에 ALTER 를 두 개가 동시에 걸면 잠금 대기로 서비스 전체가 멈출 수 있습니다.
세 가지로 막습니다.
- Job 에
activeDeadlineSeconds를 건다. Helm 의 타임아웃보다 짧게 두면 Job 이 스스로 먼저 끝납니다. backoffLimit: 0. 마이그레이션은 재시도가 안전하지 않은 경우가 많습니다.- 마이그레이션 도구의 잠금을 쓴다. Flyway·Liquibase·Alembic 모두 잠금 테이블이
있어 두 번째 실행이 기다립니다. 직접 만든 스크립트라면
pg_advisory_lock을 씁니다.
spec:
activeDeadlineSeconds: 240 # helm --timeout 5m 보다 짧게
backoffLimit: 0
template:
spec:
restartPolicy: Never
훅과 일반 매니페스트의 순서
한 차트 안에 훅과 일반 리소스가 섞여 있으면 순서가 헷갈립니다. 실제 순서는 이렇습니다.
pre-install 훅 → 일반 매니페스트 적용 → post-install 훅
(weight 순) (Helm 의 종류별 순서) (weight 순)
여기서 자주 밟는 함정. pre-install 훅은 Secret·ConfigMap 이 아직 없을 때
돕니다. 훅 Job 이 릴리스의 Secret 을 참조하면 "그런 secret 이 없다" 로 실패합니다.
훅이 쓸 값은 훅 자신이 만들거나(같은 훅 그룹에 weight 를 낮게 준 Secret), 릴리스
밖에서 미리 만들어 두어야 합니다.
hook-weight 는 문자열이지만 숫자로 정렬 됩니다. "10" 과 "9" 를 주면
9 가 먼저입니다. 음수를 쓰는 관례가 여기서 나왔습니다 — -5 를 기본으로 두고
더 먼저 돌아야 하는 것에 -10 을 줍니다.
실무에서 진짜 중요한 것
되돌릴 수 없는 마이그레이션은 배포와 분리한다. 컬럼을 지우는 변경은 세 번에 나눠 배포한다 — ① 새 컬럼 추가(옛 코드도 동작) ② 새 코드 배포 ③ 옛 컬럼 삭제. 이렇게 하면 어느 단계에서 롤백해도 앞뒤 버전이 모두 견딘다. 이걸 확장-수축(expand-contract) 패턴이라 부른다.