인사이트

인사이트리포트

디지털프렌스포메이션 최신 정보 및 트렌드를 제공합니다.

SW 테크놀로지 오픈소스 SW

Kubernetes 환경의 WildFly 26 클러스터링 이슈: DNS_PING 결함 및 KUBE_PING 활용 분석

2026.07.15민준영 프로
다운로드

들어가며

1. JDK 8 레거시와 WildFly 26

클라우드 네이티브 아키텍처로의 전환이 가속화되고 있음에도, 엔터프라이즈 IT 환경에는 여전히 JDK 8에 강하게 결합된 수많은 레거시 애플리케이션들이 존재합니다.

Java 버전 전환은 단순한 업그레이드가 아닙니다. 핵심 비즈니스 애플리케이션이 JDK 8 기반으로 구축된 조직에서는 서드파티 라이브러리 호환성, 인증·규제 요건, 방대한 코드베이스, 개발 리소스 부족 등 현실적인 제약으로 인해 JDK 버전 전환을 쉽게 결정하지 못합니다.

WildFly 프로젝트는 버전에 따라 지원하는 JDK의 최소 요구사항을 점진적으로 상향해 왔으며, WildFly 26.1.3은 JDK 8을 공식적으로 지원하는 마지막 버전입니다.

WildFly 27부터는 JDK 8 지원이 공식 종료되고 Jakarta EE 10(javax.* → jakarta.*)으로 전환됩니다. 이러한 이유로 JDK 8 의존성을 가진 수많은 엔터프라이즈 시스템이 WildFly 26에 묶여 있으며, 당장 JDK 업그레이드가 불가능한 시스템을 Kubernetes 환경으로 컨테이너화하여 이관할 때 현업에서 채택하는 표준 오픈소스 미들웨어로 자리 잡고 있습니다.

표 1-1. WildFly/JBoss EAP 버전별 JDK 지원 및 Infinispan 버전

 

2. WildFly 26 DNS_PING 클러스터링 이슈

Kubernetes는 이제 표준 인프라 플랫폼입니다. JDK 8 레거시 애플리케이션을 WildFly 26으로 Kubernetes에 컨테이너로 배포하는 조직이 늘어나면서, 실제 운영 중 다음과 같은 장애가 발견되었습니다.

장애는 주로 부하 증가에 대응하기 위해 2~3개의 신규 Pod를 동시에 수평 확장(Scale-out)하는 시점에 집중됩니다. 트래픽 분산을 위해 Pod가 추가되는 순간, 뒤늦은 병합 프로세스 과정에서 내부 세션 관리 엔진(Infinispan 13)의 쓰레드 경합이 발생합니다. 이로 인해 미들웨어의 자원이 빠르게 고갈되면서 CPU 사용률이 치솟는 극심한 과부하 현상으로 이어집니다.

문제는 이러한 자원 고갈 상태에서도 이 결함이 인프라 감지 시스템을 우회하는 ‘침묵하는 장애(Silent Failure)’ 속성을 띤다는 점입니다. 컨테이너 내부 런타임이 마비되고 사용자는 HTTP 500 에러를 겪고 있음에도, 프로세스 자체는 생존해 있어 Kubernetes 플랫폼은 해당 Pod를 정상(Ready) 상태로 인식하는 경우가 많습니다. 결국 오토스케일러(HPA)와 네트워크 라우터가 장애를 인지하지 못한 채 결함 Pod에 트래픽을 계속 공급하고, 고부하를 해소하기 위해 Pod를 추가로 띄우면서 장애가 클러스터 전체로 확산되는 악순환이 발생합니다.

본 리포트는 이 장애의 원인을 상세히 분석하고, 이를 해결하기 위해 클러스터 구성 방식을 DNS_PING에서 KUBE_PING으로 안전하게 전환하는 가이드를 제공합니다.

기술 분석: WildFly 구조와 버그 원인

1. WildFly 26 클러스터링 구조 개요

WildFly 클러스터링은 고가용성(HA) 환경을 안정적으로 지원하기 위해 JGroups(네트워크 통신 계층)와 Infinispan(분산 캐시 데이터 계층)의 협업으로 이루어집니다. JGroups가 네트워크상에서 서버들을 찾고(탐색), 서로 잘 살아있는지 확인(장애 감지)하며 전체 팀원 명단(View)을 관리하면, Infinispan은 이 통신망 위에서 사용자의 로그인 세션 정보를 안전하게 복제하고 동기화하는 실질적인 역할을 수행합니다.

