IL-0460 GoCardless · 2020-10-25kubernetesgcp
Vault 가 만료된 TLS 인증서를 계속 써서 Vault 에 기대는 서비스 파드가 연쇄로 죽고 GoCardless 서비스 전체가 22분간 멈췄다
GoCardless 는 거의 모든 운영 서비스가 기대는 비밀 관리 엔진으로 HashiCorp Vault 를 쿠버네티스 클러스터 안에서 돌렸다. Vault 의 API 와 UI 를 지키는 TLS 인증서는 cert-manager 가 Let's Encrypt 에서 받아 관리했다. cert-manager 가 인증서를 갱신하면 실행 중인 컨테이너에 마운트된 인증서 데이터는 자동으로 바뀌지만, Vault 는 이 변경을 스스로 알아채지 못한다. Vault 를 재시작하거나 SIGHUP 신호를 보내 다시 읽게 해야 새 인증서를 쓴다. GoCardless 에는 이 자동화가 없었고, Vault 는 갱신 뒤에도 옛 인증서를 계속 쓰다가 2020-10-25 07:22 에 그 인증서가 만료됐다.
애플리케이션은 theatre-envconsul 이라는 자체 프로그램으로 Vault 에 인증한 뒤 공식 envconsul 을 실행해 비밀을 넣었다. GoCardless 는 envconsul 에 -once 플래그를 주어 메인 루프를 한 번만 돌게 했다. 그래도 envconsul 은 이 모드에서 Vault 리스를 주기적으로 갱신했고, 갱신이 실패하면 오류를 내고 종료하면서 관리하던 프로세스도 함께 끝냈다. GoCardless 는 다른 문제 때문에 envconsul 의 Vault 재시도를 꺼 두었다. 그래서 운영 트래픽을 받던 정상 파드가 리스가 만료되는 차례대로 하나씩 죽었고, 07:31 에 GoCardless API 요청이 전부 실패했다.
07:42 에 대응자는 인증서가 만료됐다고 보고 cert-manager 로 인증서를 강제로 다시 만들었고, 07:48 에 새 인증서가 나오자 Vault 클러스터를 손으로 재시작했다. Vault 가 다시 뜬 뒤에는 쿠버네티스가 새 컨테이너를 띄워 거의 손대지 않고 복구됐다. 중요한 워크로드의 파드 몇 개는 5분까지 늘어난 지수 백오프를 건너뛰려고 직접 삭제했다. 07:52 에 백엔드 서비스가 정상으로 돌아왔고 장애는 07:54 에 끝났다.
- 심각도
- –
- 영향 범위
- Full outage of the GoCardless API, dashboard, payment pages and operations through partner integrations for 22 minutes
- 영향 사용자
- –
- 지역
- –
- 감지까지
- 10m
- 복구까지
- 32m
증상
07:32 에 자동 모니터링이 장애를 잡아 온콜 엔지니어를 호출했다. 07:31 부터 GoCardless API 로 가는 요청이 전부 실패했고, 대시보드, 결제 페이지, 파트너 연동을 거치는 작업도 쓸 수 없었다. 07:32 부터 07:54 GMT 까지 22분 동안 서비스 전체가 멈췄다.
07:22 에 Vault 의 인증서가 만료됐고, 07:25 부터 컨테이너가 크래시 루프에 들어갔다. envconsul 이 Vault 리스 갱신에 실패하자 오류를 내고 종료하면서 자신이 관리하던 정상 프로세스까지 함께 끝냈다. 다시 시작한 컨테이너는 Vault 에 새로 연결하지 못했다.
운영 트래픽을 받던 정상 파드들이 Vault 리스가 만료되는 차례대로 하나씩 죽었다. 결국 요청을 받을 정상 백엔드가 하나도 남지 않았다.
대응자는 TLS 인증서 자체가 만료됐다고 보고 cert-manager 로 인증서를 강제로 다시 만들었다. 실제로 인증서는 이미 갱신돼 있었고 Vault 가 새 인증서를 다시 읽지 않았을 뿐이었다. 인증서를 다시 만들려면 DNS 검증을 거쳐야 했고, 레코드가 전파되는 데 몇 분이 걸렸다.
원인
원인 인증서·자격 증명 만료
- 촉발
- cert-manager 가 Vault 의 TLS 인증서를 갱신했지만 Vault 는 새 인증서를 다시 읽지 않고 옛 인증서를 계속 썼다. 07:22 에 그 옛 인증서가 만료됐다.
감지
07:32 에 자동 모니터링이 장애를 잡아 온콜 엔지니어를 호출했다.
조치
- 07:42 에 인증서가 만료됐다고 보고 cert-manager 가 인증서를 다시 만들도록 손으로 강제했다. 실제로 인증서는 이미 갱신돼 있어 이 단계는 필요 없었다.
- 07:48 에 새 인증서가 만들어지자 Vault 가 그 인증서를 읽도록 Vault 클러스터를 손으로 재시작했다.
- 중요한 워크로드의 파드 몇 개를 직접 삭제해 5분까지 늘어난 지수 백오프를 건너뛰었고, 서비스가 몇 분 일찍 완전히 돌아왔다.
후속 조치
- L7 로드 밸런서에만 있던 TLS 인증서 만료 모니터링을 Vault 가 쓰는 단순 TCP 로드 밸런서까지 넓히고, Blackbox exporter 로 그 인증서도 주기적으로 검사한다.
- Vault 서버가 바뀐 인증서를 스스로 다시 읽는 기능이 없으므로, Reloader 컨트롤러를 배포해 인증서 데이터가 바뀌면 Vault 를 롤링 업데이트한다.
- Vault 로 가는 네트워크 분할 같은 다른 장애가 실행 중인 서비스에 번지지 않도록, theatre-envconsul 을 바꿔 envconsul 이 자식 프로세스를 관리하지 않게 하고 컨테이너 시작 때 비밀 환경 변수를 넣는 일만 하게 한다.
교훈
Vault 를 쓸 수 없게 된 것보다 Vault 에 기대는 모든 애플리케이션이 거의 곧바로 실패한 것이 훨씬 나빴다. 이런 장애 상황을 충분히 시험하지 않았고, 단일 장애점과 피해 범위를 줄여야 한다는 것을 장애를 겪고서야 다시 확인했다. 대응 중에는 인증서 자체가 만료됐다고 여겨 인증서를 다시 만들게 했다. 실제로는 인증서가 이미 갱신돼 있었고 Vault 가 다시 읽지 않았을 뿐이었다. 그래서 DNS 검증 때문에 몇 분 걸리는 이 단계는 건너뛸 수 있었다.
출처
관련 사례
–
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0460
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0460/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.