카테고리 보관물: IT

Kubernetes 기본 설치(Version 1.28)

cri-dockerd와 Calico로 구성하는 단일 마스터 클러스터

이 글은 Ubuntu 22.04 환경에서 kubeadm으로 1.28 클러스터를 구성한 Kubernetes 기본 설치 기록입니다. Kubernetes 1.24부터 Docker shim이 제거되었기 때문에 Docker 런타임을 계속 사용하려면 cri-dockerd를 컨테이너 런타임 인터페이스(CRI)로 별도 설치해야 합니다. CNI 플러그인은 Calico를 사용하며, 마스터 노드 1개와 워커 노드 2개로 구성하는 것을 기준으로 작성하였습니다.

버전 범위 안내: 이 글은 Kubernetes 1.28.2와 Ubuntu 22.04.3에서 검증한 과거 구축 기록입니다. Kubernetes 1.28은 현재 지원이 종료된 버전이므로 신규 구축 절차로 그대로 사용하지 말고, Kubernetes 공식 지원 상태와 최신 설치 문서를 먼저 확인하시기 바랍니다.

이 Kubernetes 기본 설치 가이드는 단일 마스터 클러스터 기준입니다. HA(고가용성) 멀티 마스터 구성은 HAProxy 로드밸런서와 etcd 클러스터 구성이 추가로 필요합니다.

모든 노드 공통 준비 후 마스터의 kubeadm init과 Calico 적용, 워커 2대의 kubeadm join, kubectl get nodes 확인으로 이어지는 Kubernetes 기본 설치 순서 구성도
Kubernetes 기본 설치 순서 — 모든 노드에 공통 준비를 마친 뒤 마스터에서 init과 Calico를, 워커에서 join을 실행합니다.

구성 내역

설치 환경은 다음과 같습니다.

Kubernetes 1.28.2
Ubuntu 22.04.3 LTS
Container : cri-dockerd
CNI : calico
구성용 계정 : <admin-user>
Master node : <master-node>
Worker node : <worker-node-01> ~ <worker-node-02>

시작 전 모든 노드에서 swap을 비활성화해야 합니다. kubelet은 swap이 활성화된 환경에서 기본적으로 동작을 거부합니다.

sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#/g' /etc/fstab

[공통]

cri-docker 설치

Kubernetes 1.24 이후 Docker shim이 제거되면서 Docker를 컨테이너 런타임으로 사용하려면 cri-dockerd를 별도로 설치해야 합니다. cri-dockerd는 Mirantis가 관리하는 오픈소스 프로젝트로, Docker Engine과 kubelet 사이의 CRI(Container Runtime Interface) 어댑터 역할을 합니다. 먼저 Docker Engine을 설치한 뒤 cri-dockerd 바이너리를 받아 systemd 서비스로 등록합니다.

Docker cgroup driver를 systemd로 맞추는 것이 중요합니다. kubelet도 기본적으로 systemd cgroup driver를 사용하므로, driver 불일치 시 노드가 NotReady 상태가 됩니다.

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo systemctl enable --now docker && sudo systemctl status docker --no-pager
sudo usermod -aG docker <admin-user>
sudo docker container ls

