Rocky 9.6 K8s 완벽 구축 & 트러블슈팅 가이드 (Kubespray 2.28.1 • K8s 1.31.6 • Cilium 1.17)

대상/전제
토폴로지: bastion-001(192.168.10.20) / master-001(192.168.10.30) / worker-001(192.168.10.40) / worker-002(192.168.10.41)
OS: Rocky Linux 9.6 (x86_64, cgroups v2)
런타임: containerd
CNI: Cilium (초기 kube-proxy 유지 → 이후 proxyless 전환 가이드 포함)
설치 도구: Ansible + Kubespray


0) 버전/대상 매트릭스 (검증됨)

구성요소 권장/검증 버전 비고
Kubespray 2.28.1 본 가이드 기준
ansible-core 2.17.x (≥2.17.3, <2.18) venv 사용 권장
Python 3.11 3.12 미권장(일부 플러그인 호환성)
Kubernetes 1.31.6 Kubespray 2.28.1에서 etcd 매트릭스 호환
Cilium 1.17.7 K8s 1.31과 호환
Helm 3.18.4 3.12+ 권장

⚠️ 중요: kube_version문자열이며 접두사 “ 없이 지정해야 합니다.
예) “1.31.6” (❌ “v1.31.6” 금지).
v가 있으면 Kubespray 내부의 버전 파싱/비교에서 kube_major_version 오류, etcd 매트릭스 키 미존재 등의 문제가 발생합니다.


1) 네트워크/클러스터 기본값 (권장)

  • Pod CIDR: 10.244.0.0/16
  • Service CIDR: 10.96.0.0/12
  • CoreDNS IP: 10.96.0.10 (Service CIDR 10번째)
  • MTU: 1500
  • kube-proxy: 초기엔 iptables 유지 → 안정화 후 Cilium proxyless 전환
  • Cilium 터널: VXLAN (단순하고 PoC에 안정적)

왜 이렇게?

  • Service CIDR의 10번째 IP를 CoreDNS로 고정하면 kubeadm 경고/혼동을 줄입니다.
  • VXLAN은 L2 복잡도 없이 바로 동작(라우팅/BGP 지식 불필요), 에어갭/내부망에서도 무난합니다.
  • 초기엔 kube-proxy를 유지하면 CNI 문제 발생 시 롤백이 빠릅니다. 안정화 후 proxyless로 전환하세요.

2) 노드 공통 사전 준비 (Rocky 9.6)

sudo dnf -y install epel-release yum-utils curl wget vim git tar unzip \
  net-tools lsof telnet bash-completion python3 python3-pip rsync chrony
sudo systemctl enable --now chronyd

# SELinux: 학습/PoC에선 Permissive (운영 전환 시 Enforcing+정책 권장)
sudo setenforce 0
sudo sed -ri 's/^(SELINUX=).*/\1permissive/' /etc/selinux/config

# swap off
sudo swapoff -a
sudo sed -ri 's/^([^#].*swap.*)/# \1/' /etc/fstab

# 방화벽 off (PoC 집중용)
sudo systemctl disable --now firewalld || true

# 커널 파라미터
echo 'net.bridge.bridge-nf-call-iptables = 1' | sudo tee /etc/sysctl.d/99-kubernetes.conf
echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-kubernetes.conf
sudo sysctl --system

# /etc/hosts 고정
cat <<'EOF' | sudo tee -a /etc/hosts
192.168.10.30 master-001
192.168.10.40 worker-001
192.168.10.41 worker-002
EOF

# CNI 바이너리 디렉터리 보장 (Cilium init 권한 문제 예방)
sudo mkdir -p /opt/cni/bin && sudo chown root:root /opt/cni/bin && sudo chmod 0755 /opt/cni/bin

로컬 DNS(노드 로컬 캐시) 비활성화: PoC에서는 혼선을 줄이기 위해 NodeLocalDNS를 끄는 것을 권장합니다.
Kubespray 변수에서 enable_nodelocaldns: false로 설정하고, 이미 떠있다면 kubectl -n kube-system delete ds nodelocaldns로 제거하세요.


3) 베스천 준비 (SSH + Python venv)

# SSH 키 교환
ssh-keygen -t ed25519 -N "" -f ~/.ssh/id_ed25519
for h in 192.168.10.30 192.168.10.40 192.168.10.41; do
  ssh-copy-id -i ~/.ssh/id_ed25519.pub root@$h
done

# Python 3.11 + venv
sudo dnf -y remove ansible || true
sudo dnf -y install git python3.11 python3.11-pip python3.11-devel
python3.11 -m venv ~/venvs/kubespray
source ~/venvs/kubespray/bin/activate
pip install --upgrade pip setuptools wheel

