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

사례4건

4건 중 1~4번째

  1. 증상

    2025년 2월 18일 08:41 UTC, Grafana Labs 의 애플리케이션 팀 여러 곳이 자기 엔드포인트 일부에 접속이 되지 않는 것을 알아챘다.

    촉발

    로드 밸런서의 TLS 정책 버전을 올리면서, 기본 TLS 정책을 덮어쓸 수 있도록 Crossplane 컴포지션의 병합 정책을 바꾼 변경이 개발, 스테이징, 운영 환경에 동시에 적용됐다.

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

    08:41 UTC 에 애플리케이션 팀 여러 곳이 자기 엔드포인트 일부에 접속이 되지 않는 것을 알아챘다. 조사해 보니 최근의 TLS 정책 설정 변경 때문에 그 엔드포인트의 로드 밸런서가 사라져 있었다.

    조치
    • 문제의 코드 변경을 롤백했다.
    • 영향받은 쿠버네티스 Service 를 다시 만들어 쿠버네티스가 로드 밸런서를 새로 만들게 했다.
    후속 조치
    • 한 번에 두 개 이상의 웨이브로 배포하는 것을 막는 CI 검사를 넣어, 개발에서 스테이징, 카나리, 운영으로 이어지는 순서를 벗어나는 배포를 잡아내게 했다.
    • 논의만 하던 더 정교한 웨이브 설계의 우선순위를 올려 개선안을 합의하고 구현한다.
    • 실수로 지워지지 않도록 로드 밸런서에 삭제 보호를 켜기 시작했다. Crossplane 수준의 삭제 보호보다 한 층 아래에서 막는다.
    • 로드 밸런서 말고도 중요한 클라우드 리소스를 하나씩 살펴 필요한 곳에 삭제 보호를 켠다.
    • 변경 검증과 가시성을 높이려고 만든 Crossplane Managed Resources exporter 를 플랫폼 엔지니어링 안에서 베타로 시험하고 있다. 이번 장애를 일으킨 변경에 적용해 보면 차이가 분명히 드러난다.
    • TLS 정책을 다시 설정하는 기능을 제대로 완성하고 시험해, 애플리케이션 팀이 배포할 수 있게 했다.
    교훈

    작은 변경이라 위험이 낮다고 가정하고 충분한 시험과 영향 범위 줄이기를 건너뛴 것이 장애로 이어졌다. 웨이브 배포는 절차로 기대하는 데 그치지 않고 시스템이 강제해야 한다. 비상 예외는 웨이브를 고르지 않았을 때의 기본 동작이 아니라 말 그대로 예외여야 한다. 이번 변경은 시험을 안 한 것이 아니라 시험한 범위가 충분하지 않았다. 클라우드 로드 밸런서 컨트롤러와 자체 도구까지 처음부터 끝까지 돌려 봐야 의도한 작은 변화만 일어나는지 알 수 있었다. 개발 웨이브부터만 적용하도록 강제했다면 제대로 시험할 수밖에 없었을 것이다.

  2. 증상

    LINE 의 messaging-hub 는 일본 음식 배달 서비스 Demaecan 의 Merchant 앱과 Driver 앱에 웹소켓으로 메시지를 보낸다. 어느 날 최대 트래픽 시간이 아닌데도 웹소켓 연결 수 그래프가 순간적으로 크게 떨어졌다. 이 현상은 한 번으로 끝나지 않고 불규칙하게 계속 일어났다.

    촉발

    같은 L7 로드밸런서를 공유하는 다른 서비스 담당자가 자기 LB 설정을 배포하면 HAProxy 가 재로드됐다. hard-stop-after 로 정한 30분이 지나 이전 프로세스가 종료되면서 거기 붙어 있던 웹소켓 연결이 모두 끊겼다.

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

    웹소켓 연결 수 그래프에서 최대 트래픽 시간이 아닌데도 연결 수가 순간적으로 뚝 떨어지는 것을 보고 알았다. Nginx 패킷 드롭은 네트워크 모니터링으로 원인을 찾았다.

    조치
    • Merchant 팀이 토큰 발급 API 를 따로 떼어 배포해 토큰 요청이 몰려도 다른 Merchant API 가 지연되지 않게 했다.
    • Nginx 를 늘리고 파드 안티 어피니티와 레이블 셀렉터로 Nginx 파드가 서로 다른 노드에 뜨게 해서 NF_CONNTRACK 테이블 포화로 생기던 패킷 드롭을 없앴다.
    • Ingress Nginx 의 proxy-read-timeout 과 proxy-send-timeout 을 300s 에서 3600s 로, LB timeout 도 3600s 로 올렸지만 웹소켓 드롭은 멈추지 않았다.
    • 공유하던 L7 LB 를 L4 LB 로 바꿔 웹소켓 드롭을 없앴다.
    교훈

    문제의 범위를 미리 단정해 스스로 제한하면 안 된다. 인프라 팀과 네트워크 팀이 문제없다고 답했다는 이유로 직접 다룰 수 있는 영역만 뒤지다가 공유 L7 LB 를 늦게 의심했다. LB 같은 기술 리소스를 다른 서비스와 공유하면 다른 팀의 배포가 내 서비스의 연결을 끊을 수 있다. Nginx 를 늘려 NF_CONNTRACK 부담을 나눌 때는 파드가 서로 다른 노드에 뜨게 해야 하고, 노드 수보다 많이 늘리면 Pending 상태로 남는 파드가 생긴다는 점도 알아야 한다.

  3. 증상

    2021-09-06 08:00 UTC 에 Kubernetes 의 모든 서비스가 404 를 내기 시작했다. 30분 전인 07:30 에는 노드를 자동으로 교체하는 롤오버가 시작됐다.

    촉발

    2021-09-01 11:34 UTC 에 형식이 잘못된 IngressRoute 가 Kubernetes 에 적용됐다. 이 매니페스트는 2.5 미만 Traefik v2 의 버그를 건드렸지만, 버그는 Traefik v2 파드가 모두 재시작된 뒤에야 드러났다. 2021-09-06 07:30 에 자동 노드 롤오버가 시작됐고, 08:00 에 모든 서비스가 404 를 냈다.

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

    08:00 UTC 에 모든 서비스가 404 를 내기 시작했고 08:05 에 원인 조사를 시작했다. 감지까지 5분이 걸렸다.

    조치
    • ALB 헬스체크 경로를 바꿔 08:15 에 ALB 대상을 healthy 로 되돌렸지만 404 는 멈추지 않았다.
    • 08:50 에 주 인그레스 컨트롤러를 Traefik v1 으로 되돌리기로 하고, 09:00 에 손으로 롤백했다.
    • 09:10 에 잘못된 IngressRoute 를 손으로 지워 근본 원인과 Traefik v2 버그를 확인했다.
    • 09:20 에 바꾼 내용을 업스트림에도 반영했다.
    후속 조치
    • 버그가 고쳐진 Traefik v2.5 로 올리고 버그가 사라졌는지 확인한다.
    • Traefik v2 헬스체크 경로를 더 믿을 만한 경로로 바꾼다.
    • 인그레스 컨트롤러를 클러스터 바깥에서 확인하는 liveness 점검을 둔다.
    • 워크로드를 나눠 장애가 번지는 범위를 줄이는 방안을 검토한다.
    • Traefik v2 API 에서 짧은 시간에 많은 변경이 일어나는지 모니터링하는 방법을 알아본다.
    • 노드 롤오버 중과 롤오버 뒤에 스모크 테스트를 돌리는 방법을 알아본다.
    교훈

    Traefik v2 는 오류를 드러내지 않고 실패해서 조사를 ALB 쪽으로 잘못 이끌었다. 공식 API 스키마도 잘못된 매니페스트 적용을 막지 못했다. 팀은 대응 과정에서 장애 관리자를 정했다면 도움이 됐을 것으로 봤다. 그리고 워룸 회의를 녹화하고, 대응에 참여하지 않는 사람은 글로만 제안하고, 일지를 남기자고 정리했다. 대응 초반에 각자의 생각과 직감을 짧게 나누는 시간도 갖자고 했다.

  4. 증상

    Datadog 이 스테이징 환경의 정규화한 load average 가 높다고 경보를 보냈다. 팀은 원인을 정상 서비스 하나의 부하 증가로 판단했고, 정상 동작으로 여겼다.

    촉발

    Weave Scope 를 배포할 때 쿠버네티스가 로드 밸런서에 0.0.0.0/0 에서 80 포트로 들어오는 접근을 허용하는 보안 그룹을 연결했다. 팀은 AWS 콘솔에서 이 보안 그룹을 손으로 고쳐 허용 목록 밖의 트래픽을 막았지만, 쿠버네티스가 얼마 뒤 이 수정을 되돌렸다. 그 결과 Weave Scope 대시보드가 인증 없이 인터넷에 열렸고, 자동화된 크롤러가 이를 찾아냈다.

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

    Datadog 이 정규화한 load average 경보를 0일 차 21:06 EST 에 스테이징에서, 1일 차 16:53 EST 에 개발 환경에서 보냈다. 팀은 정상 서비스의 부하로 보고 경보를 뮤트했다. 3일 차 17:40 EST 에 부하 증가가 나흘째 이어지자 엔지니어가 노드에 SSH 로 들어가 top 으로 CPU 를 100% 쓰는 gcc 프로세스를 찾았다.

    조치
    • Weave Scope 를 멈추고 모든 쿠버네티스 클러스터에서 그 배포를 지웠다.
    • 인스턴스에 무단 접근한 흔적이 있는지 접근 로그를 확인했다.
    • gcc 프로세스의 외부 연결을 조사해 마이닝 풀과만 통신한다는 것을 확인했다.
    • 영향을 받은 노드 하나를 나중에 조사하려고 클러스터에서 격리했다.
    • 모든 클러스터의 노드를 전부 새로 만들어 교체하고 감염을 모두 없앴다.
    후속 조치
    • 모든 쿠버네티스 설정 파일과 사내 배포 YAML, 보안 그룹을 감사해 의도치 않게 외부에 열린 서비스나 잘못된 설정이 없는지 확인했다.
    • load 대신 더 의미 있는 모니터를 정하고, 작업량이 늘 때 경보가 쏟아지지 않고 실제 이상을 가려내도록 다듬는다.
    • falcon 이나 sysdig 같은 도구로 행위 모니터링과 이상 탐지, 침입 탐지를 할 수 있는지 검토한다.
    • Istio 나 Linkerd 로 종단 간 네트워크 트래픽을 파악하고 통제해 무단 접근을 막는 방안을 검토한다.
    • Docker 이미지와 컨테이너에서 알려진 취약점을 검사하는 데 드는 계산 비용을 따져 본다.
    • 아키텍처 논의 시간 일부를 떼어 컨테이너를 root 로 돌리는 것처럼 예전에 내린 결정을 다시 검토하기로 했다.
    • 일부 팀원에게만 있던 정보를 팀 전체와 나눠, 대규모 쿠버네티스 버전 업그레이드 같은 큰 프로젝트를 모든 엔지니어가 볼 수 있게 한다.
    교훈

    load 기반 모니터링은 클러스터의 이상을 가려내는 좋은 지표가 아니었다. 정상 서비스가 낸 경보 소음이 침입 신호를 가렸다. 쿠버네티스가 관리하는 리소스를 손으로 고치면 쿠버네티스가 그 변경을 되돌린다. 로드 밸런서를 내부용으로 정의하거나 보안 그룹 규칙을 쿠버네티스 YAML 에 넣었어야 했다. 인증 없는 내부 대시보드, --privileged 와 root 로 도는 컨테이너, 호스트 파일 시스템 마운트가 겹치면 열린 로드 밸런서 하나로 모든 노드에서 코드가 실행될 수 있다. 컨테이너를 root 로 돌리는 것처럼 예전 인프라에서 물려받은 결정은 다시 검토해야 한다.

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

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

원인별2