kubeadm을 이용하여 쿠버네티스 클러스터를 구축하는 과정을 살펴보자
개요
아마 개발자라면 다들 나만의 서버를 갖는 것에 대한 로망이 있을 것이다. 나 또한 개발자이기에 항상 내 서버를 가지고 싶었다. 특히 나는 현업에서 쿠버네티스를 많이 다루다보니 그냥 서버가 아닌 쿠버네티스 클러스터를 가지고 싶었지만 알다시피 쿠버네티스가 돌아갈 정도의 서버 여러 대를 구하는 것은 쉽지 않은 일이다... 그렇기에 항상 포기하고 맥북에 kind나 minikube를 이용하여 로컬 쿠버네티스를 이용해왔었다😂 (성에 차지 않는다...)
그러다 이번 여름에 공모전을 진행하면서 처음으로 오라클 클라우드(OCI)를 이용해볼 기회가 생겼었다. 사용 당시에는 이런 클라우드도 있구나 라고만 생각하며 이용을 했었다. 그리고 공모전이 끝나고 남아있는 서버가 아까워 이리저리 사용해보다가 아주 혜자로운(?) OCI 정책이 있다는 것을 알았다..!!
놀랍게도 다른 제한이 있는 클라우드와 다르게 OCI에서는 평생 무료 서버를 제공한다. 심지어 째째하게 RAM 1GB 짜리가 아니라 총 24GB의 서버를 구축할 수 있다. 또한 서버를 제외하고도 아래 사진과 같이 평생 무료 리소스가 꽤나 많으니 필요한 기능이 있다면 이용하면 된다.
자세한 내용은 이 문서를 참고하면 좋을 것 같다.
하지만...
약간 안타까운 소식은 이 서버를 생성하기가 굉장히 어렵다는 것이다... 만약 저렇게 좋은 사양의 서버를 모든 사람들에게 막 뿌린다면 아마 오라클이 파산할 것이기 때문에 오라클도 몇 대만 프리티어용으로 할당해놓고 더이상 증설을 하지 않고 있어서 서버를 쟁취하는 것이 매우 빡세다. 혹시나 이 글을 보고 서버를 구축하려는 분들은 꽤나 많은 시간을 들여야 할 것이다.
다행히...
나는 운 좋게 서버를 획득하였다. 많은 우여곡절이 있었지만 어렵게 어렵게 서버를 획득할 수 있었다. 이 서버를 얻는 것이 다른 개발자들에게도 쉽지 않았는지 다른 개발자가 만들어놓은 코드를 이용하여 몇 달만에 얻어낼 수 있었다 서버 쟁취 과정(?)은 다음에 따로 글을 작성할까 싶기도 하다.
서론이 굉장히 길었는데 오늘 포스팅에서는 글 제목처럼 어렵게 얻은 서버를 가지고 kubeadm로 쿠버네티스 클러스터를 구축해보려 한다. 이 과정에서 여러 시행착오가 있었는데 이 부분까지 포함하여 자세하게 구축 과정을 작성해보려 한다.
클러스터를 위한 서버
클러스터를 구성하기에 앞서 어렵게 얻은 서버의 사양과 제약 사항을 알아보도록 하자. 사양은 2OCPU, RAM 12GB로 굉장히 준수하다.
- ARM 12GB 2OCPU - 2대
- AMD 1GB 1OCPU - 2대
특이사항은 이번에 쿠버네티스 구축에 사용할 서버는 ARM 기반 서버라는 것이다. ARM은 AMD보다는 범용성이 떨어지는 것이 사실이지만 애플을 포함하여 많은 기업들이 주목하고 있는 CPU 아키텍처이다.
OCI에서는 arm, amd 모두 무료로 제공하지만 amd 서버는 여느 클라우드와 동일하게 1GB, 1CPU 정도의 사양만을 지원하고 있어 쿠버네티스 사용에는 제약이 있다. 물론 워커노드로 활용해도 되긴 하지만 사양도 너무 적고, arm 기반 서버와 함께 워커노드로 사용하기에는 컨테이너를 따로 말고 아키텍처에 맞게 파드를 배분해야 하기 때문에 컨트롤이 쉽지 않아 그냥 arm 기반 서버만을 이용해서 쿠버네티스를 구축하기로 하였다.
이에 따라 구축하려는 쿠버네티스는 아래와 같은 설정을 갖게 된다.
- ARM 12GB 2OCPU - control plane, worker node
- ARM 12GB 2OCPU - worker node
정족수를 위해 컨트롤 플레인은 1개만 설정했고, 2개 노드 모두 워커노드로 이용하기로 하였다.
Kubernetes 필수 설정
본격적으로 쿠버네티스를 구축하기 위해 구축에 꼭 필요한 필수 설정들을 알아보자.
- 호환되는 리눅스 머신.
- 2GB, 2CPU 이상의 사양을 가진 머신.
- 클러스터의 모든 머신에 걸친 전체 네트워크 연결. (공용 또는 사설 네트워크면 괜찮음)
- 모든 노드에 대해 고유한 호스트 이름, MAC 주소 및 product_uuid.
- MAC 주소 확인: ifconfig -a
- product_uuid 확인: sudo cat /sys/class/dmi/id/product_uuid
- 스왑 비활성화. kubelet이 제대로 작동하게 하려면 반드시 스왑을 사용하지 않도록 설정한다. (이유를 알고 싶다면 여기 참고)
- 특정 포트들 개방.
- 컨테이너 런타임.
4번까지는 대부분 갖춰져 있기 때문에 보통은 5번부터 신경쓰면 된다. 나 또한 4번까지는 모두 갖춘 상태였기에 5번부터 설정을 변경해주었다.
스왑 비활성화
다음 명령어를 통해 스왑을 비활성화 할 수 있다.
swapoff -a
sudo sed -i '/swap/s/^/#/' /etc/fstab
포트 개방
쿠버네티스 컴포넌트 간 통신을 위해 필요로 하는 포트를 보안 그룹에서 열어주어야 한다.
Control Plane
프로토콜 | 방향 | 포트 번호 | 용도 |
TCP | 인바운드 | 6443 | 쿠버네티스 API 서버 |
TCP | 인바운드 | 2379-2380 | etcd 서버 클라이언트 API |
TCP | 인바운드 | 10250 | Kubelet API |
TCP | 인바운드 | 10259 | kube-scheduler |
TCP | 인바운드 | 10257 | kube-controller-manager |
Worker Node
프로토콜 | 방향 | 포트 번호 | 용도 |
TCP | 인바운드 | 10250 | Kubelet API |
TCP | 인바운드 | 30000-32767 | NodePort 서비스 |
Fannel
위의 포트와 함께 추가적으로 CNI 네트워크가 사용하는 포트도 열어주어야 한다.(이 부분을 몰라 계속 kube-proxy가 죽는 문제가 문제때문에 삽질을 했었다 ㅠㅠ) CNI는 Calico가 가장 범용적이긴 하지만 나는 가장 가볍고 간단한 플러그인인 Fannel을 사용하였다. CNI별로 벤치마크 테스트가 궁금하다면 이 글을 참고하면 좋을 것 같다.
또한 Fannel 관련 설정들을 살펴보고 싶다면 이 문서를 참고하면 된다.(여기에 필수 포트 설정도 나와있음)
프로토콜 | 방향 | 포트 번호 | 용도 |
UDP | 인바운드 | 8472 | VXLAN |
UDP | 인바운드 | 8285 | UDP |
OCI에서는 서버가 소속된 네트워크의 보안 그룹을 아래와 같이 수정해주면 된다.
컨테이너 런타임 - containerd 설치
파드에서 컨테이너를 실행하기 위해서는 컨테이너 런타임을 설치해야 한다. 1.24 버전부터 쿠버네티스는 컨테이너 런타임으로 docker를 지원하지 않는다. 여러 컨테이너 런타임 중 나는 containerd을 컨테이너 런타임으로 사용하고자 한다.
kubeadm을 이용하여 쿠버네티스를 설치하면 자동으로 컨테이너 런타임을 찾아주기 때문에 설치만 하면 사용하는 것은 아주 쉽다.
아래는 containerd를 설치하는 명령어이다.
네트워크 옵션 추가 설정
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sudo sysctl --system
Containerd 설치
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release -y
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=arm64 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-get update
sudo apt-get install containerd.io -y
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo systemctl restart containerd
⭐️ Cgroup 드라이버 systemd로 변경
위의 과정에 따라 설치를 모두 마쳐도 아마 쿠버네티스를 설치하여도 계속 주요 파드들이 죽는 문제가 발생할 것이다(관련 깃허브 이슈). 이를 알기 위해서는 먼저 리눅스의 Cgroup을 알아야 하는데 간단히 설명하면 Cgroup을 이용해서 프로세스별로 자원을 개별적으로 할당할 수 있게되고, 이를 통해 컨테이너를 사용할 수 있는 것이다.
이러한 Cgroup을 관리하기 위한 기술은 크게 cgroupfs와 systemd가 있는데 쿠버네티스 1.22부터는 Cgroup 드라이버를 systemd를 이용하도록 변경하였다고 한다. 따라서 containerd 또한 Cgroup 드라이버에 대한 default 설정을 systemd로 변경해야 하는 것이다. 변경을 위해서는 /etc/containerd/config.toml 파일에서 SystemdCgroup를 true로 수정해야 한다.
cat /etc/containerd/config.toml | grep -n SystemdCgroup
125: SystemdCgroup = false
kubeadm, kubelet 및 kubectl 설치
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로 쿠버네티스 구축
Control Plane init
이제 본격적으로 kubeadm을 이용하여 쿠버네티스 구축 해보자. 먼저 Control Plane을 만들어야 하는데 이는 kubeadm init을 이용해 손쉽게 설정할 수 있다.
sudo kubeadm init --pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=<Private IP> \
--control-plane-endpoint=<End point IP>
각 옵션의 의미는 아래와 같다.
- --pod-network-cidr: 파드 네트워크의 IP 주소 범위를 지정. 설정하면 컨트롤 플레인이 모든 노드에 대해 자동으로 CIDR을 할당한다.
- --apiserver-advertise-address: API 서버가 수신 중임을 알릴 IP 주소로 워커노드들은 이 주소로 컨트롤 플레인과 통신한다. 설정하지 않으면 기본 네트워크 인터페이스가 사용한다.
- --control-plane-endpoint: 컨트롤 플레인의 안정적인 IP 주소 또는 DNS 이름을 지정한다. 컨트롤 플레인을 다중으로 구성하였다며 로드밸런서와 같은 주소를 설정하여 안정적인 통신을 하게 해준다.
init이 성공적으로 끝나면 kubectl을 사용할 수 있게끔 인증서 kubeconfig를 옮겨준다.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Worker node join
컨트롤 플레인을 구축하였으니 여기에 워커 노드를 조인한다. kubeadm join 명령어를 이용하면 되는데 이는 kubeadm init이 끝난 뒤 터미널에 출력이 되니 이를 복붙하면 된다.
kubeadm join <IP>:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<cert hash>
Fannel CNI 배포
성공적으로 컨트롤 플레인과 워커 노드를 구축하였지만 아마 kube-proxy, coredns 등의 파드들이 제대로 뜨지 않는 것을 볼 수 있을 것이다. 이는 위에서 언급한 네트워크 CNI가 없어서 생기는 문제인데 이미 설정되어 있는 yaml을 이용하여 바로 배포할 수 있다.
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
컨트롤 플레인에 워커 노드 역할도 부여하기
이제 모든 배포가 끝이 났다. 하지만 아직 한 가지 컨트롤 플레인이 워커노드로도 돌아가게끔 하는 설정이 남았다. 컨트롤 플레인에 파드가 배정되지 않는 이유는 taint가 걸려있기 때문인데 이를 풀어주기만 하면 아주 간단하게 컨트롤 플레인에도 파드를 배정할 수 있다.
kubectl taint nodes {해제할 노드 이름} node-role.kubernetes.io/master-
마지막으로 node와 pod를 확인하며 쿠버네티스가 제대로 동작하는지 살펴보고 문제없이 동작하는 것을 확인하면 모든 구축이 끝이 난다!!
'데이터 엔지니어링 > Kubernetes' 카테고리의 다른 글
가볍게 읽는 쿠버네티스 안내서 (0) | 2024.04.29 |
---|---|
Nginx Ingress Annotation rewrite-target 알아보기 (0) | 2023.06.04 |