4) Kubespray 체크아웃 & 인벤토리

source ~/venvs/kubespray/bin/activate
cd ~
[ -d kubespray ] || git clone https://github.com/kubernetes-sigs/kubespray.git
cd kubespray
pip install -r requirements.txt
ansible --version   # 2.17.x 확인
cp -rfp inventory/sample inventory/study

inventory/study/hosts.yaml

all:
  hosts:
    master-001:
      ansible_host: 192.168.10.30
      ip: 192.168.10.30
    worker-001:
      ansible_host: 192.168.10.40
      ip: 192.168.10.40
    worker-002:
      ansible_host: 192.168.10.41
      ip: 192.168.10.41
  children:
    kube_control_plane:
      hosts:
        master-001: {}
    kube_node:
      hosts:
        worker-001: {}
        worker-002: {}
    etcd:
      hosts:
        master-001: {}
    k8s_cluster:
      children:
        kube_control_plane: {}
        kube_node: {}
    calico_rr: {}

inventory/study/group_vars/k8s_cluster/k8s-cluster.yml (핵심)

# ★ 버전(문자열, v 제거) 버전 미 지정 시 최신 버전으로 자동 설치 됨
#kube_version: "1.32.8"
kube_version: "1.31.6"

# 네트워킹
kube_network_plugin: cilium
kube_service_addresses: 10.96.0.0/12
kube_pods_subnet: 10.244.0.0/16
kube_dns_server: 10.96.0.10
kube_proxy_mode: iptables   # 초기 유지

# NodeLocalDNS 비활성화
enable_nodelocaldns: false

# Cilium (초기)
cilium_version: "1.17.7"
cilium_enable_hubble: false

# kube-proxy_script 비활성화
kube_proxy_remove: true

# Cilium VXLAN 모드 & kube-proxy 유지에 필요한 값(helm/cli 재설치 시 사용)
# 별도 값 파일에서 아래를 넘겨주는 방식을 권장
# k8sServiceHost/Port는 자동탐지가 실패할 수 있어 명시 필요
# kubeProxyReplacement는 true/false를 명확히 지정

inventory/study/group_vars/all/download.yml (다운로드/캐시)

download_localhost: true
# ↑를 쓰면 반드시 ↓도 true (클라이언트가 인터넷이 된다면 false)
download_run_once: true

# 캐시 경로
download_cache_dir: "/tmp/kubespray-cache"
local_release_dir: "/tmp/releases"

5) 설치 실행

ansible -i inventory/study/hosts.yaml all -m ping
ansible-playbook -i inventory/study/hosts.yaml -b -v cluster.yml

# kubeconfig를 베스천으로
mkdir -p ~/.kube
scp root@192.168.10.30:/etc/kubernetes/admin.conf ~/.kube/config
export KUBECONFIG=~/.kube/config

kubectl get nodes -o wide
kubectl -n kube-system get pods -o wide
kubectl -n kube-system get pods -l k8s-app=cilium -o wide

6) Cilium CLI/Helm로 재설치(필요시) + VXLAN + kube-proxy 유지

값 파일(베스천 또는 마스터; kubeconfig 필요)

sudo tee /etc/kubernetes/cilium-extra-values.yaml >/dev/null <<'EOF'
k8sServiceHost: 192.168.10.30
k8sServicePort: 6443
kubeProxyReplacement: false   # 초기엔 kube-proxy 유지
tunnel: vxlan                 # VXLAN 사용
EOF

(A) Cilium CLI 사용

# CLI 설치
sudo mkdir -p /usr/local/bin
curl -L --fail -o /tmp/cilium.tgz \
  https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz
sudo tar -xzf /tmp/cilium.tgz -C /usr/local/bin cilium

# 설치/재설치
cilium install --version 1.17.7 \
  -f /etc/kubernetes/cilium-extra-values.yaml \
  --wait

cilium status --verbose

(B) Helm 사용

helm repo add cilium https://helm.cilium.io
helm repo update
helm upgrade --install cilium cilium/cilium \
  -n kube-system \
  --version 1.17.7 \
  -f /etc/kubernetes/cilium-extra-values.yaml \
  --wait

왜 필요한가?

  • Init:CrashLoopBackOff + 로그에 https://auto:auto 류가 보이면 API 서버 자동탐지 실패 → k8sServiceHost/Port를 값으로 반드시 명시.
  • kubeProxyReplacement must be explicitly set 에러는 true/false 미지정 탓 → 값 파일에 명시.
  • cp /hostbin/cilium-mount: Permission denied는 대개 /opt/cni/bin 권한/존재 문제 → 공통 준비 스크립트에서 디렉터리/권한 보장.

