{"id":"IL-0189","org":"Adevinta","date":"2019-10-22","docs":["https://github.com/aws/aws-sdk-java/issues/1921","https://github.com/uswitch/kiam"],"title":{"en":"A JVM service moved from EC2 to Kubernetes answered up to ten times slower because KIAM's 15-minute credentials made the AWS Java SDK fetch new credentials on every request","ko":"KIAM 이 준 15분짜리 AWS 자격 증명 때문에 쿠버네티스로 옮긴 Java 서비스가 요청마다 메타데이터 API 를 불러 지연이 최대 10배 늘었다."},"domain":["kubernetes","aws"],"impact":{"scope":"One microservice being migrated to Kubernetes, on the share of production traffic routed to it","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"KIAM's short credential lifetime for pods and the AWS Java SDK's eager refresh of any credential within 15 minutes of expiry are each reasonable, but together they force a metadata round trip on every request. When latency rises after a move to Kubernetes, the cause is often the interaction of components that never ran together before rather than Kubernetes itself. Here a packet capture from inside the container found what latency metrics and DNS tests had not.","ko":"이번 지연은 쿠버네티스, KIAM, AWS Java SDK, 서비스 어느 쪽의 버그도 아니었다. 자격 증명을 일찍 갱신하는 SDK 의 정책과 짧은 만료 시간을 주는 KIAM 의 기본값은 따로 보면 둘 다 타당하지만, 함께 쓰면 요청마다 갱신이 일어난다. 마이그레이션에서 생기는 문제는 대개 처음 함께 붙여 본 구성 요소들이 만나는 지점에서 나온다. 따로따로 타당한 두 결정이 함께 쓰여도 타당하다는 보장은 없다."},"related":[],"sources":[{"url":"https://varoa.net/kubernetes/migration/latency/dns/java/aws/microservices/2019/10/22/kubernetes-added-a-0-to-my-latency.html","kind":"blog","license":"unknown","archived":null}],"summary":{"en":"Adevinta was moving a Spring JVM microservice from EC2 to its central platform with a Kubernetes runtime, as a blueprint for about 150 more services behind marketplaces in Spain such as Infojobs and Fotocasa. A Zuul API gateway sent requests either to the EC2 instances or through the NGINX ingress controller to the Kubernetes pods. For each request the service queried an AWS Elasticsearch domain one to three times, and the AWS Java SDK authorised those calls with temporary IAM credentials taken from the instance metadata API.\n\nOn Kubernetes, the instance metadata API is answered by KIAM, an agent on every node that intercepts metadata calls and serves the IAM role credentials assigned to the pod from its own cache. KIAM issues short-lived credentials, 15 minutes by default. The AWS Java SDK refreshes credentials when their expiry is within EXPIRATION_THRESHOLD, hardcoded to 15 minutes, or when the last refresh is older than REFRESH_THRESHOLD, hardcoded to 60 minutes. Every credential KIAM handed out was therefore already due for refresh, so before each Elasticsearch call the SDK fetched new credentials with two metadata calls, which added most of the extra latency. DNS lookups in the container were also slower than on EC2, but that was a smaller part.\n\nThe team found the SDK behaviour in AWS Java SDK GitHub issue #1921 and later a feature request describing the same problem. KIAM was reconfigured to request credentials with a longer expiration period. After that, requests no longer touched the metadata service and latency dropped below what it had been on EC2. The author stresses that neither Kubernetes, KIAM, the SDK nor the service had a bug: two defaults that each make sense on their own produced the problem together.","ko":"Adevinta 는 스페인의 주요 온라인 마켓플레이스를 받치는 마이크로서비스 약 150개를 쿠버네티스 기반 중앙 플랫폼으로 옮기려 했고, 그 본보기로 Spring 기반 JVM 서비스 하나를 먼저 옮기고 있었다. 요청은 Zuul API 게이트웨이를 거쳐 EC2 인스턴스나 쿠버네티스로 가고, 쿠버네티스에서는 NGINX Ingress 컨트롤러 뒤의 Deployment 가 받는다. 운영 트래픽 일부를 쿠버네티스로 보내자 백엔드 지연이 약 20ms 에서 100~200ms 로 늘었다.\n\n패킷을 떠 보니 서비스는 요청마다 AWS 인스턴스 메타데이터 API 를 두 번 불렀다. 첫 호출은 인스턴스에 연결된 IAM 역할을, 두 번째 호출은 그 역할의 임시 자격 증명을 받는다. AWS Java SDK 는 만료까지 15분 이내로 남았거나 마지막 갱신 시도 뒤로 60분이 지나면 자격 증명을 새로 받고, 두 기준값은 SDK 코드에 고정돼 있다. 쿠버네티스에서는 KIAM 이 노드마다 에이전트를 띄워 메타데이터 API 호출을 가로채고, 미리 받아 둔 자격 증명을 파드에 준다. KIAM 이 주는 자격 증명의 기본 만료 시간은 15분이다. 그래서 첫 요청에서 받은 자격 증명이 다음 요청에서 바로 갱신 대상이 됐고, 모든 요청이 메타데이터 API 호출 두 번을 더 기다렸다.\n\nKIAM 이 더 긴 만료 시간으로 자격 증명을 요청하도록 설정을 바꾸자 요청이 메타데이터 API 를 거치지 않게 됐고, 지연은 EC2 에서보다도 낮아졌다. 컨테이너 안의 DNS 조회도 조금 느렸지만 지연의 실제 원인은 아니었다."},"trigger":{"en":"Moving the service onto the Kubernetes platform, where KIAM served IAM credentials to pods with its default 15-minute lifetime.","ko":"쿠버네티스로 옮긴 서비스가 KIAM 을 거쳐 받은 AWS 임시 자격 증명의 만료 시간이 정확히 15분이었다. AWS Java SDK 는 만료까지 15분 이내로 남은 자격 증명을 곧바로 새로 받는다."},"severity":"unknown","symptoms":["high-latency"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"After part of production traffic was routed to the Kubernetes deployment, its request latency was seen to be up to 10 times higher than on EC2.","ko":"서비스를 쿠버네티스에 배포하고 운영 트래픽 일부를 보낸 뒤, 요청 지연이 EC2 보다 최대 10배 높게 나오는 것을 보고 알았다."},"components":["credential-broker"],"root_cause":"config-change","remediation":[{"text":{"en":"reconfigured KIAM to request IAM credentials with a longer expiration period","ko":"KIAM 이 더 긴 만료 시간으로 자격 증명을 요청하도록 설정을 바꿨다."}}],"action_items":[],"contributing":["default-config-unchanged"],"recurrence_of":null,"symptoms_text":{"en":"After a Spring-based JVM microservice was deployed to Kubernetes and part of production traffic was routed to it, request latency there was up to 10 times higher than on EC2. Behind the NGINX ingress controller the backend took 100 to 200 ms to answer, where the EC2 instances took about 20 ms.\n\nThe team ruled out the usual suspects. The JVM version was the same, the application already ran in containers on EC2, GC pauses were negligible, and latency was high even at one request per second, so load was not the cause. A Kubernetes admin suggested DNS, which had caused similar trouble before. In a shell inside the container, dig for the Elasticsearch domain took 21 to 43 ms per query (\"Query time: 22 msec\", \"Query time: 43 msec\"), while on an EC2 instance the queries after the first returned in 0 ms. The JVM DNS cache was set to networkaddress.cache.ttl = 10. Mapping the Elasticsearch domain to its IP in /etc/hosts improved latency somewhat but nowhere near the target.\n\nA tcpdump capture taken in the container and read in Wireshark showed where the time went. Accepting the incoming request took 1 ms and the Elasticsearch call took 18 ms, but between them the service spent 86 ms on two HTTP calls to the AWS instance metadata endpoint on one connection: GET /latest/meta-data/iam/security-credentials and then GET /latest/meta-data/iam/security-credentials/arn:... These two calls appeared on every request in the capture.\n\nFetching the credentials by hand with curl, from the container and from an EC2 instance, showed the difference: the credentials served inside the container expired after exactly 15 minutes.","ko":"마이크로서비스 하나를 쿠버네티스 기반 중앙 플랫폼에 배포하고 운영 트래픽 일부를 보내자 요청 지연이 EC2 보다 최대 10배 높게 나왔다. 백엔드 응답 시간이 EC2 에서는 약 20ms 였는데 쿠버네티스에서는 100~200ms 였다.\n\n처음에는 실행 환경이 바뀌면서 생길 만한 원인부터 하나씩 배제했다. JVM 버전은 같았다. 애플리케이션은 EC2 에서도 컨테이너로 돌고 있었으니 컨테이너화 문제도 아니었다. 초당 요청 1건에서도 지연이 높아 부하 문제가 아니었고, GC 멈춤도 무시할 수준이었다.\n\n다음으로 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 조회를 건너뛰자 조금 나아졌지만 목표 지연에는 한참 못 미쳤다.\n\n컨테이너에서 tcpdump 로 패킷을 떠 Wireshark 로 보니, 요청을 받는 데 1ms, ElasticSearch 호출에 18ms 가 걸렸고, 그 둘 사이에 86ms 가 더 들었다. 그 구간에서 서비스는 /latest/meta-data/iam/security-credentials 와 /latest/meta-data/iam/security-credentials/arn:... 로 GET 요청을 연달아 보냈다. 이 호출은 모든 요청마다 일어났다."},"updated_at":"2026-11-09T11:55:57.946102Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0189","org_slug":"adevinta"}