본문 바로가기
삵

자바 앱이 내부 ALB로 붙는데 며칠 지나면 연결 타임아웃이 납니다

조회 3 · 댓글 1
영영웅문작성자2026년 10월 8일

사내 배치성 자바 앱(Spring Boot 2.7, Temurin JDK 17, RHEL 8)에서 AWS 내부 ALB의 DNS 이름으로 REST 호출을 하는데, 배포 직후에는 멀쩡하다가 며칠 지나면 특정 인스턴스에서만 연결이 안 됩니다. 앱을 재기동하면 바로 정상으로 돌아옵니다.

증상이 나는 쪽 스택은 이렇게 찍힙니다.

java.net.SocketTimeoutException: Connect timed out
  at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect
  ...
Caused while calling https://internal-xxxx.ap-northeast-2.elb.amazonaws.com/api/...

그 시점에 장비에서 직접 확인해보면 DNS 자체는 정상입니다.

# dig +short internal-xxxx.ap-northeast-2.elb.amazonaws.com
10.31.20.41
10.31.20.9
# (앱 재기동 전에 잡아둔 패킷에서는)
# SYN 목적지가 10.31.20.133 -> 응답 없음

즉 OS resolver가 주는 IP와 앱이 실제로 붙는 IP가 다릅니다. ALB가 스케일되면서 예전 노드 IP가 빠진 걸로 보이는데, 앱만 옛날 주소를 계속 물고 있는 모양입니다. JVM 기동 옵션에는 DNS 관련으로 넣은 게 없고 -Dsun.net.inetaddr.ttl 같은 걸 줘봐도 안 먹는 것 같습니다.

ALB는 60초 TTL이라고 알고 있는데 왜 앱은 하루이틀 지난 IP를 붙잡고 있는지, 그리고 이걸 애플리케이션 레벨에서 어디로 제어하는 게 맞는지 궁금합니다. 조언 부탁드립니다.

주말만기다려주말만기다려2026년 10월 9일

증상만 보면 ALB 문제가 아니라 JVM이 한 번 resolve한 IP를 프로세스가 끝날 때까지 캐시해서 생기는 현상으로 보입니다. OS resolver(dig)는 최신 IP를 주는데 앱만 옛날 IP로 SYN을 보내고 그 노드는 ALB 스케일로 빠진 상태라, 응답이 없어 Connect timed out이 나고, 재기동하면 캐시가 비워지면서 다시 정상이 되는 그림입니다.

핵심은 Java의 DNS 캐시 TTL을 정하는 값이 sun.net.inetaddr.ttl(시스템 프로퍼티)이 아니라 보안 프로퍼티 networkaddress.cache.ttl라는 점입니다. 공식 문서 기준으로 동작은 이렇습니다.

  • networkaddress.cache.ttl은 성공한 조회를 몇 초 동안 캐시할지 지정하는 값이고, -1이면 JVM이 사는 동안 영구 캐시, 0이면 캐시 안 함입니다.
  • SecurityManager가 설치돼 있으면 기본값이 -1(영구 캐시)입니다. 없으면 구현에 따라 다르고 대개 수십 초 수준입니다. 앱이 하루이틀 지난 IP를 물고 있는 걸 보면 사실상 영구 캐시로 동작하는 상태일 가능성이 큽니다.
  • sun.net.inetaddr.ttl은 networkaddress.cache.ttl에 대응하는 sun 전용 프로퍼티라서 같은 의미지만, 권장 설정 방법은 보안 프로퍼티 쪽이고 보안 프로퍼티가 설정돼 있으면 그쪽이 우선합니다. -D로 넣어도 안 먹는 것처럼 보이는 이유가 이것입니다.

그래서 애플리케이션 레벨에서는 networkaddress.cache.ttl을 유한한 값으로 내려주면 됩니다. AWS는 자사 리소스에 대해 TTL 5초를 권장하는데, 내부 배치 앱이라면 ALB의 DNS TTL(60초)과 맞춰 30~60 정도로 둬도 충분합니다.

Temurin JDK 17이면 영구 설정 파일은 $JAVA_HOME/conf/security/java.security입니다. JDK 자체를 건드리고 싶지 않으면 별도 파일로 오버라이드하는 방법을 추천합니다.

# 1) 별도 security 파일 작성
# /etc/sarc/dns.security
networkaddress.cache.ttl=30
networkaddress.cache.negative.ttl=5

# 2) 기동 옵션에 추가 ( = 는 덧붙이기, == 는 전체 대체 )
java -Djava.security.properties=/etc/sarc/dns.security -jar app.jar

또는 Spring Boot 기동 직후 가장 먼저 코드로 박아도 됩니다. 단, 첫 DNS 조회가 일어나기 전에 호출돼야 합니다.

java.security.Security.setProperty("networkaddress.cache.ttl", "30");

적용 확인은 아래로 합니다. 재기동 후 ALB가 스케일돼서 resolver IP가 바뀌면, 설정한 TTL이 지난 뒤 새 커넥션이 새 IP로 붙는지 보시면 됩니다.

# 설정값 확인 (기동 로그나 간단한 확인용 코드)
java.security.Security.getProperty("networkaddress.cache.ttl")

# 재현 시점에 앱 PID가 실제로 붙는 목적지 확인
ss -tanp | grep <PID>
# resolver가 주는 현재 IP와 비교
dig +short internal-xxxx.ap-northeast-2.elb.amazonaws.com

한 가지 더 짚자면, TTL을 고쳐도 HTTP 클라이언트 커넥션 풀이 keep-alive로 쥐고 있는 기존 커넥션은 별개입니다. 지금 증상은 Connect timed out(새 커넥션 시도 실패)이라 DNS 캐시가 1차 원인으로 보이지만, 재조회 후에도 풀에 남은 오래된 커넥션을 재사용하면 비슷한 문제가 날 수 있습니다. 쓰시는 클라이언트(Apache HttpClient, OkHttp 등)에 커넥션 idle 만료나 validate-after-inactivity를 함께 걸어두시면 더 안전합니다.

참고: Oracle Networking Properties(networkaddress.cache.ttl), AWS SDK for Java - Set the JVM TTL for DNS name lookups

🔗 https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html

🔗 https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/jvm-ttl-dns.html

로그인 후 답글을 남길 수 있습니다.