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

사례14건

14건 중 11~14번째

  1. 증상

    2018년 12월 4일 아침 8시 회의 중 운영자가 NRE Labs의 일일 통계를 보다가 레슨 사용량이 떨어진 것을 알아챘다.

    촉발

    kubeadm이 etcd 정적 파드 매니페스트에 넣은 liveness probe가 실패하기 시작했다. 이 probe는 etcdctl로 foo 키를 조회한다. failureThreshold가 8, timeoutSeconds가 15라서 약 2분 동안 실패가 이어지면 kubelet이 etcd를 재시작한다. probe가 왜 실패하기 시작했는지는 끝내 밝히지 못했다.

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

    운영자가 아침 회의 중 NRE Labs 일일 통계를 보다가 레슨 사용량이 떨어진 것을 보고 알았다. 클러스터 모니터링은 갖추지 않은 상태였고, 원문은 모니터링이 우선순위에서 밀려 있었다고 적었다.

    조치
    • kubeadm이 만든 etcd 매니페스트에서 liveness probe 부분을 통째로 지우고, 다음 etcd 재시작 때 kubectl apply로 반영했다.
    후속 조치
    • 단일 마스터 클러스터에 모니터링을 갖춘다.
    • 장애가 나면 트위터를 보라고 안내하는 점검 페이지를 Google Cloud Functions에 만든다. 계획된 작업이든 아니든 일이 생기면 트래픽을 그 페이지로 돌린다.
    교훈

    모든 인프라를 코드로 관리한 덕에 클러스터를 금방 다시 만들 수 있었다. 하지만 원인을 모른 채 지우면 증거도 함께 사라진다. 단일 마스터로 운영한다면 모니터링이 더 중요하다. kubectl 출력에서 Running만 보지 말고 재시작 횟수까지 읽어야 한다. 복잡한 원인을 기대하기 전에 기본부터 의심하고, 장애의 급박함에 밀려 성급한 결정을 내리지 않는다. 사이트를 반쯤 망가진 채로 두지 말고 점검 페이지로 상황을 알려야 한다.

  2. 증상

    CronJob 하나가 계속 실패했다. 실패한 작업이 끝나지 않은 채 다음 작업이 계속 만들어졌고, 재시작을 거듭하는 파드가 수천 개로 쌓였다. 발표자의 계산으로는 매분 실행되는 작업 하나가 사흘 동안 실패하면 재시작하는 파드가 4320개가 된다.

    촉발

    CronJob 하나가 계속 실패했다.

    원인
    구성 요소
    기여 요인
    교훈

    CronJob 을 배포할 때는 concurrencyPolicy 를 Replace 나 Forbid 로 정한다. 기본값에서는 실패한 작업이 쌓여 한꺼번에 재시작한다. 오토스케일링이 켜진 클러스터에서는 이렇게 늘어난 파드가 그대로 노드 증가와 비용 증가로 이어진다. 발표자는 의존 대상에 연결하지 못할 때 바로 죽어 재시작을 반복하기보다 지수 백오프로 일정 시간 재시도한 뒤 종료하라고 권했다. readiness 검사와 liveness 검사도 쓰라고 했다.

  3. 증상

    사용자가 파드가 떴다가 사라진다고 클러스터 운영 팀에 알렸다.

    촉발

    etcd 의 쓰기 지연이 크게 늘었다. 발표자는 정상인 etcd 의 쓰기 지연이 한 자릿수 밀리초이고, 데이터베이스 동기화 지연이 두 자릿수나 수백 밀리초를 넘으면 문제가 시작된다고 설명했다.

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

    팀은 사용자가 파드가 떴다가 사라진다고 알려 와서 장애를 알게 됐다. 팀이 kubectl 로 확인하자 같은 명령이 실행할 때마다 다른 결과를 냈다.

    조치
    • 새 클러스터를 만들어 애플리케이션을 다시 배포하고 서비스를 복구했다.
    • 볼륨을 옛 클러스터에서 하나씩 손으로 풀어 새 클러스터에서 바인딩했다.
    • 로드 밸런서를 쓰던 팀들이 DNS 레코드를 새 클러스터의 로드 밸런서 이름으로 손으로 바꿨다.
    후속 조치
    • API 서버가 etcd 에서 읽을 때 항상 쿼럼 읽기를 하도록 설정한다.
    교훈

    etcd 는 쿼럼 읽기를 요청하지 않으면 오래된 데이터로 응답할 수 있고, 이 동작은 문서에 적혀 있다. 쿠버네티스 1.9 부터는 쿼럼 읽기가 기본이므로, 그 전 버전을 쓰면서 API 서버 설정을 직접 관리한다면 설정을 확인해야 한다. 쿼럼 읽기는 성능 걱정 때문에 처음에는 권장되지 않았지만, etcd 3.1 에서는 읽기가 디스크를 거치지 않아 부담이 크게 줄었다. etcd 는 쓰기 지연에 매우 민감하므로 데이터베이스 동기화 지연을 지켜봐야 한다. 고가용성 문서처럼 계속 바뀌는 문서는 꾸준히 다시 읽어야 한다. 로드 밸런서 이름을 직접 가리키는 DNS 레코드와 한 노드에 묶이는 볼륨은 클러스터 교체를 느리게 만든다.

  4. 증상

    SRE 팀장이 애플리케이션 Helm 차트를 고친 뒤 자기 머신에서 CI 과정을 돌렸다. watch kubectl get pods 로 지켜보니 파드가 모두 Running 이 되었는데, 그중 일부가 다시 Pending 으로 바뀌었다. 그는 저절로 풀릴 일시적인 문제라고 보고 회의에 들어갔다.

    촉발

    CI 과정을 로컬에서 돌려 시장 네 곳의 Helm 차트를 비운영 클러스터에 한꺼번에 설치했다. 그 결과 t2.medium 워커 노드 세 대가 여러 컨테이너의 이미지를 동시에 받고 컨테이너를 동시에 만들었다.

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

    담당자가 CI 과정을 돌리며 watch kubectl get pods 로 지켜보다가 Running 이던 파드가 Pending 으로 바뀌는 것을 보았다. 두 시간 뒤 Unknown 상태의 파드를 보고서야 내부에서 무언가 확실히 잘못됐다고 판단했다. 워커 노드 세 대가 모두 NotReady 였지만 어떤 모니터링도 이를 잡지 못했다.

    조치
    • 워커 노드 세 대를 종료하고 오토스케일링 그룹이 새 인스턴스를 띄우게 했고, 약 10분 뒤 클러스터가 정상으로 돌아왔다.
    후속 조치
    • 비운영 AWS 계정과 DataDog 의 연동 문제를 고쳐 지표가 들어오게 하고, 그 지표를 보며 설치 과정을 다시 돌렸다.
    • 워커 노드의 루트 EBS 볼륨을 20GB 에서 120GB 로 늘렸다.
    • ruby:2.4, alpine:3.2 같은 공통 베이스 이미지 안에서 sleep 루프만 도는 DaemonSet 을 배포해 노드에 이미지를 미리 받아 두었다. kops 가 사용자 훅을 지원할 때까지 쓰는 임시 방편이다.
    • 쿠버네티스의 계층마다 할당 한도를 다르게 설정한다.
    • tiller 가 크래시 루프에 빠지면 차트 설치와 업그레이드로 수정을 배포할 수 없으므로 tiller 고가용성을 Helm 팀과 논의한다.
    • 새로 만드는 파드 수를 조절할 방법을 찾는다.
    교훈

    평소 하던 애플리케이션 배포만으로도 클러스터가 무너질 수 있었다. 워커 노드를 종료하는 방법은 효과가 있었지만 파드를 다른 노드로 옮기지 않으므로 운영 환경에서는 고객이 서비스를 못 쓰게 된다. 그래서 이런 장애의 표준 절차가 될 수 없다. 노드 상태, 파드 단계, 노드 리소스 지표가 없으면 이런 장애를 알아채지 못한다. kops 의 기본 20GB gp2 루트 볼륨은 IOPS 와 디스크 공간이 모두 모자랐다. 새로 만드는 파드 수를 조절할 수단이 있으면 문제를 풀지는 못해도 위험을 줄일 수 있고, 무언가 잘못됐을 때 제 손으로 클러스터를 마비시키는 일을 막을 수 있다.

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

