컨테이너를 사용하지 않던 시절에는 물리적인 개발장비에 OS 를 설치하고 각각의 서비스를 직접 프로세스로 띄워서 실행하곤 했습니다. 이로 인해 서비스간에 간섭이 발생할 가능성이 많았고, OS 및 SW Stack 과 같은 환경에 따라 문제가 발생하는 상황도 종종 있었습니다. 그러다가 Docker 기반의 컨테이너 환경이 유행하면서 직접 서비스를 프로세스로 띄우는 대신 서비스를 컨테이너에 이식한 후 컨테이너를 배포하는 방식으로 발전했습니다. 이로 인해 OS 및 SW Stack 이 다른 환경에서도 단일한 방법으로 간단하게 배포를 할 수 있게 되었습니다. 하지만 이렇게 편리해진 환경에서도, 서로 다른 물리적인 장비에서 실행중인 컨테이너는 각각의 장비에 직접 접속해서 관리(생성/실행/중지/삭제 등)해야하는 번거로움은 여전했습니다. 수많은 워크로드를 감당하기 위해 물리적인 장비가 수십~수백대에 달하게 되면 각각의 장비에 접속해서 관리하는 것은 사실상 불가능에 가까워지게 됩니다. 사람이 수동으로 직접 관리하는 작업을 자동으로 해주는 니즈가 강했고, 이른바 Container Orchestration 도구라 불리우는 Kubernetes, Docker Swarm, Apache Mesos 등이 탄생하게 되었습니다. 심지어 AWS, Google Cloud 와 같은 Cloud 벤더사에서는 EKS, GKE 와 같이 Kubernetes 를 SaaS 형태로 제공하기도 합니다. 저도 Kubernetes 에 관심이 있는 터라 제 프로젝트를 Kubernetes 기반으로 구축하려고 합니다. 이제 Virtualbox VM 기반으로 Kubernetes Cluster 를 구성하는 방법에 대해서 이야기해보도록 하겠습니다.

  • Swap 메모리 해제 및 Host 파일 변경
$ sudo swapoff -a && sudo sed -i '/ swap / s/^/#/' /etc/fstab

우선 Ubuntu 20.04 LTS 에서 swap 으로 잡혀있는 메모리를 해제해줍니다. Memory Swap은 물리적인 메모리가 부족할 경우 디스크의 일부 공간을 메모리처럼 사용하는 개념인데, 디스크는 RAM 에 비해 훨씬 느리므로 잘못 사용할 경우 심각한 성능저하를 초래할 수 있습니다. 또한 Kubernetes 도 반드시 Swap 메모리를 해제하라고 가이드하고 있습니다.

$ sudo bash -c 'cat >> /etc/hosts << EOF
192.168.0.100 master
192.168.0.101 worker1
192.168.0.102 worker2
EOF
'

Host 파일에 master vm 과 worker vm 들을 추가해줍니다.

$ sudo hostnamectl set-hostname master

그리고 호스트네임을 master 로 변경해줍니다.

  • Docker 설치 및 데몬 설정 변경
$ sudo apt install docker.io
$ sudo docker version

위 명령어를 통해 Docker 를 설치 후 정상적으로 설치되었는지 버전을 확인합니다. Docker를 설치하는 이유는 Kubernetes 의 컨테이너 런타임으로 사용하기 위해서입니다. 참고로 Kubernetes 는 v1.20 이후 컨테이너 런타임으로서 Docker 를 사용중단한다고 합니다. 향후에 Migration 에 대한 작업을 별도로 진행할 예정입니다.

$ sudo bash -c 'cat > /etc/docker/daemon.json <<EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF'

Kubernetes 공식 가이드에 따르면 컨테이너 런타임과 kubelet이 systemd를 cgroup 드라이버로 사용하도록 설정을 변경하면 시스템이 안정화된다고 합니다. 이를 위해 Docker Daemon 의 설정을 위와 같이 변경해줍니다.

$ sudo mkdir -p /etc/systemd/system/docker.service.d
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

Docker Daemon 의 설정을 수정하였으므로 설정파일을 Reload 후 재시작 해줍니다.

  • Kubernetes 설치
$ sudo apt update && sudo apt install -y apt-transport-https curl 
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ cat << EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
$ sudo apt update
$ sudo apt install -y kubelet kubeadm kubectl
$ sudo apt-mark hold kubelet kubeadm kubectl
$ kubeadm version

Kubernetes 공식 홈페이지에서 가이드하는 설치명령어입니다. 설치 후 버전을 확인합니다. 저의 경우 v1.20.2 을 설치하였습니다.

$ sudo shutdown -h now

이제 vm을 종료해줍니다.

  • Kubernetes Cluster 구축

