MacOS 에서 쿠버네티스 클러스터를 위한 Vagrant 설치.

 

Mac Vagrant Manager
Mac Vagrant Manager

주로 인프라를 다루는 업무를 하다보니 일적으로나 취미적으로나 리눅스환경에서 쿠버네티스를 만지는일이 많다.

특히 멀티노드 클러스터로 구성된 쿠버네티스(K8s)를 필요로 하는 경우가 많은데, Minikube나 Docker Desktop의 Kubernetes로는 동일한 환경으로 가정해 테스트나 실제 작업을 염두에 두고 작업후 적용하는데 어려울 수가 있다.

회사의 클라우드 테스트환경을 아무렇게나 사용하기엔 동료들에게도 살짝 눈치가 보일때도 많아서 집에 있는 맥북과 맥미니 윈도우 데스크탑에 Vagrant로 VirtualBox 호스트를 만들어 쿠버네티스 클러스터를 구성해보려고 한다.

Vagrant Manager : Status Menu
Vagrant Manager : Status Menu

 

Vagrant와 Vagrant Manager에 대해

우선 Vagrant와 Vagrant Manager를 설치 하는 이유를 알고 넘어가자.

  • Vagrant : VirtualBox나 VMware, Hyper-V등의 가상 머신을 프로비저닝를 한꺼번에 Vagrant를 통해서 관리 할 수 있다.
  • Vagrant Manager : Vagrant Manager는 Vagrant로 가상 머신을 관리하는 UI 도구다. UI를 통해 Vagrant 가상머신을 끄고 켜고 관리를 할 수 있다. 결정적으로 Mac을 껏다 켰을때(Reboot) 자동실행된 Vagrant Manager가 가상머신을 자동시작해 줄수 있다. (운영이 편하다!)

Vagrant Manger : [ Launch at Login ] Option
Vagrant Manger : [ Launch at Login ] Option

 

Master Node : Mac Mini에  Vagrant로 가상머신을 설치해보자.

거실 티비 뒤에서 늘 켜져 있는 맥미니를 마스터 노드로 구성하려고 한다.

1. 설치 환경.

  • macOS Monterey 12.6.8 (Intel i7 CPU )
  • iTerm2
  • zsh / ohmyzsh
  • Homebrew

2. Vagrant 와 Vagrant Manager 설치.

맥에서는 brew를 이용해서 쉽게 설치 할수 있다.

> brew install vagrant vagrant-manager

> vagrant plugin install vagrant-disksize vagrant-hostmanager vagrant-qemu vagrant-vbguest

> vagrant plugin list
vagrant-disksize (0.1.3, global)
  - Version Constraint: > 0
vagrant-hostmanager (1.8.10, global)
  - Version Constraint: > 0
vagrant-qemu (0.3.5, global)
  - Version Constraint: > 0
vagrant-vbguest (0.31.0, global)
  - Version Constraint: > 0

 

3. Virtualbox 설치.

> brew install virtualbox

 

4. 가상 머신 Master Node 생성에 필요한 VagrantFile 생성.

브릿지로 사용할 네트워크 인터페이스의 정보를 먼저 확인한다.

## 인터넷 연결에 사용하는 인터페이스 이름을 확인한다.
## 나같은 경우는 'en0' 로 확인 된다.
> networksetup -listallhardwareports

Hardware Port: Ethernet
Device: en0
Ethernet Address: ac:87:b3:2f:92:7c

Hardware Port: Wi-Fi
Device: en1
Ethernet Address: 60:f8:1d:b1:f2:28

Hardware Port: Thunderbolt 1
Device: en2
Ethernet Address: 82:15:05:6f:2a:00

Hardware Port: Thunderbolt 2
Device: en3
Ethernet Address: 82:15:05:6f:2a:01

Hardware Port: Thunderbolt Bridge
Device: bridge0
Ethernet Address: 82:15:05:6f:2a:00

VLAN Configurations
===================

 