Kubernetes 환경 WildFly 26의 TCP 기반 스택에서 DNS_PING 또는 KUBE_PING 같은 디스커버리 프로토콜은 초기 클러스터 참여 노드들의 IP를 식별하는 분산 탐색 레이어 역할을 합니다. GMS(Group Membership Service)가 노드의 조인·이탈에 따른 최종 팀 명단(View)을 확정하여 클러스터 전체에 방송(Broadcast)하면, Infinispan의 캐시 토폴로지 관리 엔진이 이 이벤트를 훅(Hook)하여 세션 분산 지도(Consistent Hash)를 재계획하고 세션 동기화 네트워크를 완성합니다.

 

2. Infinispan 13.0 Race Condition 버그 분석

2.1 버그의 발생 조건과 메커니즘

WildFly 26에 탑재된 Infinispan 13.0은 클러스터 구성 효율을 위해 신규 노드의 조인 요청을 병렬 처리 기반의 비동기 파이프라인으로 수행합니다. 그러나 클라우드 네이티브 환경에서 복수의 노드가 수십~수백 밀리초 단위로 동시 스케일 아웃될 경우, 이 병렬 처리 구조는 내부 상태의 정합성을 제어하지 못하는 동시성 충돌(Race Condition)을 야기합니다. 결과적으로 다수의 요청이 순차적 검증 없이 중첩되어 내부 데이터가 오염되고, 이는 클러스터 전체의 CPU 자원 고갈과 서비스 중단으로 이어집니다.

그림 1. Infinispan 13.0 동시성 충돌(Race Condition) 메커니즘

 

2.2 Infinispan 버전별 비교

[Infinispan 13.x] 병렬 비동기 처리의 한계

13 버전은 처리 지연을 최소화하기 위해, 다중 노드의 조인 요청을 비동기 체인을 통해 일제히 통과시킵니다. 이로 인해 다중 쓰레드가 분산 지도 갱신 로직(doJoin)에 동시 접근하게 되고, 임계 구역(Critical Section)에 대한 락(Lock) 메커니즘 부재로 데이터 정합성이 훼손됩니다.

// Infinispan 13.x: 상태 제어 큐(Queue) 없이 비동기 체인으로 일제히 진입
return viewStage.thenCompose(v -> {
    ClusterCacheStatus cacheStatus = initCacheStatusIfAbsent(...);
    // 다중 쓰레드가 동시에 토폴로지 재계산에 진입하여 컨텍스트 충돌 발생
    return cacheStatus.doJoin(joiner, joinInfo); 
});

 

[Infinispan 14.x] 큐 기반 직렬화(Serialization) 도입

14 버전 이후부터는 이러한 동시성 이슈를 해결하기 위해 직렬화 아키텍처로 전면 개편되었습니다. 동시 조인 요청이 쏟아지더라도 내부 상태 업데이트 큐(Queue)에 적재한 뒤, 앞선 노드의 토폴로지 계산과 배포가 완전히 끝날 때까지 다음 요청을 처리하지 않아 동시성 충돌을 원천 차단합니다.

// Infinispan 14.x+: 상태 전이 큐를 활용한 트랜잭션 직렬화
public CompletionStage<CacheStatusResponse> doJoin(Address joiner, CacheJoinInfo joinInfo) {
    JoinAction action = new JoinAction(joiner, joinInfo);
    // 요청을 큐(Queue)에 적재하여 단일 쓰레드 기반의 순차 처리 강제
    return clusterTopologyUpdater.queueAction(action) 
        .thenApply(response -> response);
}


버전별 버그 수정 이력

표 2-1. WildFly/JBoss EAP 버전별 Infinispan Race Condition 버그 상태

 

2.3 DNS_PING vs KUBE_PING: 동작 차이와 버그 연관성

동일한 미들웨어 엔진 결함을 내포하고 있음에도, DNS_PING 인프라 환경에서만 장애로 발현되는 이유는 멤버십을 탐색하는 메커니즘 차이에 있습니다.

DNS_PING의 핵심 취약점은 Kubernetes Headless Service의 DNS는 Ready 상태인 Pod의 IP만 반환한다는 것입니다. 복수 Pod가 동시에 시작되면 각자 DNS를 조회할 때 상대방의 IP가 아직 DNS에 등록되지 않은 경우가 생깁니다. 이 경우 각 Pod가 독립 coordinator로 시작하고, MERGE3가 병합을 시도하는 과정에서 Race Condition이 트리거됩니다.

표 2-2. DNS_PING vs KUBE_PING 동작 특성 비교

테스트: DNS_PING과 KUBE_PING

1. 테스트 환경

아래와 같은 환경에서, CPU 70% 이상 부하 상황 시 아래의 케이스를 확인해보고자 했습니다.

표 3-1. 테스트 환경 정보

2. WildFly 26 테스트 시나리오

테스트는 동일한 Kubernetes 환경에서 진행하였으며, JMeter로 부하를 인가하여 Pod CPU 70% 이상 도달 후 스케일 아웃 하는 방식으로 세 가지 시나리오의 클러스터링 안정성을 검증하였습니다.

 