Kubernetes Cluster를 구축하기 위해서는 하나의 master vm 과 1개 이상의 worker vm 이 필요합니다. 저는 1개의 master vm과 2개의 worker vm 을 생성하여 프로젝트를 진행하려고 합니다. 이를 위해 지금까지 만들어놓은 master vm 을 그대로 복사해 worker1, worker2 vm 을 생성하겠습니다. 그리고 향후 worker 의 scale-out 을 위해 기본 틀이 되는 master-backup vm 까지 생성하겠습니다. master vm 을 오른쪽 클릭한 후 복제를 클릭합니다.

이름은 worker1 으로 설정하고 물리적으로 다른 VM 으로 인식시키기 위해 MAC 주소 정책을 ‘모든 네트워크 어댑터의 새 MAC 주소 생성’으로 설정 후 ‘다음’을 클릭합니다.

‘완전한 복제’를 선택 후 ‘복제’를 클릭합니다.

복제 완료되는데 시간이 꽤 걸립니다. 이 작업을 worker2, master-backup 에 대해서도 진행합니다.

모두 복제가 완료되면 좌측에 각각의 vm 리스트가 표시됩니다. worker1, worker2 vm이 새로 생성되었지만 고정IP, 호스트 네임은 변경해주어야 합니다. worker1 vm 을 선택하고 ‘시작’을 클릭합니다.

설정메뉴의 Network 설정으로 들어가 Address 를 192.168.0.101으로 변경하고 ‘Apply’ 버튼을 클릭합니다. 그런 후에 Wired 라디오버튼을 껐다가 잠시 후 다시 켜서 변경내용을 적용합니다. 이 작업을 worker2 vm 에 대해서도 동일하게 진행합니다.(worker2 의 IP 는 192.168.0.102 로 변경합니다.)

$ sudo hostnamectl set-hostname worker1
$ sudo reboot

그리고 호스트네임을 worker1 로 수정합니다. 이 작업을 worker2 vm 에 대해서도 진행합니다.(worker2 의 호스트네임은 worker2 로 변경합니다.) 그 후에 vm 을 재부팅해줍니다.

$ sudo systemctl daemon-reload
$ sudo systemctl enable --now docker
$ sudo systemctl enable --now kubelet

master vm 에서 docker 와 kubernetes 를 실행합니다.

$ sudo kubeadm reset
$ sudo kubeadm init --pod-network-cidr=20.96.0.0/12 --apiserver-advertise-address=192.168.0.100

Pod Network 와 Master VM 의 IP 주소를 옵션으로 주고 Kubernetes 초기화 명령을 실행합니다. 저는 20.96.0.0/12 대역을 사용하여 저희집 공유기 Network(192.168.0.0/16)와 Pod Network 대역을 분리시켰습니다.

....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.confYou 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 192.168.0.100:6443 --token njq3f9.eb6hfarjaqvtgcu8 \
--discovery-token-ca-cert-hash sha256:2d4b59238be59af912b466fcd98a67bbbd9fdee59402eb1a9faadee53ff383ed

명령을 실행한 결과 위와 같은 메세지가 출력됩니다. 여기서 Bold 체로 표시한 내용을 각각 master vm, worker1, worker2 vm 에서 실행합니다.

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

master vm 에서 위 명령어를 통해 로그인한 계정에 대해 kubectl 설정을 합니다.

$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

master vm 의 /etc/kubernetes/admin.conf 파일을 worker1, worker2 vm 의 $HOME/.kube/config 로 복사해 줍니다. 그리고 위 명령어를 통해 로그인 한 계정에 대해 kubectl 설정을 합니다.

$ sudo systemctl daemon-reload
$ sudo systemctl enable --now docker
$ sudo systemctl enable --now kubelet

worker1, worker2 vm 에서 docker 와 kubernetes 를 실행합니다.

$ sudo kubeadm reset
$ sudo kubeadm join 192.168.0.100:6443 --token njq3f9.eb6hfarjaqvtgcu8 --discovery-token-ca-cert-hash sha256:2d4b59238be59af912b466fcd98a67bbbd9fdee59402eb1a9faadee53ff383ed

worker1, worker2 vm 에서 위 명령어를 통해 Kubernetes Cluster 에 조인합니다.

....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.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

명령어 실행 결과 위와 같은 메세지가 나오면 worker1, worker2 노드가 Kubernetes Cluster 조인에 성공한 것입니다.

$ kubectl get nodes

master node 에서 위 명령어를 통해 현재 Kubernetes Cluster 에 존재하는 node 들의 리스트를 확인합니다.

NAME      STATUS     ROLES                  AGE     VERSION
master NotReady control-plane,master 3m20s v1.20.2
worker1 NotReady <none> 15s v1.20.2
worker2 NotReady <none> 13s v1.20.2

