우리 k8s 클러스터가 죽었어요 (feat: 삽질)
우리 k8s 클러스터가 죽었어요 (feat: 삽질)
moseoh
kubernetes linux

우리 k8s 클러스터가 죽었어요 (feat: 삽질)

moseoh · 2025년 06월 30일

평소와 다름없는 오후, 다음날 예정된 배포를 준비하며 새로 머지된 코드들의 빌드 과정을 지켜보고 있었습니다. runner 로그를 확인하던 중, 갑작스럽게 무한 로딩 상태에 빠지는 현상을 발견했습니다.

첫 번째 증상: Connection Refused

The connection to the server x.x.x.x:xxxx was refused...

TeamCity + Docker Compose 환경에서 겪었던 빈번한 문제들 때문에 GitHub Actions Runner + K8s로 변경한 지 6개월이 지났습니다. 그동안 한 번도 문제가 없었던 빌드 러너에서 처음으로 장애가 발생했습니다. 이틀 후 배포 일정을 고려했을 때 상당히 촉박한 상황이었습니다.

문제 확산: 호스트 접속 불가

kubectl 명령어는 물론, 마스터 노드로의 SSH 접속마저 불가능해졌습니다. 방금 전까지 정상 작동하던 클러스터가 갑자기 응답하지 않는 상황. 가장 먼저 의심한 것은 CI/CD 파이프라인을 위해 클러스터에서 실행 중이던 GitHub Actions Runner였습니다.

이전 TeamCity + Docker Compose 환경에서 빌드 과정 중 리소스 고갈로 인한 서버 다운 현상을 자주 경험했던 기억이 있었습니다. K8s 환경에서는 6개월간 문제가 없었지만, 과거 경험으로 인해 Runner를 가장 먼저 의심했습니다.

빌드 작업의 높은 리소스 사용량으로 인해 OOM Killer가 sshdkubelet 같은 핵심 프로세스를 종료시켰을 가능성을 고려했습니다. 우선 서버를 재시작하는 방법으로 노드를 복구했습니다.

상황 악화: 목적지를 찾을 수 없음

노드는 복구되었지만 클러스터는 여전히 응답하지 않았습니다. 이때 로그에서 새로운 단서를 발견했습니다.

[23833.461818] IPVS: rr: TCP 127.0.0.1:xxxx - no destination available

etcd 서비스 장애와 IP 할당 실패는 클러스터의 핵심 기능이 마비되었음을 의미했습니다. 이제 문제는 K8s 애플리케이션 레벨이 아닌 인프라의 근본적인 부분을 가리키고 있었습니다. 서버 콘솔에 직접 접속하여 네트워크 설정을 점검하기 시작했습니다.

핵심 단서 발견: Network Unreachable

콘솔에서 확인한 로그가 상황을 명확하게 보여주었습니다.

master-1 named[940]: network unreachable resolving './NS/IN': ...

서버의 외부 네트워크 접근 시도가 “네트워크에 도달할 수 없음” 오류로 차단되고 있었습니다. 결정적으로 ip route show 명령어 실행 시 아무 결과도 출력되지 않아 라우팅 테이블이 비어있음을 확인했습니다.

이제 원인이 네트워크 설정 문제로 좁혀졌습니다. 다른 팀에서 IP 주소 재할당 작업을 진행했거나, 기존 DNS 서버 설정에 문제가 생겼을 가능성을 검토하며 netplan 설정 수정을 통한 디버깅을 시작했습니다.

실마리 발견

nslookupping 명령어로 네트워크 연결 상태를 점검한 결과, 외부 도메인 쿼리와 ping 모두 실패했습니다. 동료의 도움으로 tcpdump 명령어를 사용해 패킷 분석을 수행한 결과, 클러스터 내부 통신만 이루어지고 있다는 것을 발견했습니다.

클러스터의 네트워크 구성은 다음과 같았습니다. 인터넷 ↔ 스위칭 허브 A ↔ 스위칭 허브 B ↔ K8s 클러스터

tcpdump 분석 결과 스위칭 허브 B 외부로 패킷이 전송되지 않는 것을 확인했습니다.

케이블 연결 상태를 점검하던 중… 스위칭 허브 A의 전원이 뽑혀있는 것을 발견했습니다.

클러스터 전체가 외부와 물리적으로 완전히 단절된 상태였습니다. tcpdump로 확인해보니 클러스터 내부 IP들 간의 통신만 계속 시도되고 있었고, 외부로 향하는 패킷들은 응답을 받지 못하고 사라지고 있었습니다. 허브에 전원을 다시 연결하자 클러스터가 즉시 정상 상태로 복구되었습니다.

남은 의문점

문제는 해결되었지만 한 가지 궁금한 점이 남았습니다. 클러스터 내부 노드들은 스위칭 허브 B를 통해 서로 연결되어 있었고, DNS 서버도 마스터 노드에서 동작하고 있었습니다. 이론적으로는 외부 인터넷 연결 없이도 etcd와 클러스터가 정상 동작할 수 있어야 하는데, 왜 외부 연결이 복구되자마자 즉시 정상화되었을까요?

named[940]: network unreachable resolving './NS/IN' 로그를 보면 DNS 서버가 루트 네임서버에 접근하려다 실패한 것으로 보이는데, 이런 외부 의존성이 클러스터 전체에 어떤 영향을 미쳤는지는 추후 더 깊이 분석해볼 예정입니다.

마치며…

이번 장애 분석 과정에서 kubectl부터 시작해 etcd, netplan을 거쳐 스위칭 허브 전원까지, OSI 7계층을 아우르는 경험을 했습니다. 이를 통해 몇 가지 중요한 교훈을 얻었습니다.

  • 물리 계층부터 확인하기: 문제 발생 시 소프트웨어보다 물리 계층(전원, 케이블)을 먼저 점검하는 것이 기본이라고 합니다.
  • 단순한 원인 우선 검토: 복잡해 보이는 오류 메시지 뒤에는 종종 매우 단순한 원인이 있습니다.
  • 예방 조치의 중요성: 간단한 “만지지 마시오” 표시만 있었다면 이런 상황을 방지할 수 있었을 것입니다. 현재 클러스터가 연구 목적으로 사무실에 임시 배치된 상황이었는데, 이번 사건 이후 전원 코드에 ‘인터넷 라우터 선’라고 적은 포스트잇을 붙여두었습니다.

이런 예상치 못한 장애들이 결국 더 나은 인프라 운영 경험으로 이어진다고 생각합니다. 오늘도 새로운 문제해결 방법과 tcpdump 명령어를 얻어가는 하루였습니다.