Profile picture

[k8s/vagrant] 쿠버네티스 클러스터 Virtual Box로 구축하기

JaehyoJJAng2023년 02월 03일

◾️ k8s (v.1.27.2) 설치하기

나는 쿠버네티스를 시작하기도 전에 많은 좌절을 느꼈었다. 도커 공부를 막 끝내고 쿠버네티스에 입문하려는 찰나 k8s 인프라 구축이라는 벽에 막혀 공부를 계속 미루고는 했었다. 하지만 이제 그 걱정은 덜어라
IaC 도구인 Vagrant를 사용하여 쿠버네티스를 쉽고 빠르게 설치해 볼 수 있다. image


▪️ 설치 과정

1. Virtualbox 설치 link


2. Vagrant 설치 link


3. Vagrant 스크립트 실행

# Vagrant 폴더 생성
$ mkdir -p /inflearn/k8s
$ cd /inflearn/k8s

# Vagrantfile 다운로드
$ curl -O  https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/vagrant-2.3.4/Vagrantfile

# Rocky linux Repo 세팅
$ curl -O https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/vagrant-2.3.4/rockylinux-repo.json
$ vagrant box add rockylinux-repo.json

# Vagrant 플러그인 설치
$ vagrant plugin install vagrant-disksize
$ vagrant plugin install vagrant-vbguest

# Vagrant 실행
$ vagrant up

4. 원격 접속

$ ssh root@192.168.56.30 # (root/vagrant)

5. 마스터노드 콘솔 접속 후 token 복사

[root@k8s-master ~]# cat ~/join.sh
kubeadm join 192.168.56.30:6443 --token bver73.wda72kx4afiuhspo --discovery-token-ca-cert-hash sha256:7205b3fd6030e47b74aa11451221ff3c77daa0305aad0bc4a2d3196e69eb42b7
# 위 토큰은 VM 생성 때 마다 달라지니 직접 마스터 노드 Console 접속 후 복사 해와야 함.

6. worker node 접속 후 토큰 붙여넣기

[root@k8s-node1 ~]# kubeadm join 192.168.56.30:6443 --token \
bver73.wda72kx4afiuhspo --discovery-token-ca-cert-hash sha256:7205b3fd6030e47b74aa11451221ff3c77daa0305aad0bc4a2d3196e69eb42b7

7. Pod 확인

$ kubectl get pods -A -o wide

8. 대쉬보드 접속하기: http://192.168.56.30:30000/#/login


9. Network 확인

$ ip addr | grep 'inet'

10. 자원(cpu,memory) 확인

$ lscpu
$ free -h

위 과정만 따라하면 정말 쉽게 쿠버네티스를 설치할 수 있지만 뭔가 찝찝하지 않은가. 쿠버네티스가 어떻게 구성되는지 조금은 알고 쿠버네티스 개념들을 공부하는게 이해가 더 잘 될 것이다. 쿠버네티스 구성에 대한 부분들은 쿠버네티스를 설치할 때 가장 배우기 좋은 내용들이다.


▪️ 구성도

image


▪️ Vagrantfile

N = 1 # Max number of worker nodes
Ver = '1.27.1-0.x86_64' # Kubernetes Version

