Kubernetes

KVM(Hypervisor) 기반으로 Kubernetes 클러스터 구축하기 1편

[혜안] 2024. 3. 19. 14:12
728x90

클라우드 서비스를 사용하는게 아니라면,

개인이 Kubernetes 클러스터를 구축하여 자체 운영하는 가장 일반적인 케이스는 개인 PC나 노트북, 또는 저와 같이 라즈베리파이 몇대를 묶어서 구성하는게 가장 일반적입니다.

그런데 이번에 구축할 Kubernetes 클러스터는 이보다 좀 더 다른 형태입니다.

하이퍼바이저인 KVM 위에 Guest VM을 여러개 올리고, 이 VM 간에 클러스터를 구축하는 모양인데요.

대략 위와같은 모양이 됩니다.

이렇게 했을 때에 몇가지 장단점이 있습니다.

 

장점

1. Host 환경 의존성을 최소화할 수 있다.

2. K8s 노드를 통째로 백업하고 쉽게 복제/확장/재생성 할 수 있다.

3. 다른 네트워크 환경에서도 K8s 노드의 IP를 고정해 놓을 수 있다.

4. 고성능 PC 1대에 여러 개의 VM으로 K8s 환경을 구축할 수 있다.

단점

1. 외부에서 노드의 서비스로 들어가는 경로를 만들어주어야 한다.

2. 구성이 복잡하다.

 

물론 위에서 나열한 장점을 대체할 수 있는 다른 대안도 있고, 단점들을 보완할 수 있는 여러 방안도 있습니다.

다만 위와 같은 구성도 가능하다 정도로 보시면 될 것 같습니다. 

 

좀 더 구체적인 환경은 아래와 같습니다.

살펴볼 부분은 IP 구성입니다.

Host OS의 IP는 192.168.123.0 대역입니다.

그리고 Guest OS는 192.168.100.0 대역으로 Host OS와는 전혀 다른 대역으로 간주할 수 있습니다.

또한 KVM의 NAT 대역을 192.168.100.200/29로 CIDR 제한을 두었습니다.

이건 나중에 Host OS 2번 3번이 추가되면 Guest OS에 할당할 IP 대역을 구분해주기 위함입니다.

결국 이 Host OS에서 할당 받을 수 있는 Guest OS IP 는 192.168.100.201 ~ 206 까지 총 6개라는 겁니다.

Host OS의 성능이 아무리 좋아도 Guest OS를 7개 이상 두기는 어려울 것 같으니까요.

 

위와 같은 설정을 위한 설치 절차는 아래와 같습니다.

설치절차

Host OS의 게이트웨이 기능 활성화

Guest OS들은 어찌되었든 Host OS의 네트워크를 통해 외부로 나갈 수 밖에 없습니다. 외부에서 Guest OS로 찾아들어와야 하는 경우도 마찬가지구요.

Ubuntu의 게이트웨이 기능을 켜주어야만 이게 가능합니다.

vi /etc/sysctl.conf
...
net.ipv4.ip_forward=1
...

sysctl -p

net.ipv4.ip_forward=1 부분이 주석처리되어 있으니 주석을 해제합니다.

그리고 sysctl -p 명령으로 적용시켜 줍니다.

KVM 설치

apt update
apt install qemu-kvm libvirt-daemon-system virtinst libvirt-clients bridge-utils
kvm-ok

설치가 완료되면 아래와 같은 커맨드가 동작하는지 확인합니다.

virsh version
virsh list

KVM NAT 네트워크 설정

위에서 설명한 바와 같이 IP대역을 192.168.100.200/29로 설정합니다.

virsh net-edit default
....
  <bridge name='virbr0' stp='on' delay='0'/>
  <mac address='52:54:00:52:52:52'/>
  <ip address='192.168.100.200' netmask='255.255.255.248'>
    <dhcp>
      <range start='192.168.100.201' end='192.168.100.206'/>
      <host mac='52:54:00:52:52:01' ip='192.168.100.201'/>
      <host mac='52:54:00:52:52:02' ip='192.168.100.202'/>
      <host mac='52:54:00:52:52:03' ip='192.168.100.203'/>
    </dhcp>
  </ip>
....

CIDR /29 에 대응하는 서브넷 마스크는 255.255.255.248 입니다.

그리고 dhcp 아래에 각 mac 주소별로 ip를 고정하여 할당해 줄 수 있습니다.

