IT/Kubernets

cert-manager 무한 재발급 루프에 걸린 새벽 이야기

gfrog 2026. 7. 19. 03:13

cert-manager 무한 재발급 루프에 걸린 새벽 이야기

새벽 3시. 폰이 울린다. 프로덕션 클러스터의 API 게이트웨이 인증서가 만료 2시간 전이라는 알림. 이상하다. cert-manager가 알아서 갱신했어야 정상인데. 눈을 비비면서 노트북을 열었다.

처음엔 그냥 일시적인 문제인 줄 알았다

kubectl get certificates -A 를 쳐봤다. Ready=False. kubectl describe로 이벤트를 보니 CertificateRequest가 계속 생성되고, 몇 초 뒤에 다시 실패하고, 또 다시 만들고... 초당 한 개꼴로 새 CR이 찍히고 있었다.

$ kubectl get certificaterequests -n gateway | wc -l
     3847

3847개. 헛웃음이 나왔다. 이 시점에서 팀 슬랙에 상황 공유하고 일단 응급조치부터 했다. cert-manager-controller를 스케일 0으로 내리고 만료된 인증서를 수동으로 발급받아 시크릿에 밀어넣었다. 게이트웨이는 살렸다. 근데 왜 이런 일이 벌어졌는지 봐야 했다.

로그를 파보니

cert-manager-controller 로그를 보니 같은 메시지가 반복되고 있었다.

certificate is already expired, requesting new one
issuer returned certificate with notAfter=... which is in the past

우리 팀은 자체 사설 CA를 쓴다. 그 사설 CA가 어떤 이유에서인지 이미 만료된 시각의 인증서를 계속 뱉어냈다. cert-manager는 "어? 만료됐네? 다시 요청해야지"라고 판단하고 또 요청. CA는 또 만료된 걸 발급. 무한루프.

이건 cert-manager v1.16까지 있던 알려진 버그였다. GitHub 이슈에서 확인해보니 최근 릴리스에서 고쳐졌다. 만료된 인증서를 받으면 무한 재요청 대신 backoff를 걸도록 바뀌었다.

우리 클러스터 버전은 v1.15.3. 사실 업그레이드를 미뤄왔던 게 맞다. "돌아가는 거 왜 굳이" 마인드였는데, 이렇게 물렸다.

CA 쪽 원인

CA는 왜 만료된 인증서를 뱉었을까. 확인해보니 CA 노드의 시스템 시간이 NTP 동기화 실패로 3시간쯤 앞서 있었다. 발급 시점 기준으로는 미래인데, cert-manager가 받아서 검증할 때는 이미 과거. 어이가 없었지만 시간 동기화는 우선 chrony 재시작으로 잡았다.

근본 원인은 두 개였다.

첫째, CA 쪽 NTP 감시가 없었다. 이건 우리 쪽 관측성 구멍이다. 노드 clock offset 메트릭을 Prometheus로 긁고는 있는데, 알림이 안 걸려 있었다.

둘째, cert-manager가 만료 인증서에 대한 방어가 없었다. 이건 프로젝트 자체 버그.

그래서 다음 날 무엇을 했나

일단 cert-manager를 v1.19로 올렸다. 릴리스 노트에 "no longer loops when an issuer returns an already-expired certificate" 라고 명확히 적혀 있어서 반가웠다. 만약 지금 v1.15 이하를 쓰고 있다면, 업그레이드 리스트 상위에 올려두는 걸 권한다.

그리고 알림도 추가했다. certmanager_certificate_expiration_timestamp_seconds 를 이용해서 만료 7일 전에 걸리는 알람이 있긴 했는데, 그것과 별개로 "CertificateRequest가 5분 안에 5개 이상 생성되면 알림"이라는 룰을 넣었다. 무한루프를 조기에 잡기 위한 서킷브레이커 역할.

- alert: CertManagerRequestStorm
  expr: |
    sum by (namespace) (
      rate(certmanager_certificate_request_events_total[5m])
    ) > 1
  for: 5m
  annotations:
    summary: "cert-manager가 CR을 초당 1개 이상 생성 중"

CA 노드 clock offset도 알람 걸었다.

- alert: NodeClockDriftHigh
  expr: abs(node_timex_offset_seconds) > 60
  for: 10m

남긴 것들

솔직히 이번 건은 여러 실패가 겹친 케이스다. 업그레이드를 미룬 것, NTP 상태를 안 본 것, 무한루프를 감지할 관측성이 없었던 것. 어느 하나라도 있었으면 새벽 3시 알림은 안 왔다.

교훈이라기보다는 반성문에 가깝다. "돌아가고 있으니까 괜찮다"는 상태와 "잘 돌아가고 있다"는 상태는 다르다. 인증서처럼 시간에 종속된 리소스는 특히 그렇다. 다음 온콜 순번에는 다른 팀원이 편하게 잘 수 있도록, 이번 주 안에 클럭 관련 알람 다 훑고 문서화하려고 한다.

혹시 다른 팀은 cert-manager 만료 관련 이슈 어떻게 잡는지 궁금하다. 댓글 주시면 감사.

추가 리소스