Vagrant.configure("2") do |config|  
  # VM 이미지
  config.vm.box = "rockylinux/8"

  # Disk 확장설정 추가
  config.disksize.size = "50GB"

  #https://cafe.naver.com/kubeops/26
  config.vbguest.installer_options = { allow_kernel_upgrade: true }
  config.vbguest.auto_update = false
  config.vm.provision :shell, privileged: true, inline: $install_default

  #==================
  #   Master Node
  #==================
  config.vm.define "master-node" do |master|
    master.vm.hostname = "k8s-master"
    master.vm.network "private_network", ip: "192.168.56.30"
    master.vm.network "forwarded_port", guest: 22,  host: 20010, auto_correct: true, id: "mac-ssh", host_ip: "192.168.219.171"

    master.vm.provider :virtualbox do |vb|
      vb.memory = 6144
      vb.cpus = 4
      vb.customize ["modifyvm", :id, "--firmware", "efi"]
    end
    master.vm.provision :shell, privileged: true, inline: $install_master
  end

  #==================
  #   Worker Nodes
  #==================
  (1.."#{N}".to_i).each do |i|
    config.vm.define "k8s-node#{i}" do |node|
      node.vm.hostname = "k8s-node#{i}"
      node.vm.network "private_network", ip: "192.168.56.10#{i}"
      node.vm.network "forwarded_port", guest: 22,  host: "2010#{i}", auto_correct: true, id: "mac-ssh", host_ip: "192.168.219.171"
      node.vm.provider :virtualbox do |vb|
        vb.memory = 4096
        vb.cpus   = 1
  	    vb.customize ["modifyvm", :id, "--firmware", "efi"]
      end
    end
  end
end

$install_default = <<-SHELL

echo '======== [4] Rocky Linux 기본 설정 ========'
echo '======== [4] 패키지 업데이트 ========'
yum -y update

echo '======== [4] 타임존 설정 ========'
timedatectl set-timezone Asia/Seoul

echo '======== [4] 호스트 설정 (/etc/hosts) ========'
echo "192.168.56.30 k8s-master" >> /etc/hosts
for i in $(seq 1 "#{N}"); do
  echo "192.168.56.10$i k8s-node$i" >> /etc/hosts; done

echo '======== [4] Disk 확장 / Bug: soft lockup 설정 추가========'
# https://cafe.naver.com/kubeops/25
yum install -y cloud-utils-growpart
growpart /dev/sda 4
xfs_growfs /dev/sda4
echo 0 > /proc/sys/kernel/hung_task_timeout_secs
echo "kernel.watchdog_thresh = 20" >> /etc/sysctl.conf

echo '======== [4] [WARNING FileExisting-tc]: tc not found in system path 로그 관련 업데이트 ========'
yum install -y yum-utils iproute-tc

echo '======== [5] kubeadm 설치 전 사전작업 ========'
echo '======== [5] 방화벽 해제 ========'
echo '======== [5] Swap 비활성화 ========'
swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab


echo '======== [6] 컨테이너 런타임 설치 ========'
echo '======== [6-1] 컨테이너 런타임 설치 전 사전작업 ========'
echo '======== [6-1] iptable 세팅 ========'
cat <<EOF |tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

cat <<EOF |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

sysctl --system

echo '======== [6-2] 컨테이너 런타임 (containerd 설치) ========'
echo '======== [6-2-1] containerd 패키지 설치 (option2) ========'
echo '======== [6-2-1-1] docker engine 설치 ========'
echo '======== [6-2-1-1] repo 설정 ========'
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

echo '======== [6-2-1-1] containerd 설치 ========'
yum install -y containerd.io-1.6.21-3.1.el8
systemctl daemon-reload
systemctl enable --now containerd

echo '======== [6-3] 컨테이너 런타임 : cri 활성화 ========'
sed -i 's/^disabled_plugins/#disabled_plugins/' /etc/containerd/config.toml
systemctl restart containerd

echo '======== [7] kubeadm 설치 ========'
echo '======== [7] repo 설정 ========'
cat <<EOF |tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

echo '======== [7] SELinux 설정 ========'
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

echo '======== [7] kubelet, kubeadm, kubectl 패키지 설치 ========'
yum install -y "kubelet-#{Ver}" "kubeadm-#{Ver}" "kubectl-#{Ver}" --disableexcludes=kubernetes
systemctl enable --now kubelet

SHELL

$install_master = <<-SHELL

echo '======== [8] kubeadm으로 클러스터 생성  ========'
echo '======== [8-1] 클러스터 초기화 (Pod Network 세팅) ========'
kubeadm init --pod-network-cidr=20.96.0.0/12 --apiserver-advertise-address 192.168.56.30
kubeadm token create --print-join-command > ~/join.sh

echo '======== [8-2] kubectl 사용 설정 ========'
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

