Kubernetes에서 Wildfly 가용성(High-Availability) 구성
몇 년 전부터 기술 스택과 환경이 극적으로 변화하면서 Monolithic Architecture 보다 MicroService Architecture 가 주목 받고 있지만 여전히 많은 웹 애플리케이션 서버(WAS) 와 JEE 애플리케이션이 프로덕션 환경에서 실행되고 있습니다.
대표적인 웹 애플리케이션 서버인 Wildfly를 예로 들어 Kubernetes 에서 여러 WAS 컨테이너를 관리하고 운영할 수 있는 방법에 대해 알아보겠습니다.

💬 Wildfly High-Availability
WildFly는 배포된 Java EE 애플리케이션의 가용성을 보장하는 기능을 제공합니다.
장애 조치 : 노드 장애가 있는 경우에도 클라이언트가 해당 애플리케이션에 중단 없이 액세스 가능합니다.
로드밸런싱 : 대량의 요청이 있는 경우에도 적시에 애플리케이션은 응답 할 수 있습니다.
🧰 Wildfly Kube-ping
Wildfly HA 구성은 TCP/UDP 프로토콜을 통해서 가능합니다. 반면 Kubernetes에서는 CNI 에 따라 UDP Multicast를 지원하지 않기 때문에 그런 경우에는 TCP를 사용해야합니다.
하지만 TCP 구성은 인스턴스 IP를 고정해야하기 때문에 Pod AutoScale을 할 수 없고 Pod IP를 고정해야하는 문제점을 가지고 있습니다. 이러한 문제점을 해결할 수 있도록 Kubernetes 환경에서 Wildfly 는 KUBE_PING 프로토콜을 지원하고 있습니다.
KUBE_PING 은 Kubernetes에서 클러스터 노드 검색 프로토콜입니다.
이번 글에서는 kube-ping을 사용하여 Kubernetes 에서 Wildfly HA 구성을 할 수 있는 방법에 대해 알아보도록 하겠습니다.
예제 파일은 아래 링크에서 확인해보실 수 있습니다.
🔗 https://github.com/mantech-accordion/wildfly-kubeping
사전환경
🔧 Kubernetes 1.18.12
🔧 Wildfly 23.0.1 Final
👨💻 Wildfly Image 작성
먼저, wildfly 베이스 이미지에 kube-ping 기능을 설정하는 embedded server 스크립트(config-wildfly.cli)을 포함하도록 Dockerfile을 작성합니다.
wildfly 버전에 따라 kube-ping 모듈을 별도 설치해야 할 수도 있습니다.
📄 Dockerfile
FROM jboss/wildfly:23.0.1.Final
LABEL MAINTAINER bskim <bskim@mantech.co.kr>
ADD config-wildfly.cli /opt/jboss/
ADD cluster.war /opt/jboss/wildfly/standalone/deployments/
RUN /opt/jboss/wildfly/bin/add-user.sh admin accordion --silent \
&& /opt/jboss/wildfly/bin/jboss-cli.sh --file=config-wildfly.cli \
&& rm -Rf /opt/jboss/wildfly/standalone/configuration/standalone_xml_history/*
EXPOSE 8080 9990 7600 8888
📄 standalone-ha.xml
스크립트가 실행되면 설정은 파일은 아래와 같이 수정됩니다.
<subsystem xmlns="urn:jboss:domain:jgroups:8.0">
<channels default="ee">
<channel name="ee" stack="tcp" cluster="kubernetes"/>
</channels>
<stack name="tcp">
<transport type="TCP" socket-binding="jgroups-tcp"/>
<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>
<property name="masterHost">kubernetes.default.svc</property>
<property name="masterPort">443</property>
</protocol>
<protocol type="MERGE3"/>
<socket-protocol type="FD_SOCK" socket-binding="jgroups-tcp-fd"/>
<protocol type="FD_ALL"/>
<protocol type="VERIFY_SUSPECT"/>
<protocol type="pbcast.NAKACK2"/>
<protocol type="UNICAST3"/>
<protocol type="pbcast.STABLE"/>
<protocol type="pbcast.GMS"/>
<protocol type="MFC"/>
<protocol type="FRAG3"/>
</stack>
...
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
<socket-binding name="http" port="${jboss.http.port:8080}"/>
<socket-binding name="https" port="${jboss.https.port:8443}"/>
<socket-binding name="jgroups-mping" interface="private" multicast-address="${jboss.default.multicast.address:230.0.0.4}" multicast-port="45700"/>
<socket-binding name="jgroups-tcp" interface="kubernetes" port="7600"/>
<socket-binding name="jgroups-tcp-fd" interface="kubernetes" port="57600"/>
👨💻 ServiceAccount, ClusterRole, ClusterRoleBinding 생성
kubernetes 에서 kube-ping을 사용하기 위해 클러스터 리소스에 접근 권한이 필요합니다. Kubneretes의 ClusterRole, ClusterRoleBinding 생성하여 kube-ping에 필요한 클러스터 리소스 권한을 얻을 수 있습니다.
$ export TARGET_NAMESPACE=shop
$ kubectl apply -f https://raw.githubusercontent.com/mantech-accordion/wildfly-kubeping/main/wildfly-sa-role.yaml -n $TARGET_NAMESPACE
📄 yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: jgroups-kubeping-service-account
namespace: $TARGET_NAMESPACE
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: jgroups-kubeping-pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: jgroups-kubeping-api-access
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: jgroups-kubeping-pod-reader
subjects:
- kind: ServiceAccount
name: jgroups-kubeping-service-account
namespace: $TARGET_NAMESPACE
👨💻 Wildfly Deployment, Service 생성
마지막으로 kube-ping이 설치된 wildfly 이미지를 배포할 차례입니다. Deployment, Service 를 생성합니다.
$ kubectl apply -f https://raw.githubusercontent.com/mantech-accordion/wildfly-kubeping/main/wildfly-deploy.yaml -n $TARGET_NAMESPACE
Deployment yaml 에 serviceAccountName: jgroups-kubeping-service-account 를 명시해야 Wildfly cluster member를 찾을 수 있습니다.
env에 명시된 POD_IP, KUBERNETES_NAMESPACE, KUBERNETES_LABELS를 통해 wildfly cluster를 namespace, labels로 분리할 수 있습니다.
📄 yaml
spec:
serviceAccountName: jgroups-kubeping-service-account
...
env:
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: KUBERNETES_LABELS
value: app=wildfly
...
👨💻 Test Cluster
생성된 Wildfly Pod 로그에서 deployment replicas 만큼 cluster member가 같은지 확인할 수 있습니다.
[org.infinispan.CLUSTER] (thread-4,null,wildfly-6bf8b74bd8-fflmc) ISPN000094: Received new cluster view for channel kubernetes: [wildfly-6bf8b74bd8-fflmc|1] (2) [wildfly-6bf8b74bd8-fflmc, wildfly-6bf8b74bd8-24cg
웹브라우저에서 배포된 테스트 애플리케이션을 통해 session 테스트 할 수 있습니다.
nodeid에 pod 이름이 보이고 pod 가 종료되었을때 sessionid 가 변경되지 않고 유지되는 것을 확인할 수 있습니다.

Kubernetes 환경에서도 자동확장이 가능한 Wildfly HA 구성 방법에 대해 알아보았습니다. 👍