KVM에서는 위와같이 가상으로 mac 주소를 할당해줄 수 있으며, 앞에 24bit는 52:54:00으로 사용하고 있습니다.

저 mac 주소는 아래에서 각 VM들을 생성할때에 파라미터로 지정해줍니다.

저장 후 설정을 적용합니다.

systemctl restart libvirtd
virsh net-destroy default
netplan apply
virsh net-start default

 

KVM의 기본 구성은 이게 끝입니다.

이제 VM을 생성하고 K8s 클러스터링을 구성하면 됩니다.

 

우선 Ubuntu iso이미지 기반으로 VM 이미지를 생성합니다.

iso 이미지는 Ubuntu 공식 사이트에서 내려받으시면 됩니다. 저는 Desktop 용이 아니고 Server 버전을 받았습니다.

https://ubuntu.com/download/server

 

Get Ubuntu Server | Download | Ubuntu

Get Ubuntu Server one of three ways; by using Multipass on your desktop, using MAAS to provision machines in your data centre or installing it directly on a server.

ubuntu.com

virt-install \
  --name k8s-base \
  --ram 4096 \
  --vcpus 2 \
  --disk path=/kvm-images/ubuntu-base.qcow2,size=20 \
  --graphics none \
  --console pty,target_type=serial \
  --extra-args 'console=ttyS0,115200n8 serial' \
  --autoconsole text \
  --location '/vm-iso/ubuntu-22.04.3-live-server-amd64.iso'

CLI 콘솔 설치 기반이고 안내에 따라 설치하면 되기 때문에 크게 어렵지는 않습니다.

우분투 설치가 완료되면 위에 지정한 이미지 경로에 qcow2 확장자로 이미지 파일이 생성되어 있습니다.

ll /kvm-images/
total 40922928
drwxr-xr-x 4 root         root        4096 Mar 14 01:56 ./
drwxr-xr-x 6 root         root        4096 Mar  4 07:08 ../
-rw------- 1 root         root 21478375424 Mar 13 04:14 k8s-base.qcow2

Ubuntu 설치가 완료되면 VM 콘솔 안에 들어가있는 상태이므로, 원하는 셋팅을 추가로 해주면 됩니다.

 

K8s-base 이미지 구성

이름(k8s-base)에서 알 수 있다시피, k8s 노드 구성을 위한 베이스 이미지입니다.

containerd, kubectl, kubelet, kubeadm까지만 설치를 진행합니다.

나중에 추가 노드 구성이 필요할때에 이 베이스 이미지 기반으로 Control Plain Node 또는 Worker Node로 만들면  아주 빠르게 노드를 생성 또는 확장 할 수 있습니다.

root로 계정 변경

sudo su

Swap off 설정

swapoff --all
sed -i '/ swap / s/^/#/' /etc/fstab

Network 설정

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# sysctl params required by setup, params persist across reboots
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

# Apply sysctl params without reboot
sudo sysctl --system

Containerd 설치

apt-get update
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg

echo \
 "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
 $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
 tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y containerd.io

containerd config default | tee /etc/containerd/config.toml
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

systemctl restart containerd

kubeadm, kubelet, kubectl 설치

apt-get update
apt-get install -y apt-transport-https ca-certificates curl gpg

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/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.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

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

여기까지가 베이스이미지 구성입니다.

로그아웃하고 Host OS로 빠져나옵니다.

exit
exit
Ctrl + ]

 

이제 베이스 이미지는 그대로 보존한 채로 추가로 Control Plane Node와 Worker Node 구성을 하겠습니다.

먼제 이미지 보존 및 복사를 위해 베이스 이미지를 내립니다.

virsh list 로 현재 동작중인 VM을 확인하고, shutdown을 시킵니다.

virsh list
 Id   Name           State
------------------------------
 4    k8s-base     running
 
virsh shutdown k8s-base
 
virsh list
 Id   Name           State
------------------------------

내려가는데에 시간이 걸릴 수 있으므로 virsh list에서 목록이 사라진걸 확인해야 합니다.

Control Plane Node, Worker Node 용으로 이미지파일을 복사합니다.

cp /kvm-images/k8s-base.qcow2 /kvm-images/k8s-master.qcow2
cp /kvm-images/k8s-base.qcow2 /kvm-images/k8s-worker01.qcow2
cp /kvm-images/k8s-base.qcow2 /kvm-images/k8s-worker02.qcow2