이 14건에서 가장 많이 나오는 구성 요소: 노드 풀, 인그레스 컨트롤러. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.

원인별8

배포 결함2건

  1. IL-0188 EKS 롤링 업데이트 때 Kubernetes 는 새 파드가 Ready 가 되자마자 옛 파드를 지웠지만 ALB 는 새 파드를 아직 healthy 로 보지 않아, 배포마다 약 30초 동안 502 가 났다.
  2. IL-0020 EKS 1.30 업그레이드 중 디스케줄러가 cordon 된 노드의 파드를 한꺼번에 쫓아내 운영 서비스가 멈췄다

의존성 장애1건

  1. IL-0187 Spring Cloud Config Server 가 잠깐 내려가자 이를 헬스 체크에 포함한 모든 앱의 Liveness Probe 가 실패해 쿠버네티스가 파드를 계속 죽이고 다시 띄우면서 한 계열사 서비스가 마비됐다.

용량 한계3건

  1. IL-0170 설날 이벤트 트래픽이 평소의 3배를 넘자 Istio ingressGateway 가 있는 EKS 노드의 nf_conntrack 테이블이 가득 차 패킷이 버려졌고, 웹과 앱 서비스 전체가 멈췄다.
  2. IL-0027 CoreDNS 파드가 모두 한 노드에 몰린 채 그 노드의 메모리가 고갈되자 DNS 가 응답하지 못해 아이디어스 서비스 전체가 멈췄다
  3. IL-0040 비운영 쿠버네티스 클러스터에 Helm 차트를 한꺼번에 설치하자 작은 gp2 루트 볼륨의 IOPS 가 바닥나, 워커 노드 세 대에서 kubelet 이나 Docker 가 내려가 세 대 모두 NotReady 가 됐다

