IL-0189 Adevinta · 2019-10-22 (게시일)kubernetesaws
Adevinta 는 스페인의 주요 온라인 마켓플레이스를 받치는 마이크로서비스 약 150개를 쿠버네티스 기반 중앙 플랫폼으로 옮기려 했고, 그 본보기로 Spring 기반 JVM 서비스 하나를 먼저 옮기고 있었다. 요청은 Zuul API 게이트웨이를 거쳐 EC2 인스턴스나 쿠버네티스로 가고, 쿠버네티스에서는 NGINX Ingress 컨트롤러 뒤의 Deployment 가 받는다. 운영 트래픽 일부를 쿠버네티스로 보내자 백엔드 지연이 약 20ms 에서 100~200ms 로 늘었다.
패킷을 떠 보니 서비스는 요청마다 AWS 인스턴스 메타데이터 API 를 두 번 불렀다. 첫 호출은 인스턴스에 연결된 IAM 역할을, 두 번째 호출은 그 역할의 임시 자격 증명을 받는다. AWS Java SDK 는 만료까지 15분 이내로 남았거나 마지막 갱신 시도 뒤로 60분이 지나면 자격 증명을 새로 받고, 두 기준값은 SDK 코드에 고정돼 있다. 쿠버네티스에서는 KIAM 이 노드마다 에이전트를 띄워 메타데이터 API 호출을 가로채고, 미리 받아 둔 자격 증명을 파드에 준다. KIAM 이 주는 자격 증명의 기본 만료 시간은 15분이다. 그래서 첫 요청에서 받은 자격 증명이 다음 요청에서 바로 갱신 대상이 됐고, 모든 요청이 메타데이터 API 호출 두 번을 더 기다렸다.
KIAM 이 더 긴 만료 시간으로 자격 증명을 요청하도록 설정을 바꾸자 요청이 메타데이터 API 를 거치지 않게 됐고, 지연은 EC2 에서보다도 낮아졌다. 컨테이너 안의 DNS 조회도 조금 느렸지만 지연의 실제 원인은 아니었다.
마이크로서비스 하나를 쿠버네티스 기반 중앙 플랫폼에 배포하고 운영 트래픽 일부를 보내자 요청 지연이 EC2 보다 최대 10배 높게 나왔다. 백엔드 응답 시간이 EC2 에서는 약 20ms 였는데 쿠버네티스에서는 100~200ms 였다.
처음에는 실행 환경이 바뀌면서 생길 만한 원인부터 하나씩 배제했다. JVM 버전은 같았다. 애플리케이션은 EC2 에서도 컨테이너로 돌고 있었으니 컨테이너화 문제도 아니었다. 초당 요청 1건에서도 지연이 높아 부하 문제가 아니었고, GC 멈춤도 무시할 수준이었다.
다음으로 DNS 를 의심했다. 서비스는 요청마다 ElasticSearch 도메인에 1~3번 질의한다. 컨테이너 안에서 dig 를 반복하니 Query time 이 21~43 msec 였고, EC2 에서는 첫 질의 77 msec 뒤로 0 msec 였다. JVM 의 DNS 캐시는 networkaddress.cache.ttl = 10 으로 설정돼 있었다. /etc/hosts 에 도메인과 IP 를 적어 DNS 조회를 건너뛰자 조금 나아졌지만 목표 지연에는 한참 못 미쳤다.
컨테이너에서 tcpdump 로 패킷을 떠 Wireshark 로 보니, 요청을 받는 데 1ms, ElasticSearch 호출에 18ms 가 걸렸고, 그 둘 사이에 86ms 가 더 들었다. 그 구간에서 서비스는 /latest/meta-data/iam/security-credentials 와 /latest/meta-data/iam/security-credentials/arn:... 로 GET 요청을 연달아 보냈다. 이 호출은 모든 요청마다 일어났다.
증상 ? 지연 시간 증가
원인 ? 설정 변경
서비스를 쿠버네티스에 배포하고 운영 트래픽 일부를 보낸 뒤, 요청 지연이 EC2 보다 최대 10배 높게 나오는 것을 보고 알았다.
–
이번 지연은 쿠버네티스, KIAM, AWS Java SDK, 서비스 어느 쪽의 버그도 아니었다. 자격 증명을 일찍 갱신하는 SDK 의 정책과 짧은 만료 시간을 주는 KIAM 의 기본값은 따로 보면 둘 다 타당하지만, 함께 쓰면 요청마다 갱신이 일어난다. 마이그레이션에서 생기는 문제는 대개 처음 함께 붙여 본 구성 요소들이 만나는 지점에서 나온다. 따로따로 타당한 두 결정이 함께 쓰여도 타당하다는 보장은 없다.
–