IL-0057 Prezi · 2019-10-03 (게시일)kubernetesawsnetworking
Prezi 는 Nginx 기반 리버스 프록시 smartgateway 를 ElasticBeanstalk 에서 AWS EKS 로 옮겼다. Prezi 에서는 서비스 간 원격 호출도 이 프록시를 거친다. 옮긴 뒤 서비스 간 호출을 연달아 많이 하는 몇몇 서비스의 p99 지연이 올라갔다. 예전처럼 평균 응답 시간만 봤다면 놓쳤을 차이였다.
팀은 AWS X-Ray 로 추적을 붙이고 $upstream_connect_time 을 기록해, 프록시가 다음 서비스로 TCP 연결을 맺는 데 1초나 3초가 걸린다는 것을 확인했다. "kubernetes slow outgoing connections" 로 검색해 찾은 XING 의 글이 같은 증상을 설명하고 있었다. 파드가 공인 IP 를 가진 서비스로 연결하면, 나가는 패킷은 기본적으로 워커 노드에서 iptables 규칙에 따라 SNAT 를 거친다. 그런데 Netfilter 모듈에 경합 조건이 있어서, conntrack 테이블에 새 연결 항목을 넣을 때 SYN 패킷이 가끔 버려지고 1초나 3초의 연결 타임아웃이 생긴다.
XING 글이 제안한 해법은 포트 할당 방식을 완전 무작위로 바꾸는 것이었다. amazon-vpc-cni-k8s 플러그인은 AWS_VPC_K8S_CNI_RANDOMIZESNAT=prng 설정을 지원하지만, 공식 AMI 에 들어 있는 iptables 버전이 이를 지원하지 않았다. 팀은 대신 AWS_VPC_K8S_CNI_EXTERNALSNAT=true 로 노드의 SNAT 를 아예 껐다. 8/15 에 이 환경 변수를 적용하자 지연이 프록시 이전 기준으로 돌아왔다. 8/10 무렵 지연이 조금 나아진 것은 파드를 더 많은 노드에 더 많이 띄워 파드 하나가 받는 트래픽을 줄였기 때문이었다.
Nginx 기반 리버스 프록시를 쿠버네티스로 옮긴 뒤, Prometheus 가 몇몇 서비스의 p99 지연 증가에 알림을 보냈다. 그래프에서 p99 지연은 7/24 부터 올라가 있었다.
지연은 서비스 간 호출을 연달아 많이 하는 서비스에서 보였고, 부하 테스트로 재현할 수 있었다. 바뀐 것은 리버스 프록시가 전용 ElasticBeanstalk 머신 대신 공용 쿠버네티스 워커 노드에서 돈다는 점 하나였다. 컨테이너는 같았고, 비슷한 사설 VPC 서브넷에서 비슷한 양의 자원을 썼다.
팀은 AWS X-Ray 추적을 붙였다. 느린 추적은 smartgateway → prezipage → smartgateway → presentationservice 순서로 흘렀다. 1.3초 걸린 추적에서 smartgateway 가 presentationservice 로 요청을 넘기는 데 꼬박 1초가 걸렸고, presentationservice 는 12.8ms 만에 응답했다. 지연은 늘 정확히 1초였고, 드물게 3초였다.
추적에 Nginx 의 $upstream_connect_time 값을 속성으로 붙여 보니, 바깥으로 나가는 TCP 연결을 맺는 단계에서 시간이 걸린다는 가정이 맞았다.
원인 ? DNS·네트워크 장애
Prometheus 가 몇몇 서비스의 p99 지연 증가에 알림을 보냈다. 예전처럼 평균 응답 시간만 봤다면 꼬리 지연만 오른 이번 문제는 십중팔구 놓쳤을 것이다.
–
쿠버네티스 같은 복잡한 시스템을 디버깅할 때는 관측 가능성이 핵심이다. 평균 응답 시간 대신 p99 를 보고 있었기에 문제를 알아챘고, 추적을 붙여 느린 요청의 특징을 알게 되자 원인 가까이 갈 수 있었다. 실제 해법을 찾으려면 SNAT 가 어떻게 동작하는지, EKS 내부와 amazon-vpc-cni-k8s 플러그인이 어떤 역할을 하는지까지 이해해야 했다. EKS 에서는 노드 AMI 의 iptables 가 무작위 포트 할당을 지원하지 않을 수 있고, 그때는 외부 SNAT 설정이 대안이 된다.
같은 원인 · 공통 리눅스 커널, conntrack 테이블
같은 원인 · 공통 conntrack 테이블
같은 원인 · 공통 AWS VPC CNI
같은 원인 · 공통 conntrack 테이블