Kubernetes 클러스터 구축기
- Published on
- Published on
배경
Kubernetes 학습 및 테스팅을 위해 3대의 가상 머신을 이용하여 간단한 쿠버네티스 클러스터를 구축한 과정을 기록합니다. 설치 과정은 FastCampus의 실무까지 한 번에 끝내는 DevOps를 위한 Docker & Kubernetes 강의를 참고하여 진행하였습니다.
배포
클러스터는 VM 3대를 배포하여 Master Node 1대, Worker Node 2대로 구성하였습니다. VM은 Proxmox VE로 Homelab 환경을 구축해둔 것이 있어 해당 환경에 배포하고, Terraform을 이용해 코드로 관리하였습니다.
준비 작업
VM 3대 준비
테스트 목적의 클러스터이고 하이퍼바이저의 자원이 넉넉하지 않아 VM의 사양은 비교적 낮게 설정했습니다. 또한, 이전에 KISA의 보안 취약점 조치 가이드를 참고하여 생성해 둔 Ubuntu 24.04 템플릿을 복제하여 사용했습니다.
참고로, 이 tf 파일은 예시이므로 실제로는 각자 환경에 맞게 수정해서 사용해야 합니다. 특히 IP 주소, 게이트웨이, DNS, SSH 키 등은 자신의 환경에 맞게 변경해야 합니다.
resource "proxmox_vm_qemu" "k8s" {
count = length(var.k8s_vm_configs)
name = var.k8s_vm_configs[count.index].name
target_node = var.k8s_vm_configs[count.index].target_node
tags = var.k8s_vm_configs[count.index].tags
clone = var.k8s_vm_configs[count.index].template
full_clone = true
cpu {
cores = var.k8s_vm_configs[count.index].core
numa = true
}
memory = var.k8s_vm_configs[count.index].mem
scsihw = "virtio-scsi-single"
disks {
scsi {
scsi0 {
disk {
size = "50G"
storage = "local-lvm"
}
}
}
}
network {
id = 0
model = "virtio"
bridge = "vmbr0"
}
ipconfig0 = "ip=${var.k8s_vm_configs[count.index].ip1}/24,gw=${var.k8s_vm_configs[count.index].gw}"
nameserver = var.k8s_vm_configs[count.index].dns
}
variable "k8s_vm_configs" {
type = list(object({
name = string
target_node = string
template = string
tags = string
ip1 = string
gw = string
dns = string
core = number
mem = number
}))
}
k8s_vm_configs = [
{
name = "k8s-master"
target_node = "pve-node1"
template = "ubuntu24-tmpl"
ip1 = "192.168.0.10"
gw = "192.168.0.1"
dns = "192.168.0.1"
core = 2
mem = 4096
tags = "k8s,prd,linux,ubuntu"
},
{
# k8s-worker1 192.168.0.11 ...
},
{
# k8s-worker2 192.168.0.12 ...
}
]
OS 기본 설정
모든 노드(마스터, 워커)에 아래 설정을 동일하게 적용합니다.
- 호스트명 및 호스트 정보 등록
각 노드가 서로를 이름으로 찾을 수 있도록 호스트명을 설정하고, /etc/hosts 파일에 모든 노드의 IP와 호스트명을 등록합니다.
호스트명 설정
# 각 노드에 맞는 호스트명으로 설정 (예: k8s-master, k8s-worker1) > hostnamectl set-hostname k8s-master호스트 정보 등록
/etc/hosts127.0.1.1 k8s-master # 또는 k8s-workerX # Kubernetes Cluster 192.168.0.10 k8s-master 192.168.0.11 k8s-worker1 192.168.0.12 k8s-worker2통신 확인
> ping k8s-master > ping k8s-worker1 > ping k8s-worker2
- 스왑 비활성화
Kubernetes는 노드의 메모리와 CPU 자원을 직접 관리하며, 예측 가능성을 높이기 위해 스왑(Swap) 메모리 사용을 지원하지 않습니다. 따라서 스왑을 비활성화하고, 시스템 재부팅 시에도 적용되도록 fstab에서 해당 라인을 주석 처리합니다.
스왑 비활성화 후 확인
> swapoff -a && free -h # /etc/fstab 파일을 열어 swap 관련 라인을 찾아 주석 처리 > vi /etc/fstabswap 메모리 관련 이전 글이 있으니 참고하면 좋을 것 같습니다.
3. 커널 모듈 및 파라미터 설정
컨테이너 런타임이 원활하게 동작하고, Pod 간의 통신이 가능하도록 필요한 커널 모듈을 로드하고 관련 파라미터를 활성화합니다.
커널 파라미터 변경
> cat <<EOF | tee /etc/sysctl.d/k8s.conf net.ipv4.ip_forward = 1 EOF > sysctl --system > sysctl net.ipv4.ip_forwardContainer Runtime으로 containerd 사용하기 위한 설정
> vi /etc/modules-load.d/containerd.conf overlay br_netfilter # 필요한 모듈을 커널에 등록 > modprobe overlay br_netfilter노드간 통신을 위해 iptables에 브릿지 관련 설정 추가
> cat <<EOF | tee /etc/sysctl.d/99-kubernetes-cri.conf net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF > cat <<EOF | tee /etc/modules-load.d/k8s.conf br_netfilter EOF > cat <<EOF | tee /etc/sysctl.d/k8s.conf net.ipv4.ip_forward = 1 net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 EOF > sysctl --system
- 컨테이너 런타임(Containerd) 설치 및 설정
Kubernetes가 컨테이너를 실행하고 관리하기 위해 컨테이너 런타임이 필요합니다. 여기서는 containerd를 사용합니다.
필수 패키지 설치
docker-ce패키지를 설치하면containerd가 의존성으로 함께 설치됩니다. Docker 공식 문서를 참고하여 설치를 진행합니다.컨테이너의 리소스 제어를 위해 사용하는 cgroup 드라이버를
systemd로 지정# 1. 기본 설정 파일 생성 > sh -c "containerd config default > /etc/containerd/config.toml" # 2. cgroup 드라이버를 systemd로 변경 > sed -i 's/ SystemdCgroup = false/ SystemdCgroup = true/' /etc/containerd/config.toml # 3. containerd 서비스 재시작 > systemctl restart containerdDocker의 cgroup 드라이버도
systemd로 통일/etc/docker/daemon.json{ "exec-opts": ["native.cgroupdriver=systemd"], "storage-driver": "overlay2" }서비스 재시작 및 확인
> usermod -aG docker $USER > systemctl restart docker.service containerd.service > reboot > docker info | grep "Cgroup Driver" Cgroup Driver: systemd
- NTP 시간 동기화
클러스터 내 모든 노드의 시간이 동기화되지 않으면 예기치 않은 문제가 발생할 수 있습니다. NTP 시간 동기화 설정 문서를 참고하여 모든 노드의 시간을 동기화합니다.
kubernetes 설치
- Kubernetes 공식 문서를 따라 설치합니다.
k8s 도구 설치
> curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/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.34/deb/ /' | tee /etc/apt/sources.list.d/kubernetes.list
> apt update && apt install -y kubelet kubeadm kubectl
> kubeadm version; kubectl version; kubelet --version
# apt update때 자동 업데이트 되지 않도록 막기
> apt-mark hold kubelet kubeadm kubectl
> systemctl enable --now kubelet
클러스터 초기화
클러스터 초기화는 마스터 노드에서만 실행합니다. 이때 --apiserver-advertise-address 옵션에는 마스터 노드의 IP 주소를 명시해야 합니다. 초기화 과정에서 문제가 발생하면, 출력되는 에러 메시지를 참고하여 문제를 해결한 뒤 kubeadm reset 명령어로 클러스터를 초기화하고 다시 시도할 수 있습니다.
> kubeadm init --pod-network-cidr=10.96.0.0/12 --apiserver-advertise-address=192.168.0.10
# ...
Your Kubernetes control-plane has initialized successfully!
# ...
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.0.10:6443 --token ... \
--discovery-token-ca-cert-hash sha256:...
클러스터 시작
초기화가 완료되면, 다음 명령어를 통해 API 서버가 정상적으로 실행되고 있는지 포트를 확인합니다.
> mkdir -p $HOME/.kube
> cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
> chown $(id -u):$(id -g) $HOME/.kube/config
- api-server 떠있고 포트 열려있는지 확인
> netstat -ntlp | grep LISTEN # ... tcp6 0 0 :::6443 :::* LISTEN 6591/kube-apiserver # 지금은 마스터 노드밖에 없는 준비가 안된 상태. 이 준비 상태는 Calico 실행 후 등록됨. > kubectl get node NAME STATUS ROLES AGE VERSION k8s-master NotReady control-plane 12m v1.32.1 # DNS도 Calico 올린 후. > kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-668d6bf9bc-bw4dw 0/1 Pending 0 14m kube-system coredns-668d6bf9bc-n8tdf 0/1 Pending 0 14m kube-system etcd-k8s-master 1/1 Running 0 14m kube-system kube-apiserver-k8s-master 1/1 Running 0 14m kube-system kube-controller-manager-k8s-master 1/1 Running 0 14m kube-system kube-proxy-4v227 1/1 Running 0 14m kube-system kube-scheduler-k8s-master 1/1 Running 0 14m
클러스터에 Worker 노드 추가
마스터 노드에서 kubeadm init이 성공적으로 완료되면, 워커 노드를 클러스터에 추가하기 위한 join 명령어가 출력됩니다. 이 명령어를 복사하여 각 워커 노드에서 root 권한으로 실행합니다.
> kubeadm join 192.168.0.10:6443 --token ... \
--discovery-token-ca-cert-hash sha256:...
# ...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
# ...
- master node
> kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master NotReady control-plane 17m v1.32.1 k8s-worker1 NotReady <none> 2m14s v1.32.1 k8s-worker2 NotReady <none> 25s v1.32.1 > kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system coredns-668d6bf9bc-bw4dw 0/1 Pending 0 17m kube-system coredns-668d6bf9bc-n8tdf 0/1 Pending 0 17m kube-system etcd-k8s-master 1/1 Running 0 17m kube-system kube-apiserver-k8s-master 1/1 Running 0 17m kube-system kube-controller-manager-k8s-master 1/1 Running 0 17m kube-system kube-proxy-4v227 1/1 Running 0 17m kube-system kube-proxy-7s45b 1/1 Running 0 111s kube-system kube-proxy-c2nqt 0/1 ContainerCreating 0 2s kube-system kube-scheduler-k8s-master 1/1 Running 0 17m
CNI (Container Network Interface)
CNI는 컨테이너 런타임과 오케스트레이션 도구(여기서는 Kubernetes) 사이의 네트워크 계층을 구현하기 위한 표준 인터페이스입니다. 이를 통해 다양한 네트워크 플러그인을 사용하여 컨테이너 간의 통신을 제어할 수 있습니다.
- master node
> curl -O https://raw.githubusercontent.com/projectcalico/calico/refs/heads/master/manifests/calico.yaml # 클러스터에 연결된 노드에 calico를 각각 심어서 연결해줌 > kubectl apply -f calico.yaml # calico가 올라오면 pending 돼있던 dns도 진행됨 > kubectl get pod -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system calico-kube-controllers-5b97c7b9dd-dqhlq 0/1 ContainerCreating 0 108s kube-system calico-node-6gv2k 1/1 Running 0 108s kube-system calico-node-bn7gh 1/1 Running 0 108s kube-system calico-node-cr42c 1/1 Running 0 108s kube-system coredns-66bc5c9577-6vkp6 1/1 Running 0 5m22s kube-system coredns-66bc5c9577-fgmpr 1/1 Running 0 5m22s kube-system etcd-k8s-master 1/1 Running 1 5m28s kube-system kube-apiserver-k8s-master 1/1 Running 1 5m28s kube-system kube-controller-manager-k8s-master 1/1 Running 1 5m28s kube-system kube-proxy-5b77n 1/1 Running 0 3m38s kube-system kube-proxy-p7l5w 1/1 Running 0 3m10s kube-system kube-proxy-q27lk 1/1 Running 0 5m22s kube-system kube-scheduler-k8s-master 1/1 Running 1 5m30s # Pod가 Running으로 바뀌면 노드도 Ready인지 확인 > kubectl get node NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 26m v1.32.1 k8s-worker1 Ready <none> 10m v1.32.1 k8s-worker2 Ready <none> 8m59s v1.32.1
결론 및 다음 단계
이 가이드를 통해 3대의 가상 머신(마스터 노드 1대, 워커 노드 2대)으로 구성된 기본적인 Kubernetes 클러스터를 성공적으로 구축했습니다. 이 글이 Kubernetes 클러스터 구축에 관심이 있는 분들에게 도움이 되었기를 바랍니다.
이제 이 클러스터를 활용하여 다양한 Kubernetes 기능을 학습하고 테스트할 수 있습니다.