IL-0047 MindTickle · 2019-08-05 (게시일)kubernetesnetworking
MindTickle 은 운영 워크로드 대부분을 쿠버네티스로 옮긴 뒤 클러스터 전체의 네트워크 스택이 내려가는 장애를 겪었고, 새 클러스터를 만들어 모든 애플리케이션을 옮겨야 했다. 그 뒤 쿠버네티스 네트워킹을 깊이 파던 중 파드 사이 통신이 5초의 배수만큼 간헐적으로 늦어지는 문제를 발견했다.
지연의 원인은 DNS 조회였다. 리눅스의 libc 는 A 레코드와 AAAA 레코드 조회를 같은 포트로 보낸다. A 레코드 응답은 바로 오지만 AAAA 레코드 조회가 타임아웃되면, libc 는 조회 전체가 실패했다고 보고 5초 기다렸다가 다시 시도한다. 원문은 이 현상이 DNAT 나 SNAT 변환이 일어나는 커널에서 흔하고, conntrack 의 경합 조건 때문에 생긴다고 설명한다.
팀은 파드 DNS 설정에 single-request-reopen 옵션을 넣어 A 와 AAAA 조회가 서로 다른 포트로 나가게 했고, 지연이 사라졌다. gRPC 애플리케이션에서는 그 뒤에도 DEADLINE_EXCEEDED 오류가 났다. gRPC 의 기본 DNS 리졸버가 libc 가 아니라 ares 여서 이 설정이 먹지 않았기 때문이다. Dockerfile 에 GRPC_DNS_RESOLVER=native 를 넣어 libc 리졸버를 쓰게 하자 문제가 풀렸다. 대부분의 애플리케이션이 Alpine 이미지를 쓰지 않아서 Alpine 쪽은 손대지 않았다.
클러스터 전체 네트워크 장애를 겪은 뒤, 팀은 파드 사이 통신이 간헐적으로 늦어지고 웹 애플리케이션 전체가 이따금 느려지는 것을 알아챘다.
파드 A 가 x 시각에 보낸 요청이 파드 B 에는 x + 5초, x + 10초, x + 15초에 도착했고, 요청 전체가 예상보다 훨씬 오래 걸렸다. 지연은 늘 5초의 배수였다.
gRPC 애플리케이션은 타임아웃을 3초로 잡아 두었는데 DEADLINE_EXCEEDED 오류가 났다. 파드 DNS 설정을 고친 뒤에도 gRPC 애플리케이션에서는 이 오류가 계속 났다.
`tcpdump port 53` 으로 보니 A 레코드와 AAAA 레코드 조회가 같은 포트로 나갔고, 응답을 받지 못한 조회가 5초 뒤 다시 나갔다. 원문의 출력에서는 18:09:09 에 보낸 A 조회가 응답 없이 남아 있다가 18:09:15 에 다시 나간다.
원인 ? DNS·네트워크 장애
웹 애플리케이션이 간헐적으로 느려지고 파드 사이 요청이 5초의 배수만큼 늦게 도착하는 것을 보고 알았다. 지연이 5초 단위라는 점에서 DNS 조회를 의심했고, tcpdump 로 확인했다.
–
지연이 5초, 10초, 15초처럼 5초의 배수로 나타나면 DNS 조회 재시도를 먼저 의심한다. 리눅스 이미지는 파드 dnsConfig 에 single-request-reopen 을 넣으면 된다. gRPC 는 기본 리졸버가 libc 를 쓰지 않을 수 있으므로 GRPC_DNS_RESOLVER=native 도 함께 설정해야 한다. 설정이 먹었는지는 tcpdump 로 A 와 AAAA 조회가 나가는 포트를 보고 확인한다.
같은 원인 · 공통 리눅스 커널, conntrack 테이블
같은 원인 · 공통 클라이언트 쪽 DNS 리졸버와 캐시
같은 원인 · 공통 conntrack 테이블
같은 원인 · 공통 conntrack 테이블