테스트 용으로 구축한 Proxmox VE 클러스터 환경에 드디어 쿠버네티스를 설치했다. 물리적인 Proxmox 클러스터 노드는 2대의 PC로 구성하였고 가상머신(VM)은 쿠버네티스 마스터 노드 용 1개와 워커노드 용 2개로 구성했다.
가상 머신의 사전 요구사항
아래 화면에서 붉색 화살표가 가리키는 prxmx와 prxmx2가 Proxmox VE가 설치된 2대의 물리PC다.

이 물리PC에 위 화면처럼 쿠버네티스 마스터 노드로 사용할 k8s-master-a 라는 가상머신과 워커노드로 사용할 k8s-worker-a, k8s-worker-b 라는 가상머신을 생성했다.
그런데 쿠버네티스를 설치할 가상머신에 두 가지 요구사항이 있다.
첫 번째 요구사항은 쿠버네티스가 설치되는 가상 머신에는 프로세서 즉 CPU(코어)가 2개 이상 할당되어야 한다는 점이다. 물론 이 CPU는 물리 CPU를 의미하는 것은 아니다. 만약 가상머신에서 CPU(코어)가 1개만 확인된다면 kubelet 서비스가 실행되지 않는다.
그래서 다음과 같이 마스터노드와 워커노드에 2개 이상의 CPU(코어)를 할당해야 한다.