echo '======== [8-3] Pod Network 설치 (calico) ========'
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/calico-3.25.1/calico.yaml
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/calico-3.25.1/calico-custom.yaml

# worker node 없이 Master Node만 사용하는 경우 아래 코드 실행 // 워커 노드 있는 경우 해당 코드 주석처리 요망
echo '======== [8-4] Master에 Pod를 생성 할수 있도록 설정 ========'
kubectl taint nodes k8s-master node-role.kubernetes.io/control-plane-

echo '======== [9] 쿠버네티스 편의기능 설치 ========'
echo '======== [9-1] kubectl 자동완성 기능 ========'
echo "source <(kubectl completion bash)" >> ~/.bashrc
echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -o default -F __start_kubectl k' >>~/.bashrc

echo '======== [9-2] Dashboard 설치 ========'
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/dashboard-2.7.0/dashboard.yaml

echo '======== [9-3] Metrics Server 설치 ========'
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/metrics-server-0.6.3/metrics-server.yaml
SHELL
  • $install_default: Node별 설치 스크립트
  • $install_master: Master Node 전용 설치 스크립트

• VM 생성 부분

image
우측 스크립트 위에서 부터 보면 , OS를 [rocky linux 8] 버전으로 설치하라는 내용이고, [master-node]는 Virtualbox로 생성된 VM에 이름을 붙여주는 부분이다. 그 밑에 hostname 지정하는 부분이 있고, [k8s-master]라고 넣으면 나중에 원격 접속 시 서버에 접속했을 때 "vagrant@k8s-master" 이런식으로 호스트이름이 나오게 된다.


• network

image
밑에 [private_network]는 virtualbox에 Host-Only Network라고 해서, 내 PC에서만 사용할 수 있는 네트워크 망을 생성해준다. 스크립트에 IP를 주면 해당 서버에 지정한 IP가 할당된다. 저 옵션을 줘야 현재 내 PC에서 원격으로 접속이 가능하고, 브라우저로 대쉬보드에도 접속이 가능해진다.


그런데 스크립트에 넣지 않아도 Vagrant가 기본적으로 생성해주는 네트워크가 있다.
바로 **NAT(Network Address Translation)**라는 것인데 IP가 알아서 할당된다. 이것의 역할은 현재 생성된 가상머신을 외부 인터넷이랑 연결해줄 수 있도록 해준다.

image
실제 내 PC에 할당된 Network는 공유기에서 할당 받은 상태이다. 위 사진의 PC는 192.168.219.0/24 서브넷 대역에서 192.168.219.100을 할당받은 상태이다. 참고로 위에서 할당한 VM 내부 서브넷 대역이 현재 호스트 서브넷 대역과 겹치면 안된다.


"forwared_port" 옵션의 경우 아래 내용을 참고하자.

  • master.vm.network "forwarded_port": master 가상 머신 포트 포워딩 설정
    • guest: 22: 가상 머신 내부에 열려 있는 포트 번호
    • host: 20010: 호스트 PC에서 열릴 포트 번호. 20010은 호스트 머신에서 master 노드에 SSH 접속하기 위해 사용할 수 있는 포트번호
  • auto_correct: true: 포트 충돌 발생할 경우 Vagrant가 포트 충돌을 자동으로 해결하도록 하는 옵션. 20010 사용 중인 경우 자동으로 다른 포트 번호를 선택하도록 Vagrant에 지시함
  • id: "mac-ssh": 포트 포워딩 구성의 고유 식별자. 이 ID를 사용하여 포트 포워딩을 수정하거나 삭제가능
  • host_ip: "192.168.219.171": 호스트의 IP 주소 지정. 20010번 포트는 이 IP 주소와 연결되며, 호스트 머신에서 해당 IP주소와 20010번 포트로 SSH 연결을 시도함.
    • 나의 경우 윈도우 데스크탑에서 해당 스크립트를 실행 후, 동일한 로컬 환경의 맥북으로 접속할 것이기 때문에 forwarded_port 옵션이 필요하였음.

