Incident Library
적용된 필터 구성 요소영구 볼륨과 스테이트풀셋기여 요인모니터링 사각지대도메인쿠버네티스모두 풀기검색어 없이 필터만 적용한 목록입니다. 최근 사례부터입니다.
추천 필터 검색하면 검색어에서 찾은 분류 값이 여기에 나옵니다.

그래프

  • 원인
  • 구성 요소
  • 증상
  • 기여 요인
  • 사례

사례5건

5건 중 1~5번째

  1. 증상

    2025-04-14 09:33 UTC 쯤 Magic Pages 의 컨트롤 플레인 노드 세 대 중 한 대가 갑자기 오프라인이 됐다. 노드를 살펴보니 메모리가 바닥나 있었다. 운영자는 평소 절차대로 고장 난 노드를 빼고 메모리가 조금 더 큰 서버로 바꾸려 했다.

    촉발

    컨트롤 플레인 노드 한 대가 메모리 부족으로 오프라인이 됐다. 이 노드를 교체하는 배포가 실패해 다시 시도하는 동안, 클러스터 내부 구성 요소들이 Hetzner API 를 계속 호출해 시간당 3600건 한도를 소진했다.

    원인
    구성 요소
    기여 요인
    감지

    모니터링 경보가 쏟아지면서 고객 웹사이트에 접속할 수 없다는 것을 알았다. API 호출 한도가 원인이라는 것은 30분쯤 조사한 뒤에야 찾았다. hetzner-k3s 가 Hetzner API 오류를 감춰서 원인 파악이 늦어졌다.

    조치
    • API 호출을 확실히 멈추려고 서버 전원을 내려 쿠버네티스 클러스터 전체를 껐다.
    • Hetzner API 의 ratelimit-remaining 값이 다시 오르는 것을 지켜보면서, 복구 중에 다시 한도에 걸리지 않도록 여유분이 쌓일 때까지 기다렸다.
    • API 호출 여유가 생긴 뒤 컨트롤 플레인 서버를 다시 켰다.
    • 워커 노드를 올린 뒤 Longhorn 의 핵심 구성 요소를 손으로 띄워 스토리지 볼륨이 스스로 복구되게 했다.
    • 17:00 UTC 쯤 사이트의 95% 가 돌아온 뒤, 남은 사이트를 손으로 다시 시작했다.
    후속 조치
    • 클러스터 노드에서 나가는 Hetzner API 호출량을 따로 모니터링해서 사용량이 높아지면 미리 알 수 있게 한다.
    • Hetzner 와 협의해 API 호출 한도를 늘린다.
    • API 하나에 기대는 구조를 줄일 방법을 찾고, hetzner-k3s 로는 그렇게 하기 어렵다면 다른 도구를 검토한다.
    • 클러스터 전체가 꺼진 뒤 Longhorn 스토리지를 복구하는 절차를 더 탄탄하게 문서로 만들고 시험한다.
    교훈

    Hetzner 의 기본 API 한도인 시간당 3600건은 장애나 복구 상황에서 클러스터가 쓰는 양에 비해 부족했다. 운영자는 배포에 쓰는 API 사용량만 봤고, 실행 중인 클러스터 내부에서 나가는 호출은 지켜보지 않았다. 네트워킹 같은 핵심 기능이 클라우드 API 하나에 기대고 있으면, 그 API 가 막히는 순간 클러스터 전체가 약해진다. hetzner-k3s 의 추상화는 API 오류를 감춰 문제 해결을 늦췄다. 클러스터가 비정상 종료된 뒤 Longhorn 을 살리는 데 손이 많이 가서 장애가 길어졌다.

  2. 증상

    2025-01-15 09:55 CET 무렵부터 11:36 CET까지 데이터베이스 장애로 Magic Pages의 고객 웹사이트가 영향을 받았다. 노드 세 대로 된 MySQL 클러스터에서 내려간 노드를 다시 붙이려 하자 데이터베이스 서버가 메타데이터 정보가 없다는 모호한 오류를 냈다.

    촉발

    그날 앞서 MySQL 클러스터의 세 노드 중 하나가 내려갔고, 창업자가 그 노드를 클러스터에 다시 붙이려 했다.

    원인
    구성 요소
    기여 요인
    조치
    • PVC 프로비저닝 문제를 풀려고 스토리지 서버를 재시작했다. 재시작은 몇 초 만에 끝났다.
    • 스토리지가 돌아온 뒤 클러스터 메타데이터를 다시 만들 수 있도록 MySQL 노드들이 복원되고 서로 동기화되기를 약 45분 기다렸다.
    • 깨진 클러스터 메타데이터를 정리했다.
    • 데이터베이스 노드로 트래픽을 보내는 MySQL Router를 다시 배포했다.
    후속 조치
    • 스토리지 시스템의 상태 모니터링을 강화한다.
    • MySQL Router의 장애 조치 절차를 개선한다.
    • 상태 페이지가 믿을 만한 정보를 보이도록 문제를 조사하고 고친다.
    교훈

    고가용성 구성 덕분에 데이터는 잃지 않았고 첫 노드 장애도 클러스터가 문제없이 넘겼다. 하지만 중복 구성인데도 클러스터 메타데이터가 단일 장애 지점이었다. PVC 상태를 더 잘 모니터링했다면 비슷한 문제를 막을 수 있었고, 45분 걸린 복구는 더 줄일 여지가 있다. 상태 페이지는 고객 사이트 실패를 감지하도록 설정했는데도 이 장애를 바로 알아채지 못했다.

  3. 증상

    PR 하나를 머지한 직후 InfluxDB Cloud 운영 환경 하나가 통째로 사라졌다. 쓰기와 읽기를 비롯한 모든 API 엔드포인트가 404 오류를 냈다.

    촉발

    팀원 한 명이 설정 파일에 새 객체 여러 개를 더하는 PR 을 올렸다. 그중 새 ArgoCD Application 과 AppProject 의 이름이 자동화 오류로 app02-cluster01 이 아니라 기존 것과 같은 app01-cluster01 로 정해졌다. 코드 리뷰에서도 app01 과 app02 의 차이를 알아채지 못해 PR 이 그대로 머지됐다.

    원인
    구성 요소
    기여 요인
    감지

    모니터링과 경보 시스템은 이 장애를 바로 잡아내지 못했다. 그래서 팀은 계획을 세워 복구하기 전에 먼저 변경을 되돌리려 했다. 팀이 장애를 무엇으로 알게 됐는지는 글에 나오지 않는다.

    조치
    • 머지된 PR 을 되돌리기 시작했다가 곧바로 멈췄다.
    • etcd 메타데이터 클러스터를 복원했다.
    • 지워진 쿠버네티스 객체를 다시 만들어 남아 있던 PersistentVolume 을 다시 쓸 수 있게 하고, StatefulSet 을 다시 만들었다.
    • Zookeeper 볼륨을 Velero 로 매시간 받아 둔 백업에서 복원했다.
    • 헬스 체크와 readiness 체크를 끈 채 Kafka StatefulSet 을 파드 하나씩 다시 만들었다.
    • 쓰기 선행 로그 역할을 하는 Kafka 로 다시 쓰기를 받고, 쓰기를 처리하는 컴포넌트의 레플리카 수와 메모리 request 와 limit 을 늘렸다.
    • 스토리지 파드를 다시 만들었다. 파드는 오브젝트 스토리지에서 데이터를 내려받아 인덱싱한 뒤 쓰기 선행 로그에 밀려 있던 데이터를 읽어 들였다.
    • ArgoCD, 쿼리, 대시보드, 예약 작업을 다시 켜고, 레플리카 수를 원래대로 돌린 뒤 최종 QA 점검을 했다.
    후속 조치
    • 상태를 되살리는 절차를 담은 런북을 만들었다. PVC 같은 쿠버네티스 객체는 지워졌지만 디스크와 볼륨에 데이터는 남아 있을 때 할 일을 자세히 적었다.
    • 모든 환경의 모든 볼륨이 PVC 객체가 지워져도 데이터를 유지하도록 설정했다.
    • 외부에 드러나는 장애를 다루는 절차를 개선했다.
    • 생성된 YAML 을 객체 하나당 파일 하나로 저장하도록 바꿨다. 예를 들어 etcd Service 는 v1.Service-(namespace).etcd.yaml 파일이 된다. 이제 이번과 같은 PR 은 새 객체를 더하는 변경이 아니라 기존 객체를 덮어쓰는 변경으로 보인다.
    • YAML 생성 도구가 중복 객체를 찾아 리뷰를 요청하기 전에 경고하도록 고쳤다. 파일 이름만 보지 않고, apiVersion 이 networking.k8s.io/v1beta1 과 networking.k8s.io/v1 처럼 달라도 네임스페이스와 이름이 같으면 같은 객체로 판단한다.
    • 모든 상태 저장 리소스와 ArgoCD 가 관리하는 Namespace 객체에 Prune=false 어노테이션을 달아, 설정이 잘못돼도 ArgoCD 가 지우지 않게 했다.
    • ArgoCD Application 에 FailOnSharedResource=true 옵션을 넣었다. 다른 Application 이 관리하거나 관리했던 객체를 바꾸려 하면 ArgoCD 는 변경을 적용하지 않고 동기화를 실패로 멈춘다.
    교훈

    무엇보다 모니터링과 경보가 장애를 바로 잡아내지 못했다. 그 탓에 팀은 계획된 복구 대신 롤백부터 시도했다. InfluxDB Cloud 인스턴스 일부나 전체를 잃었을 때 쓸 런북도 없었다. 상태 저장 애플리케이션은 변경을 되돌린다고 원래대로 돌아오지 않고, 데이터 없는 새 인스턴스가 생길 뿐이다. 이런 PR 은 시스템이 머지를 막았어야 했다. 볼륨을 Retain 으로 둔 데다 Telegraf 처럼 쓰기를 재시도하는 클라이언트가 있어서 데이터를 하나도 잃지 않았다.

  4. 증상

    2021년 6월 2일 18:02 UTC 부터 Jenkins 인프라의 AKS 클러스터에서 파드가 볼륨을 마운트하지 못한다는 오류와, 스케일 업으로 늘어난 노드가 CNI 에서 네트워크 가상 인터페이스를 받지 못한다는 오류가 나타나기 시작했다.

    촉발

    2021년 6월 2일 17:53 UTC 에 AKS 노드가 Azure API 인증에 쓰는 Azure 서비스 주체 자격 증명이 만료됐다.

    원인
    구성 요소
    기여 요인
    감지

    LTS 2.289.1 릴리스 문제의 근본 원인을 조사하던 엔지니어가 AKS 콘솔 화면에서 오류를 확인했다.

    조치
    • 인프라 팀이 서비스 주체 자격 증명을 갱신했다. 그러자 AKS 가 모든 노드를 재시작하는 작업을 자동으로 시작했고, 이 작업은 멈출 수 없었다.
    후속 조치
    • 인증서 만료 점검처럼, 서비스 주체 만료 일주일 전에 PagerDuty 알림을 울리는 자동 점검을 더한다.
    • 자격 증명 재설정 같은 수동 작업 일정을 적어 둘 팀 공용 캘린더를 마련한다.
    • 서비스 주체 대신 자동으로 갱신되는 Azure managed identity 를 쓰는 방안을 검토한다.
  5. 증상

    9am UTC 조금 넘어 gitlab.freedesktop.org 의 디스크 공간이 위급하다는 것이 분명해졌다. 디스크가 모자라 일부 API 요청이 실패했고, 매일 도는 백업 cron 작업도 같은 이유로 실패하기 시작했다. 관리자는 사람들이 IRC 에서 장애를 말하고 나서야 문제를 알았다.

    촉발

    최근 Mesa 포크가 많이 만들어지면서 GitLab 클러스터의 디스크가 거의 찼다. 관리자가 디스크 볼륨을 늘리려고 몇 시간 전에 알린 작업 시간에 GitLab 을 내리고 저장소 업그레이드를 시작했다.

    원인
    구성 요소
    기여 요인
    감지

    사람들이 IRC 에서 장애를 말하고 나서야 문제를 알았다. 디스크 사용량이나 API 오류율을 지켜보고 알리는 모니터링은 없었다.

    조치
    • 관리자가 일하던 IP 를 빼고 GitLab 으로 들어오는 트래픽을 모두 버리도록 방화벽 규칙을 고쳤다. 그래서 다른 사람들은 잠깐씩 나는 실패 대신 연결 타임아웃만 봤고, 관리자는 그 시점 뒤의 변경을 잃을 걱정 없이 한 시점의 데이터를 스냅샷으로 뜰 수 있었다.
    • GCS 에 있는 것을 뺀 GitLab 데이터 전부를 손으로 백업했다. 평소의 백업 Rake 작업은 데이터 모으기까지만 돌렸다. tar 를 돌리면 디스크가 넘치므로, tar 출력을 SSH 로 로컬의 안전한 기계에 있는 암호화된 파티션에 흘려보냈다.
    • 모든 데이터 디스크의 스냅샷을 떴다. Kubernetes PersistentVolume 마다 Google Compute Engine 디스크가 있어 손으로 스냅샷을 뜨고 태그를 붙였다.
    • Kubernetes 서비스를 모두 멈추고, 새로 키운 PV 와 옛 디스크 스냅샷을 새 임시 GCE VM 에 함께 붙여 직접 복사한 뒤 VM 을 멈추고 Kubernetes 를 다시 켰다.
    • 첫 복사가 MySQL 하위 디렉터리를 지키지 못해 MySQL 이 빈 데이터베이스를 봤다. 그래서 어긋남을 막으려고 자원을 모두 지우고, 비어 있는 새 볼륨과 새 GCE VM 을 만들어 올바른 디렉터리 구조로 다시 복사했다. 저장소와 사용자 목록이 전과 같은지, UI 일부와 흔한 작업 흐름이 되는지 손으로 확인했다. 다음 날 아침 백업 cron 작업이 또 다른 장애 없이 성공하도록 전체 백업을 백그라운드로 돌렸다.
    • 18:45 UTC 쯤 공개 접근을 되살리고 장애가 끝났다고 알렸다.
    후속 조치
    • 모니터링, 로깅, 알림을 켠다. 이 일은 gitlab.freedesktop.org 의 freedesktop 이슈 8번으로 오래전에 열어 두었고, 이 분야를 아는 사람의 도움을 구한다.
    • 소통을 개선한다. 이번 장애는 급하게 잡혀 IRC 에만 알렸고 메일링 리스트에는 알리지 않았으며, 장애가 길어질 때도 리스트에 알리지 않았다. 이런 일을 훨씬 잘 알리는 방법을 찾는다.
    • Cloud Console 웹 UI 는 페이지 사이 로딩이 길고 이동이 많아 대화형 작업에 거의 쓸 수 없다. 그 대신 콘솔 클라이언트를 실제 상황에서 쓰는 데 익숙해지고, 되도록 콘솔 클라이언트를 쓴다.
    • GitLab 관리자가 한 명뿐인 상태는 지속할 수 없으므로 관리자를 늘린다. 특히 Kubernetes 배포에 익숙한 사람이 있으면 큰 도움이 된다.
    • Omnibus 컨테이너에서 벗어난다. 지금은 gitlab.fd.o 뒤의 모든 서비스가 파드 하나에 있고, 시작할 때 Chef 가 2~3분 동안 파일 시스템을 설정해 변경마다 최소 4~5분 동안 GitLab 전체가 내려간다. GitLab 이 만드는 cloud native 배포로 옮기려면 몇 시간의 장애가 필요하므로 충분히 앞서 알린다.
    • 저장소를 크기를 바꿀 수 있는 볼륨이나 GCS 로 옮긴다. 할 수 있는 만큼 클라우드 스토리지로 옮기고, GCP 에서 Kubernetes 1.11.x 를 쓸 수 있게 되면 디스크를 필요에 따라 커지는 볼륨으로 다시 만든다.
    교훈

    잘못된 것이 많았다. 디스크 사용량이나 API 오류율을 재고 알리는 장치가 없어서 IRC 에서 사람들이 말하고 나서야 알았다. GitLab 관리자가 한 명뿐이라 그 사람이 없으면 다른 사람이 Kubernetes 계정을 만들고 배포를 익히는 데서 시작해야 했고, 그 관리자도 Kubernetes 운영 경험이 비교적 적었다. 백업 크기가 크게 늘었으니 백업이 전보다 오래 걸린다는 것을 미리 알았어야 했고, 그렇지 않더라도 작업 시간을 알리기 전에 시험 작업으로 걸리는 시간을 잴 수 있었다. 오전에 일부 변경을 시험했지만 실제와 똑같은 변경은 시험하지 않았고, 디스크 스냅샷 같은 것은 시험을 더 철저하고 실제에 가깝게 할 수 있었다. 잘된 것도 있었다. 파괴적인 작업을 시작한 뒤로 어느 시점에나 백업이 적어도 세 벌 있어서 데이터를 영영 잃을 위험은 없었다. 다만 그중 일부는 복원 원본으로 쓰려면 받아들일 수 없을 만큼 긴 장애가 필요했다. 데이터의 많은 부분이 GCS 에 있었기 때문에 이번 같은 작업이 필요해지는 시점이 오래 늦춰졌다.

내 클러스터에도 있는지 확인하기

이 5건에서 가장 많이 나오는 구성 요소: 영구 볼륨과 스테이트풀셋, Argo CD. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.

원인별4

용량 한계2건

  1. IL-0085 Hetzner Cloud 의 k3s 컨트롤 플레인 노드가 메모리 부족으로 꺼진 뒤 클러스터가 Hetzner API 호출 한도를 다 써 버려 모든 고객 사이트가 약 7.5시간 멈췄다.
  2. IL-0518 GKE 위 gitlab.freedesktop.org 의 디스크가 모자라 저장소를 늘리려던 30분 작업이 GitLab 전체의 4시간 장애가 됐다

의존성 장애1건

  1. IL-0489 Kubernetes 위 3노드 MySQL 클러스터에 노드를 다시 붙이다 메타데이터가 깨져 Magic Pages 고객 사이트가 내려갔다

설정 변경1건

  1. IL-0009 ArgoCD Application 이름이 겹쳐 InfluxDB Cloud 운영 환경 하나가 통째로 삭제됐다

인증서·자격 증명 만료1건

  1. IL-0521 AKS 클러스터의 Azure 서비스 주체 자격 증명이 만료돼 파드가 볼륨을 마운트하지 못함