그리고 코어를 늘려줄 때 NUMA 기능을 활성화해줘야 한다. 만약 CPU(코어)를 가상머신을 생성하고 사용 중에 추가해줬다면 “옵션”의 Hotplug 항목에 가서 Hotplug 기능을 사용할 하드웨어에 CPU를 추가해줘야 한다. 그렇지 않으면 CPU(코어)를 추가하고 리부팅해도 추가한 CPU가 인식되지 않는다.
두 번째 요구사항은 쿠버네티스가 설치될 가상머신의 리눅스에서 Swap 기능을 꺼야 한다는 것이다. 컨테이너를 통해 가상화 된 응용프로그램으로 인해 Swap이 발생할 경우 마스터 및 워커노드 전체에 영향을 끼쳐 전반적인 성능저하가 발생할 수 있어 Swap을 꺼줄 것을 요구하는 것이다. 만약 Swap이 활성화되어 있을 경우 마찬가지로 kubelet 서비스가 실행되지 않는다.
마스터 및 워커노드 운영체제의 환경설정 (Ubuntu 24.04 기준)
이 두 하드웨어 적인 요구사항이 충족되었다면 모두 세개의 쿠버네티스 노드로 사용될 가상머신에 쿠버네티스를 설치할 수 있다. 다만 쿠버네티스를 설치하기 전에 다음과 같이 필수적인 공통패키지를 설치하고 노드의 타임존을 맞춘다. (아래의 “쿠버네티스 노드의 IP 설정” 까지는 세개의 노드에서 공통으로 수행해야 한다.)
## 타임존이 서울인지 확이
$ sudo timedatectl
## 만약 아니라면 서울의 타임존 이름 확인
$ timedatectl list-timezones | grep Seoul
## 서울의 타임존 이름이 Asia/Seoul 인 경우 타임존의 Asia/Seoul로 변경
$ sudo timedatectl set-timezone Asia/Seoul
## 필수적인 공통패키지 설치
$ sudo apt-get install -y net-tools openssh-server vim tree htop
$ sudo apt install iptables -y
$ sudo apt-get install -y ntp
다음은 IP Forwarding과 Containerd에서 사용할 커널 모듈 설정, 그리고 쿠버네티스에서 사용할 몇 가지 설정을 해준다.
## IP Forwarding 설정
$ sudo su -
# sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'
# exit
## Containerd 런타임 환경 설정 (커널 모듈 정의)
$ cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
$ sudo modprobe overlay
$ sudo modprobe br_netfilter
## 노드간 통신을 위해 iptable에서 사용할 bridge 설정 추가
$ cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
$ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 컨테이너 런타임(CRI)을 통해 컨테이너 간 통신을 가능하게 해주는 IP Forward 관련 설정 추가
$ cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
컨테이너 런타임(도커) 설치
컨테이너 런타임은 도커(Docker) 또는 Containerd를 의미한다. 사실 도커에는 Containerd가 포함되어 있다. Containerd를 보다 쉽게 사용할 수 있게 해주는 도구가 바로 Docker인 것이다. 쿠버네티스도 초기에는 도커 수준에서 컨테이너 관리를 수행했지만 최근엔 다양한 응용프로그램 가상화 컨테이너 솔루션을 지원한다. 그리고 최근에는 도커 보다는 보다 하위레벨의 Containerd를 직접 인터페이스해 컨테이너 런타임을 지원하고 있다. 하지만 쿠버네티스 공식 문서에서는 Containerd 를 별도로 설치하는 것 보다 그냥 도커를 설치하는 것을 권장하고 있다.
## 패키지 목록 업데이트
$ sudo apt update
$ sudo apt upgrade -y
## 의존성 패키지 설치
$ sudo apt install apt-transport-https ca-certificates curl software-properties-common -y
## Docker GPG 키 추가
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
## Docker 저장소 추가
$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
## 도커 저장소 추가 후 목록 갱신
$ sudo apt update
## 도커 설치
$ sudo apt install docker-ce docker-ce-cli containerd.io -y
## 도커 서비스 구성 및 확인
$ sudo systemctl start docker
$ sudo systemctl enable docker
$ sudo systemctl status docker
## Docker Group에 현재 사용자 추가 (도커 관리자)
$ sudo usermod -aG docker $USER
## 로그아웃, 로그인하지 않고 현재 사용자의 docker 그룹 적용
$ newgrp docker
## 도커 버전확인
$ docker --version
컨테이너 런타임 환경 설정
쿠버네티스가 컨테이너를 관리하기 위해서 설정해줘야 하는 항목은 두 가지가 있다. 하나는 Containerd의 설정파일이고 다른 하나는 리눅스의 cgroup 관련 설정이다.
특히 리눅스의 cgroup(Control Group)은 시스템의 리소스를 프로세스에게 할당하는 것을 통제하는 역할을 할 수 있는 그룹이다. 따라서 쿠버네티스의 각 노드에서 실행되는 kubelet(쿠버네티스에이전트)은 POD의 컨테이너에게 리눅스의 자원할당을 관리할 수 있는 cgroup을 가져야 한다. 따라서 kubelet과 컨테이너 런타임이 “동일한 cgroup 드라이버 및 환경 설정”을 공유해야 한다. kubelet이 사용할 수 있는 cgroup 드라이버는 cgroupfs와 systemd 두가지인데 리눅스의 환경을 변경하는 것 보다 kubelet의 환경을 변경하는 것이 더 바람직하다고 한다.
## containerd의 기본 설정으로 되돌리는 것으로 보이는데 containerd 명령을 실행해 초기설정으로 되돌린다. 이 명령을 실행하면 새로운 config.toml 파일이 생성된다.
$ sudo sh -c "containerd config default > /etc/containerd/config.toml"
## vi 등 명령어로 /etc/containerd/config.toml 파일을 열고 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] 라인을 찾는다.
SystemdCgroup = faslse 를 SystemdCgroup = true 로 변경해준다.
## /etc/docker/daemon.json 파일을 새로 생성하고 (최초 설치 시 이 파일은 없음) 다음의 내용을 저장한다.
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
## 다음의 명령을 실행하여 런타임 환경 설정을 마무리한다.
$ sudo mkdir -p /etc/systemd/system/docker.service.d
$ sudo usermod -aG docker [계정명] #sudo 없이 사용하기
$ sudo systemctl daemon-reload
$ sudo systemctl enable docker
$ sudo systemctl restart docker
$ sudo systemctl status docker # Active 상태 확인
$ sudo systemctl restart containerd.service
$ sudo systemctl status containerd.service
쿠버네티스 패키지 설치
이제 실제 쿠버네티스 패키지를 설치한다.
## 의존성 패키지 먼저 설치
$ sudo apt-get install -y apt-transport-https ca-certificates curl gpg
## 패키지 설치에 필요한 공개키 다운로드. 만약 /etc/apt/keyring 디렉토리가 없을 경우 sudo mkdir -p -m 755 /etc/apt/keyrings 명령으로 생성 [ 아래 버전의 경로를 바꿔서 새 버전 설치 가능함 ]
$ curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
## 쿠버네티스 리포지토리 추가
$ echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
## 패키지 목록 업데이트
$ sudo apt-get update
## 쿠버네티스 패키지 설치
$ sudo apt-get install -y kubelet kubeadm kubectl
## 쿠버네티스 자동업데이트 방지
$ sudo apt-mark hold kubelet kubeadm kubectl
## 버전 확인
$ kubeadm version
$ kubectl version
$ kubelet --version
## 자동시작 등록
$ sudo systemctl daemon-reload
$ sudo systemctl restart kubelet.service
$ sudo systemctl enable --now kubelet.service
쿠버네티스 노드의 IP 설정
필자의 테스트 환경에서 Proxmox VE 노드(물리서버)는 서비스 IP대역 (192.168.219.0/24)과 Proxmox VE 클러스터 관리 IP 대역(10.0.0.0/24)을 갖고 있다. 이 Proxmox VE 노드에서 실행되는 쿠버네티스 마스터 노드와 워커노드는 실제 서비스는 192.168.219.0/24로 수행하지만 쿠버네티스 클러스터터와 관련된 통신은 물리 서버의 10.0.0.0/24로 통신하도록 구성해야 한다. 그리고 쿠버네티스의 마스터노드와 워커노드 간 통신은 172.16.1.0/24로 통신하도록 한번 더 인캡슐레이션 되어 있다.
따라서 쿠버네티스는 구동될 때 노드의 IP를 172.16.1.0/24 대역의 IP를 인식하여 쿠버네티스 노드간 통신이 이루어지도록 해야 한다. 따라서 다음과 같이 노드의 IP를 지정해준다.
## 마스터 및 워커노드의 VM에 2개 이상의 IP가 있을 때 쿠버네티스가 사용할 IP를 다음과 같이 지정함
## /etc/default/kubelet 파일의 KUBELET_EXTRA_AGRS에 "--node-ip=172.16.1.10" 과 같이 추가
KUBELET_EXTRA_ARGS="--node-ip=172.16.1.10"
## 마스터노드, 워커노드 모두 자기 자신의 관리용 IP를 지정. 노드 간 통신은 이 IP와 대역으로 통신
## /etc/default/kubelet 파일을 열어 KUBELET_EXTRA_ARGS에 다음과 같이 --node-ip를 추가
KUBELET_EXTRA_ARGS="--node-ip=172.16.1.10"
여기까지는 쿠버네티스의 마스터 노드와 워커 노드 모두 동일하게 진행해야 한다.
쿠버네티스 마스터 노드 초기화 (kubeadm init)
쿠버네티스를 정상적으로 설치했다면 이제 마스터 노드를 초기화해야 한다. 마스터 노드를 초기화하는 명령은 kubeadm init 명령이다. 필자의 환경에서는 다음과 같이 초기화했다.
$ sudo kubeadm init --apiserver-advertise-address=172.16.1.10 --pod-network-cidr=10.0.1.0/24
–apiserver-advertise-address=172.16.1.10 는 마스터 노드의 존재를 172.16.1.10 만 알리라는 의미다. 즉 마스터 노드에 192.168.219.0/24 대역의 IP도 있지만 이 IP 대역은 응용프로그램의 서비스 네트워크로 사용하거나 관리자 접속 시 사용할 IP 이므로 알릴 필요가 없다.
그리고 –pod-network-cidr=10.0.1.0/24 는 POD간 통신에 사용할 네트워크로 지정한 것이다. 이렇게 지정하고 초기화할 경우 마스터 노드에는 워커 노드와 통신에 사용할 터널 인터페이스가 생성되고 해당 IP 로 노드간 통신이 가능해진다.
정상적으로 쿠버네티스 마스터 노드가 초기화되면 다음과 같이 워커노드에서 마스터 노드에 Join하는 방법을 안내해준다.
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 172.16.1.10:6443 --token cembjy.vihq7ghoridvq4m5 --discovery-token-ca-cert-hash sha256:17abf3b8f9a4bbb5828b6ed5b03df4b7891578469153efa9107408c3b2ba8e25
마지막 줄에 있는 kubeadm join ~~ 명령을 복사해서 워커노드에서 실행하면 바로 클러스터에 Join 된다.
그런데 마스터 노드에서 kubeadm init ~~ 명령을 수행하는 중에 다음과 같은 에러가 발생하는 경우가 많은 듯 하다.
kubelet 서비스 실행 에러의 원인
kubeadm init 명령이 실행되는 중간에 다음과 같은 에러가 발생한다.
[kubelet-check] It seems like the kubelet isn't running or healthy.
[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp 127.0.0.1:10248: connect: connection refused.
[kubelet-check] It seems like the kubelet isn't running or healthy.
이 에러는 말 그대로 kubelet 서비스가 정상적으로 실행되지 않기 때문에 발생하는에러다. 앞에서 사전 요구사항이었던 CPU 개수가 부족한 경우, 메모리가 부족한 경우, Swap이 활성화 되어 있는 경우, cgroup 설정이 제대로 되어 있지 않는 경우 등 kubelet이 실행되지 않는 다양한 이유로 인해 위의 에러가 발생하는 것 같다.
다음의 두 명령을 통해 kubelet 서비스가 왜 실행되지 않는지 원인을 파악해보고 조치하면 된다.
$ sudo systemctl status kubelet
$ sudo journalctl -xeu kubelet
특히 아랫쪽에 저널링 된 에러 메시지를 집중적으로 보길 바란다. 에러 메시지에 해결책이 있는 경우가 대부분이다.
쿠버네티스 CNS 구성 (Container Network Interface)
쿠버네티스는 다수의 컨테이너를 품은 POD를 다수의 노드에서 분산시켜 실행하고 POD간의 통신을 통해 서비스를 수행하는 구조다. 그러기 위해서는 매우 복잡한 컨테이너 간의 네트워크를 구성할 수 있어야 한다. 이를 가능케 해주는 것이 바로 CNS(Container Network Interface)다. 그리고 이 때 사용되는 도구가 바로 Calico다. 쉽고 간단하게 설명하자면 여러개의 쿠버네티스 노드에서 동작하는 POD 네트워크를 연결하는 컨테이너 네트워크를 구성하고 그 안에서 IP(internet protocol)에서 구현되는 BGP 등 전용 IP 프로토콜이 구현된 것이라 생각하면 될 듯 하다.
$ curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml
$ kubectl apply -f calico.yaml
$ cd /usr/local/bin
$ sudo curl -O -L https://github.com/projectcalico/calicoctl/releases/download/v3.25.0/calicoctl
$ sudo chmod +x calicoctl
o Proxmox의 VXLAN 환경에서는 Calico 3.25의 경우 계속 Pod 네트워크가 다운되는 증상이 있었다. 그래서 버전 3.30으로 재설치 하니 문제가 없었다. (calicoctl 명령도 동일하게 새 버전으로 재설치 했으나 동일한 문제가 계속되었다.)
$ sudo curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.30.0/manifests/calico.yaml
워커 노드에서 쿠버네티스 클러스터에 조인하기 (워커노드)
워커노드의 쿠버네티스 클러스터 조인은 마스터 노드의 kubeadm init ~ 명령 실행이 종료되면서 표시되는 명령을 복사하여 워커노드에서 실행하면 된다.
$ sudo kubeadm join 172.16.1.10:6443 --token cembjy.vihq7ghoridvq4m5 --discovery-token-ca-cert-hash sha256:17abf3b8f9a4bbb5828b*********b7891578469153efa9107408c3b2ba8e25
쿠버네티스 실행상태 확인 (마스터노드)
.먼저 쿠버네티스 관련 서비스 실행상태와 노드 간 연결상태를 볼 수 있는 명령어다.
## 쿠버네티스 마스터 노드에서 쿠버네티스 과련 포트 사용 상태 확인
$ sudo netstat -ntlp | grep LISTEN
## 실행중인 POD 목록
$ kubectl get po -A
## 마스터 노드에서 노드 목록 확인
$ kubectl get node
이 명령어들의 실행 결과는 다음과 같이 표시된다.

부가적인 쿠버네티스 설정 (마스터 노드)
## 현재 계정(일반계정)의 클러스터 관리기능 사용을위한 구성
## 계정 홈디렉토리에서 실행
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
$ source .bashrc
## Kubelet 자동완성 기능 설치
$ sudo apt-get install -y bash-completion
$ source <(kubectl completion bash)
$ echo "source <(kubectl completion bash)" >> ~/.bashrc
답글 남기기