7) 설치 후 기본 점검

kubectl get nodes -o wide
kubectl -n kube-system get pods -l k8s-app=cilium -o wide
kubectl -n kube-system get svc kube-dns -o wide  # 10.96.0.10 확인

# 간단 배포/통신 테스트
kubectl create deploy demo-nginx --image=nginx:1.25-alpine --replicas=2
kubectl expose deploy demo-nginx --port=80 --target-port=80 --type=ClusterIP
kubectl get deploy,rs,pods,svc -o wide

8) Cilium 동작 테스트 (기본/심화)

8-1) 기본 L3/L4 연결성 테스트

# 클러스터 내부에서 서비스 접근
kubectl run -it --rm tmp --image=busybox:1.36 --restart=Never -- sh -c 'wget -qO- http://demo-nginx'

8-2) Cilium CLI Connectivity Test (권장)

cilium connectivity test --test basic
# 모든 테스트 통과 여부 확인

8-3) BPF LB 상태/서비스 맵 확인(Proxyless 전환 후 유용)

cilium status --verbose
cilium service list     # BPF 기반 K8s Service 라우팅 테이블

9) kube-proxy → Cilium Proxyless 전환 (선택)

목표: kube-proxy 제거, Cilium이 eBPF 로드밸런서로 서비스 트래픽 처리.

  1. 값 변경
sudo tee /etc/kubernetes/cilium-proxyless.yaml >/dev/null <<'EOF'
k8sServiceHost: 192.168.10.30
k8sServicePort: 6443
kubeProxyReplacement: true
# 권장: kubeProxyReplacementStrict: true (필요 시)
tunnel: vxlan
EOF
  1. 적용 (CLI 또는 Helm)
cilium upgrade --version 1.17.7 -f /etc/kubernetes/cilium-proxyless.yaml --wait
# 또는
helm upgrade cilium cilium/cilium -n kube-system \
  --version 1.17.7 -f /etc/kubernetes/cilium-proxyless.yaml --wait
  1. kube-proxy 제거
kubectl -n kube-system delete ds kube-proxy
# 이후 Kubespray 재실행 시 kube-proxy가 재배포되지 않도록
# inventory에 다음 중 하나를 반영(버전별로 변동 가능)
# kube_proxy_mode: "none"   # 또는
# kube_proxy_remove: true    # (환경에 따라 키 명 다를 수 있어 Helm/CLI 방식 유지도 방법)
  1. 검증(중요)
cilium status | grep -i kube-proxy
# => KubeProxyReplacement:   Strict|Partial/Enabled

cilium service list
# => K8s Service들이 BPF LB에 등록되어 있어야 함

kubectl -n kube-system get ds kube-proxy  # NotFound 여야 정상

왜 kube-proxy를 끄나?

  • iptables 규칙 폭증/CPU 사용을 줄이고, eBPF 기반으로 낮은 지연/높은 처리량을 얻기 위함.
  • Cilium이 BPF 로드밸런서를 통해 서비스 VIP→엔드포인트 매핑을 직접 처리합니다.

10) Hubble 구성 (가시성)

# CLI로 활성화 (UI 포함)
cilium hubble enable --ui
kubectl -n kube-system get pods -l k8s-app=hubble-relay -o wide
kubectl -n kube-system get svc hubble-ui -o wide

# UI 접근: NodePort로 노출되었는지 확인 후
# http://<worker-IP>:<nodePort>

# CLI 뷰
cilium hubble port-forward &  # 4245→ localhost
hubble status
hubble observe --from-pod kube-system/coredns-<id> --http

11) k9s 설치 (운영 편의)

# 최신 릴리스 바이너리 설치 (amd64)# 사용
export KUBECONFIG=~/.kube/config
k9s

12) 자주 만난 오류 → 원인/해결 요약

  • Ansible 버전 오류: venv에서 ansible --version 2.17.x 확인.
  • “: kube_versionv 제거, 문자열로.
  • “: Kubespray가 해당 K8s major 지원X → 1.31.6 사용.
  • “: download.yml에 download_run_once: true 추가.
  • “: download_cache_dir, local_release_dir 누락 → 추가.
  • Cilium “/API 탐지 실패: 값 파일에 k8sServiceHost/Port 명시.
  • Cilium init ************************“: 각 노드 /opt/cni/bin 존재/0755/소유자 root 보장.
  • CoreDNS Pending: Cilium 정상화 후 해결. NodeLocalDNS 끄기 권장.

13) 재설치/리셋 루틴