• resource (자원)

image
위 사진처럼 가상머신에 4G의 메모리와 4Core의 CPU를 할당하겠다고 명시되어있는데, 현재 호스트 PC의 스펙이 4Core, 16G Memory이라고 가정하면 호스트 PC는 어떻게 될까?

CPU는 필요로 하는 순간에 서로 나눠 쓰는 자원이다. 현재 위에서 할당된 4Core CPU는 내 PC CPU가 필요할 때 4 Core를 전부 다 사용할 수도 있고. VM에서 필요할 때도 최대 4Core를 끌어다 쓸 수 있도록 설정한건데, 만약 호스트와 가상머신 둘 다 CPU가 필요한 상황이라면 이 4Core의 자원을 나눠쓰게 된다. 하지만 그 만큼 처리속도는 느려지지만 문제가 발생하는 상황은 오지 않는다.

참고로 쿠버네티스 설치 시 호스트의 CPU 스펙은 2Core 이상이어야 한다. 쿠버네티스 설치 문서에서 이렇게 권고하고 있다.


◾️ 설정파일 분리하기

Vagrantfile 파일에서 작성된 $install_default, $install_master 의 스크립트를 따로 파일로 빼서 관리하는 것이 어떨까?
각 스크립트의 내용을 config.sh, master_node.sh 파일로 분리하여 관리해보자.


▪️ Vagrantfile

Vagrant.configure("2") do |config|
  N = 1 # Max number nof workder nodes
  Ver = '1.27.1-0.x86_64'

  # VM 이미지 설정
  config.vm.box = "rockylinux/8"

  # Disk 확장설정 추가
  config.disksize.size = "50GB"

  #https://cafe.naver.com/kubeops/26
  config.vbguest.installer_options = { allow_kernel_upgrade: true }
  config.vbguest.auto_update = false

  #==================
  #   Master Node
  #==================
  config.vm.define "master-node" do |master|
    master.vm.hostname = "k8s-master"
    master.vm.network "private_network", ip: "192.168.56.30"
    master.vm.network "forwarded_port", guest: 22,  host: 20010, auto_correct: true, id: "mac-ssh", host_ip: "192.168.219.171"
    master.vm.provider :virtualbox do |vb|
      vb.memory = 6144
      vb.cpus = 4
      vb.customize ["modifyvm", :id, "--firmware", "efi"]
    end
    master.vm.provision "shell", path: "config.sh", args: [N, Ver] # 추가된 부분
    master.vm.provision "shell", path: "master_node.sh"            # 추가된 부분
  end

  #==================
  #   Worker Nodes
  #==================
  (1..N).each do |i|
    config.vm.define "k8s-node#{i}" do |node|
      node.vm.hostname = "k8s-node#{i}"
      node.vm.network "private_network", ip: "192.168.56.10#{i}"
      node.vm.network "forwarded_port", guest: 22,  host: "2010#{i}", auto_correct: true, id: "mac-ssh", host_ip: "192.168.219.171"

      node.vm.provider :virtualbox do |vb|
        vb.memory = 4096
        vb.cpus   = 1
        vb.customize ["modifyvm", :id, "--firmware", "efi"]
      end
      node.vm.provision "shell", path: "config.sh", args: [N, Ver] # 추가된 부분
    end
  end
end

▪️ config.sh

echo '======== [4] Rocky Linux 기본 설정 ========'
echo '======== [4] 패키지 업데이트 ========'
yum -y update
yum install -y iproute-tc

echo '======== [4] 타임존 설정 ========'
timedatectl set-timezone Asia/Seoul

echo '======== [4] 호스트 설정 (/etc/hosts) ========'
echo "192.168.56.30 k8s-master" >> /etc/hosts
for i in $(seq 1 $1); do
    echo "192.168.56.10$i k8s-node$i" >> /etc/hosts; done