설정 변경4건

  1. IL-0153 DOKS 1.27.6-do.0 과 1.28.2-do.0 워커 노드에서 매일 아침 도는 apt 자동 업그레이드가 15분 타임아웃에 걸려 강제 종료되면서 노드 네트워킹이 망가지고, 노드가 사흘 연속 NotReady 가 됐다.
  2. IL-0109 로그 수집을 고치려던 EKS 노드 롤오버가 꼬이면서 Auto Scaling 그룹이 정상 워커 노드를 모두 종료해 클러스터에 노드가 하나도 남지 않았다.
    DFDS2023
  3. IL-0191 Loki distributor 서비스에 레이블 셀렉터가 하나 더 들어가 롤아웃 중 새 파드 몇 개에만 쓰기 트래픽이 몰렸고, 그 파드들이 OOM으로 죽으면서 로그 쓰기 경로가 약 25분간 멈췄다.
  4. IL-0055 실패하는 CronJob 이 기본 concurrencyPolicy 때문에 겹겹이 쌓여 재시작하는 파드가 수천 개로 늘었고 클러스터가 내려갔다
    Target2018

업그레이드로 깨진 호환성1건

조직
  1. IL-0198 systemd 보안 패치가 Ubuntu 22.04 노드의 systemd-networkd 를 재시작해 Cilium 이 넣은 IP 규칙을 지웠고, 여러 리전과 세 클라우드의 Kubernetes 노드가 한꺼번에 네트워크를 잃었다.

운영 실수1건

  1. IL-0054 GKE 에서 옛 노드 풀을 드레인하자 로드밸런서가 ingress-nginx 파드가 없는 새 노드만 가리켜 클러스터의 모든 인그레스 트래픽이 끊겼다

unknown1건

조직
  1. IL-0059 kubeadm이 만든 etcd 정적 파드의 liveness probe가 실패해 kubelet이 약 2분마다 etcd를 재시작했고, 단일 마스터 클러스터의 API 서버가 계속 끊겼다

소프트웨어 결함1건

조직
  1. IL-0072 etcd 쓰기 지연으로 API 서버가 오래된 데이터를 읽어 클러스터 상태가 두 가지로 번갈아 보였고, 주 운영 클러스터 전체가 약 4시간 멈췄다