source ~/venvs/kubespray/bin/activate
ansible-playbook -i inventory/study/hosts.yaml -b -v reset.yml
# 필요 시 잔여물 제거
# for d in /etc/kubernetes /var/lib/etcd /var/lib/kubelet /etc/cni/net.d /var/lib/cni; do sudo rm -rf $d; done
ansible-playbook -i inventory/study/hosts.yaml -b -v cluster.yml

14) 어디서 뭘 실행하나? (요약)

  • Ansible/Kubespray: 베스천에서 실행.
  • cilium CLI/helm: kubeconfig와 값 파일이 있는 베스천 또는 master-001 어디서든 가능.

부록: 문제 진단 명령 모음

# 버전
python -V; ansible --version; helm version; kubectl version --short

# Ansible vars 즉시 확인
d='inventory/study/hosts.yaml'
ansible -i $d all -m debug -a 'var=kube_version'
ansible -i $d all -m debug -a 'var=download_localhost'
ansible -i $d all -m debug -a 'var=download_run_once'
ansible -i $d all -m debug -a 'var=download_cache_dir'
ansible -i $d all -m debug -a 'var=local_release_dir'

# Cilium 상태/logs
kubectl -n kube-system get pods -l k8s-app=cilium -o wide
kubectl -n kube-system logs ds/cilium -c cilium-agent --tail=120

# DNS/CoreDNS
kubectl -n kube-system get svc kube-dns -o wide
kubectl -n kube-system get pods -l k8s-app=kube-dns -o wide || \
  kubectl -n kube-system get pods -l k8s-app=coredns -o wide

이 문서는 설치 절차 + 선택 전환(proxyless) + 실제 오류/해결/검증을 한 번에 정리했습니다.
PoC가 안정화되면 kubeProxyReplacement: true로 전환하고, Hubble/k9s/MetalLB/Ingress/GitOps 등으로 확장하세요.

 

 

확장편

본 문서는 기존 가이드에 테스트/검증, Cilium 연결성, kube-proxy 대체(KPR), NIC 지정, VXLAN 설정, 그리고 빠른 재적용 방법을 보강합니다.


1) 빠른 테스트 체크리스트

# 전체 상태
kubectl get nodes -o wide
kubectl -n kube-system get pods -o wide
kubectl -n kube-system get pods -l k8s-app=cilium -o wide

# cilium 상태(에이전트 내부)
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium status --verbose

2) Cilium 연결성 테스트 (CLI)

Cilium CLI가 설치되어 있고, KUBECONFIG가 설정되어 있어야 합니다.

# 기본 연결성 테스트 (추천)
cilium connectivity test --test basic

# 결과 해석 팁
# - All tests passed: 정상
# - 특정 방향/정책 실패: 테스트 리포트 내 failing test ID 중심으로 원인 파악

3) k8s-net-cilium.yml 예시 (VXLAN + kube-proxy 대체 + NodePort + NIC 지정)

아래 값은 Helm/Cilium CLI로 재적용 가능한 Value 파일 예시입니다.

# 예시: VXLAN + kube-proxy 대체 + NodePort 사용 + NIC 지정
# 파일명 예: /etc/kubernetes/k8s-net-cilium.yml

# (자동탐지 실패 대비) K8s API 주소/포트 명시
k8sServiceHost: 192.168.10.30
k8sServicePort: 6443

# 터널링 모드: VXLAN (단순/안정)
tunnel: "vxlan"

# Cilium 1.17부터 KPR은 boolean으로 명시 필요 (strict/partial 문자열 대신)
# true: kube-proxy 제거(대체), false: kube-proxy 유지
kubeProxyReplacement: true

# (멀티 NIC/본딩 환경) BPF를 붙일 인터페이스를 명시적으로 지정
# bond0, eth0 등 실제 환경에 맞게 변경
devices:
  - "bond0"

# 흔히 쓰는 옵션들
bpf:
  masquerade: true    # SNAT을 eBPF로 처리(성능/간결)

hubble:
  enabled: true       # 가시성 활성화(추후 cilium hubble enable --ui도 가능)

# (선택) NodePort LB 모드 등 추가 옵션이 필요하면 아래에 확장 가능
# nodePort:
#   enabled: true

반영(재설치/업그레이드) 방법

  • Cilium CLI
    cilium upgrade --version 1.17.7 \
      -f /etc/kubernetes/k8s-net-cilium.yml \
      --wait
    cilium status --verbose
    
  • Helm
    helm upgrade --install cilium cilium/cilium \
      -n kube-system \
      --version 1.17.7 \
      -f /etc/kubernetes/k8s-net-cilium.yml \
      --wait
    

