devlos
Devlos Archive
devlos
전체 방문자
오늘
어제
09-08 05:46

최근 글

  • 분류 전체보기 (107) N
    • 프로젝트 (1)
    • MSA 설계 & 도메인주도 설계 (9)
    • 클라우드 컴퓨팅 & NoSQL (87) N
      • [Cilium Study] 실리움 스터디 (8) N
      • [KANS] 쿠버네티스 네트워크 심화 스터디 (12)
      • [T101] 테라폼 4기 스터디 (8)
      • [CICD] CICD 맛보기 스터디 (3)
      • [T101] 테라폼 기초 입문 스터디 (6)
      • [AEWS] Amazon EKS 워크숍 스터디 (7)
      • [PKOS] 쿠버네티스 실무 실습 스터디 (7)
      • Kubernetes (13)
      • Docker (7)
      • Redis (1)
      • Jenkins (3)
      • Terraform (1)
      • Ansible (4)
      • Kafka (1)
    • 프로그래밍 (7)
      • Spring Boot (5)
      • Broker (1)
    • 성능과 튜닝 (1)
    • ALM (0)
    • 기타 (2)

인기 글

태그

  • MSA
  • kOps
  • cilium
  • DevOps
  • 도커
  • t101 4기
  • docker
  • CloudNet@
  • 쿠버네티스
  • 쿠버네티스 스터디
  • 테라폼
  • 데브옵스
  • terraform
  • Kubernetes
  • PKOS

티스토리

최근 댓글

hELLO · Designed By 정상우.
devlos
클라우드 컴퓨팅 & NoSQL/[Cilium Study] 실리움 스터디

[4주차 - Cilium 스터디] Networking - 노드에 파드들간 통신 2 & K8S 외부 노출 (25.08.03)

[4주차 - Cilium 스터디] Networking - 노드에 파드들간 통신 2 & K8S 외부 노출 (25.08.03)
클라우드 컴퓨팅 & NoSQL/[Cilium Study] 실리움 스터디

[4주차 - Cilium 스터디] Networking - 노드에 파드들간 통신 2 & K8S 외부 노출 (25.08.03)

2025. 8. 8. 03:18
반응형

들어가며

안녕하세요! Devlos입니다.

이번 포스팅은 CloudNet@ 커뮤니티에서 주최하는 Cilium Study 4주 차 주제인 "Networking - 노드에 파드들간 통신 2 & K8S 외부 노출"에 대해서 정리한 내용입니다. 노드에서 파드간의 통신을 상세하게 살펴봅니다.

 


실습환경 구성

이번 주차 실습 환경은 mac M3 Pro max 환경에서 실습을 진행했고, VirtualBox + Vagrant로 환경을 구성했어요.
실습 리소스를 줄이기 위해 Contrl Plane 한대와 Data Plane 한대로 클러스터를 구성하고, 가상 사내망 연동 환경을 구성하기 위해
라우터 한대를 추가적으로 생성합니다.

핵심 구성 요소:

  • Control Plane (k8s-ctr): Kubernetes 마스터 노드, API 서버 및 컨트롤 플레인 컴포넌트 실행
  • Worker Node (k8s-w0): 실제 워크로드가 실행되는 워커 노드
  • Worker Node (k8s-w1): 실제 워크로드가 실행되는 워커 노드
  • Router (router): 192.168.10.0/24 ↔ 192.168.20.0/24 대역 라우팅 역할, k8s 에 join 되지 않은 서버, loop1/loop2 dump

인터페이스 배치

[서브넷1]                                            [라우터]                     [서브넷2]
192.168.10.0/24(control plane, w0) ────────── 192.168.10.200/24 ────────── 192.168.20.0/24(w1)
                                              192.168.20.200/24
  • Vagrantfile : 가상머신 정의, 부팅 시 초기 프로비저닝 설정
# Variables
K8SV = '1.33.2-1.1' # Kubernetes Version : apt list -a kubelet , ex) 1.32.5-1.1
CONTAINERDV = '1.7.27-1' # Containerd Version : apt list -a containerd.io , ex) 1.6.33-1
CILIUMV = '1.18.0' # Cilium CNI Version : https://github.com/cilium/cilium/tags
N = 1 # max number of worker nodes
# Base Image [https://portal.cloud.hashicorp.com/vagrant/discover/bento/ubuntu-24.04](https://portal.cloud.hashicorp.com/vagrant/discover/bento/ubuntu-24.04)

BOX\_IMAGE = "bento/ubuntu-24.04"  
BOX\_VERSION = "202502.21.0"

Vagrant.configure("2") do |config|  
#-ControlPlane Node  
config.vm.define "k8s-ctr" do |subconfig|  
subconfig.vm.box = BOX\_IMAGE

subconfig.vm.box\_version = BOX\_VERSION  
subconfig.vm.provider "virtualbox" do |vb|  
vb.customize \["modifyvm", :id, "--groups", "/Cilium-Lab"\] # VirtualBox에서 VM을 그룹으로 분류  
vb.customize \["modifyvm", :id, "--nicpromisc2", "allow-all"\] # 네트워크 인터페이스의 promiscuous 모드 허용 (네트워크 패킷 캡처용)  
vb.name = "k8s-ctr"  
vb.cpus = 2  
vb.memory = 2560  
vb.linked\_clone = true # 기존 VM을 기반으로 빠른 클론 생성  
end  
subconfig.vm.host\_name = "k8s-ctr"  
subconfig.vm.network "private\_network", ip: "192.168.10.100" # 내부 네트워크 설정  
subconfig.vm.network "forwarded\_port", guest: 22, host: 60000, auto\_correct: true, id: "ssh" # SSH 포트 포워딩  
subconfig.vm.synced\_folder "./", "/vagrant", disabled: true # 폴더 동기화 비활성화  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/init\_cfg.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/init_cfg.sh"), args: \[ K8SV, CONTAINERDV \] # 초기 설정 스크립트 실행  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/k8s-ctr.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/k8s-ctr.sh"), args: \[ N, CILIUMV, K8SV \] # 컨트롤플레인 설정 스크립트 실행  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/route-add1.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/route-add1.sh") # 라우팅 설정 스크립트 실행  
end

#-Worker Nodes Subnet1  
(1..N).each do |i|  
config.vm.define "k8s-w#{i}" do |subconfig|  
subconfig.vm.box = BOX\_IMAGE  
subconfig.vm.box\_version = BOX\_VERSION  
subconfig.vm.provider "virtualbox" do |vb|  
vb.customize \["modifyvm", :id, "--groups", "/Cilium-Lab"\] # VirtualBox에서 VM을 그룹으로 분류  
vb.customize \["modifyvm", :id, "--nicpromisc2", "allow-all"\] # 네트워크 인터페이스의 promiscuous 모드 허용  
vb.name = "k8s-w#{i}"  
vb.cpus = 2  
vb.memory = 1536  
vb.linked\_clone = true # 기존 VM을 기반으로 빠른 클론 생성  
end  
subconfig.vm.host\_name = "k8s-w#{i}"  
subconfig.vm.network "private\_network", ip: "192.168.10.10#{i}" # 워커 노드 IP 설정  
subconfig.vm.network "forwarded\_port", guest: 22, host: "6000#{i}", auto\_correct: true, id: "ssh" # SSH 포트 포워딩  
subconfig.vm.synced\_folder "./", "/vagrant", disabled: true # 폴더 동기화 비활성화  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/init\_cfg.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/init_cfg.sh%22), args: \[ K8SV, CONTAINERDV\] # 초기 설정 스크립트 실행  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/k8s-w.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/k8s-w.sh%22) # 워커 노드 설정 스크립트 실행  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/route-add1.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/route-add1.sh%22) # 라우팅 설정 스크립트 실행  
end  
end

#-Router Node  
config.vm.define "router" do |subconfig|  
subconfig.vm.box = BOX\_IMAGE  
subconfig.vm.box\_version = BOX\_VERSION  
subconfig.vm.provider "virtualbox" do |vb|  
vb.customize \["modifyvm", :id, "--groups", "/Cilium-Lab"\] # VirtualBox에서 VM을 그룹으로 분류  
vb.name = "router"  
vb.cpus = 1  
vb.memory = 768  
vb.linked\_clone = true # 기존 VM을 기반으로 빠른 클론 생성  
end  
subconfig.vm.host\_name = "router"  
subconfig.vm.network "private\_network", ip: "192.168.10.200" # 첫 번째 네트워크 인터페이스 (Vagrant 설정방식)  
subconfig.vm.network "forwarded\_port", guest: 22, host: 60009, auto\_correct: true, id: "ssh" # SSH 포트 포워딩  
subconfig.vm.network "private\_network", ip: "192.168.20.200", auto\_config: false # 두 번째 네트워크 인터페이스 (수동 설정)  
subconfig.vm.synced\_folder "./", "/vagrant", disabled: true # 폴더 동기화 비활성화  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/router.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/router.sh%22) # 라우터 설정 스크립트 실행  
end

#-Worker Nodes Subnet2  
config.vm.define "k8s-w0" do |subconfig|  
subconfig.vm.box = BOX\_IMAGE  
subconfig.vm.box\_version = BOX\_VERSION  
subconfig.vm.provider "virtualbox" do |vb|  
vb.customize \["modifyvm", :id, "--groups", "/Cilium-Lab"\] # VirtualBox에서 VM을 그룹으로 분류  
vb.customize \["modifyvm", :id, "--nicpromisc2", "allow-all"\] # 네트워크 인터페이스의 promiscuous 모드 허용  
vb.name = "k8s-w0"  
vb.cpus = 2  
vb.memory = 1536  
vb.linked\_clone = true # 기존 VM을 기반으로 빠른 클론 생성  
end  
subconfig.vm.host\_name = "k8s-w0"  
subconfig.vm.network "private\_network", ip: "192.168.20.100" # 서브넷2의 워커 노드 IP  
subconfig.vm.network "forwarded\_port", guest: 22, host: 60010, auto\_correct: true, id: "ssh" # SSH 포트 포워딩  
subconfig.vm.synced\_folder "./", "/vagrant", disabled: true # 폴더 동기화 비활성화  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/init\_cfg.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/init_cfg.sh%22), args: \[ K8SV, CONTAINERDV\] # 초기 설정 스크립트 실행  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/k8s-w.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/k8s-w.sh%22) # 워커 노드 설정 스크립트 실행  
subconfig.vm.provision "shell", path: "[https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/route-add2.sh"](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/route-add2.sh%22) # 서브넷2용 라우팅 설정 스크립트 실행  
end

end
  • init_cfg.sh : args 참고하여 설치
#!/usr/bin/env bash
echo ">>>> Initial Config Start <<<<"

echo "\[TASK 1\] Setting Profile & Bashrc"  
echo 'alias vi=vim' >> /etc/profile # vi 명령어를 vim으로 별칭 설정  
echo "sudo su -" >> /home/vagrant/.bashrc # vagrant 사용자 로그인 시 자동으로 root로 전환  
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime # 시스템 시간대를 서울로 변경

echo "\[TASK 2\] Disable AppArmor"  
systemctl stop ufw && systemctl disable ufw >/dev/null 2>&1 # UFW 방화벽 중지 및 비활성화  
systemctl stop apparmor && systemctl disable apparmor >/dev/null 2>&1 # AppArmor 보안 모듈 중지 및 비활성화

echo "\[TASK 3\] Disable and turn off SWAP"  
swapoff -a && sed -i '/swap/s/^/#/' /etc/fstab # 스왑 메모리 비활성화 및 fstab에서 주석 처리

echo "\[TASK 4\] Install Packages"  
apt update -qq >/dev/null 2>&1 # 패키지 목록 업데이트 (조용히 실행)  
apt-get install apt-transport-https ca-certificates curl gpg -y -qq >/dev/null 2>&1 # 기본 패키지 설치

# Download the public signing key for the Kubernetes package repositories.

mkdir -p -m 755 /etc/apt/keyrings # APT 키링 디렉토리 생성  
K8SMMV=$(echo $1 | sed -En 's/^(\[0-9\]+.\[0-9\]+)..\*/\\1/p') # Kubernetes 메이저.마이너 버전 추출  
curl -fsSL [https://pkgs.k8s.io/core:/stable:/v$K8SMMV/deb/Release.key](https://pkgs.k8s.io/core:/stable:/v$K8SMMV/deb/Release.key) | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg # Kubernetes GPG 키 다운로드  
echo "deb \[signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg\] [https://pkgs.k8s.io/core:/stable:/v$K8SMMV/deb/](https://pkgs.k8s.io/core:/stable:/v$K8SMMV/deb/) /" >> /etc/apt/sources.list.d/kubernetes.list # Kubernetes 저장소 추가  
curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) -o /etc/apt/keyrings/docker.asc # Docker GPG 키 다운로드  
echo "deb \[arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc\] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) $(. /etc/os-release && echo "$VERSION\_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null # Docker 저장소 추가

# packets traversing the bridge are processed by iptables for filtering

echo 1 > /proc/sys/net/ipv4/ip\_forward # IP 포워딩 활성화  
echo "net.ipv4.ip\_forward = 1" >> /etc/sysctl.d/k8s.conf # IP 포워딩 설정을 영구적으로 저장

# enable br\_netfilter for iptables

modprobe br\_netfilter # 브리지 네트워크 필터 모듈 로드  
modprobe overlay # 오버레이 파일시스템 모듈 로드  
echo "br\_netfilter" >> /etc/modules-load.d/k8s.conf # 시스템 부팅 시 자동 로드  
echo "overlay" >> /etc/modules-load.d/k8s.conf # 시스템 부팅 시 자동 로드

echo "\[TASK 5\] Install Kubernetes components (kubeadm, kubelet and kubectl)"

# Update the apt package index, install kubelet, kubeadm and kubectl, and pin their version

apt update >/dev/null 2>&1 # 패키지 목록 업데이트

# apt list -a kubelet ; apt list -a containerd.io

apt-get install -y kubelet=$1 kubectl=$1 kubeadm=$1 containerd.io=$2 >/dev/null 2>&1 # Kubernetes 컴포넌트 설치  
apt-mark hold kubelet kubeadm kubectl >/dev/null 2>&1 # 패키지 버전 고정 (자동 업데이트 방지)

# containerd configure to default and cgroup managed by systemd

containerd config default > /etc/containerd/config.toml # containerd 기본 설정 생성  
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml # systemd cgroup 드라이버 사용

# avoid WARN&ERRO(default endpoints) when crictl run

cat < /etc/crictl.yaml # crictl 설정 파일 생성  
runtime-endpoint: unix:///run/containerd/containerd.sock  
image-endpoint: unix:///run/containerd/containerd.sock  
EOF

# ready to install for k8s

systemctl restart containerd && systemctl enable containerd # containerd 재시작 및 자동 시작 설정  
systemctl enable --now kubelet # kubelet 자동 시작 설정

echo "\[TASK 6\] Install Packages & Helm"  
export DEBIAN\_FRONTEND=noninteractive # 패키지 설치 시 대화형 프롬프트 비활성화  
apt-get install -y bridge-utils sshpass net-tools conntrack ngrep tcpdump ipset arping wireguard jq yq tree bash-completion unzip kubecolor termshark >/dev/null 2>&1 # 네트워크 도구 및 유틸리티 설치  
curl -s [https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3](https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3) | bash >/dev/null 2>&1 # Helm 3 설치

echo ">>>> Initial Config End <<<<"
  • k8s-ctr.sh : kubeadm init, Cilium CNI 설치, 편리성 설정(k, kc), k9s, local-path-sc, metrics-server
echo ">>>> K8S Controlplane config Start <<<<"
echo "\[TASK 1\] Initial Kubernetes"  
curl --silent -o /root/kubeadm-init-ctr-config.yaml [https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/kubeadm-init-ctr-config.yaml](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/kubeadm-init-ctr-config.yaml) # kubeadm 설정 파일 다운로드  
K8SMMV=$(echo $3 | sed -En 's/^(\[0-9\]+.\[0-9\]+.\[0-9\]+).\*/\\1/p') # Kubernetes 버전에서 메이저.마이너.패치 추출  
sed -i "s/K8S\_VERSION\_PLACEHOLDER/v${K8SMMV}/g" /root/kubeadm-init-ctr-config.yaml # 설정 파일에서 버전 플레이스홀더 치환  
kubeadm init --config="/root/kubeadm-init-ctr-config.yaml" >/dev/null 2>&1 # Kubernetes 클러스터 초기화