위와 같이 master, worker1, worker2 가 정상적으로 Kubernetes Cluster 에 조인된 것을 확인할 수 있습니다. 하지만 Status 는 NotReady 인데 아직 Overlay Netowrk Addon 을 설치하지 않았기 때문입니다.

  • Overlay Network 설치
$ curl https://docs.projectcalico.org/manifests/calico.yaml -O
$ kubectl apply -f calico.yaml
$ kubectl get nodes

Kubernetes는 컨테이너 간에 통신을 위해 calico, flannel, weaveNet 등 여러 오버레이 네트워크를 사용할 수 있습니다. 저는 calico 를 통해 Overlay Network 를 구성하도록 하겠습니다. master 노드에서 위 명령어를 실행합니다.

NAME      STATUS   ROLES                  AGE   VERSION
master Ready control-plane,master 20m v1.20.2
worker1 Ready <none> 17m v1.20.2
worker2 Ready <none> 17m v1.20.2

위와 같이 Status 가 Ready 로 표시되면 정상적으로 설정된 것입니다.

$ kubectl get pods --namespace kube-system

Kubernetes 핵심 컴포넌트들이 정상적으로 설치 & 실행되었는지를 확인합니다.

NAME READY STATUS RESTARTS AGE
calico-kube-controllers-56b44cd6d5-klstg 1/1 Running 4 2m57s
calico-node-65rlz 1/1 Running 1 2m57s
calico-node-r9vf4 1/1 Running 2 2m57s
calico-node-skq56 1/1 Running 2 2m57s
coredns-74ff55c5b-m2pn6 1/1 Running 0 7m9s
coredns-74ff55c5b-zw5nv 1/1 Running 0 7m9s
etcd-master 1/1 Running 0 7m15s
kube-apiserver-master 1/1 Running 0 7m15s
kube-controller-manager-master 1/1 Running 0 7m15s
kube-proxy-9584f 1/1 Running 0 3m58s
kube-proxy-nhrsw 1/1 Running 0 7m9s
kube-proxy-plnp4 1/1 Running 0 4m1s
kube-scheduler-master 1/1 Running 0 7m15s

위와 같이 Status 에 전부 Running 이라고 뜨면 모두 정상적으로 설정된 것입니다.

  • worker1, worker2 의 VM 에 대해 공유기 차원에서 IP 고정 및 Port Fowarding

이전에 mater vm 에 대해 IPTIME 공유기차원에서 IP고정, Port Fowarding 설정을 해주었듯이, worker1, worker2 vm 에 대해서도 동일하게 진행하도록 하겠습니다. Chrome 을 실행시켜 주소창에 IPTIME 공유기 Admin Page 인 192.168.0.1을 입력 후 이동합니다. Setting 페이지에서 Advanced Setup -> Network -> DHCP Server Setup 으로 가서 우측 하단의 IP 목록 중 192.168.0.101, 192.168.0.102 행의 우측 체크박스를 클릭 후 ‘+Add’ 버튼을 클릭합니다.

192.168.0.100, 192.168.0.101, 192.168.0.102 모두 IP 가 고정된 것을 확인할 수 있습니다. 다만 여기서 MAC 주소가 세 VM 모두 동일하게 나오고 이 MAC 주소는 사실 Host Machine 의 MAC 주소입니다. 이 부분은 확인이 필요해 보입니다. 작동은 정상적으로 잘 되니 일단 넘어가겠습니다.

Advanced Setup -> NAT/Routing -> Port Fowarding 으로 가서 우측 하단에 worker1, worker2 vm 에 대해 위 화면과 같이 port forwarding 설정을 해줍니다. 저는 내부 ssh port 에 대해 외부 port 를 10023, 10024 로 설정했습니다. 지금까지는 위 작업들을 모두 VirtualBox 의 각 VM 에 직접 접속해서 세 화면을 띄워놓고 각각의 화면에서 terminal 을 통해 명령을 실행했지만, 이제 Host 머신(Windows 10) 에서 putty / superputty 와 같은 프로그램을 통해 한 화면에서 세 VM 을 제어할 수 있게 되었습니다.

이렇게 말입니다. putty / superputty 설치 및 설정에 대한 내용은 인터넷에 많이 나와있으므로 생략하도록 하겠습니다.

  • 마무리

위 작업을 통해 master, worker1, worker2 로 이루어진 Kubernetes Cluster 를 구성완료하였습니다. 이제 이 Kubernetes Cluster 에 원하는 서비스를 구현하여 배포하는 작업이 가능합니다.

다음으로 Raspberry Pi 4B에 Ubuntu Server 20.04.2 LTS 를 설치하는 작업을 진행하겠습니다.

--

--