echo '======== [4] Disk 확장 / Bug: soft lockup 설정 추가========'
# https://cafe.naver.com/kubeops/25
yum install -y cloud-utils-growpart
growpart /dev/sda 4
xfs_growfs /dev/sda4
echo 0 > /proc/sys/kernel/hung_task_timeout_secs
echo "kernel.watchdog_thresh = 20" >> /etc/sysctl.conf

echo '======== [4] [WARNING FileExisting-tc]: tc not found in system path 로그 관련 업데이트 ========'
yum install -y yum-utils iproute-tc

echo '======== [5] kubeadm 설치 전 사전작업 ========'
echo '======== [5] 방화벽 해제 ========'
echo '======== [5] Swap 비활성화 ========'
swapoff -a && sed -i '/ swap / s/^/#/' /etc/fstab

echo '======== [6] 컨테이너 런타임 설치 ========'
echo '======== [6-1] 컨테이너 런타임 설치 전 사전작업 ========'
echo '======== [6-1] iptable 세팅 ========'
cat <<EOF |tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

modprobe overlay
modprobe br_netfilter

cat <<EOF |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

sysctl --system

echo '======== [6-2] 컨테이너 런타임 (containerd 설치) ========'
echo '======== [6-2-1] containerd 패키지 설치 (option2) ========'
echo '======== [6-2-1-1] docker engine 설치 ========'
echo '======== [6-2-1-1] repo 설정 ========'
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

echo '======== [6-2-1-1] containerd 설치 ========'
yum install -y containerd.io-1.6.21-3.1.el8
systemctl daemon-reload
systemctl enable --now containerd

echo '======== [6-3] 컨테이너 런타임 : cri 활성화 ========'
sed -i 's/^disabled_plugins/#disabled_plugins/' /etc/containerd/config.toml
systemctl restart containerd

echo '======== [7] kubeadm 설치 ========'
echo '======== [7] repo 설정 ========'
cat <<EOF |tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

echo '======== [7] SELinux 설정 ========'
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

echo '======== [7] kubelet, kubeadm, kubectl 패키지 설치 ========'
yum install -y kubelet-$2 kubeadm-$2 kubectl-$2 --disableexcludes=kubernetes
systemctl enable --now kubelet

▪️ master_node.sh

echo '======== [8] kubeadm으로 클러스터 생성  ========'
echo '======== [8-1] 클러스터 초기화 (Pod Network 세팅) ========'
kubeadm init --pod-network-cidr=20.96.0.0/12 --apiserver-advertise-address 192.168.56.30
kubeadm token create --print-join-command > ~/join.sh

echo '======== [8-2] kubectl 사용 설정 ========'
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

echo '======== [8-3] Pod Network 설치 (calico) ========'
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/calico-3.25.1/calico.yaml
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/calico-3.25.1/calico-custom.yaml

# worker node 없이 Master Node만 사용하는 경우 아래 코드 실행 // 워커 노드 있는 경우 해당 코드 주석처리 요망
echo '======== [8-4] Master에 Pod를 생성 할수 있도록 설정 ========'
kubectl taint nodes k8s-master node-role.kubernetes.io/control-plane-

echo '======== [9] 쿠버네티스 편의기능 설치 ========'
echo '======== [9-1] kubectl 자동완성 기능 ========'
echo "source <(kubectl completion bash)" >> ~/.bashrc
echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -o default -F __start_kubectl k' >>~/.bashrc

echo '======== [9-2] Dashboard 설치 ========'
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/dashboard-2.7.0/dashboard.yaml

echo '======== [9-3] Metrics Server 설치 ========'
kubectl create -f https://raw.githubusercontent.com/k8s-1pro/install/main/ground/k8s-1.27/metrics-server-0.6.3/metrics-server.yaml

🔴 트러블슈팅

vagrant up 실행 시, 잘 되다가 아래 에러가 뜨는 경우

/sbin/mount.vboxsf: mounting failed with the error : No such device 

설치가 멈추면 vagrant destroy -f로 가상 머신을 지우지 말고 vagrant up을 하여 계속 이어서 설치하면 됨.


📕 Ref

[인프런] 쿠버네티스 어나더 클래스 (지상편)


Loading script...