연구실 이사를 하며 서버룸을 정리하게 됐다.
맨날 도커를 쓰는 것도 너무 매몰되는 게 아닌가 싶어 서버를 쿠버네티스(Kubernetes)로 관리하기로 했다.
예전부터 쿠버네티스를 써보고 싶기도 했었는데, 도커에 비해 너무 복잡해 공부할 엄두를 못 내고 있었던 참이기도 하다.
사실 요즘은 쿠버네티스 설치 가이드가 워낙 많아서 굳이 포스팅씩이나 할 필요는 없다.
다만 공식 홈페이지의 가이드가 너무 불친절해서 정보를 취합하는 김에 글을 정리해보았다.
설치 환경은 Rocky Linux 9.5를 기준으로 하였다.
마스터 노드는 한 개, 워커 노드는 네 개이다.
마스터 노드가 단일 실패점(SPOF)이 되기에 기분이 썩 좋진 않으나, 마스터 노드를 추가하기엔 배꼽이 커진다.
참고로 쿠버네티스에서는 마스터 노드를 3개 이상의 홀수 개로 구성하는 것을 권장한다.
마스터 노드를 하나 추가해 2개를 쓰는 경우, 과반수 알고리즘(Quotum)이 정상 동작하지 않을 가능성이 있다.
솔직히 학교 연구 환경에서는 서버가 죽는 일이 생각보다 빈번하기에 상당히 불안하지만 방법이 없다.
편의를 위해 쿠버네티스를 k8s로 줄여 서술한다.
k8s는 쿠버네티스를 축약해서 부르는 공식 용어로, k와 s 사이의 영문자가 8개이기에 이러한 이름을 붙였다고 한다.
1. 공통 설치
마스터 노드에는 최소 4GB의 메모리를 확보해주자.
그렇지 않으면 k8s 구성요소가 정상적으로 동작하지 않는다.
이런 저런 세팅이 많긴 한데, 굳이 하나씩 설명하지 않고 한 번에 나타내보았다.
복사 붙여넣기가 편하도록 쉘 기호($)를 생략하고 커맨드를 작성하였다.
# SELinux 해제 sudo setenforce 0 sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config # 방화벽 해제 (firewalld 기준) sudo systemctl stop firewalld sudo systemctl disable firewalld # SWAP 공간 해제 및 비활성화 sudo swapoff -a sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab # 커널 모듈 로드 cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter # 커널 파라미터 설정 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 sudo sysctl --system # 컨테이너 런타임 설치 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install containerd.io -y sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml sudo systemctl restart containerd sudo systemctl enable containerd # 쿠버네티스 컴포넌트 설치 cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/ enabled=1 gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.key exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni EOF # Worker Node는 kubectl을 설치하지 않아도 됨 sudo dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes sudo systemctl enable --now kubelet
2. 마스터 노드 설정
현재 노드를 마스터 노드로 설정한다.
다음은 노드 이름을 master-node로 지정하고, calico 네트워크 플러그인을 설치하는 커맨드이다.
k8s의 기본 네트워크는 192.168.0.0/16이다.
VMWare같은 가상환경에서 테스트하는 경우 192.168.0.0/24 네트워크가 점유되어 k8s의 네트워크와 충돌이 발생한다.
그래서 다른 블로그들과 달리 pod 네트워크로 10.0.0.0/16 등의 다른 네트워크를 사용하여야 한다.
# 마스터 노드 설정 sudo kubeadm init --pod-network-cidr=10.0.0.0/16 --node-name=master-node # 이후 화면에 표시된 지침에 따라 추가 설정 # 출력되는 커맨드는 추후 워커 노드에서 마스터 노드와 연결할 때 사용되니 저장할 것) # 아래는 필자 환경에서 사용한 예시 커맨드임 mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config # Calico 설치 (네트워크 플러그인) # 원래는 custom-resources.yaml 파일을 바로 배포해도 되나, 필자는 pod 네트워크를 바꾸었기 때문에 파일을 그대로 배포하면 안된다. # 참고: https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises#install-calico kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.29.3/manifests/tigera-operator.yaml curl https://raw.githubusercontent.com/projectcalico/calico/v3.29.3/manifests/custom-resources.yaml -O sed -i 's/192\.168\.0\.0\/16/10.0.0.0\/16/g' custom-resources.yaml # pod 네트워크 변경 (192.168.0.0/16 -> 10.0.0.0/16) kubectl create -f custom-resources.yaml
설정은 모두 끝났다.
참고로 calico를 사용하는 경우 firewalld같은 별도의 방화벽 도구를 사용하면 안된다. [Calico]
calico가 iptable을 직접 관리하기 때문에 다른 도구와 충돌하기 때문이다.
(이것 때문에 얼마나 디버깅을 했는지..)
그래서 다른 블로그에서 이야기하는 것처럼 방화벽을 일일이 열어줄 필요가 없다.
포트 개방이 필요한 경우에는 공식 docs를 참고하면 된다. [k8s][Calico]
3. 워커 노드 설정
마스터 노드에서 받아온 토큰과 해시값, 마스터 노드의 ip를 이용해 다음 커맨드를 실행한다.
워커 노드의 이름은 중복되면 안되기에 워커 노드의 이름을 잘 설정해주는 걸 잊지 말자.
# 워커 노드 연결 커맨드 sudo kubeadm join <master-ip>:6443 --token <token> --discovery-token-ca-cert-hash sha256:<hash> --node-name=worker-node # 토큰값을 까먹었을 경우 아래 커맨드로 토큰값 확인 sudo kubeadm token create --print-join-command
마스터 노드와 마찬가지로 워커 노드에서도 일일이 포트를 개방할 필요가 없다.
참고로 마스터 노드나 워커 노드 모두 무언가 문제가 생겼다면 다음 커맨드로 노드를 초기화할 수 있다.
sudo kubeadm reset
4. 테스트
이제 마스터 노드에서 다음 커맨드를 통해 워커 노드가 잘 조회되는 지 확인해보자.
이 장부터는 커맨드 결과를 병기하기 위해 쉘 기호($)를 표시하였다.
$ kubectl get nodes NAME STATUS ROLES AGE VERSION master-node Ready control-plane 4m28s v1.28.15 worker-node-1 Ready <none> 43s v1.28.15 worker-node-2 Ready <none> 6s v1.28.15 # Calico는 워커 노드에 따라 파드를 생성하므로 모든 파드가 READY 상태인지 반드시 확인 후 진행해야 한다. # 시간이 좀 걸리니 watch로 모두 running하는지 보고 있자. $ kubectl get pods -A calico-apiserver calico-apiserver-595897c7c6-gw492 1/1 Running 0 3m57s calico-apiserver calico-apiserver-595897c7c6-x8jbn 1/1 Running 0 3m57s calico-system calico-kube-controllers-6f875fb958-9sn2t 1/1 Running 0 3m57s calico-system calico-node-khr8h 1/1 Running 0 3m57s calico-system calico-node-v87gr 1/1 Running 0 68s calico-system calico-node-z87k8 1/1 Running 0 77s calico-system calico-typha-67559776f7-jx9jd 1/1 Running 0 3m57s calico-system calico-typha-67559776f7-twpjv 1/1 Running 0 68s calico-system csi-node-driver-9th4m 2/2 Running 0 3m57s calico-system csi-node-driver-g2b29 2/2 Running 0 77s calico-system csi-node-driver-mkmq9 2/2 Running 0 68s kube-system coredns-5dd5756b68-rrjjc 1/1 Running 0 4m1s kube-system coredns-5dd5756b68-tzlz5 1/1 Running 0 4m1s kube-system etcd-master-node 1/1 Running 8 4m16s kube-system kube-apiserver-master-node 1/1 Running 8 4m18s kube-system kube-controller-manager-master-node 1/1 Running 21 4m16s kube-system kube-proxy-gkb4m 1/1 Running 0 4m1s kube-system kube-proxy-pd5vk 1/1 Running 0 68s kube-system kube-proxy-z27vj 1/1 Running 0 77s kube-system kube-scheduler-master-node 1/1 Running 22 4m16s tigera-operator tigera-operator-c78c656bf-tgsqb 1/1 Running 0 4m1s
모든 노드가 Ready 상태이면 워커 노드를 사용할 수 있는 상태이다.
이제 웹서버를 배포하고 세팅을 검증해보자.
웹서버를 세 개 만들고 로드밸런싱을 하면서 30000번 포트를 통해 외부에서 접속하려 한다.
먼저, yaml 파일을 만든다.
커맨드로도 웹서버를 생성할 수 있지만, 역시 파일을 만들어 실행하는 게 깔끔하다.
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: NodePort selector: app: nginx ports: - protocol: TCP port: 80 # 서비스 포트 (클러스터 내부) targetPort: 80 # 컨테이너 포트 (Pod 내부) nodePort: 30000 # 외부에서 접근할 포트 (30000으로 고정)
이제 위 yaml 파일을 통해 서비스를 배포한다.
(설명이 길어질 것 같아 NodePort와 Service, Deployment에 대한 언급을 애써 피하고 있다)
$ kubectl apply -f web-test.yaml $ kubectl get services # 서비스가 정상적으로 동작하는지 확인한다. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5m21s nginx-service NodePort 10.101.139.145 <none> 80:30000/TCP 6s # kubectl get pods # 파드가 READY 상태가 되어야 서비스를 사용할 수 있다. # 혹시 READY가 되지 않는다면 높은 확률로 네트워크 플러그인(Calico)이 제대로 설치되지 않았을 수 있다. NAME READY STATUS RESTARTS AGE nginx-deployment-7c79c4bf97-66b99 1/1 Running 0 39s nginx-deployment-7c79c4bf97-nmzj4 1/1 Running 0 39s nginx-deployment-7c79c4bf97-r8w8p 1/1 Running 0 39s
이제 어떤 워커노드의 IP든 상관없이 외부에서도 30000번 포트를 통해 서비스에 접근할 수 있다.
웹브라우저에서 http://<워커노드 IP>:30000로 접속이 되는지 확인하자.
로드밸런싱이 잘 되는지는 아래 커맨드를 통해 알 수 있다.
$ kubectl logs nginx-service
확인이 끝났다면 아까 만들어둔 yaml 파일을 이용해 서비스를 모두 삭제할 수 있다.
kubectl delete -f nginx-nodeport.yaml kubectl get services # 서비스가 정상적으로 제거되었는지 확인한다.
다 끝난 것 같지만 k8s를 제대로 활용하기 위해선 아직 설정해주어야 할 것이 많다.
다음 포스팅에서 다뤄보도록 한다.