$ vim ~/vagrant/VagrantFile 으로 Vagrant 프로비저닝에 필요한 파일을 생성합니다.

  • 마스터 노드를 생성하고 업데이트나 쿠버네티스 설치등의 기본적인 설정을 함께 하도록 스크립트를 포함하고 있다.
  • 2cpu에 4G 메모리
  • 가상머신이 맥미니와 같은 네트워크에 독립적인 호스트로 기능을 할수 있도록 IP를 "192.168.1.200" 으로하고
  • config.vm.network을 "public_network"으로 설정했다.
  • 쿠버네티스 워커노드 Join에 필요한 커맨드는 마스터노드의 ~/vagrant/join.sh 로 저장됩니다. 
더보기
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  # config.vm.box = "base"
  config.vm.box = "ubuntu/jammy64"
  config.vbguest.auto_update = false
  config.hostmanager.enable = false
  config.hostmanager.manage_host = true
  config.hostmanager.include_offline = true
  config.hostmanager.ignore_private_ip = false

  # master
  config.vm.define :master do |config|
    config.vm.provider :virtualbox do |vb|
      vb.name = "master"
      vb.gui = false
      vb.cpus = 2
      vb.memory = 4096
      # vb.customize ["modifyvm", :id, "--cpus", "2"]
      # vb.customize ["modifyvm", :id, "--memory", "4096"]
    end
    config.vm.hostname = "master"
    # config.vm.synced_folder ".", "/vargrant", disabled: true
    config.vm.network "public_network", bridge: 'en0: eth0', ip: "192.168.1.200"
    config.disksize.size = "150GB"
    config.vm.provision "set_net_foward", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
cat <<-'EOF' >/etc/modules-load.d/kubernetes.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

cat <<-'EOF' >/etc/sysctl.d/kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sudo sysctl --system
SHELL

    config.vm.provision "set_containerd", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
sudo DEBIAN_FRONTEND=noninteractive apt-get update -yq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y curl gnupg2 gnupg-agent software-properties-common apt-transport-https ca-certificates

sudo rm -f /etc/apt/trusted.gpg.d/docker.gpg
sudo rm -f /usr/share/keyrings/kubernetes-archive-keyring.gpg

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg
sudo curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg| sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo DEBIAN_FRONTEND=noninteractive add-apt-repository -y "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo DEBIAN_FRONTEND=noninteractive apt-get install -y containerd.io

containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml
sudo sed -i 's#registry.k8s.io/pause:3.6#registry.k8s.io/pause:3.9#g' /etc/containerd/config.toml

sudo systemctl daemon-reload

sudo systemctl restart containerd
sudo systemctl enable containerd
SHELL

    config.vm.provision "set_kubelet", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
cat <<-'EOF' >/etc/default/kubelet
KUBELET_EXTRA_ARGS=--node-ip=192.168.1.200
EOF

sudo DEBIAN_FRONTEND=noninteractive apt-get update -yq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y kubelet kubeadm kubectl
sudo DEBIAN_FRONTEND=noninteractive apt-mark hold kubelet kubeadm kubectl
SHELL

    config.vm.provision "set_kube_join_sh", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
OUTPUT_FILE=/vagrant/join.sh
rm -rf $OUTPUT_FILE
rm -rf /vagrant/.kube
sudo kubeadm config images pull --cri-socket=unix:///run/containerd/containerd.sock
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --control-plane-endpoint=192.168.1.200 --apiserver-advertise-address=192.168.1.200  --cri-socket=unix:///run/containerd/containerd.sock
sudo kubeadm token create --print-join-command > $OUTPUT_FILE
chmod +x $OUTPUT_FILE
SHELL

    config.vm.provision "set_kubadmin", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
cp -R $HOME/.kube /vagrant/.kube
cp -R $HOME/.kube /home/vagrant/.kube
sudo chown -R vagrant:vagrant /home/vagrant/.kube
# kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/tigera-operator.yaml
# curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/custom-resources.yaml -O
# kubectl create -f custom-resources.yaml
kubectl completion bash >/etc/bash_completion.d/kubectl
echo 'alias k=kubectl' >>/home/vagrant/.bashrc
SHELL

    config.vm.provision "set_harbor", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