echo "\[TASK 2\] Setting kube config file"  
mkdir -p /root/.kube # kubeconfig 디렉토리 생성  
cp -i /etc/kubernetes/admin.conf /root/.kube/config # 관리자 kubeconfig 파일 복사  
chown $(id -u):$(id -g) /root/.kube/config # 파일 소유권 설정

echo "\[TASK 3\] Source the completion"  
echo 'source <(kubectl completion bash)' >> /etc/profile # kubectl 자동완성 설정  
echo 'source <(kubeadm completion bash)' >> /etc/profile # kubeadm 자동완성 설정

echo "\[TASK 4\] Alias kubectl to k"  
echo 'alias k=kubectl' >> /etc/profile # kubectl을 k로 별칭 설정  
echo 'alias kc=kubecolor' >> /etc/profile # kubecolor를 kc로 별칭 설정  
echo 'complete -F \_\_start\_kubectl k' >> /etc/profile # k 별칭에 대한 자동완성 설정

echo "\[TASK 5\] Install Kubectx & Kubens"  
git clone [https://github.com/ahmetb/kubectx](https://github.com/ahmetb/kubectx) /opt/kubectx >/dev/null 2>&1 # kubectx 도구 다운로드  
ln -s /opt/kubectx/kubens /usr/local/bin/kubens # kubens 심볼릭 링크 생성  
ln -s /opt/kubectx/kubectx /usr/local/bin/kubectx # kubectx 심볼릭 링크 생성

echo "\[TASK 6\] Install Kubeps & Setting PS1"  
git clone [https://github.com/jonmosco/kube-ps1.git](https://github.com/jonmosco/kube-ps1.git) /root/kube-ps1 >/dev/null 2>&1 # kube-ps1 도구 다운로드  
cat <<"EOT" >> /root/.bash\_profile # 프롬프트 설정 스크립트 추가  
source /root/kube-ps1/kube-ps1.sh  
KUBE\_PS1\_SYMBOL\_ENABLE=true  
function get\_cluster\_short() {  
echo "$1" | cut -d . -f1 # 클러스터 이름을 짧게 표시하는 함수  
}  
KUBE\_PS1\_CLUSTER\_FUNCTION=get\_cluster\_short  
KUBE\_PS1\_SUFFIX=') '  
PS1='$(kube\_ps1)'$PS1 # 프롬프트에 현재 컨텍스트 표시  
EOT  
kubectl config rename-context "kubernetes-admin@kubernetes" "HomeLab" >/dev/null 2>&1 # 컨텍스트 이름을 HomeLab으로 변경

echo "\[TASK 7\] Install Cilium CNI"  
NODEIP=$(ip -4 addr show eth1 | grep -oP '(?<=inet\\s)\\d+(.\\d+){3}') # 노드 IP 주소 추출  
helm repo add cilium [https://helm.cilium.io/](https://helm.cilium.io/) >/dev/null 2>&1 # Cilium Helm 저장소 추가  
helm repo update >/dev/null 2>&1 # Helm 저장소 업데이트  
helm install cilium cilium/cilium --version $2 --namespace kube-system  
\--set k8sServiceHost=192.168.10.100 --set k8sServicePort=6443  
\--set ipam.mode="cluster-pool" --set ipam.operator.clusterPoolIPv4PodCIDRList={"172.20.0.0/16"} --set ipv4NativeRoutingCIDR=172.20.0.0/16  
\--set routingMode=native --set autoDirectNodeRoutes=true  
\--set kubeProxyReplacement=true --set bpf.masquerade=true --set installNoConntrackIptablesRules=true  
\--set endpointHealthChecking.enabled=false --set healthChecking=false  
\--set hubble.enabled=true --set hubble.relay.enabled=true --set hubble.ui.enabled=true  
\--set hubble.ui.service.type=NodePort --set hubble.ui.service.nodePort=30003  
\--set prometheus.enabled=true --set operator.prometheus.enabled=true --set hubble.metrics.enableOpenMetrics=true  
\--set hubble.metrics.enabled="{dns,drop,tcp,flow,port-distribution,icmp,httpV2:exemplars=true;labelsContext=source\_ip,source\_namespace,source\_workload,destination\_ip,destination\_namespace,destination\_workload,traffic\_direction}"  
\--set operator.replicas=1 --set debug.enabled=true >/dev/null 2>&1 # Cilium CNI 설치 (Hubble, Prometheus, 메트릭 포함)

echo "\[TASK 8\] Install Cilium / Hubble CLI"  
CILIUM\_CLI\_VERSION=$(curl -s [https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt](https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)) # 최신 Cilium CLI 버전 가져오기  
CLI\_ARCH=amd64  
if \[ "$(uname -m)" = "aarch64" \]; then CLI\_ARCH=arm64; fi # 아키텍처 확인  
curl -L --fail --remote-name-all [https://github.com/cilium/cilium-cli/releases/download/${CILIUM\_CLI\_VERSION}/cilium-linux-${CLI\_ARCH}.tar.gz](https://github.com/cilium/cilium-cli/releases/download/$%7BCILIUM_CLI_VERSION%7D/cilium-linux-$%7BCLI_ARCH%7D.tar.gz) >/dev/null 2>&1 # Cilium CLI 다운로드  
tar xzvfC cilium-linux-${CLI\_ARCH}.tar.gz /usr/local/bin # Cilium CLI 압축 해제 및 설치  
rm cilium-linux-${CLI\_ARCH}.tar.gz # 임시 파일 삭제

HUBBLE\_VERSION=$(curl -s [https://raw.githubusercontent.com/cilium/hubble/master/stable.txt](https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)) # 최신 Hubble 버전 가져오기  
HUBBLE\_ARCH=amd64  
if \[ "$(uname -m)" = "aarch64" \]; then HUBBLE\_ARCH=arm64; fi # 아키텍처 확인  
curl -L --fail --remote-name-all [https://github.com/cilium/hubble/releases/download/$HUBBLE\_VERSION/hubble-linux-${HUBBLE\_ARCH}.tar.gz](https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-$%7BHUBBLE_ARCH%7D.tar.gz) >/dev/null 2>&1 # Hubble CLI 다운로드  
tar xzvfC hubble-linux-${HUBBLE\_ARCH}.tar.gz /usr/local/bin # Hubble CLI 압축 해제 및 설치  
rm hubble-linux-${HUBBLE\_ARCH}.tar.gz # 임시 파일 삭제

echo "\[TASK 9\] Remove node taint"  
kubectl taint nodes k8s-ctr node-role.kubernetes.io/control-plane- # 컨트롤플레인 노드의 테인트 제거 (Pod 스케줄링 허용)

echo "\[TASK 10\] local DNS with hosts file"  
echo "192.168.10.100 k8s-ctr" >> /etc/hosts # 컨트롤플레인 노드 호스트명 추가  
echo "192.168.10.200 router" >> /etc/hosts # 라우터 노드 호스트명 추가  
echo "192.168.20.100 k8s-w0" >> /etc/hosts # 서브넷2 워커 노드 호스트명 추가  
for (( i=1; i<=$1; i++ )); do echo "192.168.10.10$i k8s-w$i" >> /etc/hosts; done # 서브넷1 워커 노드들 호스트명 추가

echo "\[TASK 11\] Dynamically provisioning persistent local storage with Kubernetes"  
kubectl apply -f [https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml](https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.31/deploy/local-path-storage.yaml) >/dev/null 2>&1 # local-path-provisioner 설치  
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' >/dev/null 2>&1 # local-path를 기본 스토리지 클래스로 설정

echo "\[TASK 12\] Install Prometheus & Grafana"  
kubectl apply -f [https://raw.githubusercontent.com/cilium/cilium/1.18.0/examples/kubernetes/addons/prometheus/monitoring-example.yaml](https://raw.githubusercontent.com/cilium/cilium/1.18.0/examples/kubernetes/addons/prometheus/monitoring-example.yaml) >/dev/null 2>&1 # Prometheus & Grafana 설치  
kubectl patch svc -n cilium-monitoring prometheus -p '{"spec": {"type": "NodePort", "ports": \[{"port": 9090, "targetPort": 9090, "nodePort": 30001}\]}}' >/dev/null 2>&1 # Prometheus 서비스를 NodePort로 변경  
kubectl patch svc -n cilium-monitoring grafana -p '{"spec": {"type": "NodePort", "ports": \[{"port": 3000, "targetPort": 3000, "nodePort": 30002}\]}}' >/dev/null 2>&1 # Grafana 서비스를 NodePort로 변경

echo "\[TASK 13\] Install Metrics-server"  
helm repo add metrics-server [https://kubernetes-sigs.github.io/metrics-server/](https://kubernetes-sigs.github.io/metrics-server/) >/dev/null 2>&1 # metrics-server Helm 저장소 추가  
helm upgrade --install metrics-server metrics-server/metrics-server --set 'args\[0\]=--kubelet-insecure-tls' -n kube-system >/dev/null 2>&1 # metrics-server 설치 (TLS 검증 비활성화)

echo "\[TASK 14\] Install k9s"  
CLI\_ARCH=amd64  
if \[ "$(uname -m)" = "aarch64" \]; then CLI\_ARCH=arm64; fi # 아키텍처 확인  
wget [https://github.com/derailed/k9s/releases/latest/download/k9s\_linux\_${CLI\_ARCH}.deb](https://github.com/derailed/k9s/releases/latest/download/k9s_linux_$%7BCLI_ARCH%7D.deb) -O /tmp/k9s\_linux\_${CLI\_ARCH}.deb >/dev/null 2>&1 # k9s 패키지 다운로드  
apt install /tmp/k9s\_linux\_${CLI\_ARCH}.deb >/dev/null 2>&1 # k9s 설치

echo ">>>> K8S Controlplane Config End <<<<"
  • k8s-w.sh : kubeadm join
echo "\[TASK 1\] K8S Controlplane Join"  
curl --silent -o /root/kubeadm-join-worker-config.yaml [https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/2w/kubeadm-join-worker-config.yaml](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/2w/kubeadm-join-worker-config.yaml) # kubeadm 조인 설정 파일 다운로드  
NODEIP=$(ip -4 addr show eth1 | grep -oP '(?<=inet\\s)\\d+(.\\d+){3}') # 노드 IP 주소 추출  
sed -i "s/NODE\_IP\_PLACEHOLDER/${NODEIP}/g" /root/kubeadm-join-worker-config.yaml # 설정 파일에서 IP 플레이스홀더 치환  
kubeadm join --config="/root/kubeadm-join-worker-config.yaml" > /dev/null 2>&1 # Kubernetes 클러스터에 워커 노드로 조인

echo ">>>> K8S Node config End <<<<"
  • route-add1.sh : k8s node 들이 내부망과 통신을 위한 route 설정
#!/usr/bin/env bash

echo ">>>> Route Add Config Start <<<<"  
chmod 600 /etc/netplan/01-netcfg.yaml # netplan 설정 파일 권한 설정  
chmod 600 /etc/netplan/50-vagrant.yaml # vagrant netplan 설정 파일 권한 설정

cat <> /etc/netplan/50-vagrant.yaml # 라우팅 규칙 추가  
routes:  
\- to: 192.168.20.0/24 # 서브넷2로 가는 트래픽  
via: 192.168.10.200 # 라우터를 통해 라우팅  
\- to: 172.20.0.0/16 # Pod 네트워크로 가는 트래픽  
via: 192.168.10.200 # 라우터를 통해 라우팅  
\- to: 10.10.0.0/16 # 더미 네트워크로 가는 트래픽  
via: 192.168.10.200 # 라우터를 통해 라우팅  
EOT  
netplan apply # 네트워크 설정 적용

echo ">>>> Route Add Config End <<<<"
  • route-add2.sh : k8s node 들이 내부망과 통신을 위한 route 설정
  • #!/usr/bin/env bash
echo ">>>> Route Add Config Start <<<<"

chmod 600 /etc/netplan/01-netcfg.yaml # netplan 설정 파일 권한 설정  
chmod 600 /etc/netplan/50-vagrant.yaml # vagrant netplan 설정 파일 권한 설정

cat <> /etc/netplan/50-vagrant.yaml # 라우팅 규칙 추가  
routes:  
\- to: 192.168.10.0/24 # 서브넷1로 가는 트래픽  
via: 192.168.20.200 # 라우터를 통해 라우팅  
\- to: 172.20.0.0/16 # Pod 네트워크로 가는 트래픽  
via: 192.168.20.200 # 라우터를 통해 라우팅  
\- to: 10.10.0.0/16 # 더미 네트워크로 가는 트래픽  
via: 192.168.20.200 # 라우터를 통해 라우팅  
EOT

netplan apply # 네트워크 설정 적용

echo ">>>> Route Add Config End <<<<"
  • router.sh : router 역할의 (웹) 서버
  • #!/usr/bin/env bash
echo ">>>> Initial Config Start <<<<"
echo "\[TASK 0\] Setting eth2"  
chmod 600 /etc/netplan/01-netcfg.yaml # netplan 설정 파일 권한 설정  
chmod 600 /etc/netplan/50-vagrant.yaml # vagrant netplan 설정 파일 권한 설정

cat << EOT >> /etc/netplan/50-vagrant.yaml # 두 번째 네트워크 인터페이스 설정 추가  
eth2:  
addresses:  
\- 192.168.20.200/24 # 서브넷2의 IP 주소  
EOT

netplan apply # 네트워크 설정 적용

echo "\[TASK 1\] Setting Profile & Bashrc"  
echo 'alias vi=vim' >> /etc/profile # vi 명령어를 vim으로 별칭 설정  
echo "sudo su -" >> /home/vagrant/.bashrc # vagrant 사용자 로그인 시 자동으로 root로 전환  
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime # 시스템 시간대를 서울로 변경

echo "\[TASK 2\] Disable AppArmor"  
systemctl stop ufw && systemctl disable ufw >/dev/null 2>&1 # UFW 방화벽 중지 및 비활성화  
systemctl stop apparmor && systemctl disable apparmor >/dev/null 2>&1 # AppArmor 보안 모듈 중지 및 비활성화

echo "\[TASK 3\] Add Kernel setting - IP Forwarding"  
sed -i 's/#net.ipv4.ip\_forward=1/net.ipv4.ip\_forward=1/g' /etc/sysctl.conf # IP 포워딩 활성화 (주석 해제)  
sysctl -p >/dev/null 2>&1 # sysctl 설정 적용

echo "\[TASK 4\] Setting Dummy Interface"
  • 실행
mkdir cilium-lab && cd cilium-lab curl -O [https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/Vagrantfile](https://raw.githubusercontent.com/gasida/vagrant-lab/refs/heads/main/cilium-study/4w/Vagrantfile) vagrant up

실습환경 확인

  • 배포후 기본 정보 확인
cat /etc/hosts
sshpass -p 'vagrant' ssh -o StrictHostKeyChecking=no vagrant@k8s-w0 hostname
sshpass -p 'vagrant' ssh -o StrictHostKeyChecking=no vagrant@k8s-w1 hostname
sshpass -p 'vagrant' ssh -o StrictHostKeyChecking=no vagrant@router hostname


# 클러스터 정보 확인
kubectl cluster-info
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"

kubectl describe cm -n kube-system kubeadm-config
kubectl describe cm -n kube-system kubelet-config

# 노드 정보 : 상태, INTERNAL-IP 확인
ifconfig | grep -iEA1 'eth[0-9]:'
kubectl get node -owide

# 파드 정보 : 상태, 파드 IP 확인
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'
kubectl get ciliumnode -o json | grep podCIDRs -A2
kubectl get pod -A -owide
kubectl get ciliumendpoints -A

# ipam 모드 확인
cilium config view | grep ^ipam

# iptables 확인
iptables-save
iptables -t nat -S
iptables -t filter -S
iptables -t mangle -S
iptables -t raw -S

✅ 실행 결과 요약

(⎈|HomeLab:N/A) root@k8s-ctr:~# 
cat /etc/hosts
sshpass -p 'vagrant' ssh -o StrictHostKeyChecking=no vagrant@k8s-w0 hostname
sshpass -p 'vagrant' ssh -o StrictHostKeyChecking=no vagrant@k8s-w1 hostname
sshpass -p 'vagrant' ssh -o StrictHostKeyChecking=no vagrant@router hostname
127.0.0.1 localhost
127.0.1.1 vagrant

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.2.1 k8s-ctr k8s-ctr
192.168.10.100 k8s-ctr
192.168.10.200 router
192.168.20.100 k8s-w0
192.168.10.101 k8s-w1
Warning: Permanently added 'k8s-w0' (ED25519) to the list of known hosts.
k8s-w0
Warning: Permanently added 'k8s-w1' (ED25519) to the list of known hosts.
k8s-w1
Warning: Permanently added 'router' (ED25519) to the list of known hosts.
router
(⎈|HomeLab:N/A) root@k8s-ctr:~# 
kubectl cluster-info dump | grep -m 2 -E "cluster-cidr|service-cluster-ip-range"
                            "--service-cluster-ip-range=10.96.0.0/16",
                            "--cluster-cidr=10.244.0.0/16",
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl describe cm -n kube-system kubeadm-config
Name:         kubeadm-config
Namespace:    kube-system
Labels:       <none>
Annotations:  <none>

Data
====
ClusterConfiguration:
----
apiServer: {}
...
networking:
  dnsDomain: cluster.local
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/16
proxy: {}
scheduler: {}



BinaryData
====

Events:  <none>
(⎈|HomeLab:N/A) root@k8s-ctr:~# ifconfig | grep -iEA1 'eth[0-9]:'
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
--
eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.10.100  netmask 255.255.255.0  broadcast 192.168.10.255

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get node -owide
NAME      STATUS   ROLES           AGE     VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME
k8s-ctr   Ready    control-plane   13m     v1.33.2   192.168.10.100   <none>        Ubuntu 24.04.2 LTS   6.8.0-53-generic   containerd://1.7.27
k8s-w0    Ready    <none>          5m19s   v1.33.2   192.168.20.100   <none>        Ubuntu 24.04.2 LTS   6.8.0-53-generic   containerd://1.7.27
k8s-w1    Ready    <none>          9m19s   v1.33.2   192.168.10.101   <none>        Ubuntu 24.04.2 LTS   6.8.0-53-generic   containerd://1.7.27

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.podCIDR}{"\n"}{end}'
k8s-ctr 10.244.0.0/24
k8s-w0  10.244.2.0/24
k8s-w1  10.244.1.0/24

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ciliumnode -o json | grep podCIDRs -A2
                    "podCIDRs": [
                        "172.20.0.0/24"
                    ],
--
                    "podCIDRs": [
                        "172.20.2.0/24"
                    ],
--
                    "podCIDRs": [
                        "172.20.1.0/24"
                    ],

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get pod -A -owide
NAMESPACE            NAME                                      READY   STATUS    RESTARTS   AGE     IP               NODE      NOMINATED NODE   READINESS GATES
cilium-monitoring    grafana-5c69859d9-sd2px                   1/1     Running   0          12m     172.20.0.177     k8s-ctr   <none>           <none>
cilium-monitoring    prometheus-6fc896bc5d-6zp8l               1/1     Running   0          12m     172.20.0.147     k8s-ctr   <none>           <none>
kube-system          cilium-envoy-jxfql                        1/1     Running   0          9m41s   192.168.10.101   k8s-w1    <none>           <none>
kube-system          cilium-envoy-ktv8n                        1/1     Running   0          5m41s   192.168.20.100   k8s-w0    <none>           <none>
kube-system          cilium-envoy-qcq4n                        1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          cilium-fbmct                              1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          cilium-kz49w                              1/1     Running   0          5m41s   192.168.20.100   k8s-w0    <none>           <none>
kube-system          cilium-operator-76788cffb7-h7tl2          1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          cilium-vmvxn                              1/1     Running   0          9m41s   192.168.10.101   k8s-w1    <none>           <none>
kube-system          coredns-674b8bbfcf-qnc4f                  1/1     Running   0          13m     172.20.0.164     k8s-ctr   <none>           <none>
kube-system          coredns-674b8bbfcf-zr9kv                  1/1     Running   0          13m     172.20.0.51      k8s-ctr   <none>           <none>
kube-system          etcd-k8s-ctr                              1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          hubble-relay-5b48c999f9-d9vwl             1/1     Running   0          13m     172.20.0.136     k8s-ctr   <none>           <none>
kube-system          hubble-ui-655f947f96-zl8tb                2/2     Running   0          13m     172.20.0.153     k8s-ctr   <none>           <none>
kube-system          kube-apiserver-k8s-ctr                    1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          kube-controller-manager-k8s-ctr           1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          kube-proxy-mgwgt                          1/1     Running   0          9m41s   192.168.10.101   k8s-w1    <none>           <none>
kube-system          kube-proxy-wqrg7                          1/1     Running   0          5m41s   192.168.20.100   k8s-w0    <none>           <none>
kube-system          kube-proxy-zz4dx                          1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          kube-scheduler-k8s-ctr                    1/1     Running   0          13m     192.168.10.100   k8s-ctr   <none>           <none>
kube-system          metrics-server-5dd7b49d79-dzqx8           1/1     Running   0          12m     172.20.0.233     k8s-ctr   <none>           <none>
local-path-storage   local-path-provisioner-74f9666bc9-pjlvv   1/1     Running   0          12m     172.20.0.223     k8s-ctr   <none>           <none>

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ciliumendpoints -A
NAMESPACE            NAME                                      SECURITY IDENTITY   ENDPOINT STATE   IPV4           IPV6
cilium-monitoring    grafana-5c69859d9-sd2px                   47241               ready            172.20.0.177   
cilium-monitoring    prometheus-6fc896bc5d-6zp8l               50875               ready            172.20.0.147   
kube-system          coredns-674b8bbfcf-qnc4f                  16563               ready            172.20.0.164   
kube-system          coredns-674b8bbfcf-zr9kv                  16563               ready            172.20.0.51    
kube-system          hubble-relay-5b48c999f9-d9vwl             1879                ready            172.20.0.136   
kube-system          hubble-ui-655f947f96-zl8tb                9336                ready            172.20.0.153   
kube-system          metrics-server-5dd7b49d79-dzqx8           307                 ready            172.20.0.233   
local-path-storage   local-path-provisioner-74f9666bc9-pjlvv   14074               ready            172.20.0.223   

(⎈|HomeLab:N/A) root@k8s-ctr:~# cilium config view | grep ^ipam
ipam                                              cluster-pool
ipam-cilium-node-update-rate                      15s

✅ 실행 결과 요약
실습 환경이 성공적으로 구축되어 3개 노드(컨트롤플레인 1개, 워커노드 2개)가 모두 Ready 상태로 동작하고 있는 것을 확인할 수 있습니다.

Cilium CNI가 native routing 모드로 설정되어 Pod 네트워크(172.20.0.0/16)와 노드 네트워크(192.168.10.0/24, 192.168.20.0/24)가 분리되어 구성되었고,
모든 시스템 Pod와 모니터링 도구가 정상 동작하며, 8개의 Cilium 엔드포인트(ciliumendpoints로 확인) ready 상태인 것을 확인하였습니다.

  • 네트워크 정보 확인 : autoDirectNodeRoutes=true 동작 이해
# router 네트워크 인터페이스 정보 확인
sshpass -p 'vagrant' ssh vagrant@router ip -br -c -4 addr


# k8s node 네트워크 인터페이스 정보 확인
ip -c -4 addr show dev eth1  
sshpass -p 'vagrant' ssh vagrant@k8s-w1 ip -c -4 addr show dev eth1  
sshpass -p 'vagrant' ssh vagrant@k8s-w0 ip -c -4 addr show dev eth1

# 라우팅 정보 확인
sshpass -p 'vagrant' ssh vagrant@router ip -c route  
ip -c route | grep static

## \--set routingMode=native --set autoDirectNodeRoutes=true 동작을 정확히 이해해보자!

## 같은 네트워크 대역에 있는 애들끼리만 Pod CIDR IP가 그 대역이 있는 노드의 static IP로 등록됨

ip -c route  
sshpass -p 'vagrant' ssh vagrant@k8s-w1 ip -c route  
sshpass -p 'vagrant' ssh vagrant@k8s-w0 ip -c route

# 통신 확인

ping -c 1 10.10.1.200 # router loop1  
ping -c 1 192.168.20.100 # k8s-w0 eth1

# 목적지까지 경우하는 라우팅 정보 제공

## Path MTU (pmtu): 출발지에서 목적지까지 모든 네트워크 경로 상에서 통과할 수 있는 최대 패킷 크기(Byte), IP fragmentation 없이 전송 가능한 가장 큰 패킷 크기를 의미.

## pmtu 1500: 전체 경로의 최소 MTU는 1500 , hops 2: 총 2단계 라우터/노드를 거쳐서 도달 , back 2: 응답도 동일한 hop 수로 돌아옴

tracepath -n 192.168.20.100

✅ 실행 결과 요약

(⎈|HomeLab:N/A) root@k8s-ctr:~# sshpass -p 'vagrant' ssh vagrant@router ip -br -c -4 addr
lo               UNKNOWN        127.0.0.1/8 
eth0             UP             10.0.2.15/24 metric 100 
eth1             UP             192.168.10.200/24 
eth2             UP             192.168.20.200/24 
loop1            UNKNOWN        10.10.1.200/24 
loop2            UNKNOWN        10.10.2.200/24 

(⎈|HomeLab:N/A) root@k8s-ctr:~# ip -c -4 addr show dev eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    altname enp0s9
    inet 192.168.10.100/24 brd 192.168.10.255 scope global eth1
       valid_lft forever preferred_lft forever

(⎈|HomeLab:N/A) root@k8s-ctr:~# sshpass -p 'vagrant' ssh vagrant@k8s-w1 ip -c -4 addr show dev eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    altname enp0s9
    inet 192.168.10.101/24 brd 192.168.10.255 scope global eth1
       valid_lft forever preferred_lft forever

(⎈|HomeLab:N/A) root@k8s-ctr:~# sshpass -p 'vagrant' ssh vagrant@k8s-w0 ip -c -4 addr show dev eth1
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    altname enp0s9
    inet 192.168.20.100/24 brd 192.168.20.255 scope global eth1
       valid_lft forever preferred_lft forever

(⎈|HomeLab:N/A) root@k8s-ctr:~# sshpass -p 'vagrant' ssh vagrant@router ip -c route
default via 10.0.2.2 dev eth0 proto dhcp src 10.0.2.15 metric 100 
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100 
10.0.2.2 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.0.2.3 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.10.1.0/24 dev loop1 proto kernel scope link src 10.10.1.200 
10.10.2.0/24 dev loop2 proto kernel scope link src 10.10.2.200 
192.168.10.0/24 dev eth1 proto kernel scope link src 192.168.10.200 #라우팅 가능하도록 설정 되어 있음
192.168.20.0/24 dev eth2 proto kernel scope link src 192.168.20.200 #라우팅 가능하도록 설정 되어 있음

(⎈|HomeLab:N/A) root@k8s-ctr:~# ip -c route | grep static
10.10.0.0/16 via 192.168.10.200 dev eth1 proto static 
172.20.0.0/16 via 192.168.10.200 dev eth1 proto static 
192.168.20.0/24 via 192.168.10.200 dev eth1 proto static 

(⎈|HomeLab:N/A) root@k8s-ctr:~# ip -c route
default via 10.0.2.2 dev eth0 proto dhcp src 10.0.2.15 metric 100 
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100 
10.0.2.2 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.0.2.3 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.10.0.0/16 via 192.168.10.200 dev eth1 proto static 
172.20.0.0/24 via 172.20.0.43 dev cilium_host proto kernel src 172.20.0.43 
172.20.0.0/16 via 192.168.10.200 dev eth1 proto static 
172.20.0.43 dev cilium_host proto kernel scope link 
172.20.1.0/24 via 192.168.10.101 dev eth1 proto kernel #같은 네트워크 대역(w1)은 Pod CIDR이 할당된 노드로 직접 라우팅 되도록 규칙이 설정됨
192.168.10.0/24 dev eth1 proto kernel scope link src 192.168.10.100
192.168.20.0/24 via 192.168.10.200 dev eth1 proto static  #다른 네트워크 대역(w2)은 라우터를 통한 정적 라우팅 사용

# 192.168.10.0/24 dev eth1 proto kernel scope link src 192.168.10.100 의 의미
# - 192.168.10.0/24: 로컬 네트워크 대역
# - dev eth1: eth1 인터페이스를 통해 접근
# - proto kernel: 커널이 자동으로 생성한 라우팅 규칙
# - scope link: 같은 네트워크 링크 내에서만 유효
# - src 192.168.10.100: 이 노드의 IP 주소

(⎈|HomeLab:N/A) root@k8s-ctr:~# sshpass -p 'vagrant' ssh vagrant@k8s-w1 ip -c route
default via 10.0.2.2 dev eth0 proto dhcp src 10.0.2.15 metric 100 
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100 
10.0.2.2 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.0.2.3 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.10.0.0/16 via 192.168.10.200 dev eth1 proto static 
172.20.0.0/24 via 192.168.10.100 dev eth1 proto kernel 
172.20.0.0/16 via 192.168.10.200 dev eth1 proto static 
172.20.1.0/24 via 172.20.1.12 dev cilium_host proto kernel src 172.20.1.12 
172.20.1.12 dev cilium_host proto kernel scope link 
192.168.10.0/24 dev eth1 proto kernel scope link src 192.168.10.101 
192.168.20.0/24 via 192.168.10.200 dev eth1 proto static # 스태틱 라우팅

(⎈|HomeLab:N/A) root@k8s-ctr:~# sshpass -p 'vagrant' ssh vagrant@k8s-w0 ip -c route
default via 10.0.2.2 dev eth0 proto dhcp src 10.0.2.15 metric 100 
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100 
10.0.2.2 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.0.2.3 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.10.0.0/16 via 192.168.20.200 dev eth1 proto static 
172.20.0.0/16 via 192.168.20.200 dev eth1 proto static 
172.20.2.0/24 via 172.20.2.241 dev cilium_host proto kernel src 172.20.2.241 
172.20.2.241 dev cilium_host proto kernel scope link 
192.168.10.0/24 via 192.168.20.200 dev eth1 proto static # 스태틱 라우팅
192.168.20.0/24 dev eth1 proto kernel scope link src 192.168.20.100 

(⎈|HomeLab:N/A) root@k8s-ctr:~# ping -c 1 10.10.1.200     # router loop1 
PING 10.10.1.200 (10.10.1.200) 56(84) bytes of data.
64 bytes from 10.10.1.200: icmp_seq=1 ttl=64 time=0.499 ms
# 통신 됨

--- 10.10.1.200 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.499/0.499/0.499/0.000 ms

(⎈|HomeLab:N/A) root@k8s-ctr:~# ping -c 1 192.168.20.100  # k8s-w0 eth1
PING 192.168.20.100 (192.168.20.100) 56(84) bytes of data.
64 bytes from 192.168.20.100: icmp_seq=1 ttl=63 time=0.786 ms
# 통신 됨

--- 192.168.20.100 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.786/0.786/0.786/0.000 ms

(⎈|HomeLab:N/A) root@k8s-ctr:~# tracepath -n 192.168.20.100
 1?: [LOCALHOST]                      pmtu 1500
 1:  192.168.10.200                                        0.620ms 
 1:  192.168.10.200                                        0.628ms 
 2:  192.168.20.100                                        0.799ms reached
 #라우터가 통신을 가로 채가는 것을 확인

네트워크 구성이 성공적으로 구축되어 라우터가 6개의 인터페이스(eth0, eth1, eth2, loop1, loop2)를 통해 다중 네트워크 환경을 제공하고 있으며, 각 Kubernetes 노드들이 정적 라우팅 규칙을 통해 라우터를 거쳐 서로 다른 서브넷 간 통신이 가능한 상태입니다.

Cilium의 native routing 모드가 정상 동작하여 Pod 네트워크(172.20.0.0/16)와 노드 네트워크가 분리되어 관리되고, 통신 테스트 결과 라우터 loop1(0.499ms)과 k8s-w0 노드(0.786ms)까지 모두 정상 연결되며 tracepath를 통해 2홉 경로로 라우터를 거쳐 목적지에 도달함을 확인할 수 있습니다.

라우터는 다중 네트워크 환경의 중앙 허브 역할을 수행하며, eth1(192.168.10.200)과 eth2(192.168.20.200)를 통해 서로 다른 서브넷을 연결하고 loop1(10.10.1.200)과 loop2(10.10.2.200)를 통해 외부 네트워크를 시뮬레이션합니다.

통신 테스트 결과는 네트워크 구성이 올바르게 설정되었으며 라우터를 통한 패킷 전달이 정상적으로 동작함을 보여줍니다.

racepath -n 192.168.20.100 을통해 네트워크 동작을 확인해 보았습니다.

1단계: 로컬 호스트 확인
1?: 첫 번째 홉에서 응답을 받지 못함 (정상적인 현상)
pmtu 1500: Path MTU가 1500바이트로 설정됨
Path MTU: 출발지에서 목적지까지 모든 네트워크 경로에서 통과할 수 있는 최대 패킷 크기

2단계: 라우터 도달
192.168.10.200: 라우터의 첫 번째 인터페이스 (eth1)
0.620ms, 0.628ms: 라우터까지의 왕복 시간 (RTT)
중복 응답: ICMP 패킷의 특성상 두 번의 응답이 나타남

3단계: 목적지 도달
reached
192.168.20.100: 최종 목적지 (k8s-w0 노드)
0.799ms: 전체 경로의 왕복 시간
reached: 목적지에 성공적으로 도달함

Native Routing Mode 실습

샘플 애플리케이션 배포

# 샘플 애플리케이션 배포
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webpod
spec:
  replicas: 3 # 3개의 Pod 생성
  selector:
    matchLabels:
      app: webpod
  template:
    metadata:
      labels:
        app: webpod
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - sample-app
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: webpod
        image: traefik/whoami
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: webpod
  labels:
    app: webpod
spec:
  selector:
    app: webpod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
EOF


# k8s-ctr 노드에 curl-pod 파드 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: curl-pod
  labels:
    app: curl
spec:
  nodeName: k8s-ctr
  containers:
  - name: curl
    image: nicolaka/netshoot
    command: ["tail"]
    args: ["-f", "/dev/null"]
  terminationGracePeriodSeconds: 0
EOF

확인: Cilium-dbg

# 배포 확인
kubectl get deploy,svc,ep webpod -owide # 웹 애플리케이션의 배포, 서비스, 엔드포인트 상태 확인
kubectl get endpointslices -l app=webpod # 웹 애플리케이션의 엔드포인트 슬라이스 확인
kubectl get ciliumendpoints # Cilium 엔드포인트 IP 확인

# Cilium 디버깅 명령어
kubectl exec -n kube-system ds/cilium -- cilium-dbg ip list # Cilium이 관리하는 IP 목록 확인
kubectl exec -n kube-system ds/cilium -- cilium-dbg endpoint list # Cilium 엔드포인트 목록 확인

kubectl exec -n kube-system ds/cilium -- cilium-dbg service list # Cilium이 관리하는 서비스 목록 확인

kubectl exec -n kube-system ds/cilium -- cilium-dbg bpf lb list # BPF 로드밸런서 목록 확인
kubectl exec -n kube-system ds/cilium -- cilium-dbg bpf lb list | grep 10.96.32.212 # 특정 서비스 IP의 로드밸런서 정보 확인

kubectl exec -n kube-system ds/cilium -- cilium-dbg bpf nat list # BPF NAT 테이블 확인

# BPF 맵 조회 명령어
kubectl exec -n kube-system ds/cilium -- cilium-dbg map list | grep -v '0             0' # 사용 중인 BPF 맵 목록 확인 (빈 맵 제외) 

# 스터디에서 언급된 것처럼 사용중인 BPF 맵 리스트를 가져와서 각 맵들에 상세 내용을 get으로 살펴봅니다.
kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_lb4_services_v2 # IPv4 서비스 로드밸런서 맵 조회
kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_lb4_services_v2 | grep 10.96.32.212 # 특정 서비스 IP의 로드밸런서 맵 엔트리 확인

kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_lb4_backends_v3 # IPv4 백엔드 로드밸런서 맵 조회
kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_lb4_reverse_nat # IPv4 역방향 NAT 맵 조회
kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_ipcache_v2 # IP 캐시 맵 조회

✅ 실행 결과 요약

# webpod 디플로이먼트 상태 확인 - 3개 파드 모두 정상 실행
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get deploy,svc,ep webpod -owide
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE    CONTAINERS   IMAGES           SELECTOR
deployment.apps/webpod   3/3     3            3           119s   webpod       traefik/whoami   app=webpod

NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE    SELECTOR
service/webpod   ClusterIP   10.96.189.34   <none>        80/TCP    119s   app=webpod

NAME               ENDPOINTS                                        AGE
endpoints/webpod   172.20.0.121:80,172.20.1.108:80,172.20.2.33:80   119s

# EndpointSlice 확인 - 3개 노드에 분산 배치됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get endpointslices -l app=webpod
NAME           ADDRESSTYPE   PORTS   ENDPOINTS                               AGE
webpod-sqw6v   IPv4          80      172.20.0.121,172.20.2.33,172.20.1.108   2m4s

# Cilium IP 캐시 확인 - 각 노드별 webpod IP 할당 상태
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg ip list 
# ... 생략 ...
172.20.0.121/32     k8s:app=webpod                                                                      custom-resource
# ... 생략 ...
172.20.1.108/32     k8s:app=webpod                                                                      custom-resource
# ... 생략 ...
172.20.2.33/32      k8s:app=webpod                                                                      custom-resource

# Cilium 엔드포인트 상태 확인 - webpod 엔드포인트 정상 등록
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg endpoint list # 데몬 셋이라 특정 노드 중 하나가 선택됨
# ... 생략 ...
56         Disabled           Disabled          62886      k8s:app=webpod                                                                             172.20.0.121   ready   
# ... 생략 ...

# Cilium 서비스 목록 - webpod 서비스 로드밸런싱 설정 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg service list
# ... 생략 ...
23   10.96.189.34:80/TCP      ClusterIP      1 => 172.20.0.121:80/TCP (active)
                                             2 => 172.20.1.108:80/TCP (active)       
                                             3 => 172.20.2.33:80/TCP (active) 
# 인입되는 IP가 10.96.189.34       

# BPF 로드밸런서 맵 확인 - webpod 서비스의 백엔드 엔드포인트 매핑
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg bpf lb list
# ... 생략 ...
10.96.189.34:80/TCP (1)        172.20.0.121:80/TCP (23) (1)                                  
10.96.189.34:80/TCP (2)        172.20.1.108:80/TCP (23) (2)                                  
10.96.189.34:80/TCP (3)        172.20.2.33:80/TCP (23) (3)                                   

# BPF 서비스 맵 상세 확인 - webpod 서비스의 로드밸런싱 설정
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_lb4_services_v2
# ... 생략 ...
10.96.189.34:80/TCP (1)        14 0[0] (23) [0x0 0x0]            
10.96.189.34:80/TCP (3)        16 0[0] (23) [0x0 0x0]            
10.96.189.34:80/TCP (2)        15 0[0] (23) [0x0 0x0]            
10.96.189.34:80/TCP (0)        0 3[0] (23) [0x0 0x0]            

# BPF 서비스 맵 필드 설명:
# Key: 서비스 주소 (IP:포트/프로토콜) (슬롯번호)
# Value: 백엔드 ID 개수[현재인덱스] (리버스NAT_ID) [플래그1 플래그2]
# - 슬롯번호: 로드밸런싱 알고리즘에서 사용하는 백엔드 선택 인덱스
# - 백엔드 ID: cilium_lb4_backends_v3 맵에서 참조하는 백엔드 식별자
# - 리버스NAT_ID: cilium_lb4_reverse_nat 맵에서 참조하는 NAT 테이블 식별자
# - 플래그: 서비스 타입 및 상태 정보 (ClusterIP, NodePort, External 등)

# BPF 백엔드 맵 확인 - webpod 파드들의 실제 IP 주소
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_lb4_backends_v3
# ... 생략 ...
15    TCP://172.20.1.108             
16    TCP://172.20.2.33              
14    TCP://172.20.0.121             

# BPF 백엔드 맵 필드 설명:
# Key: 백엔드 ID (서비스 맵에서 참조하는 식별자)
# Value: 프로토콜://실제_파드_IP:포트
# - 백엔드 ID: 서비스 맵의 Value에서 참조하는 고유 식별자
# - 실제 파드 IP: 로드밸런싱 대상이 되는 실제 파드의 IP 주소

# BPF 리버스 NAT 맵 확인 - webpod 서비스의 NAT 설정
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_lb4_reverse_nat
# ... 생략 ...
23    10.96.189.34:80                

# BPF 리버스 NAT 맵 필드 설명:
# Key: 리버스NAT_ID (서비스 맵의 Value에서 참조하는 식별자)
# Value: 서비스_IP:포트 (응답 패킷의 소스 주소로 사용)
# - 리버스NAT_ID: 서비스 맵에서 참조하는 NAT 테이블 식별자
# - 서비스 IP: 클라이언트가 접근하는 가상 IP 주소

# IP 캐시 맵 확인 - webpod 파드들의 네트워크 정체성 및 터널링 설정
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg map get cilium_ipcache_v2
# ... 생략 ...
172.20.0.121/32     identity=62886 encryptkey=0 tunnelendpoint=0.0.0.0 flags=<none>             sync    
172.20.1.108/32     identity=62886 encryptkey=0 tunnelendpoint=192.168.10.101 flags=hastunnel   sync    
172.20.2.33/32      identity=62886 encryptkey=0 tunnelendpoint=192.168.20.100 flags=hastunnel   sync    

# IP 캐시 맵 필드 설명:
# Key: IP주소/서브넷마스크
# Value: identity=정체성ID encryptkey=암호화키 tunnelendpoint=터널엔드포인트 flags=플래그
# - identity: Cilium이 할당한 네트워크 정체성 ID (보안 정책 적용에 사용)
# - encryptkey: IPsec 암호화 키 (0은 암호화 비활성화)
# - tunnelendpoint: 터널링을 위한 엔드포인트 IP (0.0.0.0은 로컬 노드)
# - flags: 네트워크 특성 플래그 (hastunnel, encrypt 등)
# - sync: 동기화 상태

웹팟 디플로이먼트가 3개 노드에 성공적으로 분산 배치되었습니다. 각 노드에는 하나씩의 파드가 실행되고 있으며, 모든 파드가 정상적으로 동작하고 있습니다.
또한 각 노드별로 고유한 Pod IP가 할당되었습니다. 컨트롤 플레인 노드에는 172.20.0.121, 첫 번째 워커 노드에는 172.20.1.108, 두 번째 워커 노드에는 172.20.2.33이 각각 할당되어 있습니다.

로드밸런싱 설정을 살펴보면 서비스 IP인 10.96.189.34:80으로 들어오는 모든 트래픽이 3개의 백엔드 파드로 균등하게 분산됩니다.

IP 캐시 맵에서 확인할 수 있듯이 hastunnel 플래그가 설정되어 있어, 노드 간 통신이 터널을 통해 이루어지고 있음을 알 수 있습니다.

즉 클라이언트가 서비스 IP인 10.96.189.34:80으로 접근하면, Cilium의 BPF 로드밸런서가 이 트래픽을 3개의 백엔드 파드(172.20.0.121, 172.20.1.108, 172.20.2.33)로 분산하여 전달하도록 구성되어있습니다.

통신확인

# 통신 확인 : 문제 확인
kubectl exec -it curl-pod -- curl webpod | grep Hostname
kubectl exec -it curl-pod -- sh -c 'while true; do curl -s --connect-timeout 1 webpod | grep Hostname; echo "---" ; sleep 1; done'

# 실행결과 통신이 안되는 상황 발생
Hostname: webpod-697b545f57-sgb7k
--- #안됨
--- #안됨
Hostname: webpod-697b545f57-t87x7
--- #안됨

# k8s-w0 노드에 배포된 webpod 파드 IP 지정
export WEBPOD=$(kubectl get pod -l app=webpod --field-selector spec.nodeName=k8s-w0 -o jsonpath='{.items[0].status.podIP}')
echo $WEBPOD
172.20.2.33

# 신규 터미널 [router]
tcpdump -i any icmp -nn

# 
kubectl exec -it curl-pod -- ping -c 2 -w 1 -W 1 $WEBPOD
PING 172.20.2.33 (172.20.2.33) 56(84) bytes of data.

--- 172.20.2.33 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

command terminated with exit code 1


# 신규 터미널 [router] : 라우팅이 어떻게 되는가?
root@router:~# tcpdump -i any icmp -nn
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
02:57:08.004967 eth1  In  IP 172.20.0.88 > 172.20.2.33: ICMP echo request, id 698, seq 1, length 64 #이더넷 1번으로 핑을 날림
02:57:08.004983 eth0  Out IP 172.20.0.88 > 172.20.2.33: ICMP echo request, id 698, seq 1, length 64 #이더넷 0번으로 라우팅 시켜버림


# 신규 터미널 [router]
ip -c route
default via 10.0.2.2 dev eth0 proto dhcp src 10.0.2.15 metric 100 
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15 metric 100 
10.0.2.2 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.0.2.3 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100 
10.10.0.0/16 via 192.168.10.200 dev eth1 proto static 
172.20.0.0/24 via 172.20.0.43 dev cilium_host proto kernel src 172.20.0.43 
172.20.0.0/16 via 192.168.10.200 dev eth1 proto static 
172.20.0.43 dev cilium_host proto kernel scope link 
172.20.1.0/24 via 192.168.10.101 dev eth1 proto kernel 
192.168.10.0/24 dev eth1 proto kernel scope link src 192.168.10.100 
192.168.20.0/24 via 192.168.10.200 dev eth1 proto static 

라우팅 테이블에 Pod 대역(172.20.2.33)이 등록되지 않아 eth0으로 가버림


#
kubectl exec -it curl-pod -- sh -c 'while true; do curl -s --connect-timeout 1 webpod | grep Hostname; echo "---" ; sleep 1; done'

# 신규 터미널 [router]
tcpdump -i any tcp port 80 -nn


# hubble 확인
# hubble ui 웹 접속 주소 확인 : default 네임스페이스 확인
NODEIP=$(ip -4 addr show eth1 | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
echo -e "http://$NODEIP:30003"

# hubble relay 포트 포워딩 실행
cilium hubble port-forward&
hubble status

# flow log 모니터링
hubble observe -f --protocol tcp --pod curl-pod
ℹ️  Hubble Relay is available at 127.0.0.1:4245
hubble observe -f --protocol tcphubble observe -f --protocol tcp --pod curl-pod
Aug  6 18:00:59.143 [hubble-relay-5b48c999f9-d9vwl]: 1 nodes are unavailable: k8s-w0 #W0 노드가 사용불가능으로 뜨게 됨

Overlay Network 적용 실습


오버레이 네트워크는 물리적 네트워크 위에 가상의 네트워크를 구축하는 방식입니다. Pod 간 통신 시 원본 패킷을 캡슐화하여 외부 헤더로 감싸고, 이를 통해 노드 간 통신이 가능하도록 합니다.

 

이 방식은 기존 네트워크 인프라를 변경하지 않고도 Pod 간 격리된 통신 환경을 제공할 수 있습니다.

# [커널 구성 옵션] Requirements for Tunneling and Routing
grep -E 'CONFIG_VXLAN=y|CONFIG_VXLAN=m|CONFIG_GENEVE=y|CONFIG_GENEVE=m|CONFIG_FIB_RULES=y' /boot/config-$(uname -r)
CONFIG_FIB_RULES=y # 커널에 내장됨
CONFIG_VXLAN=m # 모듈로 컴파일됨 → 커널에 로드해서 사용
CONFIG_GENEVE=m # 모듈로 컴파일됨 → 커널에 로드해서 사용

#  커널 로드
lsmod | grep -E 'vxlan|geneve'
modprobe vxlan # modprobe geneve
lsmod | grep -E 'vxlan|geneve'

for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i sudo modprobe vxlan ; echo; done
for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i sudo lsmod | grep -E 'vxlan|geneve' ; echo; done


# k8s-w1 노드에 배포된 webpod 파드 IP 지정
export WEBPOD1=$(kubectl get pod -l app=webpod --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].status.podIP}')
echo $WEBPOD1

# 반복 ping 실행해두기
kubectl exec -it curl-pod -- ping $WEBPOD1


# 업그레이드
helm upgrade cilium cilium/cilium --namespace kube-system --version 1.18.0 --reuse-values \
  --set routingMode=tunnel --set tunnelProtocol=vxlan \
  --set autoDirectNodeRoutes=false --set installNoConntrackIptablesRules=false

kubectl rollout restart -n kube-system ds/cilium


# 설정 확인
cilium features status
cilium features status | grep datapath_network

kubectl exec -it -n kube-system ds/cilium -- cilium status | grep ^Routing
cilium config view | grep tunnel

# cilium_vxlan 확인
ip -c addr show dev cilium_vxlan
for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i ip -c addr show dev cilium_vxlan ; echo; done

# 라우팅 정보 확인 : k8s node 간 다른 네트워크 대역에 있더라도, 파드의 네트워크 대역 정보가 라우팅에 올라왔다!
ip -c route | grep cilium_host

ip route get 172.20.1.10
ip route get 172.20.2.10

for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i ip -c route | grep cilium_host ; echo; done


# cilium 파드 이름 지정
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1  -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w0  -o jsonpath='{.items[0].metadata.name}')
echo $CILIUMPOD0 $CILIUMPOD1 $CILIUMPOD2

# router 역할 IP 확인 - 각 노드의 라우터 IP 주소 확인
kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium status --all-addresses | grep router
kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium status --all-addresses | grep router
kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium status --all-addresses | grep router

# BPF IP 캐시 맵 확인 - 터널 모드에서 IP 정체성 매핑 상태
kubectl exec -n kube-system ds/cilium -- cilium-dbg bpf ipcache list
kubectl exec -n kube-system $CILIUMPOD0 -- cilium-dbg bpf ipcache list
kubectl exec -n kube-system $CILIUMPOD1 -- cilium-dbg bpf ipcache list
kubectl exec -n kube-system $CILIUMPOD2 -- cilium-dbg bpf ipcache list

# BPF 소켓 NAT 맵 확인 - 터널 모드에서 NAT 규칙 상태
kubectl exec -n kube-system ds/cilium -- cilium-dbg bpf socknat list
kubectl exec -n kube-system $CILIUMPOD0 -- cilium-dbg bpf socknat list
kubectl exec -n kube-system $CILIUMPOD1 -- cilium-dbg bpf socknat list
kubectl exec -n kube-system $CILIUMPOD2 -- cilium-dbg bpf socknat list

✅ 실행 결과 요약

# 커널 구성 확인 - VXLAN 터널링 지원 여부 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# grep -E 'CONFIG_VXLAN=y|CONFIG_VXLAN=m|CONFIG_GENEVE=y|CONFIG_GENEVE=m|CONFIG_FIB_RULES=y' /boot/config-$(uname -r)
CONFIG_FIB_RULES=y
CONFIG_VXLAN=m
CONFIG_GENEVE=m

# 기본적으로 vxlan 적용이 되어 있지 않은 것을 확인할 수 있습니다.
(⎈|HomeLab:N/A) root@k8s-ctr:~# lsmod | grep -E 'vxlan|geneve'


# VXLAN 모듈 로드 - 터널링 프로토콜 활성화
(⎈|HomeLab:N/A) root@k8s-ctr:~# modprobe vxlan 

# VXLAN 적용이 되었습니다!
(⎈|HomeLab:N/A) root@k8s-ctr:~# lsmod | grep -E 'vxlan|geneve'
vxlan                 147456  0
ip6_udp_tunnel         16384  1 vxlan
udp_tunnel             36864  1 vxlan

# 3. 모든 워커 노드에 VXLAN 모듈 로드 - 클러스터 전체에서 터널링 지원
(⎈|HomeLab:N/A) root@k8s-ctr:~# for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i sudo modprobe vxlan ; echo; done
>> node : k8s-w1 <<

>> node : k8s-w0 <<

(⎈|HomeLab:N/A) root@k8s-ctr:~# for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i sudo lsmod | grep -E 'vxlan|geneve' ; echo; done
>> node : k8s-w1 <<
vxlan                 147456  0
ip6_udp_tunnel         16384  1 vxlan
udp_tunnel             36864  1 vxlan

>> node : k8s-w0 <<
vxlan                 147456  0
ip6_udp_tunnel         16384  1 vxlan
udp_tunnel             36864  1 vxlan

# 통신 테스트 - 터널 모드 적용 전후 비교
(⎈|HomeLab:N/A) root@k8s-ctr:~# export WEBPOD1=$(kubectl get pod -l app=webpod --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].status.podIP}')

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it curl-pod -- ping $WEBPOD1
PING 172.20.1.108 (172.20.1.108) 56(84) bytes of data.
64 bytes from 172.20.1.108: icmp_seq=19 ttl=62 time=0.512 ms
64 bytes from 172.20.1.108: icmp_seq=20 ttl=62 time=0.474 ms
From 172.20.0.43 icmp_seq=21 Time to live exceeded # cilium 데몬셋을 재실행하며 통신이 끊김
64 bytes from 172.20.1.108: icmp_seq=32 ttl=63 time=0.734 ms
64 bytes from 172.20.1.108: icmp_seq=33 ttl=63 time=0.585 ms

# overlay-vxlan 모드로 설정됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# cilium features status | grep datapath_network
Yes      cilium_feature_datapath_network                                         mode=overlay-vxlan                                1        1       1       

# VXLAN 터널 모드로 라우팅 설정됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it -n kube-system ds/cilium -- cilium status | grep ^Routing
Routing:                 Network: Tunnel [vxlan]   Host: BPF

# 터널 모드와 VXLAN 프로토콜 설정 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# cilium config view | grep tunnel
routing-mode                                      tunnel
tunnel-protocol                                   vxlan
tunnel-source-port-range                          0-0


# 6VXLAN 인터페이스 생성 확인 - 터널링을 위한 가상 인터페이스
(⎈|HomeLab:N/A) root@k8s-ctr:~# ip -c addr show dev cilium_vxlan
26: cilium_vxlan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    link/ether 9a:19:81:dc:91:8e brd ff:ff:ff:ff:ff:ff
    inet6 fe80::9819:81ff:fedc:918e/64 scope link 
       valid_lft forever preferred_lft forever

(⎈|HomeLab:N/A) root@k8s-ctr:~# for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i ip -c addr show dev cilium_vxlan ; echo; done
>> node : k8s-w1 <<
8: cilium_vxlan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    link/ether 32:f7:07:ae:22:61 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::30f7:7ff:feae:2261/64 scope link 
       valid_lft forever preferred_lft forever

>> node : k8s-w0 <<
8: cilium_vxlan: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default 
    link/ether 2a:9d:39:98:aa:db brd ff:ff:ff:ff:ff:ff
    inet6 fe80::289d:39ff:fe98:aadb/64 scope link 
       valid_lft forever preferred_lft forever

# 라우팅 테이블 변경 확인 - 터널 모드 적용 후 Pod 네트워크 대역이 라우팅에 등록됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# ip -c route | grep cilium_host
172.20.0.0/24 via 172.20.0.43 dev cilium_host proto kernel src 172.20.0.43 
172.20.0.43 dev cilium_host proto kernel scope link 
172.20.1.0/24 via 172.20.0.43 dev cilium_host proto kernel src 172.20.0.43 mtu 1450 # 모든 Pod 네트워크 대역이 cilium_host를 통해 라우팅됨
172.20.2.0/24 via 172.20.0.43 dev cilium_host proto kernel src 172.20.0.43 mtu 1450 # 모든 Pod 네트워크 대역이 cilium_host를 통해 라우팅됨


(⎈|HomeLab:N/A) root@k8s-ctr:~# ip route get 172.20.1.10
172.20.1.10 dev cilium_host src 172.20.0.43 uid 0 
    cache mtu 1450 
(⎈|HomeLab:N/A) root@k8s-ctr:~# ip route get 172.20.2.10
172.20.2.10 dev cilium_host src 172.20.0.43 uid 0 
    cache mtu 1450 

# 각 노드별 라우팅 테이블 확인 - 모든 노드에서 Pod 네트워크 대역 라우팅 설정됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# for i in w1 w0 ; do echo ">> node : k8s-$i <<"; sshpass -p 'vagrant' ssh vagrant@k8s-$i ip -c route | grep cilium_host ; echo; done
>> node : k8s-w1 <<
172.20.0.0/24 via 172.20.1.12 dev cilium_host proto kernel src 172.20.1.12 mtu 1450 
172.20.1.0/24 via 172.20.1.12 dev cilium_host proto kernel src 172.20.1.12 
172.20.1.12 dev cilium_host proto kernel scope link 
172.20.2.0/24 via 172.20.1.12 dev cilium_host proto kernel src 172.20.1.12 mtu 1450 

>> node : k8s-w0 <<
172.20.0.0/24 via 172.20.2.241 dev cilium_host proto kernel src 172.20.2.241 mtu 1450 
172.20.1.0/24 via 172.20.2.241 dev cilium_host proto kernel src 172.20.2.241 mtu 1450 
172.20.2.0/24 via 172.20.2.241 dev cilium_host proto kernel src 172.20.2.241 
172.20.2.241 dev cilium_host proto kernel scope link 

# 각 노드의 라우터 IP 확인 - 터널링을 위한 엔드포인트 주소
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it $CILIUMPOD0 -n kube-system -c cilium-agent -- cilium status --all-addresses | grep router
  172.20.0.43 (router)

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it $CILIUMPOD1 -n kube-system -c cilium-agent -- cilium status --all-addresses | grep router
  172.20.1.12 (router)

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it $CILIUMPOD2 -n kube-system -c cilium-agent -- cilium status --all-addresses | grep router
  172.20.2.241 (router)

# 1BPF IP 캐시 맵 확인 - 터널 엔드포인트 정보 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system ds/cilium -- cilium-dbg bpf ipcache list
# ... 생략 ...
172.20.1.12/32      identity=6 encryptkey=0 tunnelendpoint=192.168.10.101 flags=hastunnel       
172.20.2.0/24       identity=2 encryptkey=0 tunnelendpoint=192.168.20.100 flags=hastunnel       
# hastunnel 플래그와 tunnelendpoint 설정 확인

# 통신 성공 확인 - 터널 모드 적용 후 정상 통신
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it curl-pod -- curl webpod | grep Hostname
Hostname: webpod-697b545f57-2dg5m

# Hubble 모니터링 - 터널링 패킷 흐름 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# hubble observe -f --protocol tcp --pod curl-pod
Aug  6 18:34:25.150: default/curl-pod (ID:45966) <> 10.96.189.34:80 (world) pre-xlate-fwd TRACED (TCP)
Aug  6 18:34:25.150: default/curl-pod (ID:45966) <> default/webpod-697b545f57-2dg5m:80 (ID:62886) post-xlate-fwd TRANSLATED (TCP)
Aug  6 18:34:25.151: default/curl-pod:56950 (ID:45966) -> default/webpod-697b545f57-2dg5m:80 (ID:62886) to-overlay FORWARDED (TCP Flags: SYN) # to-overlay 상태로 터널링 패킷 전송 확인


# 13. 실제 VXLAN 패킷 캡처 - 물리적 네트워크에서 터널링 패킷 확인
root@router:~# tcpdump -i any udp port 8472 -nn
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
03:35:21.286843 eth1  In  IP 192.168.10.100.46993 > 192.168.20.100.8472: OTV, flags [I] (0x08), overlay 0, instance 45966
IP 172.20.0.88.41794 > 172.20.2.33.80: Flags [S], seq 951063534, win 64860, options [mss 1410,sackOK,TS val 3355448797 ecr 0,nop,wscale 7], length 0
# VXLAN 헤더로 캡슐화된 Pod 간 통신 패킷 확인

router의 터미널 샤크에서 패킷 캡쳐하여을 확인해 보면 다음과 같이 오버레이 패킷을 확인할 수 있고, 아우터에 설정된 IP만 확인하고 라우팅 처리를 하는 것을 볼 수 있습니다.
내부 패킷에 Pod의 IP가 캡슐화 되어 들어 있습니다.

오버레이 패킷을 보면 알 수 있듯이, 아래의 그림과 같이 파드 > 출발 노드 > 라우터 > 타겟 노드 > 파드로 통신이 일어날 때 인캡슐레이션과 디캡슐레이션이 발생하게 됩니다.

Overlay 모드에서 파드가 노드를 빠져나갈 때 패킷 인캡슐레이션 수행

Overlay 모드에서 노드에서 패킷의 디캡슐레이션 수행

K8S Service 종류

Kubernetes에서 서비스는 파드에 대한 안정적인 엔드포인트를 제공하는 리소스입니다. 파드의 IP는 동적이므로 직접 접근하기 어렵고, 서비스는 이를 해결하기 위한 추상화 계층을 제공합니다.

1. 파드 생성

  • Kubernetes 클러스터 내부에서만 접속 가능
  • 파드는 동적 IP를 가지므로 직접 접근이 어려움
  • 파드가 재시작되면 IP가 변경되어 접근이 불가능해짐
  • 로드 밸런싱이나 고정 엔드포인트 기능 없음

2. 서비스(ClusterIP Type) 연결

  • Kubernetes 클러스터 내부에서만 접속 가능
  • 동일한 애플리케이션의 다수의 파드에 대한 로드 밸런싱 제공
  • 고정 접속점(호출) 방법을 제공:
    • 고정 VirtualIP: 파드가 재시작되어도 변경되지 않는 고정 IP
    • 도메인 주소: 서비스명.네임스페이스.svc.cluster.local 형태의 DNS 이름
  • 파드의 상태와 관계없이 일관된 엔드포인트 제공
  • 헬스체크를 통한 자동 파드 제외/포함 기능

3. NodePort Type 연결

  • 외부 클라이언트가 서비스를 통해서 클러스터 내부의 파드로 접속 가능
  • 모든 노드의 동일한 포트(30000-32767 범위)를 통해 서비스에 접근
  • 클러스터 외부에서 노드IP:NodePort 형태로 접근
  • 내부적으로는 ClusterIP 서비스의 기능을 모두 포함
  • 단일 노드 장애 시에도 다른 노드를 통해 접근 가능

서비스 종류

Kubernetes의 서비스 타입은 외부에서 클러스터 내부의 파드에 접근하는 방식을 결정합니다. 각 타입별로 접근 범위와 사용 목적이 다릅니다.

ClusterIP

  • 클러스터 내부에서만 접근 가능
  • 파드 간 통신을 위한 내부 서비스
  • 고정된 Virtual IP와 DNS 이름 제공
  • 로드 밸런싱과 헬스체크 기능
  • 외부에서는 직접 접근 불가능

NodePort Type

  • 외부에서 노드 IP:포트로 접근 가능
  • 모든 노드의 동일한 포트(30000-32767)를 통해 서비스 노출
  • 내부적으로는 ClusterIP 기능 포함
  • 개발/테스트 환경에서 주로 사용
  • 단일 노드 장애 시에도 다른 노드로 접근 가능

LoadBalancer Type

  • 클라우드 로드밸런서를 통한 외부 접근
  • NodePort + 외부 로드밸런서 조합
  • 고정된 외부 IP와 포트 제공
  • 프로덕션 환경에서 주로 사용
  • 클라우드 제공업체의 로드밸런서 또는 MetalLB와 같은 소프트웨어 로드벨런서 필요

Service LB-IPAM

IPAM (IP Address Management)
IPAM은 Kubernetes 클러스터에서 IP 주소를 자동으로 할당하고 관리하는 시스템입니다.

  • IP 주소 할당 및 관리 시스템
  • Kubernetes 클러스터에서 파드와 서비스에 IP 주소를 자동으로 할당
  • IP 풀 관리: 사용 가능한 IP 주소 범위 관리
  • IP 주소 충돌 방지 및 효율적인 주소 공간 관리
  • LoadBalancer 서비스에서 외부 IP 할당을 담당
  • 회수 및 재사용: 파드 삭제 시 IP 주소 회수하여 재사용

BGP (Border Gateway Protocol)

BGP의 개념:

  • BGP는 인터넷에서 사용되는 표준 라우팅 프로토콜입니다
  • 자율 시스템(AS) 간의 라우팅 정보를 교환하는 데 사용됩니다
  • 현재 인터넷의 핵심 라우팅 프로토콜로, 전 세계 네트워크 연결을 담당합니다

Cilium에서 BGP의 역할:

  • LoadBalancer IP 광고: LB-IPAM이 할당한 IP를 네트워크 전체에 광고
  • 외부 네트워크 연결: 클러스터 외부에서 LoadBalancer 서비스 접근 가능
  • 자동 라우팅: 네트워크 장비가 자동으로 트래픽을 클러스터로 전달
  • 고가용성: 여러 노드에 분산된 서비스에 대한 안정적인 접근

BGP 작동 방식:

BGP 라우터 동작 과정 설명:

  1. BGP 세션 수립 단계
    • BGP 라우터들이 서로 연결을 시도하여 BGP 세션을 수립합니다.
    • 세션이 성공적으로 수립되면 라우팅 정보 교환이 시작됩니다.
  2. 라우팅 정보 교환 단계
    • 각 BGP 라우터가 자신이 알고 있는 네트워크 정보를 상대방에게 전송합니다.
    • 라우팅 테이블에 저장된 경로 정보를 BGP 메시지 형태로 교환합니다.
    • 네트워크 도달 가능성과 경로 속성 정보를 포함하여 전달합니다.
  3. 라우팅 테이블 업데이트 단계
    • 수신된 BGP 정보를 바탕으로 각 라우터의 라우팅 최적의 경로를 선택하여 라우팅 테이블에 등록합니다.
  4. 트래픽 라우팅 단계
    • 외부 클라이언트의 요청이 BGP 라우터에 도달합니다.
    • 라우터는 라우팅 테이블을 참조하여 최적 경로를 선택하여 선택된 경로를 따라 트래픽을 다음 홉(Next Hop)으로 전달합니다.
  5. 경로 장애 대응 단계
    • 네트워크 장애 발생 시 BGP 라우터가 장애를 감지하면 장애가 발생한 경로를 라우팅 테이블에서 제거합니다.
    • 대체 경로가 있다면 자동으로 대체 경로로 트래픽을 전환합니다.

LB-IPAM(LoadBalancer IP Address Management)


LB-IPAM의 개념과 필요성:

  • LB-IPAM은 Cilium이 IP 주소를 LoadBalancer 유형의 서비스에 자동으로 할당할 수 있게 해주는 핵심 기능입니다. 이 기능은 일반적으로 클라우드 제공업체(AWS, GCP, Azure 등)에서 제공하는 서비스이지만, 프라이빗 클라우드 환경이나 온프레미스 환경에서는 이러한 기능을 항상 사용할 수 있는 것은 아닙니다. LB-IPAM은 이러한 환경에서도 클라우드와 동일한 수준의 LoadBalancer 기능을 제공합니다.

LB-IPAM의 연동 기능:

  • LB-IPAM은 단독으로 작동하지 않고, Cilium BGP Control Plane 및 L2 Announcements / L2 Aware LB(베타)와 같은 기능과 함께 작동합니다. LB-IPAM이 서비스 객체에 IP를 할당하는 역할을 담당한다면, 이러한 연동 기능들은 할당된 IP의 로드 밸런싱 및 네트워크 광고를 담당합니다.

IP 주소 광고 방식:

  • BGP를 통한 광고: Cilium BGP Control Plane을 사용하여 LB-IPAM이 할당한 IP 주소를 BGP 프로토콜을 통해 네트워크 전체에 광고합니다. 이는 대규모 네트워크에서 효율적인 라우팅을 가능하게 합니다.
  • 로컬 광고: L2 Announcements / L2 Aware LB(베타)를 통해 할당된 IP를 로컬 네트워크에 직접 광고합니다. 이는 간단한 네트워크 환경에서 빠른 접근을 제공합니다.

LB-IPAM의 활성화 과정:

  • LB-IPAM은 항상 시스템에 활성화되어 있지만 초기에는 휴면 상태로 대기합니다. 첫 번째 IP 풀이 클러스터에 추가되면 컨트롤러가 자동으로 활성화되어 IP 할당 작업을 시작합니다. 이는 리소스를 효율적으로 사용하면서 필요할 때만 동작하는 설계입니다.

LB-IPAM의 역할:

  • LoadBalancer 서비스 전용 IP 주소 관리 시스템
  • 외부에서 접근 가능한 고정 IP 주소 풀 관리
  • LoadBalancer 서비스 생성 시 자동으로 외부 IP 할당
  • 서비스 삭제 시 IP 주소 자동 회수

작동 방식:

LB-IPAM 작동 과정 설명:

  1. IP 풀 구성 단계
    • 관리자가 외부 IP 주소 범위를 설정하여 IP 풀을 구성합니다.
    • 예시: 192.168.1.100 ~ 192.168.1.200 범위의 IP 주소를 사용 가능한 풀로 등록
  2. LoadBalancer 서비스 생성 및 IP 할당
    • 사용자가 LoadBalancer 타입의 서비스를 생성하면,
    • LB-IPAM 시스템이 IP 풀에서 사용 가능한 IP 주소를 자동으로 할당합니다.
    • 할당된 IP는 해당 서비스의 고정 외부 IP가 됩니다.
  3. 외부 클라이언트 접근
    • 외부 클라이언트가 할당된 IP 주소로 서비스에 접근합니다.
    • LoadBalancer가 외부 요청을 받아서 클러스터 내부의 파드로 트래픽을 분산합니다.
  4. 서비스 삭제 및 IP 회수
    • LoadBalancer 서비스가 삭제되면,
    • LB-IPAM이 해당 IP 주소를 자동으로 IP 풀로 반환합니다.
    • 반환된 IP는 다른 서비스에서 재사용할 수 있습니다.

LB IPAM으로 webpod 서비스를 LoadBalancer Type으로 설정 실습

내부 접근 테스트

# 초기 상태 확인 - IP 풀이 없음을 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get CiliumLoadBalancerIPPool -A
No resources found

# CiliumLoadBalancerIPPool 생성 - 192.168.10.211~215 범위의 IP 풀 설정
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2"  # v1.17 : cilium.io/v2alpha1
kind: CiliumLoadBalancerIPPool
metadata:
  name: "cilium-lb-ippool"
spec:
  blocks:
  - start: "192.168.10.211"
    stop:  "192.168.10.215"
EOF
ciliumloadbalancerippool.cilium.io/cilium-lb-ippool created

# webpod 서비스를 LoadBalancer 타입으로 변경
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl patch svc webpod -p '{"spec":{"type":"LoadBalancer"}}'
service/webpod patched

# 서비스 상태 확인 - EXTERNAL-IP가 192.168.10.211로 할당됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc webpod
NAME     TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
webpod   LoadBalancer   10.96.189.34   192.168.10.211   80:31016/TCP   21h

# LoadBalancer IP 확인 및 변수 설정
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc webpod -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
192.168.10.211
(⎈|HomeLab:N/A) root@k8s-ctr:~# LBIP=$(kubectl get svc webpod -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

# 외부에서 LoadBalancer IP로 접근 테스트 - 성공
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LBIP
Hostname: webpod-697b545f57-sgb7k
IP: 127.0.0.1
IP: ::1
IP: 172.20.1.108
IP: fe80::ac41:1aff:feb9:e365
RemoteAddr: 172.20.0.43:51752
GET / HTTP/1.1
Host: 192.168.10.211
User-Agent: curl/8.5.0
Accept: */*

# 클러스터 내부에서 LoadBalancer IP로 접근 테스트 - 성공
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -it curl-pod -- curl -s $LBIP
Hostname: webpod-697b545f57-2dg5m
IP: 127.0.0.1
IP: ::1
IP: 172.20.2.33
IP: fe80::5cfb:28ff:fe36:fa98
RemoteAddr: 172.20.0.88:53482
GET / HTTP/1.1
Host: 192.168.10.211
User-Agent: curl/8.14.1
Accept: */*

# 로드 밸런싱 테스트 - 여러 파드에 요청이 분산됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# while true; do kubectl exec -it curl-pod -- curl -s $LBIP | grep Hostname; sleep 0.1; done
Hostname: webpod-697b545f57-t87x7
Hostname: webpod-697b545f57-t87x7
Hostname: webpod-697b545f57-sgb7k
Hostname: webpod-697b545f57-sgb7k
Hostname: webpod-697b545f57-2dg5m
Hostname: webpod-697b545f57-t87x7
Hostname: webpod-697b545f57-sgb7k
Hostname: webpod-697b545f57-t87x7

# 100회 요청 통계 - 로드 밸런싱이 균등하게 작동함
(⎈|HomeLab:N/A) root@k8s-ctr:~# for i in {1..100};  do kubectl exec -it curl-pod -- curl -s $LBIP | grep Hostname; done | sort | uniq -c | sort -nr
      36 Hostname: webpod-697b545f57-t87x7  # 36% 분배
      35 Hostname: webpod-697b545f57-sgb7k  # 35% 분배
      29 Hostname: webpod-697b545f57-2dg5m  # 29% 분배

# IP 풀 상태 확인 - 5개 IP 중 1개 사용, 4개 사용 가능
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ippools
NAME               DISABLED   CONFLICTING   IPS AVAILABLE   AGE
cilium-lb-ippool   false      False         4               77s

# IP 풀 상세 정보 - 총 5개 IP, 4개 사용 가능, 1개 사용 중
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get ippools -o jsonpath='{.items[*].status.conditions[?(@.type!="cilium.io/PoolConflict")]}' | jq
{
  "lastTransitionTime": "2025-08-07T15:14:07Z",
  "message": "5",  # 총 IP 개수
  "observedGeneration": 1,
  "reason": "noreason",
  "status": "Unknown",
  "type": "cilium.io/IPsTotal"
}
{
  "lastTransitionTime": "2025-08-07T15:14:07Z",
  "message": "4",  # 사용 가능한 IP 개수
  "observedGeneration": 1,
  "reason": "noreason",
  "status": "Unknown",
  "type": "cilium.io/IPsAvailable"
}
{
  "lastTransitionTime": "2025-08-07T15:14:07Z",
  "message": "1",  # 사용 중인 IP 개수
  "observedGeneration": 1,
  "reason": "noreason",
  "status": "Unknown",
  "type": "cilium.io/IPsUsed"
}

# 서비스 상세 정보 - LoadBalancer 상태 및 할당된 IP 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc webpod -o json | jq
{
  "apiVersion": "v1",
  "kind": "Service",
  "metadata": {
    "annotations": {
      "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"labels\":{\"app\":\"webpod\"},\"name\":\"webpod\",\"namespace\":\"default\"},\"spec\":{\"ports\":[{\"port\":80,\"protocol\":\"TCP\",\"targetPort\":80}],\"selector\":{\"app\":\"webpod\"},\"type\":\"ClusterIP\"}}\n"
    },
    "creationTimestamp": "2025-08-06T17:32:54Z",
    "labels": {
      "app": "webpod"
    },
    "name": "webpod",
    "namespace": "default",
    "resourceVersion": "92465",
    "uid": "9727df2d-82ca-4d38-837f-f161ff898517"
  },
  "spec": {
    "allocateLoadBalancerNodePorts": true,
    "clusterIP": "10.96.189.34",  # 내부 클러스터 IP
    "clusterIPs": [
      "10.96.189.34"
    ],
    "externalTrafficPolicy": "Cluster",
    "internalTrafficPolicy": "Cluster",
    "ipFamilies": [
      "IPv4"
    ],
    "ipFamilyPolicy": "SingleStack",
    "ports": [
      {
        "nodePort": 31016,  # NodePort 번호
        "port": 80,
        "protocol": "TCP",
        "targetPort": 80
      }
    ],
    "selector": {
      "app": "webpod"
    },
    "sessionAffinity": "None",
    "type": "LoadBalancer"  # 서비스 타입이 LoadBalancer로 변경됨
  },
  "status": {
    "conditions": [
      {
        "lastTransitionTime": "2025-08-07T15:14:19Z",
        "message": "",
        "reason": "satisfied",
        "status": "True",
        "type": "cilium.io/IPAMRequestSatisfied"  # IP 할당 요청이 만족됨
      }
    ],
    "loadBalancer": {
      "ingress": [
        {
          "ip": "192.168.10.211",  # 할당된 외부 IP
          "ipMode": "VIP"  # Virtual IP 모드
        }
      ]
    }
  }
}

# 서비스 상태 요약 - IP 할당 완료 및 VIP 모드 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc webpod -o jsonpath='{.status}' | jq
{
  "conditions": [
    {
      "lastTransitionTime": "2025-08-07T15:14:07Z",
      "message": "",
      "reason": "satisfied",
      "status": "True",
      "type": "cilium.io/IPAMRequestSatisfied"  # IP 할당 요청 만족
    }
  ],
  "loadBalancer": {
    "ingress": [
      {
        "ip": "192.168.10.211",  # 할당된 외부 IP
        "ipMode": "VIP"  # Virtual IP 모드로 설정됨
      }
    ]
  }
}

외부 접근 테스트

(⎈|HomeLab:N/A) root@k8s-ctr:~# LBIP=192.168.10.211

(⎈|HomeLab:N/A) root@k8s-ctr:~# curl --connect-timeout 1 $LBIP
Hostname: webpod-697b545f57-2dg5m
IP: 127.0.0.1
IP: ::1
IP: 172.20.2.33
IP: fe80::5cfb:28ff:fe36:fa98
RemoteAddr: 172.20.0.43:56814
GET / HTTP/1.1
Host: 192.168.10.211
User-Agent: curl/8.5.0
Accept: */*

(⎈|HomeLab:N/A) root@k8s-ctr:~# arping -i eth1 $LBIP -c 1
ARPING 192.168.10.211
Timeout

--- 192.168.10.211 statistics ---
1 packets transmitted, 0 packets received, 100% unanswered (0 extra)

외부에서는 접근이 되지 않는것을 확인할 수 있습니다.

Cilium L2 Announcement 실습

MetalLB Layer2 모드

MetalLB Layer2 모드의 개념:

  • Layer2 모드는 MetalLB가 할당된 IP 주소를 네트워크에 ARP/NDP를 통해 광고하는 방식입니다
  • 클러스터 내의 특정 노드가 해당 IP의 MAC 주소를 가지게 되어 트래픽을 받습니다
  • 단순하고 효율적인 방식으로, 추가 네트워크 장비 없이도 LoadBalancer 기능을 제공합니다

Metal LB의 작동원리는 다음과 같습니다.

1단계: IP 할당 및 ARP 광고

  • LoadBalancer 서비스가 생성되면 MetalLB가 IP 풀에서 IP 주소를 할당합니다.
  • 할당된 IP를 담당하는 노드가 ARP 응답을 통해 자신의 MAC 주소를 네트워크에 광고합니다.
  • 네트워크의 다른 장비들(라우터, 스위치 등)이 해당 IP의 MAC 주소를 ARP 테이블에 저장합니다.

2단계: 외부 트래픽 수신

  • 외부 클라이언트가 할당된 IP(예: 192.168.10.211)로 요청을 보냅니다.
  • 네트워크 라우터가 ARP 테이블을 확인하여 해당 IP의 MAC 주소를 찾습니다.
  • 라우터가 저장된 MAC 주소를 기반으로 트래픽을 담당 노드로 전송합니다.

3단계: 내부 로드 밸런싱

  • 트래픽을 받은 노드에서 LoadBalancer 서비스로 패킷을 전달합니다.
  • 서비스는 로드 밸런싱 알고리즘을 사용하여 여러 파드 중 하나를 선택합니다.
  • 선택된 파드로 트래픽을 전달하고, 파드에서 응답을 생성합니다.

4단계: 응답 전송

  • 파드에서 생성된 응답이 노드를 통해 라우터로 전송됩니다.
  • 라우터는 응답을 외부 클라이언트로 전달하여 통신이 완료됩니다.

장애 상황에서의 동작:

노드 장애 발생 시:

  • 현재 IP를 담당하는 노드가 장애가 발생하면 응답을 할 수 없게 됩니다.
  • 네트워크에서 ARP 타임아웃이 발생하여 해당 MAC 주소를 무효화합니다.
  • MetalLB가 다른 정상 노드로 IP를 자동으로 이전합니다.

IP 이전 및 복구:

  • 새로운 노드가 해당 IP의 MAC 주소를 ARP로 다시 광고합니다.
  • 네트워크 장비들이 새로운 MAC 주소로 트래픽을 전송하기 시작합니다.
  • 새로운 노드에서 LoadBalancer 서비스가 정상 작동하여 서비스가 복구됩니다.

Cilium Layer2 Announcement using ARP

  • Cilium이 MetalLB와 유사한 방식으로 Layer2 네트워크에서 LoadBalancer IP를 광고하는 기능입니다
  • ARP(Address Resolution Protocol)를 사용하여 할당된 IP 주소를 네트워크에 알리는 방식입니다
  • 클러스터 내의 특정 노드가 해당 IP의 MAC 주소를 가지게 되어 외부 트래픽을 수신합니다

Cilium Layer2 Announcement 구조

출처 - https://isovalent.com/blog/post/migrating-from-metallb-to-cilium/#l2-announcement-with-arp

 

Migrating from MetalLB to Cilium

In this blog post, you will learn how to migrate from MetalLB to Cilium for local service advertisement over Layer 2.

isovalent.com

 

Cilium Layer2 Announcement의 전체 구조를 보여줍니다. 그림의 상단에는 외부 네트워크에서 LoadBalancer IP인 192.168.1.100으로 접근하는 클라이언트가 있습니다.


중간 부분에는 Kubernetes 클러스터 내부의 3개 노드(노드1, 노드2, 노드3)가 배치되어 있으며, 각 노드에서는 Cilium 에이전트가 실행되고 있습니다.


핵심적인 부분은 노드1이 LoadBalancer IP의 MAC 주소를 가지게 되어 외부 트래픽을 수신하고, 내부에서 여러 파드로 로드 밸런싱을 수행하는 구조입니다.

# 모니터링 : router VM
arping -i eth1 $LBIP -c 100000
...

# 설정 업그레이드
helm upgrade cilium cilium/cilium --namespace kube-system --version 1.18.0 --reuse-values \
   --set l2announcements.enabled=true && watch -d kubectl get pod -A

kubectl rollout restart -n kube-system ds/cilium

# 확인
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium-dbg config --all | grep EnableL2Announcements
EnableL2Announcements             : true

cilium config view | grep enable-l2
enable-l2-announcements                           true
enable-l2-neigh-discovery                         true


# 정책 설정 : arp 광고하게 될 service 와 node 지정(controlplane 제외) -> 설정 직후 arping 확인!
## 제약사항 : L2 ARP 모드에서 LB IPPool 은 같은 네트워크 대역에서만 유효. -> k8s-w0 을 제외한 이유. 포함 시 리더 노드 선정 시 동작 실패 상황 발생!
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"  # not v2
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy1
spec:
  serviceSelector:
    matchLabels:
      app: webpod
  nodeSelector:
    matchExpressions:
      - key: kubernetes.io/hostname
        operator: NotIn
        values:
          - k8s-w0
  interfaces:
  - ^eth[1-9]+
  externalIPs: true
  loadBalancerIPs: true
EOF


# 확인
kubectl -n kube-system get lease
kubectl -n kube-system get lease | grep "cilium-l2announce"                                                               62s
...

# 현재 리더 역할 노드 확인
kubectl -n kube-system get lease/cilium-l2announce-default-webpod -o yaml | yq
...
spec:
  acquireTime: "2025-06-18T09:46:41.081674Z"
  holderIdentity: k8s-w1
  leaseDurationSeconds: 15
  leaseTransitions: 0
  renewTime: "2025-06-18T09:48:45.753123Z"


# cilium 파드 이름 지정
export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1  -o jsonpath='{.items[0].metadata.name}')
export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w0  -o jsonpath='{.items[0].metadata.name}')
echo $CILIUMPOD0 $CILIUMPOD1 $CILIUMPOD2

# 현재 해당 IP에 대한 리더가 위치한 노드의 cilium-agent 파드 내에서 정보 확인
kubectl exec -n kube-system $CILIUMPOD0 -- cilium-dbg shell -- db/show l2-announce
kubectl exec -n kube-system $CILIUMPOD1 -- cilium-dbg shell -- db/show l2-announce
kubectl exec -n kube-system $CILIUMPOD2 -- cilium-dbg shell -- db/show l2-announce

# 로그 확인
kubectl -n kube-system logs ds/cilium | grep "l2"


# router VM : LBIP로 curl 요청 확인
arping -i eth1 $LBIP -c 1000
curl --connect-timeout 1 $LBIP

# VIP 에 대한 mac 주소가 리더 노드의 mac 주소와 동일함을 확인
arp -a
? (192.168.10.211) at 08:00:27:01:19:f3 [ether] on eth1
? (192.168.10.101) at 08:00:27:01:19:f3 [ether] on eth1
? (192.168.10.100) at 08:00:27:64:bf:57 [ether] on eth1
...

curl -s $LBIP
curl -s $LBIP | grep Hostname
curl -s $LBIP | grep RemoteAddr

# 리더 노드가 아닌 다른 노드에 webpod 통신 시, SNAT 됨 : arp 동작(리더 노드)으로 인한 제약 사항
while true; do curl -s --connect-timeout 1 $LBIP | grep Hostname; sleep 0.1; done
while true; do curl -s --connect-timeout 1 $LBIP | grep RemoteAddr; sleep 0.1; done

 

✅ 실행 결과 요약

(⎈|HomeLab:N/A) root@k8s-ctr:~# helm upgrade cilium cilium/cilium --namespace kube-system --version 1.18.0 --reuse-values \
   --set l2announcements.enabled=true && watch -d kubectl get pod -A
Release "cilium" has been upgraded. Happy Helming!
NAME: cilium
LAST DEPLOYED: Fri Aug  8 01:18:46 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 3
TEST SUITE: None
NOTES:
You have successfully installed Cilium with Hubble Relay and Hubble UI.

Your release version is 1.18.0.

For any further help, visit https://docs.cilium.io/en/v1.18/gettinghelp
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl rollout restart -n kube-system ds/cilium
daemonset.apps/cilium restarted
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium-dbg config --all | grep EnableL2Announcements
EnableL2Announcements             : true
(⎈|HomeLab:N/A) root@k8s-ctr:~# cilium config view | grep enable-l2
enable-l2-announcements                           true
enable-l2-neigh-discovery                         true

Timeout
Timeout
Timeout
60 bytes from 08:00:27:ed:ef:cd (192.168.10.211): index=0 time=259.041 usec
60 bytes from 08:00:27:ed:ef:cd (192.168.10.211): index=1 time=383.917 usec
60 bytes from 08:00:27:ed:ef:cd (192.168.10.211): index=2 time=269.667 usec
60 bytes from 08:00:27:ed:ef:cd (192.168.10.211): index=3 time=263.083 usec

(⎈|HomeLab:N/A) root@k8s-ctr:~# cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"  # not v2
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy1
spec:
  serviceSelector:
    matchLabels:
      app: webpod
  nodeSelector:
    matchExpressions:
      - key: kubernetes.io/hostname
        operator: NotIn
        values:
          - k8s-w0
  interfaces:
  - ^eth[1-9]+
  externalIPs: true
  loadBalancerIPs: true
EOF

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system get lease
NAME                                   HOLDER                                                                      AGE
apiserver-k3qt3hgfvd4qocxh5wccoxpss4   apiserver-k3qt3hgfvd4qocxh5wccoxpss4_afedfd22-5480-4ebe-9089-d921c3601c1f   23h
cilium-l2announce-default-webpod       k8s-w1                                                                      9m8s
cilium-operator-resource-lock          k8s-ctr-tc2hrtw5f9                                                          23h
kube-controller-manager                k8s-ctr_bb685c8f-b316-4f0b-b378-dd8f608ab257                                23h
kube-scheduler                         k8s-ctr_5ffac59a-b7e4-4756-9171-b5b4811c1d0c                                23h

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-default-webpod       k8s-w1                
                                                      9m12s
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system get lease/cilium-l2announce-default-webpod -o yaml | yq
{
  "apiVersion": "coordination.k8s.io/v1",
  "kind": "Lease",
  "metadata": {
    "creationTimestamp": "2025-08-07T16:22:34Z",
    "name": "cilium-l2announce-default-webpod",
    "namespace": "kube-system",
    "resourceVersion": "102271",
    "uid": "5347f935-6f2e-4686-923b-3065f74a16fe"
  },
  "spec": {
    "acquireTime": "2025-08-07T16:22:34.050793Z",
    "holderIdentity": "k8s-w1",
    "leaseDurationSeconds": 15,
    "leaseTransitions": 0,
    "renewTime": "2025-08-07T16:31:53.348482Z"
  }
}

(⎈|HomeLab:N/A) root@k8s-ctr:~# export CILIUMPOD0=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].metadata.name}')
(⎈|HomeLab:N/A) root@k8s-ctr:~# export CILIUMPOD1=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w1  -o jsonpath='{.items[0].metadata.name}')
(⎈|HomeLab:N/A) root@k8s-ctr:~# export CILIUMPOD2=$(kubectl get -l k8s-app=cilium pods -n kube-system --field-selector spec.nodeName=k8s-w0  -o jsonpath='{.items[0].metadata.name}')

(⎈|HomeLab:N/A) root@k8s-ctr:~# echo $CILIUMPOD0 $CILIUMPOD1 $CILIUMPOD2
cilium-f8f6c cilium-f5cr4 cilium-jc7wx

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system $CILIUMPOD0 -- cilium-dbg shell -- db/show l2-announce
IP   NetworkInterface

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system $CILIUMPOD1 -- cilium-dbg shell -- db/show l2-announce
IP               NetworkInterface
192.168.10.211   eth1

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl exec -n kube-system $CILIUMPOD2 -- cilium-dbg shell -- db/show l2-announce
IP   NetworkInterface

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system logs ds/cilium | grep "l2"
Found 3 pods, using pod/cilium-f5cr4
L2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"
time=2025-08-07T16:29:13.839893376Z level=debug source=/go/src/github.com/cilium/cilium/pkg/datapath/l2responder/l2responder.go:195 msg="l2 announcer table full reconciliation" module=agent.datapath.l2-responder
time=2025-08-07T16:29:13.842255335Z level=debug source=/go/src/github.com/cilium/cilium/vendor/github.com/cilium/hive/job/timer.go:200 msg="Timer job finished" module=agent.controlplane.l2-announcer name=l2-announcer-lease-gc func="l2announcer.NewL2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"
time=2025-08-07T16:30:13.839583238Z level=debug source=/go/src/github.com/cilium/cilium/vendor/github.com/cilium/hive/job/timer.go:184 msg="Timer job triggered" module=agent.controlplane.l2-announcer name=l2-announcer-lease-gc func="l2announcer.NewL2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"
time=2025-08-07T16:30:13.843235905Z level=debug source=/go/src/github.com/cilium/cilium/vendor/github.com/cilium/hive/job/timer.go:200 msg="Timer job finished" module=agent.controlplane.l2-announcer name=l2-announcer-lease-gc func="l2announcer.NewL2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"
time=2025-08-07T16:31:13.8397541Z level=debug source=/go/src/github.com/cilium/cilium/vendor/github.com/cilium/hive/job/timer.go:184 msg="Timer job triggered" module=agent.controlplane.l2-announcer name=l2-announcer-lease-gc func="l2announcer.NewL2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"
time=2025-08-07T16:31:13.84236235Z level=debug source=/go/src/github.com/cilium/cilium/vendor/github.com/cilium/hive/job/timer.go:200 msg="Timer job finished" module=agent.controlplane.l2-announcer name=l2-announcer-lease-gc func="l2announcer.NewL2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"
time=2025-08-07T16:32:13.839929587Z level=debug source=/go/src/github.com/cilium/cilium/vendor/github.com/cilium/hive/job/timer.go:184 msg="Timer job triggered" module=agent.controlplane.l2-announcer name=l2-announcer-lease-gc func="l2announcer.NewL2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"
time=2025-08-07T16:32:13.842217587Z level=debug source=/go/src/github.com/cilium/cilium/vendor/github.com/cilium/hive/job/timer.go:200 msg="Timer job finished" module=agent.controlplane.l2-announcer name=l2-announcer-lease-gc func="l2announcer.NewL2Announcer.func1 (pkg/l2announcer/l2announcer.go:131)"

(⎈|HomeLab:N/A) root@k8s-ctr:~# arping -i eth1 $LBIP -c 1000
ARPING 192.168.10.211
60 bytes from 08:00:27:ed:ef:cd (192.168.10.211): index=0 time=4.792 usec
60 bytes from 08:00:27:ed:ef:cd (192.168.10.211): index=1 time=690.180 msec
60 bytes from 08:00:27:ed:ef:cd (192.168.10.211): index=2 time=377.667 usec

--- 192.168.10.211 statistics ---
2 packets transmitted, 3 packets received,   0% unanswered (1 extra)
rtt min/avg/max/std-dev = 0.005/230.188/690.180/325.264 ms

(⎈|HomeLab:N/A) root@k8s-ctr:~# curl --connect-timeout 1 $LBIP
Hostname: webpod-697b545f57-sgb7k
IP: 127.0.0.1
IP: ::1
IP: 172.20.1.108
IP: fe80::ac41:1aff:feb9:e365
RemoteAddr: 172.20.0.43:33860
GET / HTTP/1.1
Host: 192.168.10.211
User-Agent: curl/8.5.0
Accept: */*

(⎈|HomeLab:N/A) root@k8s-ctr:~# arp -a
k8s-w1 (192.168.10.101) at 08:00:27:ed:ef:cd [ether] on eth1
? (10.0.2.3) at 52:55:0a:00:02:03 [ether] on eth0
? (172.20.0.121) at c6:55:1a:e5:f8:dc [ether] on lxccab88d2aa1c1
_gateway (10.0.2.2) at 52:55:0a:00:02:02 [ether] on eth0
router (192.168.10.200) at 08:00:27:95:f9:8a [ether] on eth1
? (192.168.10.1) at 62:3e:5f:d3:80:66 [ether] on eth1
? (172.20.0.223) at 4e:64:a3:b2:0c:57 [ether] on lxc5e99ff677740
? (172.20.0.88) at d6:1a:f3:74:49:6f [ether] on lxcaeddd5f79898
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LBIP
Hostname: webpod-697b545f57-sgb7k
IP: 127.0.0.1
IP: ::1
IP: 172.20.1.108
IP: fe80::ac41:1aff:feb9:e365
RemoteAddr: 172.20.0.43:41766
GET / HTTP/1.1
Host: 192.168.10.211
User-Agent: curl/8.5.0
Accept: */*

(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LBIP | grep Hostname
Hostname: webpod-697b545f57-t87x7

(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LBIP | grep RemoteAddr
RemoteAddr: 172.20.0.43:34582

(⎈|HomeLab:N/A) root@k8s-ctr:~# while true; do curl -s --connect-timeout 1 $LBIP | grep Hostname; sleep 0.1; done
Hostname: webpod-697b545f57-2dg5m
Hostname: webpod-697b545f57-2dg5m
Hostname: webpod-697b545f57-2dg5m
Hostname: webpod-697b545f57-2dg5m
Hostname: webpod-697b545f57-sgb7k
Hostname: webpod-697b545f57-t87x7
Hostname: webpod-697b545f57-sgb7k

(⎈|HomeLab:N/A) root@k8s-ctr:~# while true; do curl -s --connect-timeout 1 $LBIP | grep RemoteAddr; sleep 0.1; done
RemoteAddr: 172.20.0.43:42332
RemoteAddr: 172.20.0.43:42344
RemoteAddr: 172.20.0.43:42352
RemoteAddr: 172.20.0.43:39556
RemoteAddr: 172.20.0.43:39562
RemoteAddr: 172.20.0.43:59584
RemoteAddr: 172.20.0.43:39574

L2 Announcement 리더 노드에 장애 주입후 Failover 확인

# 신규 터미널 (router) : 반복 호출
while true; do curl -s --connect-timeout 1 $LBIP | grep Hostname; sleep 0.1; done


# 현재 리더 노드 확인
kubectl -n kube-system get lease | grep "cilium-l2announce"
NAME                                   HOLDER                                                                      AGE
cilium-l2announce-default-webpod      k8s-w1                                                                      62s
...

# 리더 노드 강제 reboot
sshpass -p 'vagrant' ssh vagrant@k8s-w1  sudo reboot
혹은
sshpass -p 'vagrant' ssh vagrant@k8s-ctr sudo reboot


# 신규 터미널 (router) : arp 변경(갱신) 확인
arp -a

 

✅ 실행 결과 요약

(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-default-webpod       k8s-w1                                                                      19m

(⎈|HomeLab:N/A) root@k8s-ctr:~# sshpass -p 'vagrant' ssh vagrant@k8s-w1  sudo reboot

(⎈|HomeLab:N/A) root@k8s-ctr:~# arp -a
k8s-w1 (192.168.10.101) at 08:00:27:ed:ef:cd [ether] on eth1
? (10.0.2.3) at 52:55:0a:00:02:03 [ether] on eth0
? (172.20.0.121) at c6:55:1a:e5:f8:dc [ether] on lxccab88d2aa1c1
_gateway (10.0.2.2) at 52:55:0a:00:02:02 [ether] on eth0
router (192.168.10.200) at 08:00:27:95:f9:8a [ether] on eth1
? (192.168.10.1) at 62:3e:5f:d3:80:66 [ether] on eth1
? (172.20.0.223) at 4e:64:a3:b2:0c:57 [ether] on lxc5e99ff677740
? (172.20.0.88) at d6:1a:f3:74:49:6f [ether] on lxcaeddd5f79898

테스트시 약 2초 정도 통신이 끊겼다가 동작하는 것을 확인할 수 있었습니다.

Service LB-IPAM 기능

Service LB-IPAM은 Kubernetes LoadBalancer 서비스에 외부 IP 주소를 자동으로 할당하고 관리하는 Cilium의 핵심 기능입니다
클라우드 환경이 아닌 온프레미스나 프라이빗 클라우드에서도 LoadBalancer 서비스를 사용할 수 있게 해주는 솔루션입니다
IP 주소 풀을 관리하고, 서비스 생성/삭제 시 자동으로 IP를 할당/해제하는 기능을 제공합니다

주요 구성 요소:

  1. CiliumLoadBalancerIPPool:
    • 사용 가능한 IP 주소 범위를 정의하는 리소스
    • LoadBalancer 서비스가 사용할 IP 풀을 관리
    • 예시: 192.168.10.211 ~ 192.168.10.215 범위 설정
  2. LB-IPAM Controller:
    • LoadBalancer 서비스의 생명주기를 모니터링
    • IP 풀에서 IP를 자동으로 할당/해제
    • IP 충돌 방지 및 효율적인 IP 관리
  3. L2 Announcement:
    • 할당된 IP를 네트워크에 ARP를 통해 광고
    • 외부에서 접근 가능한 엔드포인트 제공

Service 추가시 동작

# Pod 배포
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-web
  labels:
    app: netshoot-web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: netshoot-web
  template:
    metadata:
      labels:
        app: netshoot-web
    spec:
      terminationGracePeriodSeconds: 0
      containers:
        - name: netshoot
          image: nicolaka/netshoot
          ports:
            - containerPort: 8080
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          command: ["sh", "-c"]
          args:
            - |
              while true; do 
                { echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nOK from \$POD_NAME"; } | nc -l -p 8080 -q 1;
              done
EOF

# 서비스 배포
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: netshoot-web
  labels:
    app: netshoot-web
spec:
  type: LoadBalancer
  selector:
    app: netshoot-web
  ports:
    - name: http
      port: 80      
      targetPort: 8080
EOF

#
kubectl get svc netshoot-web
NAME           TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)        AGE
netshoot-web   LoadBalancer   10.96.64.29   192.168.10.212   80:30131/TCP   8s

#
cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"  # not v2
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy2
spec:
  serviceSelector:
    matchLabels:
      app: netshoot-web
  nodeSelector:
    matchExpressions:
      - key: kubernetes.io/hostname
        operator: NotIn
        values:
          - k8s-w0
  interfaces:
  - ^eth[1-9]+
  externalIPs: true
  loadBalancerIPs: true
EOF

# Service 별로 리더 노드가 다르다 : 즉, 외부 인입 시 Service 별로 나름 분산(?) 처리.. 
kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-default-netshoot-web   k8s-w1                                                                     
cilium-l2announce-default-webpod         k8s-ctr    


# 호출 확인
## LBIP로 curl 요청 확인 : k8s 노드들에서 LB EXIP로 통신 가능!
kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
LB2IP=$(kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -s $LB2IP

## 신규터미널 (router)
LB2IP=192.168.10.212
arping -i eth1 $LB2IP -c 2
curl -s $LB2IP

✅ 실행 결과 요약

# LoadBalancer 타입 서비스 생성을 위한 디플로이먼트 생성
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-web
  labels:
    app: netshoot-web
spec:
  replicas: 3  # 3개 레플리카로 워크로드 생성
  selector:
    matchLabels:
      app: netshoot-web
  template:
    metadata:
      labels:
        app: netshoot-web
    spec:
      terminationGracePeriodSeconds: 0
      containers:
        - name: netshoot
          image: nicolaka/netshoot
          ports:
            - containerPort: 8080
          env:
            - name: POD_NAME
              valueFrom:
EOF           done| nc -l -p 8080 -q 1; OK\r\nContent-Type: text/plain\r\n\r\nOK fr
deployment.apps/netshoot-web created
# LoadBalancer 타입 서비스 생성으로 외부 IP 할당
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: netshoot-web
  labels:
    app: netshoot-web
spec:
  type: LoadBalancer  # LoadBalancer 타입으로 외부 접근 가능
  selector:
    app: netshoot-web
  ports:
    - name: http
      port: 80      
      targetPort: 8080
EOF
service/netshoot-web created
# LoadBalancer 서비스 상태 확인 - 외부 IP 192.168.10.212 할당됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc netshoot-web
NAME           TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
netshoot-web   LoadBalancer   10.96.147.23   192.168.10.212   80:32464/TCP   16s
# Cilium L2 Announcement Policy 적용 - k8s-w0 노드 제외하고 L2 통신 담당
(⎈|HomeLab:N/A) root@k8s-ctr:~# cat << EOF | kubectl apply -f -
apiVersion: "cilium.io/v2alpha1"  # not v2
kind: CiliumL2AnnouncementPolicy
metadata:
  name: policy2
spec:
  serviceSelector:
    matchLabels:
      app: netshoot-web
  nodeSelector:
    matchExpressions:
      - key: kubernetes.io/hostname
        operator: NotIn
        values:
          - k8s-w0  # k8s-w0 노드는 L2 통신에서 제외
  interfaces:
  - ^eth[1-9]+  # eth1-9 인터페이스에서 L2 통신 처리
  externalIPs: true  # 외부 IP 통신 허용
  loadBalancerIPs: true  # LoadBalancer IP 통신 허용
EOF
ciliuml2announcementpolicy.cilium.io/policy2 created
# L2 announcement lease 확인 - k8s-w1 노드가 L2 통신 담당
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-default-netshoot-web   k8s-w1                                                                      4s
cilium-l2announce-default-webpod         k8s-ctr                                                                     29m
# LoadBalancer IP 확인 및 접근 테스트
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
192.168.10.212

(⎈|HomeLab:N/A) root@k8s-ctr:~# LB2IP=$(kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

# 클러스터 내부에서 LoadBalancer IP 접근 테스트 - 로드밸런싱 동작 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LB2IP
OK from netshoot-web-5c59d94bd4-z4rxn

(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LB2IP
OK from netshoot-web-5c59d94bd4-m4f42

# 라우터에서 L2 통신 확인
#라우터
root@router:~# LB2IP=192.168.10.212

# ARP 응답 확인 - MAC 주소 08:00:27:ed:ef:cd로 L2 통신 정상 동작
root@router:~# arping -i eth1 $LB2IP -c 2
ARPING 192.168.10.212
60 bytes from 08:00:27:ed:ef:cd (192.168.10.212): index=0 time=335.666 usec
60 bytes from 08:00:27:ed:ef:cd (192.168.10.212): index=1 time=346.416 usec

--- 192.168.10.212 statistics ---
2 packets transmitted, 2 packets received,   0% unanswered (0 extra)
rtt min/avg/max/std-dev = 0.336/0.341/0.346/0.005 ms

# 외부에서 LoadBalancer IP 접근 테스트 - 정상 응답 확인
root@router:~# curl -s $LB2IP
OK from netshoot-web-5c59d94bd4-z4rxn

IP 원하는 값으로 세팅하기

Cilium LB-IPAM을 사용하여 LoadBalancer 서비스의 외부 IP를 직접 지정하는 방법

# Service netshoot-web 에 EX-IP를 직접 지정 변경
k9s → svc → <e> edit

## metadata.annotations 아래 아래 추가
  annotations:
    "lbipam.cilium.io/ips": "192.168.10.215"  # 원하는 IP 주소 직접 지정

# LoadBalancer 서비스 상태 확인 - 지정한 IP로 변경됨
kubectl get svc netshoot-web
NAME           TYPE           CLUSTER-IP    EXTERNAL-IP      PORT(S)        AGE
netshoot-web   LoadBalancer   10.96.64.29   192.168.10.215   80:30131/TCP   8m24s

# 변경된 IP로 접근 테스트
kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
LB2IP=$(kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -s $LB2IP

실행 결과 요약

# LoadBalancer 서비스 상태 확인 - 외부 IP가 192.168.10.215로 변경됨
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc netshoot-web
NAME           TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
netshoot-web   LoadBalancer   10.96.147.23   192.168.10.215   80:32464/TCP   10m
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
192.168.10.215
(⎈|HomeLab:N/A) root@k8s-ctr:~# LB2IP=$(kubectl get svc netshoot-web -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LB2IP
OK from netshoot-web-5c59d94bd4-fbpdt

Cilium LB-IPAM을 통해 LoadBalancer 서비스의 외부 IP를 192.168.10.215로 직접 지정하여 변경 성공

Sharing Keys: EX-IP 1개를 각기 다른 포트를 통해서 사용

Cilium LB-IPAM의 sharing-key 기능을 사용하여 하나의 IP 주소를 여러 서비스가 공유하는 방법

# 두 번째 LoadBalancer 서비스 생성 (포트 8080)
cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: netshoot-web2
  labels:
    app: netshoot-web
spec:
  type: LoadBalancer
  selector:
    app: netshoot-web
  ports:
    - name: http
      port: 8080      
      targetPort: 8080
EOF

# 두 서비스 상태 확인 - 각각 다른 IP 할당됨
kubectl get svc -l app=netshoot-web
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)          AGE
netshoot-web    LoadBalancer   10.96.64.29     192.168.10.215   80:30131/TCP     11m
netshoot-web2   LoadBalancer   10.96.191.204   192.168.10.212   8080:31946/TCP   8s

# Service netshoot-web, netshoot-web2 에 sharing-key annotations 추가
k9s → svc → <e> edit 
## metadata.annotations 아래 아래 추가
  annotations:
    "lbipam.cilium.io/ips": "192.168.10.215"  # 공유할 IP 주소 지정
    "lbipam.cilium.io/sharing-key": "1234"     # 같은 sharing-key로 IP 공유

# sharing-key 적용 후 두 서비스가 같은 IP를 공유함
kubectl get svc -l app=netshoot-web
NAME            TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)          AGE
netshoot-web    LoadBalancer   10.96.64.29     192.168.10.215   80:30131/TCP     14m
netshoot-web2   LoadBalancer   10.96.191.204   192.168.10.215   8080:31946/TCP   3m16s

# sharing-key 사용되는 IP는 모든 같은 리더 노드 사용
kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-default-netshoot-web    k8s-ctr                                                                     7m44s
cilium-l2announce-default-netshoot-web2   k8s-ctr                                                                     4m22s
cilium-l2announce-default-webpod          k8s-ctr                                                                     34m

# 같은 IP의 다른 포트로 접근 테스트
curl -s $LB2IP        # 포트 80 접근
curl -s $LB2IP:8080   # 포트 8080 접근

# 라우터에서 외부 접근 테스트
LB2IP=192.168.10.215
arping -i eth1 $LB2IP -c 2  # ARP 응답 확인
curl -s $LB2IP               # 포트 80 접근
curl -s $LB2IP:8080          # 포트 8080 접근

 

✅ 실행 결과 요약

# 두 서비스가 같은 IP 192.168.10.215를 공유하는 것을 확인
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl get svc -l app=netshoot-web
NAME            TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)          AGE
netshoot-web    LoadBalancer   10.96.147.23   192.168.10.215   80:32464/TCP     17m
netshoot-web2   LoadBalancer   10.96.95.238   192.168.10.215   8080:30989/TCP   113s
(⎈|HomeLab:N/A) root@k8s-ctr:~# kubectl -n kube-system get lease | grep "cilium-l2announce"
cilium-l2announce-default-netshoot-web    k8s-w1                                                                      17m
cilium-l2announce-default-netshoot-web2   k8s-w1                                                                      2m4s
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LB2IP
OK from netshoot-web-5c59d94bd4-m4f42
(⎈|HomeLab:N/A) root@k8s-ctr:~# curl -s $LB2IP:8080
OK from netshoot-web-5c59d94bd4-z4rxn

# 라우터에서 외부 접근 테스트 - 같은 IP의 다른 포트로 정상 접근 가능
root@router:~# LB2IP=192.168.10.215
root@router:~# arping -i eth1 $LB2IP -c 2
ARPING 192.168.10.215
60 bytes from 08:00:27:ed:ef:cd (192.168.10.215): index=0 time=490.500 usec
60 bytes from 08:00:27:ed:ef:cd (192.168.10.215): index=1 time=242.417 usec

--- 192.168.10.215 statistics ---
2 packets transmitted, 2 packets received,   0% unanswered (0 extra)
rtt min/avg/max/std-dev = 0.242/0.366/0.490/0.124 ms
root@router:~# curl -s $LB2IP
OK from netshoot-web-5c59d94bd4-m4f42
root@router:~# curl -s $LB2IP:8080
OK from netshoot-web-5c59d94bd4-m4f42

 

Cilium LB-IPAM sharing-key 기능으로 하나의 IP 주소를 여러 서비스가 포트별로 공유하는 기능이 적용된 모습을 k9s로 확인하였습니다.

sharingkey 적용전

적용 후


마치며

이번 4주차 실습을 통해 Kubernetes 클러스터의 외부 네트워킹과 LoadBalancer 서비스의 동작 원리를 깊이 있게 분석했습니다.

 

Cilium의 L2 Announcement Policy를 통한 Layer 2 통신과 LB-IPAM을 활용한 LoadBalancer IP 관리, 그리고 sharing-key 기능을 통한 IP 공유 메커니즘을 직접 확인할 수 있었습니다.

 

긴 글 읽어주셔서 감사합니다 :)

반응형

'클라우드 컴퓨팅 & NoSQL > [Cilium Study] 실리움 스터디' 카테고리의 다른 글

[6주차 - Cilium 스터디] Cilium ServiceMesh (25.08.17)  (0) 2025.08.23
[5주차 - Cilium 스터디] BGP Control Plane & ClusterMesh (25.08.10)  (4) 2025.08.16
[3주차 - Cilium 스터디] Networking (25.07.27)  (0) 2025.08.02
[2주차 - Cilium 스터디] (Observabilty) Hubbkem Prometheus/Grafana (25.07.20)  (3) 2025.07.26
[1주차 - Cilium 스터디] Cilium 이란? (25.07.13)  (2) 2025.07.18
  • 들어가며
  • 실습환경 구성
  • 실습환경 확인
  • Native Routing Mode 실습
  • Overlay Network 적용 실습
  • K8S Service 종류
  • 서비스 종류
  • Service LB-IPAM
  • LB IPAM으로 webpod 서비스를 LoadBalancer Type으로 설정 실습
  • Cilium L2 Announcement 실습
  • Service LB-IPAM 기능
  • 마치며
devlos
devlos
안녕하세요, Devlos 입니다. 새로 공부 중인 지식들을 공유하고, 명확히 이해하고자 블로그를 개설했습니다 :) 여러 DEVELOPER 분들과 자유롭게 지식을 공유하고 싶어요! 방문해 주셔서 감사합니다 😀 - DEVLOS -

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.