S-1) DNS_PING + 동시 2개 Pod 추가 (2 → 4) = Race Condition 발생

DNS TTL로 인해 신규 Pod들이 서로를 인지하지 못하고 각자 독립 coordinator로 시작, MERGE3가 병합을 시도하지만 Infinispan 13 Race Condition으로 실패합니다. 이 시점부터 ISPN000217 에러가 연속 발생하며 Pod CPU가 급등하고, 사용자 요청에 대해 HTTP 500 에러 페이지가 반환되기 시작합니다.

① Pod 리소스 현황

② JMeter 결과

③ 핵심 로그

15:14:26,901 ERROR [io.undertow.request] UT005023: Exception handling request to /
  Caused by: org.infinispan.remoting.RemoteException:
    ISPN000217: Received exception from wildfly26-dns-...-q4sc2
      Caused by: java.lang.ClassCastException
        at ThrowableExternalizer.readGenericThrowable

  (원격 노드 캐시 엔트리 타입 불일치 → 예외 역직렬화 실패)

 

S-2) DNS_PING + 1개씩 순차 Pod 추가 (2 → 3 → 4) = Race Condition 발생

Race Condition 회피책으로 Pod를 1개씩 순차 추가하는 방식을 테스트하였습니다. 2→3→4 순으로 증설하는 시나리오를 계획하였으나, 2→3 단계에서 즉시 동일한 에러가 발생하여 3→4 증설 자체가 무의미하다고 판단하고 이후 진행을 중단하였습니다. 단일 JOIN 처리 중에도 토폴로지 재계산 불안정 구간에 세션 쓰기 요청이 집중되면서 버그가 트리거됨을 확인하였습니다.

① Pod 리소스 현황

② JMeter 결과

③ 핵심 로그

15:30:39,254 ERROR [io.undertow.request] UT005023: Exception handling request to /
  Caused by: org.infinispan.remoting.RemoteException:
    ISPN000217: Received exception from wildfly26-dns-...-8llqk
      Caused by: java.lang.ClassCastException:
        org.infinispan.container.entries.NullCacheEntry
        cannot be cast to
        org.infinispan.container.entries.MVCCEntry

  (신규 Pod 토폴로지 불완전 설치 → 캐시 엔트리 타입 불일치)

 

S-3) KUBE_PING + 동시 2개 Pod 추가 (2 → 4) = 정상 동작

DNS_PING을 KUBE_PING으로 변경 후 S-1과 동일한 동시 스케일 아웃 시나리오를 수행하였습니다. Kubernetes API를 통해 기존 coordinator를 정확히 파악한 신규 Pod들이 순차적으로 JOIN을 요청하여 Race Condition이 발생하지 않았습니다. 에러 로그는 단 한 건도 발생하지 않았으며, ISPN100010(리밸런싱 완료) 로그와 함께 클러스터가 정상적으로 4노드 구성을 완료하였습니다.

① Pod 리소스 현황

② JMeter 결과

③ 핵심 로그

15:53:25 INFO [ISPN] ISPN100002: Starting rebalance with members
  [dgfzk, t49hp, 92gv6], phase READ_OLD_WRITE_ALL, topology id 6
15:53:25 INFO [ISPN] ISPN100009: Advancing to rebalance phase READ_ALL_WRITE_ALL
15:53:25 INFO [ISPN] ISPN100009: Advancing to rebalance phase READ_NEW_WRITE_ALL
15:53:25 INFO [ISPN] ISPN100010: Finished rebalance with members
  [dgfzk, t49hp, 92gv6, l88ll], topology id 13  ← 4개 노드 정상 조인 완료

 

3. WildFly 26 테스트 결과 종합

표 3-2. WildFly 26 시나리오별 테스트 결과 종합

표 3-3. 제품/버전별 클러스터링 안정성 종합

KUBE_PING 전환 가이드

다음과 같은 순서로 적용합니다. 롤링 업데이트 방식으로 서비스 중단 없이 전환이 가능합니다.

 

1. RBAC 리소스 생성

apiVersion: v1
kind: ServiceAccount
metadata:
  name: wildfly-app-sa
  namespace: <YOUR_NAMESPACE>
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: wildfly-kube-ping
  namespace: <YOUR_NAMESPACE>
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: wildfly-kube-ping-binding
  namespace: <YOUR_NAMESPACE>
subjects:
- kind: ServiceAccount
  name: wildfly-app-sa
  namespace: <YOUR_NAMESPACE>
roleRef:
- kind: ClusterRole
  name: wildfly-kube-ping
  apiGroup: rbac.authorization.k8s.io

 

2. JGroups 설정 변경 (standalone-ha.xml)

