사례6건
6건 중 1~5번째
IL-0138nvidia-dkms-390 드라이버 빌드 실패로 망가진 커널 헤더가 몇 주 동안 방치됐다가, 정전 뒤 재부팅하자 하나뿐인 마스터 노드가 커널 패닉에 빠져 홈랩 쿠버네티스 클러스터가 6일 동안 멈췄다.Barush Mendez2025- 증상
2025년 3월 3일 14:15:34부터 홈랩 쿠버네티스에 올린 서비스에 접속되지 않았다. 운영자가 서비스에 들어가려다 직접 알아챘고, 알아채기까지 3시간이 걸렸다.
- 촉발
정전 때문에 마스터 노드가 재부팅됐다.
- 원인
- 구성 요소
- 기여 요인
- 감지
운영자가 서비스에 접속하려다 직접 알아챘다. 자동 알림이 없어서 알아채기까지 3시간이 걸렸다.
- 조치
- 원격 접속이 되지 않아 모니터를 연결해 부팅 로그를 보고 커널 패닉을 확인했다.
- 이전 커널 버전으로 부팅해 시스템을 되살렸다.
- sudo apt-get remove --purge '^nvidia-.*' 로 망가진 패키지를 지우고 sudo dpkg --configure -a 로 패키지 설정 문제를 고쳤다.
- dpkg -l | grep -v ^ii 로 남은 망가진 패키지를 찾아 sudo apt-get remove --purge <package_name> 으로 하나씩 지웠다.
- sudo apt-get install -f 로 빠진 의존성을 채우고 sudo apt-get install --reinstall linux-headers-$(uname -r) 로 커널 헤더를 다시 설치했다.
- 머신을 재부팅해 커널 패닉이 사라진 것을 확인했다.
- 후속 조치
- GPU 드라이버 설치가 완전히 성공했는지 확인하고, 오류가 나면 바로 변경을 되돌린다.
- 단일 장애 지점을 없애도록 클러스터를 고가용성 구성으로 옮긴다.
- 고가용성 구성에서 Kube-API 를 로드 밸런서 뒤에 둔다.
- 장애를 기록하고 해결 과정을 추적할 티켓 시스템을 만든다.
- 미리 정해 둔 문제 해결 절차를 만들어 홈랩 장애 대응 시간을 줄인다.
- Prometheus 와 Grafana 같은 인프라 모니터링을 넣어 문제를 미리 잡는다.
- 주요 서비스에 HTTP, TCP 검사를 거는 블랙박스 모니터링을 넣어 쿠버네티스 클러스터 밖에서도 장애를 잡는다.
- 교훈
드라이버 빌드 실패를 그대로 두면 몇 주 뒤 재부팅 때 터질 수 있다. 자동 알림이 없어서 발견과 복구가 늦었다. 마스터 노드가 하나뿐이라 노드 하나의 고장이 클러스터 전체 장애가 됐다. 원인을 찾은 뒤에는 비교적 간단히 고쳤고, 데이터 손실은 없었다.
- 증상
2023-02-23 20:48 UTC 에 운영 환경에서 Deployment Replicas Unavailable 알림이 울리기 시작했다. 21:10 UTC 에 운영 Live API 와 Test API 가 내려가 Stytch API 호출이 모두 5XX 를 냈고, 21:17 UTC 에는 Dashboard 와 SDK 도 내려갔다.
- 촉발
14:21 UTC 에 팀은 쓰지 않는 관리형 노드 그룹을 지우는 변경을 반영했고, 두 엔지니어가 AWS 콘솔에서 스테이징과 운영의 노드 그룹을 지웠다. 노드 그룹을 지우자 AWS 가 뒤에서 그 노드 그룹과 엮인 리소스를 정리하면서 Karpenter 노드도 쓰던 instance profile 을 지웠다.
- 원인
- 구성 요소
- 기여 요인
- 감지
운영에서는 20:48 UTC 에 Deployment Replicas Unavailable 알림이 울렸고, 21:14 UTC 에 지원팀이 장애를 알렸다. 그보다 앞서 16:06 UTC 에 스테이징, 17:48 UTC 에 운영에서 Karpenter 가 instance profile 을 찾지 못한다는 낮은 긴급도 경고가 왔지만 급하지 않은 문제로 분류되었다. 20:26 UTC 에 Karpenter 오류율이 치솟자 팀은 장애 대응을 시작했다.
- 조치
- 노드 그룹 삭제를 되돌려 노드 그룹을 다시 만들었다. 새 파드는 새 노드가 뜨기를 기다려야 해서 충분히 빨리 스케줄되지 않았다.
- Live API 노드 그룹 하나를 컴퓨팅 크기가 훨씬 큰 노드로 손으로 늘리고, 그 노드 그룹에서 taint 를 지웠다.
- 애플리케이션의 node selector 를 차례로 지워 파드가 그 노드에 스케줄되게 했다. 몇 분 안에 애플리케이션마다 필요한 수의 노드에 파드가 스케줄되었다.
- 후속 조치
- Karpenter 오류의 심각도를 올려 당직 호출기로 바로 가는 높은 긴급도 알림으로 다룬다.
- 클러스터 알림의 임계값을 조정해 높은 긴급도 호출이 더 일찍 가게 한다.
- 노드가 오랫동안 NotReady 나 Unknown 으로 표시되면 알림을 보내 기존 노드의 상태를 더 잘 지켜본다.
- 스케줄할 수 없는 파드, 사용할 수 없는 레플리카를 비롯한 다른 배포 관련 알림의 임계값을 올린다.
- Karpenter 가 쓰는 IAM instance profile 을 노드 그룹이 쓰는 IAM profile 과 다른 것으로 바꾼다.
- 여러 용도로 겹쳐 쓰던 다른 AWS 리소스, 특히 IAM 과 보안 그룹을 나눈다.
- EKS 와 Karpenter 설정을 크게 고쳐 복잡도를 줄인다. 새 시스템 instance profile 은 Karpenter 가 클러스터마다 고유하고 고정된 이름으로 만들게 한다.
- 개발팀이 인프라를 바꿀 때 쓸 수 있도록 클라우드 시각화 도구를 들인다.
- 상태 페이지에 실시간 시스템 지표와 제품 지표를 넣어 Stytch API 가 지금 어떻게 동작하는지 보여 준다.
- 문서에 없는 동작과 부작용을 AWS 에 문의해 확인한다.
- 인프라에서 IAM 역할과 보안 그룹을 여러 용도로 겹쳐 쓰던 것을 바로잡는다.
- 교훈
Karpenter 의 자체 관리 노드에 노드 그룹과 같은 IAM 역할을 쓰지 말았어야 했다. 한 IAM 역할을 여러 용도로 겹쳐 쓰면 이번 같은 문제가 그대로 난다. 팀은 Karpenter 를 처음 설정할 때 빠르고 단순하게 하려다 역할을 나누지 않았다. 원인과 결과 사이가 몇 시간 벌어져 있어 장애 전체를 판단하기 어려웠다. AWS 는 이 지연이 예상된 동작이라고 확인했고, 팀은 앞으로 IAM instance profile 을 바꿀 때 이를 고려한다.
IL-0191Loki distributor 서비스에 레이블 셀렉터가 하나 더 들어가 롤아웃 중 새 파드 몇 개에만 쓰기 트래픽이 몰렸고, 그 파드들이 OOM으로 죽으면서 로그 쓰기 경로가 약 25분간 멈췄다.Grafana Labs2022- 증상
Grafana Cloud Logs 의 한 환경에서 Grafana Loki 의 쓰기 경로가 내려갔다. 롤아웃 중이던 distributor 파드가 OOM으로 죽었다가 다시 뜨기를 되풀이했다.
- 촉발
키-값 저장소에서 내장 memberlist 로 옮기는 마이그레이션의 1단계가 distributor 디플로이먼트와 서비스에 새 레이블을 붙였다. 서비스를 만드는 함수가 이 레이블을 셀렉터로도 복사했기 때문에, 롤아웃이 시작되자 distributor 서비스는 새 레이블을 단 새 파드만 가리켰다.
- 원인
- 구성 요소
- 기여 요인
- 감지
처음에는 distributor 가 OOM으로 죽는 장애로 보였다. 첫 단서는 kubectl get pods 와 kube_deployment_status_replicas_updated 지표에서 distributor 의 10%만 재시작됐다는 점이었다. 팀은 PR 마다 Kubernetes 매니페스트 diff 를 만들어 PR 에 올리고 별도 레포에도 저장하는 자동화를 갖고 있었다. 이 diff 에서 distributor 서비스에 셀렉터가 추가되고 서비스가 다시 만들어진 것을 찾았다. 평소 배포에서는 서비스가 다시 만들어지지 않는다.
- 조치
- distributor 디플로이먼트의 레플리카를 늘렸다. 새로 뜬 파드도 새 레이블을 달고 있어서 부하가 더 많은 파드로 나뉘었고, OOM으로 죽는 파드가 줄면서 Kubernetes 가 옛 파드 교체를 이어 갔다.
- 마이그레이션 단계의 변경을 되돌려 장애를 완전히 끝내고 원인을 조사할 시간을 벌었다.
- service_ignored_labels:: [$._config.gossip_member_label] 을 설정하고 서비스 생성 함수 k.util.serviceFor 에 넘겨, memberlist 레이블이 서비스 셀렉터로 복사되지 않게 했다.
- 후속 조치
- 디플로이먼트나 스테이트풀셋의 레이블을 서비스 셀렉터로 복사하던 함수가 이번 같은 예외 상황도 처리하도록 고쳤다.
- 교훈
서비스 셀렉터를 디플로이먼트 레이블에서 자동으로 만들면, 롤아웃 중에 새 레이블이 붙는 순간 서비스가 새 파드만 가리킬 수 있다. 다른 환경에서 본 작은 오류를 롤아웃 때 흔한 잡음으로 넘기면 이런 신호를 놓친다. 환경마다 수평 확장과 메모리 한도 설정이 다르면 다른 환경에서 문제없던 변경이 특정 환경에서만 장애를 낸다. 원인을 빨리 찾는 데에는 Loki 와 Kubernetes 지표, PR 마다 남겨 둔 매니페스트 diff 가 도움이 됐다.
- 증상
미뤄 둔 GKE 업그레이드를 하던 저녁, 마스터는 문제없이 올라갔지만 노드 풀 업그레이드가 예상보다 훨씬 오래 걸렸다. 노드가 14개이고 노드당 5분쯤 걸리는 클러스터도 한 시간 넘게 걸렸고, 일부 노드는 드레인에 15분 넘게 걸렸다. emptyDir 를 쓰는 파드와 5분에서 10분짜리 terminationGracePeriodSeconds 가 드레인을 늦췄다.
- 촉발
마이그레이션에 쫓겨 미뤄 둔 업그레이드로 운영 클러스터를 1.10.X 에서 1.12.5-gke.10 으로 올렸다. 구글은 3월 14일 GKE 릴리스 노트에서, 이 버전으로 올린 마스터의 오류율이 높아진다는 보고가 있어 새 클러스터와 마스터 업그레이드에서 이 버전을 빼는 중이라고 밝혔다. 그런데도 3월 19일에 이 버전을 고를 수 있었다.
- 원인
- 구성 요소
- 기여 요인
- 감지
업그레이드하던 엔지니어가 드레인이 느리고 노드 풀 업그레이드가 멈추고 파드가 Pending 에 머무는 것을 직접 보았다. 인그레스 장애는 업그레이드 뒤에 드러났고, 로그로 원인을 찾지 못해 구글 지원팀에 물어 알려진 버그라는 것을 알았다. HPA 이상은 업그레이드 다음 날 발견했다.
- 조치
- 노드 풀의 노드 목록을 뽑아 kubectl drain 에 --ignore-daemonsets --delete-local-data 를 주어 노드를 미리 드레인했다.
- 노드 풀에 해당하는 관리형 인스턴스 그룹을 찾아 gcloud compute instance-groups managed delete-instances 로 멈춘 인스턴스를 지웠다.
- gcloud container operations list 로 operationType: UPGRADE_NODES, status: RUNNING 인 작업을 찾아 gcloud beta container operations cancel 로 멈춘 노드 풀 업그레이드를 취소했다.
- 동작을 멈춘 내부 인그레스 대신 한동안 port-forward 로 접근했다.
- HPA 가 이상해진 배포에서 활성 레플리카셋이 두 개면 오래된 레플리카셋을 천천히 줄였고, 하나면 더미 환경 변수를 바꿔 다시 배포했다.
- 후속 조치
- HorizontalPodAutoscaler 가 최대치의 70% 와 100% 에 닿으면 Alertmanager, Prometheus, Slack 으로 경보를 보내게 했다.
- 배포에 활성 레플리카셋 두 개가 30분 동안 남아 있을 때와 60분 동안 남아 있을 때 경보를 보내게 했다.
- 임의로 크게 잡은 종료 유예 시간을 다시 따져 본다.
- 클러스터를 업그레이드하기 전에 GKE 릴리스 노트를 먼저 확인한다.
- 운영이 아닌 환경에는 자동 업그레이드를 쓰고, 운영 환경은 클러스터를 수동으로 업그레이드하고 노드는 자동 업그레이드를 쓴다.
- 사전 검증 환경을 이름까지 운영과 최대한 똑같이 맞춘다.
- 교훈
클러스터를 업그레이드하기 전에는 GKE 릴리스 노트를 먼저 봐야 한다. 운영이 아닌 환경에는 자동 업그레이드를 써서 영향을 먼저 겪고, 운영에는 수동 클러스터 업그레이드와 자동 노드 업그레이드를 섞어 예측 가능성과 유지 부담 사이에서 균형을 잡는다. 사전 검증 환경은 이름까지 운영과 같게 둬야 한다. 노드 풀 업그레이드 시간은 노드 수와 드레인 시간에 달려 있다. emptyDir 를 쓰는 파드에 safe-to-evict 어노테이션을 달고, 지나치게 긴 종료 유예 시간을 다시 따져 보고, PodDisruptionBudget 을 두면 드레인이 빨라진다.
IL-0059kubeadm이 만든 etcd 정적 파드의 liveness probe가 실패해 kubelet이 약 2분마다 etcd를 재시작했고, 단일 마스터 클러스터의 API 서버가 계속 끊겼다NRE Labs2018- 증상
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만 보지 말고 재시작 횟수까지 읽어야 한다. 복잡한 원인을 기대하기 전에 기본부터 의심하고, 장애의 급박함에 밀려 성급한 결정을 내리지 않는다. 사이트를 반쯤 망가진 채로 두지 말고 점검 페이지로 상황을 알려야 한다.
이 6건에서 가장 많이 나오는 구성 요소: 설정 템플릿, 쿠버네티스 API 서버. kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이런 틈을 점검합니다. 점검 항목의 근거는 이 라이브러리와 같은 공개 보고서입니다.