# cri-docker Install
VER=$(curl -s https://api.github.com/repos/Mirantis/cri-dockerd/releases/latest|grep tag_name | cut -d '"' -f 4|sed 's/v//g')
echo $VER
wget https://github.com/Mirantis/cri-dockerd/releases/download/v${VER}/cri-dockerd-${VER}.amd64.tgz
tar xvf cri-dockerd-${VER}.amd64.tgz
sudo mv cri-dockerd/cri-dockerd /usr/local/bin/

# cri-docker Version Check
cri-dockerd --version

wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.service
wget https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.socket
sudo mv cri-docker.socket cri-docker.service /etc/systemd/system/
sudo sed -i -e 's,/usr/bin/cri-dockerd,/usr/local/bin/cri-dockerd,' /etc/systemd/system/cri-docker.service

sudo systemctl daemon-reload
sudo systemctl enable cri-docker.service
sudo systemctl enable --now cri-docker.socket

# cri-docker Active Check
sudo systemctl restart docker && sudo systemctl restart cri-docker
sudo systemctl status cri-docker.socket --no-pager

# Docker cgroup Change Require to Systemd
sudo mkdir /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF

sudo systemctl restart docker && sudo systemctl restart cri-docker
sudo docker info | grep Cgroup

환경 설정

Kubernetes 네트워킹이 정상 동작하려면 커널 모듈과 sysctl 파라미터를 설정해야 합니다. br_netfilter 모듈은 Linux Bridge를 통과하는 패킷을 iptables/ip6tables에서 처리할 수 있게 해주며, overlay 모듈은 컨테이너 파일시스템에 사용하는 OverlayFS를 위해 필요합니다. net.ipv4.ip_forward = 1은 노드가 패킷을 다른 노드로 포워딩할 수 있도록 허용합니다. 이 설정은 모든 노드(마스터 + 워커)에 적용해야 합니다.

# Kernel Forwarding
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

sudo sysctl --system

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# 필요한 sysctl 파라미터를 설정하면, 재부팅 후에도 값이 유지된다.
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

# 재부팅하지 않고 sysctl 파라미터 적용하기
sudo sysctl --system

Package 설치

kubeadm, kubelet, kubectl 세 패키지를 설치합니다. 각 패키지의 역할은 다음과 같습니다.

  • kubeadm — 클러스터 초기화(init)와 노드 합류(join)를 담당하는 부트스트랩 도구
  • kubelet — 각 노드에서 실행되며 Pod 생명주기를 관리하는 에이전트
  • kubectl — 클러스터를 제어하는 CLI 클라이언트

apt-mark hold로 패키지를 고정하면 apt upgrade로 인한 의도치 않은 버전 업그레이드를 방지할 수 있습니다. Kubernetes는 컴포넌트 버전을 맞춰 관리해야 하므로 반드시 hold 설정을 유지하고, 업그레이드 시에는 kubeadm upgrade 절차를 따릅니다.

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl

sudo curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg https://dl.k8s.io/apt/doc/apt-key.gpg && echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

sudo systemctl daemon-reload
sudo systemctl restart kubelet

위 방법으로 패키지를 찾지 못하는 경우(E: Unable to locate package kubelet), pkgs.k8s.io 신규 리포지토리를 사용합니다.

$ sudo apt-get install -y kubelet kubeadm kubectl
E: Unable to locate package kubelet
E: Unable to locate package kubeadm
E: Unable to locate package kubectl

# kubelet kubeadm kubectl 설치시 위 에러 발생할 경우 아래 방안으로 설치
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

sudo systemctl daemon-reload
sudo systemctl restart kubelet

[Master node]

kubeadm init으로 마스터 노드를 초기화합니다. --cri-socket 옵션으로 cri-dockerd 소켓을 명시하지 않으면 kubeadm이 런타임을 자동 감지하는 과정에서 오류가 발생할 수 있습니다. 초기화가 완료되면 출력 마지막에 워커 노드 join 명령어가 표시됩니다. 이 명령어의 토큰은 24시간 후 만료되므로 즉시 복사해 두거나 kubeadm token create --print-join-command로 재발급합니다.

kubeconfig 설정 후 Calico CNI를 설치합니다. CNI가 설치되기 전까지는 노드 상태가 NotReady로 표시되며, calico.yaml 적용 후 수 분 내에 Ready로 전환됩니다.

sudo kubeadm config images pull --cri-socket unix:///run/cri-dockerd.sock
sudo kubeadm init --cri-socket /var/run/cri-dockerd.sock

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

kubectl get nodes -o wide
kubectl get pods -A
kubectl describe node <master-node>

# Calico CNI 설치
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -O
kubectl apply -f calico.yaml

kubectl get nodes
kubectl get pod --all-namespaces

[worker node]

워커 노드에서 kubeadm join 명령어를 실행하여 클러스터에 합류합니다. kubeadm init 출력에서 복사한 명령어를 그대로 사용하되, --cri-socket 옵션을 추가해야 합니다. join이 완료된 후 마스터 노드에서 kubectl get nodes를 실행하여 워커 노드가 Ready 상태로 등록되는지 확인합니다.

# kubeadm init 실행시 마지막 출력되는 명령어 사용
sudo kubeadm join x.x.x.x:6443 --token xxxxxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxx --cri-socket /var/run/cri-dockerd.sock

kubectl get nodes

kubectl 명령어 자동 완성

kubectl bash completion을 설정하면 Tab 키로 리소스명, 네임스페이스 등을 자동 완성할 수 있어 운영 편의성이 크게 향상됩니다. alias k=kubectl과 함께 설정하면 짧은 명령어로 빠르게 조작할 수 있습니다. 자세한 내용은 kubectl 자동 완성 설정 — Kubernetes 공식 문서를 참고하세요.

echo 'source <(kubectl completion bash)' >> ~/.bashrc
echo 'alias k=kubectl' >> ~/.bashrc
echo 'complete -o default -F __start_kubectl k' >> ~/.bashrc
source ~/.bashrc

노드 Ready와 kube-system 파드 상태 확인

모든 노드가 정상적으로 등록되고 kube-system 파드들이 Running 상태가 되면 클러스터 구성이 완료된 것입니다. 아래 명령어로 최종 상태를 확인합니다.

# 노드 상태 확인 — 모든 노드 Ready 여야 정상
kubectl get nodes -o wide

# 시스템 파드 상태 확인 — 모두 Running 또는 Completed
kubectl get pods -n kube-system

# 클러스터 정보 확인
kubectl cluster-info

워커 노드가 NotReady로 유지될 경우 해당 노드에서 sudo systemctl status kubelet과 journalctl -u kubelet -n 50으로 에러 메시지를 확인합니다. 대부분은 cgroup driver 불일치 또는 cri-dockerd 미기동으로 인한 문제입니다.

참고

참고 문서: kubeadm으로 클러스터 생성 — Kubernetes 공식 문서 · Calico 온프레미스 설치 문서

Hazelcast Client XML Fails with cvc-complex-type.2.3: A Hidden U+FEFF in Indentation

세션 클러스터링이 멈춘 원인은 보이지 않는 문자 하나

Hazelcast 세션 클러스터링을 붙인 WAS에서 클라이언트 설정 XML이 cvc-complex-type.2.3 검증 오류로 거부되고, 그 여파로 세션 저장이 NullPointerException으로 실패했던 기록입니다. 설정 값 자체는 맞았습니다.

원인은 눈에 보이지 않는 U+FEFF 문자였습니다. 파일 첫머리의 BOM이 아니라 태그 앞 들여쓰기에 섞여 있었기 때문에, 흔히 쓰는 BOM 제거 방법으로는 해결되지 않았습니다.

WAS 로그에 남은 두 줄의 경고

로그에는 경고 두 줄이 연달아 남았습니다. 첫 줄은 Hazelcast 클라이언트가 설정을 읽다가 실패한 것이고, 둘째 줄은 연결되지 않은 클라이언트로 세션을 쓰려다 난 NullPointerException입니다.

09:52:06,504 WARNING [com.hazelcast.web.ClusteredSessionService] (default task-1) Cannot connect to Hazelcast server: cvc-complex-type.2.3: Element 'near-cache' cannot have character [children], because the type's content type is element-only.
09:52:06,962 WARNING [com.hazelcast.web.HazelcastHttpSession] (default task-1) Unexpected error occurred.: java.lang.NullPointerException
 at com.hazelcast.web.ClusteredSessionService.updateAttributes(ClusteredSessionService.java:285)
 at com.hazelcast.web.HazelcastHttpSession.sessionDeferredWrite(HazelcastHttpSession.java:300)
 at com.hazelcast.web.WebFilter.doFilter(WebFilter.java:303)
 at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
 at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
 ...

첫 경고만 보면 Hazelcast 서버에 연결하지 못한 네트워크 문제처럼 읽힙니다. XML 스키마 검증 오류 코드가 메시지 뒤쪽에 붙어 있다는 점이 단서였습니다. 클라이언트가 설정을 읽는 단계에서 멈췄기 때문에 연결은 시도조차 못 한 것입니다. Undertow 기반 WAS였다는 것은 스택의 io.undertow에서 알 수 있습니다.

오류 문구가 가리키는 것

“Element ‘near-cache’ cannot have character [children]”은 near-cache 요소 안에 자식 요소가 아닌 글자가 들어 있다는 뜻입니다. XML 스키마에서 element-only로 정의된 요소는 자식 요소와 공백만 가질 수 있습니다. 여기서 공백은 XML 규격이 정한 스페이스, 탭, CR, LF 네 가지뿐입니다.

U+FEFF(Zero Width No-Break Space)는 이 네 가지에 속하지 않습니다. 화면에는 아무것도 보이지 않지만 검증기에게는 엄연한 문자 내용입니다. 그래서 들여쓰기에 하나만 섞여도 같은 오류가 납니다. Java 검증기는 이 규칙 위반을 cvc-complex-type.2.3으로 표시하고, libxml2는 “Character content other than whitespace is not allowed”로 표시합니다.

cvc-complex-type.2.3 오류 개념도: hazelcast-client.xml의 near-cache 들여쓰기에 숨은 U+FEFF가 element-only 규칙을 어겨 클라이언트 설정 로드가 실패하는 과정
들여쓰기에 섞인 U+FEFF가 cvc-complex-type.2.3 오류로 이어지는 과정과, 흔한 확인 방법이 이를 놓치는 이유입니다.

BOM 없는 UTF-8로 저장해도 남아 있던 이유

처음에는 Windows 에디터가 붙이는 BOM을 의심했습니다. 그런데 에디터에서 BOM 없는 UTF-8로 저장하는 방법이 통하지 않았고, 태그 앞 여백을 모두 지운 뒤에야 오류가 사라졌습니다.

BOM은 파일 맨 앞 3바이트(EF BB BF)를 가리키고, 에디터의 “BOM 없이 저장”도 그 3바이트만 뺍니다. U+FEFF가 줄 중간에 있으면 BOM이 아니라 평범한 문자로 취급되어 그대로 남습니다. HTML 문서나 웹 페이지에서 설정 예제를 복사해 붙일 때 이런 문자가 함께 딸려 오기도 합니다.

숨은 문자를 찾고 지우는 방법

아래는 같은 상황을 재현한 예제 파일로 확인한 결과입니다. 4번째 줄의 들여쓰기에 U+FEFF를 하나 넣었습니다.

$ file hazelcast-client.xml
hazelcast-client.xml: XML 1.0 document, Unicode text, UTF-8 text

$ grep -c $'\xEF\xBB\xBF' hazelcast-client.xml
1

$ grep -nP '\x{FEFF}' hazelcast-client.xml | cat -A
4:    M-oM-;M-?<in-memory-format>OBJECT</in-memory-format>$

file은 BOM이 파일 첫머리에 있을 때만 “with BOM”이라고 알려 줍니다. 그래서 이 경우는 그냥 UTF-8 텍스트로 보고합니다. EF BB BF 바이트 열을 grep으로 직접 찾는 편이 확실하고, cat -A로 보면 M-oM-;M-? 표시로 위치까지 드러납니다.

지울 때는 첫 줄만 고치는 sed '1s/^\xEF\xBB\xBF//'로는 부족합니다. 파일 전체에서 바꿔야 줄 중간의 문자까지 사라집니다.

$ sed -i 's/\xEF\xBB\xBF//g' hazelcast-client.xml
$ grep -c $'\xEF\xBB\xBF' hazelcast-client.xml
0

제거 전의 예제 파일을 최소 스키마로 검증하면 libxml2가 near-cache 요소에서 element-only 위반을 보고했습니다. 제거한 뒤에는 같은 스키마로 검증을 통과했습니다.

다시 겪지 않으려면

  • 설정 파일을 배포하기 전에 grep -rl $'\xEF\xBB\xBF' <설정 디렉터리>로 한 번 훑습니다. CI 단계에 넣어 두면 사람이 놓쳐도 걸러집니다.
  • VS Code는 보이지 않는 유니코드 문자를 강조하는 editor.unicodeHighlight.invisibleCharacters 설정을 제공합니다. Windows에서 설정 파일을 편집한다면 이 설정이 켜져 있는지 확인합니다.
  • .editorconfig의 charset = utf-8은 파일 첫머리 BOM만 막아 줍니다. 줄 중간의 문자는 따로 검사해야 합니다.

참고

관련 포스트:

참고 문서: XML 1.0 공백 정의 · XML Schema cvc-complex-type 규칙 · Unicode BOM FAQ · 바이트 순서 표식(위키백과)

Hazelcast WEB Session Clustering

WAS 장애에도 세션을 잃지 않는 IMDG 공유 방식

Hazelcast WEB Session Clustering 구성은 IMDG(In-Memory Data Grid)를 이용해 로드밸런서로 묶인 WAS 그룹 간에 세션 정보를 공유합니다. 특정 WAS 노드에 장애가 발생해도 세션 데이터를 잃지 않고 다른 노드에서 세션을 이어받을 수 있어 고가용성(HA) 웹 서비스 환경에서 필수 구성 요소입니다. 이 글에서는 Hazelcast 3.12 기준으로 web.xml과 hazelcast-client.xml 설정 방법을 정리합니다.

버전 범위 안내: 이 글은 Hazelcast 3.12와 JDK 8에서 검증한 레거시 구성 기록입니다. 현재 Hazelcast 5.x 계열은 모듈과 설정 방식에 차이가 있으므로 신규 구성에는 이 예제를 그대로 적용하지 말고 Hazelcast 최신 공식 문서를 기준으로 검토하시기 바랍니다.

개념 (Concept)

기본 목적은 로드 밸런싱으로 묶인 WAS 그룹간의 세션 정보의 공유이며 그룹내의 임의의 WAS가 Fail over되어도 세션 정보를 유실하지 않고 다른 WAS에서 세션 연결을 가능케 하는 것입니다.

그로 인해 부가적으로 이기종 WAS간에도 세션 공유를 가능케 할 수 있다는 큰 이점이 있습니다.

구동 방식

  1. 사용자의 request가 was에 도달(servlet container)
  2. request의 session id를 대조 하는 절차 진행시(WAS에서 세션 정보 조회 전)
  3.  webfilter를 통해 request의 쿠키가 가진 session id를 IMDG의 맵에서 조회
  4. 조회 값을 반환 할 경우 기존 session으로 할당 처리( 후 request 응답 처리)
  5. 조회 값이 없을 경우 webfilter를 통해 지정된 형식으로 session id 생성 후 그 id로 sessionlistener를 통해 httpsession 생성
  6. request가 완료되면 생성된 session 정보를 IMDG 맵에 저장

IMDG 기반 분산 세션 관리

노드들을 클러스터 구성 하여 여러 서버의 메모리를 파티셔닝 또는 샤딩이라는 기술을 통해 하나의 메모리 공간처럼 활용, 데이터를 임의의 노드 메모리에 분산 저장시 실시간 또는 비동기로 생성된 백업 데이터는 원본 데이터를 소유한 노드 이외의 다른 노드에 분산 저장됩니다.
이 기능을 이용하여 세션 정보의 손실을 방지 하며 빠른 I/O 성능을 보장합니다.

L4 로드밸런서 뒤 WAS A와 WAS B의 WebFilter가 Hazelcast IMDG 맵에서 세션을 조회하고 저장하는 Hazelcast Session Clustering 구성도와 처리 순서
Hazelcast Session Clustering 흐름 — WebFilter가 쿠키의 session id로 IMDG 맵을 조회하고, 요청이 끝나면 세션을 맵에 저장합니다.

구성 (hazelcast 3.12, JDK 1.8)

  1. WEB-INF에 web.xml 및 hazelcast-client.xml 설정
  2. WEB-INF/lib 또는 Classpath에 hazelcast-wm.jar(hazelcast-all.jar 가능) 위치

web.xml 설정

<!-- hazelcast filter start -->
<filter>
    <filter-name>hazelcast-filter</filter-name>
    <filter-class>com.hazelcast.web.WebFilter</filter-class>
    <init-param>
        <param-name>map-name</param-name>
        <param-value>hazelcast-sessions</param-value>
    </init-param>
    <init-param> <!-- Embedded only -->
        <param-name>session-ttl-seconds</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>keep-remote-active</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>sticky-session</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>
        <param-name>cookie-name</param-name>
        <param-value>hazelcast.sessionId</param-value>
    </init-param>
    <init-param> <!-- 도메인 없을 때 비울것 -->
        <param-name>cookie-domain</param-name>
        <param-value>www.example.com</param-value>
    </init-param>
    <init-param>
        <param-name>cookie-secure</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>cookie-http-only</param-name>
        <param-value>false</param-value><!-- default false -->
    </init-param>
    <init-param>
        <param-name>debug</param-name>
        <param-value>true</param-value><!-- default false -->
    </init-param>
    <init-param>
        <param-name>shutdown-on-destroy</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>    <!--    이부분이 설정파일의 위치(Embaded 설정)    -->
        <param-name>config-location</param-name>
        <param-value>/WEB-INF/config/spring/my-hazelcast.xml</param-value>
    </init-param>
    <init-param>
        <param-name>instance-name</param-name>
        <param-value>hazel-ses</param-value>
    </init-param>
    <init-param> <!-- Client mode only -->
        <param-name>use-client</param-name>
        <param-value>true</param-value>
    </init-param>
    <init-param>  <!-- Client Configuration(or hazelcast-client.xml) -->
        <param-name>client-config-location</param-name>
        <param-value>/WEB-INF/classes/hazelcast-client.properties</param-value>
    </init-param>
    <init-param>
        <param-name>deferred-write</param-name>
        <param-value>true</param-value><!-- default false -->
    </init-param>
    <init-param>
        <param-name>cookie-path</param-name>
        <param-value>/</param-value>
    </init-param>
    <init-param>
        <param-name>cookie-max-age</param-name>
        <param-value>-1</param-value>
    </init-param>
    <init-param>
        <param-name>use-request-parameter</param-name>
        <param-value>false</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>hazelcast-filter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

<listener>
    <listener-class>com.hazelcast.web.SessionListener</listener-class>
</listener>
<!-- hazelcast filter end -->
Topic Desc Default
map-name 세션 오브젝트가 저장될 분산맵의 이름 선언
session-ttl-seconds 세션 오브젝트가 저장될 분산맵의 수명(초) 설정, 0이상의 정수형(최대2147483647)으로 설정 1800(30분)
sticky-session true 설정시, 모든 세션 요청이 세션이 처음 만들어진 멤버로 라우팅 됨. 더 좋은 성능을 제공함. false 설정시, 세션이 멤버상에서 업데이트 될 때, 모든 멤버상의 이 세션 엔트리는 무효화 됨.
이 파라메터를 설정하기 전에 로드 밸런서(L4) 구성상황을 알 필요가 있음
true
Generic Parameter 의미
  • Client config 구성 ( hazelcast-client.xml )
<hazelcast-client>
...
    <group>
        <name>dev</name>
        <password>dev-pass</password>
    </group>

    <network>
        <cluster-members>
            <!--MEMBERS-->
            <address>192.168.x.x</address>
            <address>192.168.x.x</address>
            <address>192.168.x.x</address>
        </cluster-members>
    </network>
    <load-balancer type="round-robin"/>

...
</hazelcast-client>

참고

관련 포스트:

참고 문서: Hazelcast Web Session Replication 공식 문서 · Hazelcast IMDG 개요

MikroTik wireless Config

RB2011 wlan1을 ap-bridge로 켜는 기본 설정

RouterOS 무선 인터페이스를 이용한 MikroTik Wireless AP 설정 방법을 정리합니다. AP(Access Point) 모드와 Station(Client) 모드 구성을 포함하며, WPA2 보안 설정, SSID 설정, Bridge 연동까지 다룹니다. MikroTik 소형 라우터를 무선 AP 또는 클라이언트 브리지로 활용할 때 참고하세요.

버전 범위 안내: 이 글은 MikroTik RB2011의 legacy wireless 패키지를 기준으로 작성한 구성 기록입니다. RouterOS 버전과 장비의 무선 패키지에 따라 메뉴와 명령이 다를 수 있으므로 적용 전에 MikroTik 공식 Wireless 문서와 설치된 패키지를 확인하시기 바랍니다.

  • 미크로틱 RB2011 기준으로 작성하였습니다.
  • 한국 환경에 맞도록 설정 하였습니다.
/interface wireless security-profiles
set [ find default=yes ] supplicant-identity=MikroTik
add authentication-types=wpa2-psk eap-methods="" management-protection=\
    allowed mode=dynamic-keys name=profile1 supplicant-identity="" \
    wpa-pre-shared-key=password wpa2-pre-shared-key=password
/interface wireless
set [ find default-name=wlan1 ] antenna-gain=0 band=2ghz-onlyn country=\
    "korea republic" disabled=no frequency=auto mode=ap-bridge \
    mtu=1600 security-profile=profile1 ssid=MySSID wps-mode=disabled
  • password와 MySSID는 원하는 값으로 변경 하시면 됩니다.
  • 설정 이후 frequency는 auto로 사용하여도 큰 지장이 없지만 사용하지 않는 대역을 확인하여 Winbox에서 변경해주시는 것이 좋습니다.

AP 모드 vs Station 모드 비교

MikroTik 무선 인터페이스는 AP(Access Point) 모드와 Station 모드로 동작할 수 있습니다. AP 모드는 클라이언트 장비(노트북, 스마트폰 등)가 접속하는 무선 기지국 역할을 하며, Station 모드는 MikroTik이 다른 AP에 클라이언트로 접속하는 방식입니다. 이 글의 구성은 RB2011에서 무선 인터페이스를 AP 모드로 설정하여 유선 LAN 브리지에 통합하는 방식입니다.

AP 모드에서는 SSID, 보안 프로파일, 채널, 주파수 대역을 직접 지정합니다. 브리지에 무선 인터페이스를 포함시키면 유선 포트와 무선 클라이언트가 동일한 L2 네트워크를 공유하여 별도의 라우팅 없이 통신할 수 있습니다.

Security Profile을 적용한 wlan1(ap-bridge)을 bridge-lan에 넣어 무선 클라이언트와 유선 장비가 같은 L2를 공유하는 RB2011의 MikroTik Wireless AP 설정 구성도
Security Profile을 적용한 wlan1을 bridge-lan에 넣어 유선 포트와 같은 L2로 묶는 RB2011 구성입니다.

WPA2 보안 프로파일 설정

MikroTik에서 무선 보안은 Security Profile로 분리 관리합니다. 프로파일에 인증 방식(WPA2 PSK), 암호화 알고리즘(aes ccm), 사전 공유 키(WPA2 Pre-Shared Key)를 정의한 후 무선 인터페이스에 적용합니다. 이를 통해 여러 SSID에서 동일한 보안 프로파일을 재사용하거나, SSID별 다른 보안 정책을 독립적으로 관리할 수 있습니다.

# 보안 프로파일 생성 (WPA2-PSK, AES 암호화)
/interface wireless security-profiles
add authentication-types=wpa2-psk mode=dynamic-keys     name=wpa2-home wpa2-pre-shared-key=<REDACTED>

# 무선 인터페이스에 보안 프로파일 적용
/interface wireless
set wlan1 mode=ap-bridge ssid=HomeWiFi     security-profile=wpa2-home band=2ghz-b/g/n

# 무선 인터페이스를 브리지에 추가
/interface bridge port
add bridge=bridge-lan interface=wlan1

WPA2-PSK 키는 보안상 설정 파일이나 백업 파일에 평문으로 노출되지 않도록 주의합니다. RB2011 등 구형 장비에서 WPA3 지원 여부는 RouterOS 버전 및 하드웨어에 따라 다르므로, 최신 RouterOS로 업그레이드한 후 지원 여부를 확인하는 것이 좋습니다.

채널 설정과 간섭 최소화

무선 네트워크 성능을 최적화하려면 인접 AP와 채널이 겹치지 않도록 설정해야 합니다. 2.4GHz 대역에서 서로 간섭을 주지 않는 채널 조합은 1, 6, 11입니다. MikroTik에서는 /interface wireless 설정에서 channel-width와 frequency 또는 band를 지정합니다.

5GHz 대역은 채널 수가 많아 간섭이 적고 최대 속도도 높지만, 장애물 투과력이 2.4GHz보다 낮아 좁은 범위에서 더 적합합니다. RB2011 시리즈의 무선 카드 사양에 따라 지원하는 주파수 대역과 최대 속도가 다르므로, /interface wireless info 명령으로 지원 사양을 먼저 확인한 후 채널을 설정합니다.

참고

참고 문서: MikroTik Wireless AP Client 가이드 · MikroTik Bridge 인터페이스 설정