주의: kube-proxy를 실제로 제거하려면, KPR=true 적용 후 kube-proxy DaemonSet을 삭제하고 재생성되지 않도록 Kubespray 변수(예: kube_proxy_mode: "none" 또는 kube_proxy_remove: true)를 관리하세요. 전환 직후에는 cilium service list와 실제 서비스 접근이 모두 정상인지 반드시 확인합니다.


4) Ansible 태그를 이용한 Cilium만 재적용

Cilium 관련 태그만 실행해 빠르게 재적용/수정할 수 있습니다.

ansible-playbook -i inventory/study/hosts.yaml -b \
  --tags cilium cluster.yml

5) kube-proxy를 끄는(대체하는) 이유

  • iptables 기반 서비스 처리를 eBPF 로드밸런서로 대체 → 낮은 지연/높은 처리량
  • iptables 규칙 폭증/관리 복잡도 완화
  • Cilium이 Service VIP → Pod 엔드포인트 매핑을 BPF 맵으로 처리

확인 방법

# KPR 상태
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium status | grep -i kube-proxy
# 예) KubeProxyReplacement: Enabled (Strict)

# BPF 기반 서비스 맵
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium service list
# => ClusterIP/NodePort 서비스가 BPF LB에 등록되어 있어야 함

6) 로컬 DNS(NodeLocalDNS) 비활성화 이유/방법

  • 학습/초기 구축 단계에서 DNS 경로 단순화로 문제 범위를 줄임
  • Cilium/Pod-DNS/CoreDNS 경로만 확인하면 되어 트러블슈팅이 쉬움
# inventory/study/group_vars/k8s_cluster/k8s-cluster.yml
enable_nodelocaldns: false

이미 배포되었다면:

kubectl -n kube-system delete ds nodelocaldns || true

7) VXLAN을 권장하는 이유

  • 라우팅/BGP 지식 없이 즉시 동작(PoC, 단일 L2/L3 환경에 적합)
  • 운영에서 네이티브 라우팅(BGP)로 확장 가능하나, PoC/내부망에선 VXLAN이 가장 단순/안성

8) Cilium 동작 검증 플로우 (요약)

# 1) Cilium 상태
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium status --verbose

# 2) BPF 서비스 테이블
kubectl -n kube-system exec ds/cilium -c cilium-agent -- cilium service list

# 3) 간단 워크로드 + 접근
kubectl create deploy demo-nginx --image=nginx:1.25-alpine --replicas=2
kubectl expose deploy demo-nginx --port=80 --target-port=80 --type=ClusterIP
kubectl run -it --rm tmp --image=busybox:1.36 --restart=Never -- sh -c 'wget -qO- http://demo-nginx'

# 4) Cilium CLI 연결성 테스트
cilium connectivity test --test basic

9) 자주 하는 실수/주의점 (추가)

  • kube_version접두사 v 금지 (예: "1.31.6" OK, "v1.31.6" NOK)
  • kubeProxyReplacement반드시 boolean (true/false)로 명시
  • API 서버 자동탐지 실패 시 **k8sServiceHost/Port**를 값 파일에 명시
  • CNI 디렉터리 권한/존재 보장: /opt/cni/bin (0755, root:root)
  • 베스천에서 CLI/Helm 실행 시 KUBECONFIG/값 파일 경로 확인

10) 부록: k9s / Hubble 빠른 구성

# k9s (베스천)
export KUBECONFIG=~/.kube/config
# 최신 릴리스 바이너리 설치 후 실행 (배포판별 설치 방식 상이)
k9s

# Hubble (Cilium CLI)
cilium hubble enable --ui

# Hubble NodePort 지정 생성
kubectl -n kube-system patch svc hubble-ui \
  -p '{"spec":{"type":"NodePort","ports":[{"name":"http","port":80,"targetPort":8081,"protocol":"TCP","nodePort":31234}]}}'

kubectl -n kube-system get svc hubble-ui -o wide    # NodePort 확인
# 브라우저: http://<worker-IP>:<nodePort>

# CLI 관측
cilium hubble port-forward &
hubble status
hubble observe --http --follow

참고) 단계별 롤백/리셋

# kube-proxy 대체 전 상태로 되돌리기 (예: KPR=false)
helm upgrade --install cilium cilium/cilium \
  -n kube-system --version 1.17.7 \
  -f /etc/kubernetes/cilium-extra-values.yaml --wait

# 클러스터 전체 리셋(필요 시)
ansible-playbook -i inventory/study/hosts.yaml -b reset.yml
# 잔여물 수동 정리 후 재설치
ansible-playbook -i inventory/study/hosts.yaml -b cluster.yml