IL-0040 Saltside · 2017-04-26 (게시일)kubernetesaws
Saltside 의 SRE 팀장 Adam Hawkins 가 공개한 회사의 첫 쿠버네티스 장애 포스트모템이다. 장애가 난 곳은 개발자 실험과 CI 빌드 대상으로 쓰는 비운영 클러스터 np.k8s.saltside.io 였다. 클러스터 안의 서비스를 쓸 수 없었고, tiller 가 내려가 helm 명령이 모두 실패했다. 새 파드를 만들 수 없었고, Pending 이던 파드는 Unknown 이 되었으며, 삭제도 될 때가 있고 안 될 때가 있었다.
CI 과정은 Efritin.com, Tonaton.com, Ikman.lk, Bikroy.com 네 시장의 차트를 만들어 클러스터에 설치하고 릴리스마다 helm test 를 실행한다. 이 과정을 돌리자 파드가 Running 에서 Pending 으로, 두 시간 뒤에는 Unknown 으로 바뀌었다. 담당자는 이미지 풀 과부하로 보고 리소스를 지우려 했지만 Helm 으로는 지울 수 없었고 kubectl delete 로도 지워지지 않았다. 노드를 확인하니 컨테이너 런타임이나 kubelet 이 내려가 있었고 SSH 접속도 되지 않았다. 워커 노드 세 대를 종료하고 오토스케일링 그룹이 새 인스턴스를 띄우게 하자 약 10분 뒤 모든 것이 정상으로 돌아왔다.
이후 DataDog 연동을 고쳐 지표를 받으면서 같은 과정을 다시 돌렸다. 담당자는 디스크나 Docker 데몬을 병목으로 의심했고, 차트를 설치하는 동안 워커 노드의 EBS 대기열 길이가 급격히 치솟는 것을 확인했다. kops 는 기본으로 20GB gp2 EBS 볼륨을 썼고, gp2 의 IOPS 는 디스크 크기에 비례한다. df 로 본 디스크 사용률은 90%였다. 노드에는 IOPS 와 디스크 공간이 모두 모자랐다. kops 가 EBS 최적화를 지원하지 않는다는 사실도 드러났다. 루트 EBS 볼륨을 120GB 로 늘리고 공통 베이스 이미지를 노드에 미리 받아 둔 뒤 다시 돌리자 kubelet 이 죽지 않고 차트가 설치됐다. 하지만 다른 곳에서 문제가 생겨, 클러스터는 여전히 이만큼 많은 컨테이너 작업을 동시에 감당하지 못했다.
팀은 다른 문제도 정리했다. 노드가 NotReady 가 되어도 알려 주는 모니터링이 없었다. 파드 단계별 개수 지표와 노드의 CPU, 메모리, 디스크 지표가 없었고, 이 AWS 계정의 CloudWatch 지표도 DataDog 에 들어오지 않았다. tiller 파드는 CPU 한도 때문에 다시 스케줄되지 못했는데, 노드 수준에서는 CPU 요청에 문제가 없었으므로 이유를 더 조사해야 한다. helm init 은 tiller 를 레플리카 하나로만 띄워서 고가용성을 갖추지 못했다. 팀은 kubelet 검사가 무엇을 확인하는지, 예를 들어 Disk Pressure 가 무엇인지 몰랐고, 새로 만드는 파드 수를 조절할 방법도 없었다.
SRE 팀장이 애플리케이션 Helm 차트를 고친 뒤 자기 머신에서 CI 과정을 돌렸다. watch kubectl get pods 로 지켜보니 파드가 모두 Running 이 되었는데, 그중 일부가 다시 Pending 으로 바뀌었다. 그는 저절로 풀릴 일시적인 문제라고 보고 회의에 들어갔다.
두 시간 뒤 다시 보니 문제는 더 커져 있었고 Unknown 상태의 파드가 보였다. kubectl describe pod 의 이벤트 로그에는 컨테이너 런타임인 Docker 의 이미지 풀 오류가 가득했다. kube-system 의 tiller 파드는 이유를 알 수 없이 크래시 루프에 빠져 helm 명령이 실패했다. kubectl delete 명령은 성공한 것처럼 끝났지만 실제로 지워진 것은 없었다.
kubectl get pods -n kube-system 으로 보니 파드들이 여러 가지 비정상 상태였고 일부는 DeadNode 였다. kubectl describe nodes 의 출력에서는 어떤 노드는 컨테이너 런타임이 내려갔다고 나왔고, 어떤 노드는 여러 검사에 실패해 kubelet 이 내려갔다고 나왔다. 노드에 SSH 로 접속하려 했지만 연결이 계속 타임아웃됐다. kubectl get nodes 는 워커 노드 세 대가 모두 NotReady 라고 보여 줬지만 어떤 모니터링도 이를 알리지 않았다.
처음에는 이미지 풀 오류를 대수롭지 않게 여겼다. 비운영 클러스터였고, 노드는 t2.medium 이었고, 공식 Docker 레지스트리가 그동안 자주 불안정했기 때문이다. 담당자는 이미지를 한꺼번에 받다가 과부하가 걸렸다고 추정하고 리소스를 지우기로 했다. Helm 릴리스를 지우려 했지만 tiller 가 크래시 루프에 빠져 있어 할 수 없었다. 그래서 kubectl delete 에 --force --grace-period=0 을 붙여 파드를 강제로 지웠다. 파드는 kubectl get 에서 사라졌지만 Docker 에서도 지워졌는지는 확인하지 않았다.
담당자가 CI 과정을 돌리며 watch kubectl get pods 로 지켜보다가 Running 이던 파드가 Pending 으로 바뀌는 것을 보았다. 두 시간 뒤 Unknown 상태의 파드를 보고서야 내부에서 무언가 확실히 잘못됐다고 판단했다. 워커 노드 세 대가 모두 NotReady 였지만 어떤 모니터링도 이를 잡지 못했다.
평소 하던 애플리케이션 배포만으로도 클러스터가 무너질 수 있었다. 워커 노드를 종료하는 방법은 효과가 있었지만 파드를 다른 노드로 옮기지 않으므로 운영 환경에서는 고객이 서비스를 못 쓰게 된다. 그래서 이런 장애의 표준 절차가 될 수 없다. 노드 상태, 파드 단계, 노드 리소스 지표가 없으면 이런 장애를 알아채지 못한다. kops 의 기본 20GB gp2 루트 볼륨은 IOPS 와 디스크 공간이 모두 모자랐다. 새로 만드는 파드 수를 조절할 수단이 있으면 문제를 풀지는 못해도 위험을 줄일 수 있고, 무언가 잘못됐을 때 제 손으로 클러스터를 마비시키는 일을 막을 수 있다.
같은 원인 · 공통 노드 루트 디스크, 컨테이너 런타임, kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet
같은 원인 · 공통 kubelet