디스크 볼륨을 20GB로 생성했기 때문에 복사하는 동안 잠시 대기하고 생성을 확인합니다.

ls -al /kvm-images/
total 40922928
drwxr-xr-x 4 root         root        4096 Mar 14 01:56 ./
drwxr-xr-x 6 root         root        4096 Mar  4 07:08 ../
-rw------- 1 root         root 21478375424 Mar 13 04:14 k8s-base.qcow2
-rw------- 1 root         root 21478375424 Mar 13 04:27 k8s-master.qcow2
-rw------- 1 root         root 21478375424 Mar 13 04:27 k8s-worker01.qcow2
-rw------- 1 root         root 21478375424 Mar 13 04:27 k8s-worker02.qcow2

 

 

Control Plane Node 구성

Control Plane Node를 기동합니다.

virt-install
--name k8s-master \
--memory 4096 \
--vcpus 2 \
--disk /kvm-images/k8s-master.qcow2 \
--import --graphics none \
--console pty,target_type=serial \
--osinfo ubuntu22.04 \
--mac=52:54:00:cf:62:01

기동 옵션 마지막에 mac 주소를 위에 virsh net-edit로 설정한 mac 주소로 지정해줍니다. 

기동이 완료되면 마찬가지로 VM 콘솔에 들어가 있는 상태이고, Control Plane Node 구성을 위한 작업을 바로 시작하면 됩니다.

우선 해야할 것은, Node 이름 변경입니다.

베이스 이미지에서 복제한 VM이므로 베이스 이미지의 이름을 변경해줄 필요가 있습니다.

sudo su
hostname k8s-master

vi /etc/hostname
k8s-master

vi /etc/hosts
...
127.0.0.1 k8s-master
...

그리고 적용을 위해 로그아웃 후 재 로그인을 해줍니다.

 

exit
exit
Ctrl + ]
virsh console k8s-master

Containerd등 기본 구성요소는 모두 설치된 이미지이므로 간편하게 바로 Control Plane Node 구성이 가능합니다.

sudo kubeadm init --apiserver-advertise-address=192.168.100.201 --pod-network-cidr=10.244.0.0/16

구성 완료 후 출력되는 node join 커맨드는 Worker Node 구성에 사용해야 하므로 따로 복사해둡니다.

추가로 Network plugon을 설치합니다.

kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

설치 상태를 확인합니다.

kubectl get node
NAME           STATUS   ROLES           AGE    VERSION
k8s-master     Ready    control-plane   5m   v1.29.2

 

Worker Node 구성

Worker01 Node를 기동합니다.

virt-install
--name k8s-worker01 \
--memory 4096 \
--vcpus 2 \
--disk /kvm-images/k8s-worker01.qcow2 \
--import --graphics none \
--console pty,target_type=serial \
--osinfo ubuntu22.04 \
--mac=52:54:00:cf:62:02

마찬가지로 Node 이름을 변경해줍니다.

sudo su
hostname k8s-worker01

vi /etc/hostname
k8s-worker01

vi /etc/hosts
...
127.0.0.1 k8s-worker01
...

그리고 재 로그인을 합니다.

exit
exit
Ctrl + ]
virsh console k8s-worker01

이번에 Woker Node 이므로 위에서 복사한 node join 커맨드를 바로 실행합니다.

sudo kubeadm join 192.168.100.201 --token xxxxxxxxxxxxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxx

마스터 노드에서 node join 상태를 확인할 수 있습니다.

kubectl get node
NAME           STATUS   ROLES           AGE    VERSION
k8s-master     Ready    control-plane   10m   v1.29.2
k8s-worker01   Ready    <none>          5m    v1.29.2

Woker02 Node 도 동일한 과정으로 반복해서 생성이 가능합니다.

베이스 이미지에서 바로 노드 확장을 할 수있다는 점이 KVM을 사용하는 장점 중 하나입니다.

포스팅이 길어져 이번 내용은 여기까지 작성하겠습니다.

다음 포스팅에서는 Host 한 대를 두 대 혹은 세 대로 확장했을 때에 Kubenetes Node 들이 어떻게 클러스터링을 하고 서로 통신을 할 수 있는지 구성하겠습니다.

대략 아래와 같은 그림이 됩니다.

728x90