git clone https://github.com/litmudoc/vbox_harbor_installer.git $HOME/vbox_harbor_installer
chmod +x $HOME/vbox_harbor_installer/harbor.sh
IPorFQDN=192.168.1.200 $HOME/vbox_harbor_installer/harbor.sh
sudo chmod 666 /home/vagrant/harbor/common/config/*/env
SHELL

    config.vm.provision "restart_harbor", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
cd /home/vagrant/harbor
docker compose stop
docker compose start
SHELL

    config.vm.provision "set_upgrade_reboot", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
sudo DEBIAN_FRONTEND=noninteractive apt-get -y full-upgrade
[ -f /var/run/reboot-required ] && sudo reboot -f
SHELL
    end
end

 

5. VagrantFile 구성 파일로 Master Node를 생성 하자.

Vagrant Manager에서 'Provisioning' 을 클릭해 생성 하거나, 쉘에서 커맨드를 통해서 로그를 확인 하면서 생성 할 수 있습니다.

* 쉘에서 Log를 보면서 생성하는것이 좋습니다. (마스터노드 프로비저닝에는 10분 정도가 소요 됩니다.)

## 설정파일이 있는 위치로 이동후 진행 하면 됩니다.
> cd ~/vagrant
> vagrant up master

Bringing machine 'master' up with 'virtualbox' provider...
==> master: Importing base box 'ubuntu/jammy64'...
==> master: Matching MAC address for NAT networking...
==> master: Checking if box 'ubuntu/jammy64' version '20230302.0.0' is up to date...
==> master: Setting the name of the VM: master
==> master: Clearing any previously set network interfaces...
==> master: Preparing network interfaces based on configuration...
    master: Adapter 1: nat
    master: Adapter 2: bridged
==> master: Forwarding ports...
    master: 22 (guest) => 2222 (host) (adapter 1)
==> master: Running 'pre-boot' VM customizations...
==> master: Resized disk: old 40960 MB, req 153600 MB, new 153600 MB
==> master: You may need to resize the filesystem from within the guest.
==> master: Booting VM...
==> master: Waiting for machine to boot. This may take a few minutes...
    master: SSH address: 127.0.0.1:2222
    master: SSH username: vagrant
    master: SSH auth method: private key
...
    master: done
    master: NEEDRESTART-VER: 3.5
    master: NEEDRESTART-KCUR: 5.15.0-67-generic
    master: NEEDRESTART-KEXP: 5.15.0-88-generic
    master: NEEDRESTART-KSTA: 3
    master: NEEDRESTART-SVC: containerd.service
    master: NEEDRESTART-SVC: cron.service
    master: NEEDRESTART-SVC: dbus.service
    master: NEEDRESTART-SVC: docker.service
    master: NEEDRESTART-SVC: getty@tty1.service
    master: NEEDRESTART-SVC: kubelet.service
    master: NEEDRESTART-SVC: multipathd.service
    master: NEEDRESTART-SVC: networkd-dispatcher.service
    master: NEEDRESTART-SVC: packagekit.service
    master: NEEDRESTART-SVC: polkit.service
    master: NEEDRESTART-SVC: serial-getty@ttyS0.service
    master: NEEDRESTART-SVC: systemd-logind.service
    master: NEEDRESTART-SVC: systemd-manager
    master: NEEDRESTART-SVC: unattended-upgrades.service
    master: NEEDRESTART-SVC: user@1000.service
    master: Rebooting.
>

 

또는 Vagrant Manager에서 Provisioning 합니다.

Vagrant Manager에서 생성
Vagrant Manager에서 생성

 

VirtualBox Desktop을 실행해서 생성된 Master Node를 확인할 수 있습니다.

VirtualBox : Master Node
VirtualBox : Master Node

  • 마스터 노드를 정해준 스펙대로 VirtualBox로 프로비저닝하고
  • 자동적으로 쿠버네티스 마스터 노드 구성에 필요한 내용까지 완료 하도록 했습니다.
    • VM에 Harbor Image Registry를 자동설치 하도록 했습니다.
    • CNI는 Calico, Flannel등을 노드 구성이 완료 된후 진행하도록 주석 처리 했다. (K8s 내용으로 포스팅을 하나 해야 겠다.)

 

Worker Node : Mac Book에  Vagrant로 가상머신을 설치해보자.

종종 들고 다니기도 하고 집에서 작업용으로 사용하는 맥북을 Worker 노드로 구성하자. (메인 워커노드는 Window 데스크탑에 구성할 예정)

1. 설치 환경.

  • macOS Big Sur 11.7.10 (Intel i7 CPU)
  • iTerm2
  • zsh / ohmyzsh
  • Homebrew

2. Vagrant 와 Vagrant Manager 설치.

맥에서는 brew를 이용해서 설치 할수 있다.

> brew install vagrant vagrant-manager

> vagrant plugin install vagrant-disksize vagrant-hostmanager vagrant-qemu vagrant-vbguest

> vagrant plugin list
vagrant-disksize (0.1.3, global)
  - Version Constraint: > 0
vagrant-hostmanager (1.8.10, global)
  - Version Constraint: > 0
vagrant-qemu (0.3.5, global)
  - Version Constraint: > 0
vagrant-vbguest (0.31.0, global)
  - Version Constraint: > 0

 

3. Virtualbox 설치.

> brew install virtualbox

 

4. 가상 머신 Worker Node 생성에 필요한 VagrantFile 생성.

브릿지로 사용할 네트워크 인터페이스의 정보를 먼저 확인한다.

## 인터넷 연결에 사용하는 인터페이스 이름을 확인한다.
## 나같은 경우는 'en0' 로 확인 된다.
> networksetup -listallhardwareports

Hardware Port: Wi-Fi
Device: en0
Ethernet Address: b8:e8:52:3c:06:b6

Hardware Port: Bluetooth PAN
Device: en3
Ethernet Address: b8:e8:52:3c:06:b7

Hardware Port: Thunderbolt 1
Device: en1
Ethernet Address: 82:0f:01:11:f3:00

Hardware Port: Thunderbolt 2
Device: en2
Ethernet Address: 82:0f:01:11:f3:01

Hardware Port: Thunderbolt Bridge
Device: bridge0
Ethernet Address: 82:0f:01:11:f3:00

VLAN Configurations
===================

 

$ vim ~/vagrant/VagrantFile 으로 Vagrant 프로비저닝에 필요한 파일을 생성합니다.

  • 마스터 노드를 생성하고 업데이트나 쿠버네티스 설치등의 기본적인 설정을 함께 하도록 스크립트를 포함하고 있다.
  • 2cpu에 4G 메모리
  • 가상머신이 호스트와 같은 네트워크에서 독립적인 호스트로 기능을 할수 있도록 IP를 "192.168.1.202" 으로하고
  • config.vm.network을 "public_network"으로 설정했다.
더보기
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  # config.vm.box = "base"
  config.vm.box = "ubuntu/jammy64"
  config.vbguest.auto_update = false
  config.hostmanager.enable = false
  config.hostmanager.manage_host = true
  config.hostmanager.include_offline = true
  config.hostmanager.ignore_private_ip = false

  # worker02
  config.vm.define :worker02 do |config|
    config.vm.provider :virtualbox do |vb|
      vb.name = "worker02"
      vb.gui = false
      vb.cpus = 2
      vb.memory = 4096
    end
    config.vm.hostname = "worker02"
    # config.vm.synced_folder ".", "/vargrant", disabled: true
    config.vm.network "public_network", bridge: 'en0: Wi-Fi (AirPort)', ip: "192.168.1.202"
    config.disksize.size = "40GB"
    config.vm.provision "0", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
cat <<-'EOF' >/etc/modules-load.d/kubernetes.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

cat <<-'EOF' >/etc/sysctl.d/kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sudo sysctl --system

sudo DEBIAN_FRONTEND=noninteractive apt-get update -yq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates

sudo rm -f /etc/apt/trusted.gpg.d/docker.gpg
sudo rm -f /usr/share/keyrings/kubernetes-archive-keyring.gpg

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg
sudo curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg| sudo gpg --dearmor -o /usr/share/keyrings/kubernetes-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo DEBIAN_FRONTEND=noninteractive add-apt-repository -y "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo DEBIAN_FRONTEND=noninteractive apt-get update -yq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y containerd.io

containerd config default | sudo tee /etc/containerd/config.toml >/dev/null 2>&1
sudo sed -i 's/SystemdCgroup \= false/SystemdCgroup \= true/g' /etc/containerd/config.toml
sudo sed -i 's#registry.k8s.io/pause:3.6#registry.k8s.io/pause:3.9#g' /etc/containerd/config.toml

sudo systemctl daemon-reload

sudo systemctl restart containerd
sudo systemctl enable containerd

cat <<-'EOF' >/etc/default/kubelet
KUBELET_EXTRA_ARGS=--node-ip=192.168.1.202
EOF

sudo DEBIAN_FRONTEND=noninteractive apt-get update -yq
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y kubelet kubeadm kubectl
sudo DEBIAN_FRONTEND=noninteractive apt-mark hold kubelet kubeadm kubectl
SHELL

    config.vm.provision "set_upgrade_reboot", type: "shell", preserve_order: true, privileged: true, inline: <<-SHELL
sudo DEBIAN_FRONTEND=noninteractive apt-get -y full-upgrade
[ -f /var/run/reboot-required ] && sudo reboot -f
SHELL
  end
end

 

5. VagrantFile 설정파일에서 Worker Node를 생성 하자.

Vagrant Manager에서 'Provisioning' 을 클릭해 생성 하거나, 쉘에서 커맨드를 통해서 로그를 확인 하면서 생성 할 수 있습니다.

* 쉘에서 Log를 보면서 생성하는것이 좋습니다. (워커노드 프로비저닝에는 10분 정도가 걸립니다.)

## 설정파일이 있는 위치로 이동후 진행 하면 됩니다.
> cd ~/vagrant
> vagrant up worker02

Bringing machine 'worker02' up with 'virtualbox' provider...
==> worker02: Importing base box 'ubuntu/jammy64'...
==> worker02: Matching MAC address for NAT networking...
==> worker02: Checking if box 'ubuntu/jammy64' version '20230302.0.0' is up to date...
==> worker02: There was a problem while downloading the metadata for your box
==> worker02: to check for updates. This is not an error, since it is usually due
==> worker02: to temporary network problems. This is just a warning. The problem
==> worker02: encountered was:
==> worker02:
==> worker02: dyld: Symbol not found: _iconv
==> worker02:   Referenced from: /usr/lib/libarchive.2.dylib
==> worker02:   Expected in: /opt/vagrant/embedded/lib/libiconv.2.dylib
==> worker02:  in /usr/lib/libarchive.2.dylib
==> worker02:
==> worker02:
==> worker02: If you want to check for box updates, verify your network connection
==> worker02: is valid and try again.
==> worker02: Setting the name of the VM: worker02
==> worker02: Fixed port collision for 22 => 2222. Now on port 2200.
==> worker02: Clearing any previously set network interfaces...
==> worker02: Preparing network interfaces based on configuration...
    worker02: Adapter 1: nat
    worker02: Adapter 2: bridged
==> worker02: Forwarding ports...
    worker02: 22 (guest) => 2200 (host) (adapter 1)
==> worker02: Running 'pre-boot' VM customizations...
==> worker02: Booting VM...
==> worker02: Waiting for machine to boot. This may take a few minutes...
    worker02: SSH address: 127.0.0.1:2200
    worker02: SSH username: vagrant
    worker02: SSH auth method: private key
...
    worker02: NEEDRESTART-SVC: systemd-logind.service
    worker02: NEEDRESTART-SVC: systemd-manager
    worker02: NEEDRESTART-SVC: unattended-upgrades.service
    worker02: NEEDRESTART-SVC: user@1000.service
    worker02: Rebooting.
>

 

또는 Vagrant Manager에서 Provisioning 합니다.

Vagrant Manager에서 생성
Vagrant Manager에서 생성

 

6. Worker Node를 K8s 클러스터에 Join 시킵니다.

Master 노드에 있는 ~/vagrant/join.sh 의 내용을 복사해 Worker Node에서 쿠버네티스  클러스터에 Join 합니다.

> sudo kubeadm join 192.168.1.200:6443 --token 0arcno.4t1aj5wamt88cruk --discovery-token-ca-cert-hash sha256:f16fabbc67ccf5cbf554bd7623111b785b74f21e0abec5463ddfd9ec498e7b50

 

생성된 가상머신에 접근할때는 vagrant ssh 명령을 통해 접근하거나 Vagrant Manager에서 SSH로 접근할 수 있다.