IL-0038 Oath · 2018-05-04 (게시일)kubernetes
Oath 의 플랫폼팀은 쿠버네티스를 1.5 무렵부터 써 왔고 1.9 까지 여러 번 업그레이드했다. 그중 kubelet 변경을 롤링 업데이트로 배포하다가 쿠버네티스 버그를 만났다. kubelet 은 재시작할 때 임시 cgroup 파일을 정리한 뒤 올라오는데, 이 버그 때문에 kubelet 이 멈췄다. 롤링 업데이트는 한 클러스터의 노드 전체로 퍼졌다.
kubelet 이 올라오지 않자 쿠버네티스 API 는 노드가 죽었다고 보았고, 컨트롤러는 NotReady 노드를 eviction 큐에 넣어 파드를 축출하기 시작했다. 컨트롤러는 클러스터 노드의 50% 이상이 NotReady 가 되면 클러스터 자체에 문제가 있다고 보고 축출 속도를 늦춘다. 매초 하던 축출을 15초, 20초 간격으로 늦추는 식이다. 노드가 모두 내려간 것을 보면 full disruption 모드로 들어가 축출을 아예 멈춘다. 발표자는 이 동작이 문서에 잘 나와 있지 않다고 했다.
이 동작 덕분에 파드 전체가 축출되지는 않았다. 파드의 50% 정도가 축출되어 Pending 상태로 남았고, 나머지 50% 가 버텨 준 덕분에 서비스는 완전히 멈추지 않고 품질만 떨어졌다. 팀은 해당 데이터 센터에서 다음 데이터 센터로 페일오버해 사용자를 옮겼다.
팀은 원래 한 번에 30% 씩 업데이트했지만, 업그레이드 뒤 kubelet 이 제대로 동작하는지 확인하는 기능 테스트가 없었다. kubelet API 에 curl 명령 하나만 보내 봤어도 충분히 잡을 수 있었다.
kubelet 변경을 클러스터 전체에 롤링 업데이트하던 중 kubelet 이 다시 올라오지 못했다. 쿠버네티스 API 는 노드가 죽었다고 판단했고 노드들이 NotReady 상태로 바뀌었다.
그래프에서는 eviction 큐에 들어가 축출되는 노드 수를 나타내는 선이 계속 올라갔다. 컨트롤러는 NotReady 노드를 eviction 큐에 넣고 그 노드의 파드를 축출해 다른 노드에 다시 스케줄하려 했다.
축출된 파드는 갈 곳이 없었다. Ready 상태인 노드가 없었으므로 다시 스케줄되지 못하고 Pending 상태에 머물렀다.
실제로 망가진 것은 kubelet 프로세스와 API 사이의 통신뿐이었다. 인그레스에서 파드로 가는 경로는 살아 있었고 축출되지 않은 파드는 계속 실행 중이었다.
원인 ? 소프트웨어 결함
업데이트 중 kubelet 이 올라오지 않으면서 노드가 NotReady 로 바뀌었고, 그래프에서 eviction 큐에 들어가 축출되는 노드 수가 늘어나는 것이 보였다.
kubelet 을 업그레이드한 뒤에는 반드시 테스트한다. kubelet API 에 간단한 curl 명령 하나만 보내 봐도 충분하다.
노드 컨트롤러는 노드의 50% 이상이 NotReady 가 되면 축출 속도를 늦추고, 노드가 모두 내려가면 full disruption 모드로 들어가 축출을 멈춘다. 이 동작 덕분에 이번 장애에서 파드의 절반이 살아남았다.
데이터 센터와 클러스터가 여럿이면 품질이 떨어진 데이터 센터에서 다른 데이터 센터로 페일오버해 사용자를 빠르게 옮길 수 있다.
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet