쿠버네티스 클러스터 구축

김유경·2025년 5월 31일
post-thumbnail

들어가며

Kubernetes 클러스터를 처음 구축할 때 가장 어려운 부분 중 하나는 복잡한 네트워킹 설정과 여러 컴포넌트 간의 호환성 문제입니다. 저도 직접 설치하면서 가장 큰 어려움을 겪었던 부분이 Calico CNI의 BGP 관련 오류였습니다. 그래서 이런 문제를 처음부터 피할 수 있도록, Kubernetes 마스터 노드를 자동으로 설치해주는 스크립트를 만들었습니다.


CNI(Container Network Interface)란?

CNI(Container Network Interface)는 컨테이너 런타임과 네트워크 플러그인 간의 표준 인터페이스입니다. 쿠버네티스에서는 pod가 생성될 때 이 CNI를 통해 네트워크를 자동으로 설정합니다.

주요 역할

  • Pod 간 통신 경로 구성
  • 컨테이너에 네트워크 인터페이스 생성
  • IP 주소 할당 및 라우팅 테이블 설정

대표적인 CNI 플러그인

  • Calico
  • Flannel
  • Weave
  • Cilium

😭 문제 상황: Calico가 Running인데 NotReady

$ kubectl get pods -n calico-system
calico-node-xxxxx   0/1 Running   0   Xs

Calico는 기본적으로 BGP(Border Gateway Protocol)를 이용해 노드 간 라우팅 정보를 주고받습니다. 하지만 제 환경(KakaoCloud Compute Instance)에서는 BGP 연결이 제대로 되지 않아, Calico가 정상적으로 작동하지 않았습니다. 구글링을 수십 번 하고, config 수정도 이것저것 해봤지만 결국 해결되지 않았습니다...

😃 그래서 결국 VXLAN 모드로 재설치를 선택하였습니다.

지금 생각해보면 그냥 179번 포트만 열었줬어도 됐던 게 아닐까…? 싶기도 합니다... (BGP 피어링을 할 때 사용하는 기본 포트가 TCP 179번이기 때문!!!)

BGP와 VXLAN

Calico CNI는 BGP와 VXLAN 두 가지 주요 방식으로 네트워크를 구성할 수 있습니다.

BGP란?

BGP(Border Gateway Protocol)는 인터넷의 핵심 라우팅 프로토콜로, 서로 다른 자율 시스템 간의 경로 정보를 교환하여 최적의 라우팅 경로를 결정합니다. Calico는 이 BGP 프로토콜을 쿠버네티스 환경에 적용하여 각 노드 간 라우팅 정보를 직접 교환함으로써 pod 네트워크를 구성합니다.

BGP의 장점

  • 라우팅 테이블 기반으로 동작 → 고성능, 오버헤드 없음
  • 대규모 클러스터에서도 확장성 우수

BGP의 단점

  • 물리 네트워크에 의존: 라우터, 스위치가 BGP를 지원해야 함
  • 설정이 어렵고, 문제 발생 시 디버깅이 어려움

VXLAN이란?

VXLAN(Virtual eXtensible LAN)은 물리적인 네트워크 위에 가상의 L2 네트워크를 구성할 수 있도록 해주는 오버레이 네트워크 기술입니다. Calico에서 BGP 대신 VXLAN 모드를 선택하면, 노드 간 직접 라우팅 없이도 Pod 간 통신이 가능합니다.

VXLAN의 장점

  • 물리 네트워크와 무관하게 작동
  • 설정이 간단하고, 기본 설정만으로도 즉시 통신 가능

VXLAN의 단점

  • 네트워크 패킷을 캡슐화하기 때문에 성능 손실 발생 (보통 5~10%)
  • 캡슐화로 인해 MTU 설정 조정 필요할 수 있음

Master Node 설치

📜 필요 환경

  • root 권한 또는 sudo
  • 외부 인터넷 연결 필요 (패키지 설치, manifest 다운로드)

⚙️ 실행 방법

nano init-master.sh
chmod +x init-master.sh
sudo ./init-master.sh

✏️ 스크립트 주요 구성

  1. 시스템 초기화
apt-get update && apt-get upgrade -y
swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab

‼️ swap을 비활성화하지 않으면 kubeadm init 단계에서 오류가 발생합니다. 쿠버네티스는 정확한 메모리 스케줄링을 위해 swap을 비활성화해야 하며, swap이 켜져 있으면 실제 사용 가능한 RAM과의 차이로 인해 예측 불가능한 리소스 할당 문제가 발생할 수 있습니다. 따라서 쿠버네티스는 기본적으로 swap이 꺼져 있는 환경만 지원하며, kubeadm도 이를 체크해서 swap이 켜져 있으면 설치를 중단합니다.

  1. containerd 설치 및 Systemd CGroup 설정
containerd config default > /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' config.toml
  1. Kubernetes 설치 (v1.29)
curl -s https://pkgs.k8s.io/... | gpg --dearmor ...
apt install -y kubelet kubeadm kubectl
  1. 클러스터 초기화 (Pod CIDR 지정)
kubeadm init \
  --apiserver-advertise-address=$MASTER_IP \
  --pod-network-cidr=192.168.0.0/16 \
  ...

‼️ $MASTER_IP는 자동 감지되며, 192.168.0.0/16은 Calico의 기본 pod CIDR입니다.

  1. Calico CNI 설치 (BGP 비활성화 + VXLAN 모드)
spec:
  calicoNetwork:
    bgp: Disabled
    encapsulation: VXLAN
  1. Helm 설치
curl https://baltocdn.com/helm/signing.asc | gpg ...
apt-get install -y helm

Worker Node 설치

워커 노드에서는 마스터 노드와 거의 동일한 환경을 구성해 주면 됩니다. 기본 설정이 완료되면, 마스터 노드에서 발급한 kubeadm join 명령어만 실행하면 클러스터에 쉽게 연결할 수 있습니다.


전체 스크립트

init-master.sh

#!/bin/bash

# Kubernetes 클러스터 새 설치 스크립트
# Calico BGP 문제 해결 포함

set -e

# 색상 코드
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

error_exit() {
    log_error "$1"
    exit 1
}

if [ "$EUID" -ne 0 ]; then
    error_exit "root 권한으로 실행하세요: sudo $0"
fi

log_warning "새로운 Kubernetes 클러스터를 설치합니다."
read -p "계속하시겠습니까? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    exit 1
fi

# ================================
# 1. 설치 시작
# ================================
log_info "새로운 Kubernetes 클러스터 설치 시작..."

# ================================
# 2. 시스템 준비
# ================================
log_info "시스템 업데이트 및 패키지 설치..."

apt-get update -y
apt-get upgrade -y

apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release \
    software-properties-common \
    wget \
    vim \
    net-tools \
    htop

# ================================
# 3. Swap 및 시스템 설정
# ================================
log_info "시스템 설정 중..."

# Swap 비활성화
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

# 커널 모듈
cat <<EOF > /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

# 네트워크 설정
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
net.ipv4.conf.all.forwarding        = 1
EOF

sysctl --system

# ================================
# 4. containerd 설치 및 설정
# ================================
log_info "containerd 설치 및 설정..."

# 기존 containerd 제거
systemctl stop containerd 2>/dev/null || true
apt-get remove -y containerd.io docker-ce docker-ce-cli 2>/dev/null || true

# Docker 저장소 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list

apt-get update -y
apt-get install -y containerd.io

# containerd 설정
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml

# SystemdCgroup 활성화 (중요!)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

systemctl restart containerd
systemctl enable containerd

# ================================
# 5. Kubernetes 설치
# ================================
log_info "Kubernetes 패키지 설치..."

# Kubernetes 저장소 추가 (최신 방식)
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' > /etc/apt/sources.list.d/kubernetes.list

apt-get update -y
apt-get install -y kubelet kubeadm kubectl

apt-mark hold kubelet kubeadm kubectl
systemctl enable kubelet

# ================================
# 6. 클러스터 초기화
# ================================
log_info "Kubernetes 클러스터 초기화..."

# 마스터 IP 자동 감지
MASTER_IP=$(ip route get 8.8.8.8 | awk '{print $7; exit}')
log_info "마스터 노드 IP: $MASTER_IP"

# Pod CIDR (Calico 기본값)
POD_CIDR="192.168.0.0/16"

# kubeadm init
kubeadm init \
    --apiserver-advertise-address=$MASTER_IP \
    --pod-network-cidr=$POD_CIDR \
    --service-cidr=10.96.0.0/12 \
    --cri-socket=unix:///var/run/containerd/containerd.sock \
    --ignore-preflight-errors=all

# ================================
# 7. kubectl 설정
# ================================
log_info "kubectl 설정..."

# root 사용자
mkdir -p $HOME/.kube
cp -f /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

# sudo 사용자가 있다면
if [ -n "$SUDO_USER" ]; then
    sudo -u $SUDO_USER mkdir -p /home/$SUDO_USER/.kube
    cp -f /etc/kubernetes/admin.conf /home/$SUDO_USER/.kube/config
    chown $SUDO_USER:$SUDO_USER /home/$SUDO_USER/.kube/config
fi

# ================================
# 8. Calico 설치 (BGP 비활성화)
# ================================
log_info "Calico CNI 설치 (BGP 비활성화 모드)..."

# Calico Operator 설치
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml

# BGP 비활성화된 Calico 설정 적용
cat <<EOF | kubectl apply -f -
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
  name: default
spec:
  calicoNetwork:
    bgp: Disabled
    ipPools:
    - blockSize: 26
      cidr: $POD_CIDR
      encapsulation: VXLAN
      natOutgoing: Enabled
      nodeSelector: all()
    nodeAddressAutodetectionV4:
      firstFound: true
---
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
  name: default
spec: {}
EOF

log_info "Calico 설치 완료. BGP는 비활성화되고 VXLAN 모드로 설정되었습니다."

# ================================
# 9. Helm 설치
# ================================
log_info "Helm 설치..."

curl https://baltocdn.com/helm/signing.asc | gpg --dearmor > /usr/share/keyrings/helm.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" > /etc/apt/sources.list.d/helm-stable-debian.list

apt-get update -y
apt-get install -y helm

# ================================
# 10. 클러스터 상태 확인
# ================================
log_info "클러스터 준비 상태 확인 중..."

echo "Calico 파드가 준비될 때까지 대기 중... (최대 5분)"
for i in {1..30}; do
    if kubectl get pods -n calico-system | grep -q "Running.*1/1"; then
        log_success "Calico 파드가 준비되었습니다!"
        break
    fi
    echo "대기 중... ($i/30)"
    sleep 10
done

# ================================
# 11. 결과 출력
# ================================
log_success "Kubernetes 클러스터 설치 완료!"

echo ""
echo "=============================================="
echo "설치 정보"
echo "=============================================="
echo "- Kubernetes 버전: $(kubectl version --short --client 2>/dev/null || echo "확인 중...")"
echo "- 마스터 노드 IP: $MASTER_IP"
echo "- Pod Network CIDR: $POD_CIDR"
echo "- CNI: Calico (VXLAN 모드, BGP 비활성화)"
echo ""

echo "노드 상태:"
kubectl get nodes -o wide || echo "노드 상태 확인 중..."

echo ""
echo "시스템 파드 상태:"
kubectl get pods -n kube-system || echo "시스템 파드 확인 중..."

echo ""
echo "Calico 파드 상태:"
kubectl get pods -n calico-system || echo "Calico 파드 확인 중..."

echo ""
echo "=============================================="
echo "다음 단계"
echo "=============================================="
echo ""
echo "1. 워커 노드 추가 명령어:"
echo "   (각 워커 노드에서 실행)"
echo ""
kubeadm token create --print-join-command 2>/dev/null || echo "   토큰 생성 중 오류 발생"
echo ""

echo "2. 일반 사용자 kubectl 설정:"
echo "   mkdir -p \$HOME/.kube"
echo "   sudo cp -i /etc/kubernetes/admin.conf \$HOME/.kube/config"
echo "   sudo chown \$(id -u):\$(id -g) \$HOME/.kube/config"
echo ""

echo "3. 클러스터 확인 명령어:"
echo "   kubectl get nodes"
echo "   kubectl get pods -A"
echo "   kubectl describe nodes"
echo ""

echo "4. Calico 상태 확인:"
echo "   kubectl get pods -n calico-system"
echo "   kubectl logs -n calico-system -l k8s-app=calico-node"
echo ""

log_success "설치가 완료되었습니다!"
log_info "만약 여전히 문제가 있다면 다음을 확인하세요:"
echo "- 방화벽 설정 (ufw status)"
echo "- 네트워크 인터페이스 상태 (ip addr)"
echo "- containerd 로그 (journalctl -u containerd)"

init-worker.sh

#!/bin/bash

# Kubernetes 워커 노드 설치 스크립트
# Ubuntu 20.04/22.04 기준

set -e

# 색상 코드
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

error_exit() {
    log_error "$1"
    exit 1
}

if [ "$EUID" -ne 0 ]; then
    error_exit "root 권한으로 실행하세요: sudo $0"
fi

# 워커 노드 기본 설치 (join은 수동으로)

log_warning "새로운 Kubernetes 워커 노드를 설치합니다."
read -p "계속하시겠습니까? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    exit 1
fi

# ================================
# 1. 설치 시작
# ================================
log_info "새로운 Kubernetes 워커 노드 설치 시작..."

# ================================
# 2. 시스템 준비
# ================================
log_info "시스템 업데이트 및 패키지 설치..."

apt-get update -y
apt-get upgrade -y

apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release \
    software-properties-common \
    wget \
    vim \
    net-tools \
    htop

# ================================
# 3. Swap 및 시스템 설정
# ================================
log_info "시스템 설정 중..."

# Swap 비활성화
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

# 커널 모듈
cat <<EOF > /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

# 네트워크 설정
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
net.ipv4.conf.all.forwarding        = 1
EOF

sysctl --system

# ================================
# 4. containerd 설치 및 설정
# ================================
log_info "containerd 설치 및 설정..."

# Docker 저장소 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list

apt-get update -y
apt-get install -y containerd.io

# containerd 설정
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml

# SystemdCgroup 활성화 (중요!)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

systemctl restart containerd
systemctl enable containerd

# ================================
# 5. Kubernetes 설치
# ================================
log_info "Kubernetes 패키지 설치..."

# Kubernetes 저장소 추가 (최신 방식)
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' > /etc/apt/sources.list.d/kubernetes.list

apt-get update -y
apt-get install -y kubelet kubeadm kubectl

apt-mark hold kubelet kubeadm kubectl
systemctl enable kubelet

# ================================
# 6. 설치 완료
# ================================
log_success "워커 노드 기본 설치 완료!"

echo ""
echo "=============================================="
echo "설치 정보"
echo "=============================================="
echo "- 워커 노드 IP: $(ip route get 8.8.8.8 | awk '{print $7; exit}')"
echo "- Kubernetes 버전: $(kubelet --version)"
echo "- 컨테이너 런타임: containerd"
echo ""

echo "=============================================="
echo "클러스터 조인 방법"
echo "=============================================="
echo ""
echo "1. 마스터 노드에서 join 명령어 생성:"
echo "   kubeadm token create --print-join-command"
echo ""
echo "2. 이 워커 노드에서 join 명령어 실행:"
echo "   sudo kubeadm join <마스터IP>:6443 --token <토큰> --discovery-token-ca-cert-hash <해시>"
echo ""
echo "3. 마스터 노드에서 노드 상태 확인:"
echo "   kubectl get nodes -o wide"
echo ""

log_success "워커 노드 준비가 완료되었습니다!"
log_info "이제 수동으로 클러스터에 조인하세요."

0개의 댓글