<!-- 변경 전: DNS_PING -->
<protocol type="dns.DNS_PING">
  <property name="dns_query">${env.PING_SERVICE_NAME}</property>
</protocol>

<!-- 변경 후: KUBE_PING -->
<protocol type="kubernetes.KUBE_PING">
  <property name="namespace">${env.KUBERNETES_NAMESPACE}</property>
  <property name="labels">${env.KUBERNETES_LABELS}</property>
  <property name="port_range">0</property>
</protocol>

 

3. Deployment 설정 변경

spec:
  template:
    spec:
      serviceAccountName: wildfly-app-sa    # ← 추가
      containers:
      - name: wildfly
        env:
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace

 

4. Headless Service 삭제 (선택사항)

DNS_PING용으로 생성했던 Headless Service는 KUBE_PING 전환 후 더 이상 클러스터링에 사용되지 않으므로 삭제합니다.

결론

WildFly 26은 JDK 8 환경에서 널리 활용되지만, 클라우드 환경에서는 내장된 Infinispan 13의 동시성 제어에 구조적 한계가 존재합니다. 특히 DNS_PING 프로토콜을 사용할 경우 동시 스케일 아웃 과정에서 노드 간 인지 공백이 발생하여 심각한 지연과 에러를 유발합니다. 실제 부하 테스트에서도 DNS_PING은 증설 방식과 무관하게 가용성 저하를 초래한 반면, 실시간 노드 동기화가 가능한 KUBE_PING 환경은 동시 증설 시에도 완벽한 정합성을 유지하며 안정적으로 확장됨이 증명되었습니다.

따라서 단기적으로는 무중단 롤링 업데이트를 통해 KUBE_PING으로 즉시 전환하거나, 인프라 환경 제약이 있다면 동시성 결함이 없는 WildFly 23 또는 JBoss EAP 7.4로 대안을 구성할 것을 권고합니다. 중장기적으로는 관련 문제가 해결된 WildFly 27 이상 버전으로 업그레이드하거나, 세션 관리를 Redis와 같은 외부 저장소로 분리하는 전략을 검토해야 합니다. 다만 WildFly 27 이상으로 전환 시에는 애플리케이션을 JDK 11 이상(~21) 기반으로 마이그레이션하는 호환성 검증이 필요하며, Redis 도입 역시 애플리케이션 코드 및 설정 수정이 수반되므로 충분한 공수를 고려하여 단계별로 추진해야 합니다.

마치며

클라우드 네이티브 아키텍처로의 전환이 가속화되는 가운데, 엔터프라이즈 IT 환경에는 여전히 수많은 레거시 시스템이 존재합니다. JDK 8에 강하게 결합된 시스템을 Kubernetes 환경으로 이관하는 시도는 예측하기 어려운 문제들을 수반합니다. 본 리포트에서 다룬 WildFly 26 + DNS_PING + Kubernetes의 클러스터링 이슈는 그 대표적인 사례입니다.

본 리포트가 JDK 8 레거시 환경을 유지하면서 Kubernetes로의 전환을 준비하거나 이미 운영 중인 엔지니어들에게 실질적인 도움이 되기를 바랍니다.

# References

[1] WildFly 26 공식 문서: https://docs.wildfly.org/26/
[2] Infinispan 13.0 릴리스 노트: https://infinispan.org/docs/13.0.x/
[3] JGroups 4.2 공식 문서: http://jgroups.org/manual4/index.html
[4] ISPN 이슈 트래커: https://issues.redhat.com/projects/ISPN
[5] Kubernetes RBAC: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
[6] WildFly DNS_PING 가이드: https://www.wildfly.org/news/2021/03/12/Configuring-DNS-ping-on-a-WildFly-Bootable-JAR-cluster-application/

민준영 프로

민준영 프로

오픈소스사업부 오픈소스기술팀

에스코어에서 미들웨어 엔지니어로 근무하고 있으며, 삼성 그룹사 및 국내 주요 대기업, 공공기관의 미들웨어 설계 및 기술지원을 담당하고 있습니다.

연관 아티클

  • AI Agent 시대의 오픈소스 인프라 재편:  보안과 운영 관점에서
    SW 테크놀로지2026.06.21

    AI Agent 시대의 오픈소스 인프라 재편: 보안과 운영 관점에서

    자세히 보기
  • 2026.06.16

    "무작정 압축하지 마라" 4가지 실측 데이터로 보는 PostgreSQL 백업의 진실

    자세히 보기
  • Kafka 운영의 갈림길: 전통적 서버의 성능인가, 쿠버네티스의 자동화인가?
    2026.05.04

    Kafka 운영의 갈림길: 전통적 서버의 성능인가, 쿠버네티스의 자동화인가?

    자세히 보기