본문 바로가기

DevOps/Kubernetes

[Ubuntu 22.04] Kubernetes Cluster 구축 - 2편

0. 들어가며

 [Ubuntu 22.04] Kubernetes Cluster 구축 - 1편에서 이어지는 내용으로 전편을 참고한 후 해당 포스팅을 보는 것을 추천드린다. 

 

[Ubuntu 22.04] Kubernetes Cluster 구축 - 1편

0. 들어가며 Kubernetes 강의를 들어보면 대부분 Vagrant나 Minikube와 같은 것으로 클러스터를 구축한다. 이 부분이 항상 아쉬운 부분이다. 물론 수강생들의 수강 환경이 천차만별이기 때문에 이를 통

tech-recipe.tistory.com

 Ubuntu 설치가 성공적으로 완료되었다면 ssh 툴을 이용하여 k8s-cp에 접속한 후 다음 작업을 실행하도록 한다. 본 포스팅에서는 Termius라는 ssh 툴을 사용하였으며, 다른 익숙한 툴이 있다면 어떤 것을 사용해도 무방하다. 바로 다음 설치 과정에 대하여 알아보도록 하겠다. 모든 과정은 admin 권한을 가지고 있는 sudo 유저로 진행되는데 전편에서 설치 중 생성한 계정이 이를 만족하므로 크게 신경 쓰지 않아도 된다.

 

1. 설치 진행

 2) Kubernetes 클러스터 구성을 위한 공통 설정 작업

  (1) /etc/hosts 파일 내용 수정

 ssh툴을 통해 k8s-cp 노드에 접속 후, /etc/hosts 파일에 내용을 아래와 같이 수정해주고 저장한다.

#/etc/hosts 파일 내용 수정

127.0.0.1 localhost
127.0.1.1 k8s-cp
192.168.0.150 k8s-cp
192.168.0.151 k8s-w01
192.168.0.152 k8s-w02
192.168.0.153 k8s-w03

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

[사진 1] /ect/hosts 파일 수정

 만약 hostname이 정상적이지 않으면 Kubernetes 클러스터 구성 시 문제가 생길 수 있다. 사소한 부분인 것 같지만 신경 써 줘야 한다. 이전 설치 과정에서 server name을 지정해 주었기 때문에 컨트롤 플레인 노드에서는 /etc/hosts 파일만 수정하면 되지만, 이후 VM 복사로 만들어지는 워커 노드의 경우 hostname 수정이 꼭 필요하다.

 

  (2) Swap 메모리 기능 끄기와 커널 파라미터 추가

 Kubernetes에서는 메모리 오버 커밋으로 인한 성능 저하 이슈를 피하고자 의도적으로 Swap 메모리 기능을 끄도록 권장한다. 물론 Kubernetes 1.22 버전 부터 Swap 메모리를 사용하여도 설치가 가능하도록 변경되긴 하였으나 그럼에도 불구하고 Swap 메모리를 끄는 것이 더 권장되고 있다. 아래 링크를 참고하거나 구글에서 'Swap off on Kubernetes'와 같은 키워드로 검색하면 Swap 메모리를 끄는 이유에 대해 좀 더 자세한 이유를 확인할 수 있다.

 

Why disable swap on kubernetes

Since Kubernetes 1.8, it seems I need to disable swap on my nodes (or set --fail-swap-on to false). I cannot find the technical reason why Kubernetes insists on the swap being disabled. Is this for

serverfault.com

sudo swapoff -a
#swap 메모리 기능 끄기

sudo vim /etc/fstab
#/etc/fstab 파일 편집

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/ubuntu-vg/ubuntu-lv during curtin installation
/dev/disk/by-id/dm-uuid-LVM-x93Y2LQWPORE2IhzvLJa1nWivxAsIJYKgeZukll779GGILhsrRZN0ndEcYTJmfik / ext4 defaults 0 1
# /boot was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/e2778f13-50eb-4a1b-b6ff-e2608ac1cfa8 /boot ext4 defaults 0 1
#/swap.img      none    swap    sw      0       0

#가장 아래의 /swap.img행 맨 앞에 '#'를 추가하여 주석 처리

 우선 sudo swapoff -a를 통해 현재 활성화된 swap 메모리를 꺼주고, 재부팅시에도 해당 설정이 적용될 수 있도록 /etc/fstab 파일을 열어서 맨 아래 행에 있는 /swap.img를 주석처리 해 준다.

#커널 모듈 로드

sudo tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

 그리고 커널 모듈을 로드하기 위해 위 명령어를 입력해 준다. 이 후, tee 명령어를 통해 커널 파라미터 설정을 해 준다.

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

[사진 2] 커널 모듈과 관련된 설정을 위와 같이 입력

 이렇게 하면 /etc/modules-load.d 경로에 container.conf 파일과 /etc/sysctl.d 경로에 kubernetes.conf 파일이 생성되고 EOF 문자 사이의 내용이 입력되게 된다. 위 변경사항을 적용하기 위해 아래 명령어를 입력한다.

sudo sysctl --system

[사진 3] 하단 '* Applying /ect/sysctl.d/kubernetes.conf ...' 설정 확인 가능

 

  (3) 컨테이너 런타임 설치

 Kubernetes의 Pod 내부의 컨테이너는 컨테이너 런타임에 의해 생성된다. 해당 포스팅에서는 Kubernetes의 CRI(Container Runtime Interface) 표준을 만족하는 Containerd를 컨테이너 런타임으로 사용한다. 여담으로, 예전에는 Kubernetes에서는 컨테이너 런타임으로 Docker를 사용할 수 있었지만 1.20 버전 이후부터 Docker의 지원을 중단했다. 관련 내용은 아래 링크 및 'docker shim'에 관한 키워드로 구글링해 보면 자세한 내용을 알 수 있다.

 

Don't Panic: Kubernetes and Docker

Update: Kubernetes support for Docker via dockershim is now removed. For more information, read the removal FAQ. You can also discuss the deprecation via a dedicated GitHub issue. Authors: Jorge Castro, Duffie Cooley, Kat Cosgrove, Justin Garrison, Noah Ka

kubernetes.io

 이미 많은 분들이 해당 내용에 대해 이해를 하고 있을 것이라고 생각한다. 그럼에도 불구하고 이 내용을 언급하는데는, Kubernetes를 학습하는 데 있어 중요한 부분이라 생각하기 때문이다. 시간을 들여서 해당 내용을 이해한다면 Kubernetes에 대한 식견이 조금 더 넓어질 수 있을 것이다.

 

 먼저, 의존성이 있는 패키지를 먼저 설치하고 Containerd를 설치한다.

sudo apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates

 이후 docker 리포지터리를 enable 해 준다.

sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
#진행을 위해 Enter 입력

 그리고, 아래 명령어를 실행하여 Containerd를 설치한다.

sudo apt update
sudo apt install -y containerd.io

 설치가 완료되면, Containerd가 systemd를 cgroup으로 사용하여 시작할 수 있도록 구성한다.

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

 Containerd 서비스를 재시작 하고 자동 실행 등록을 해 준다.

sudo systemctl restart containerd
sudo systemctl enable containerd

 

  (4) Kubernetes용 APT 리포지터리 추가 및 Kubelet, Kubeadm, Kubectl 설치

 Kubernetes 설치를 위한 사전 설정 작업이 모두 끝나고 본격적으로 Kubernetes 클러스터 구성을 위한 과정을 진행한다. 먼저 Kubernetes용 APT 리포지터리를 추가한다.

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/kubernetes-xenial.gpg
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
#진행을 위해 Enter 입력

 이제 Kubelet, Kubeadm, Kubectl을 설치할 차례이다. 아래 명령어를 입력해 준다.

sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

참고 : 포스팅 시점인 2023년 10월 기준 현재 Kubernetes 최신 버전인 1.28.2가 설치된다.

 

 3) 설정 작업이 끝난 k8s-cp용 VM 전원을 끈 후 워커 노드로 복사

  (1) k8s-cp 노드 복제

 각 노드에 공통으로 해 주어야 할 작업은 모두 마무리되었다. 이제 k8s-cp VM의 전원을 끄고 복제를 통해 워커 노드 3대를 생성해 준다.

[사진 4] k8s-cp를 복제하여 워커 노드 3대를 생성

 복제가 완료되면 모든 노드의 전원을 켠 후, 각각의 노드에 웹 콘솔로 접근하여(설정 전에는 동일한 IP주소를 사용하기 때문에 ssh로는 접근이 불가능하다. 따라서 각 Hypervisor가 직접 제공하는 가상 머신 콘솔을 통해 접속하여 IP주소를 변경해 주어야 한다.) IP 주소와 호스트 네임을 변경해 주어야 한다. k8s-cp를 복제하였기 때문에 기존 k8s-cp의 설정을 그대로 가지고 있기 때문이다.

 

 

 4) 각 노드별 호스트 이름과 IP 주소 변경

  (1) 호스트 이름 변경

 우선, /etc/hosts 파일부터 변경해 주도록 하자.

sudo vim /etc/hosts

127.0.0.1 localhost
==================================================================================
127.0.1.1 k8s-w01        #k8s-w01 설정 값
127.0.1.1 k8s-w02        #k8s-w02 설정 값
127.0.1.1 k8s-w03        #k8s-w01 설정 값
#호스트 별로 각각 한줄씩만 입력
==================================================================================
192.168.0.150 k8s-cp
192.168.0.151 k8s-w01
192.168.0.152 k8s-w02
192.168.0.153 k8s-w03

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

 그리고 각 워커 노드의 호스트 이름도 변경해 준다.

sudo hostnamectl set-hostname k8s-w01   #모든 워커 노드에서 작업 진행, 단 워커 노드의 번호에 주의
exec bash

====================================================================================

sudo hostnamectl set-hostname k8s-w02
exec bash

====================================================================================

sudo hostnamectl set-hostname k8s-w03   
exec bash

 

  (2) IP 주소 변경

다음으로 IP 주소를 변경해 준다. IP 주소는 /etc/netplan/00-installer-config.yaml 파일을 편집하여 변경할 수 있다.

sudo vim /etc/netplna/00-installer-config.yaml

# This is the network config written by 'subiquity'
network:
  ethernets:
    ens160:
      addresses:
      - 192.168.0.151/24     #w01의 경우 192.168.0.151/24, w02의 경우 192.168.0.152/24
      nameservers:           #w03의 경우 192.168.0.153/24로 각각 알맞은 IP 주소 입력  
        addresses:
        - 8.8.8.8
        search:
        - study.local
      routes:
      - to: default
        via: 192.168.0.1
  version: 2
#IP 주소 변경 후 저장하고 닫기

sudo netplan apply   #변경 내용 적용

 실수 없이 컨트롤 플래인과 워커 노드의 호스트 이름과 IP 주소를 모두 입력했으면 Kubeadm을 통해 Kubernetes 클러스터를 구성하는 작업을 수행한다.

 

 

5) Kubeadm을 통한 Kubernetes 클러스터 구성

  (6) 컨트롤 플레인 구성

 여기서부터 컨트롤 플레인 노드와 워커 노드에 입력하는 명령어가 다르기 때문에 실수하지 않도록 주의하여야 한다. 먼저 컨트롤 플레인 노드에서 아래와 같은 명령어를 입력한다

 

주의 : 컨트롤 플레인에서 작업할 것!

sudo kubeadm init --apiserver-advertise-address=192.168.0.150
#--apiserver-sdvertise-address의 경우 kubectl 명령어를 통해 
#kubernetes api server를 호출할 endpoint
#포스팅에서는 컨트롤 플레인 노드의 주소를 사용

[init] Using Kubernetes version: v1.28.2
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
W1015 04:35:14.897758    2458 checks.go:835] detected that the sandbox image "registry.k8s.io/pause:3.6" of the container runtime is inconsistent with that used by kubeadm. It is recommended that using "registry.k8s.io/pause:3.9" as the CRI sandbox image.

...(중략)

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
#위 3줄의 명령어를 입력하여 일반 사용자 권한으로 Kubernetes 클러서트를 사용할 수 있도록 허용

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You 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.150:6443 --token tnbij4.klamn95e7dqb9koq \
        --discovery-token-ca-cert-hash sha256:c3012bb5ec60782b30dfc01b14a44774d0fb21ef8a41d47dbd5b03db581ab235
#위의 'kubeadm join 192.168.0.150:6443...' 명령어는 앞에 sudo를 붙여서 각 워커 노드에 입력
#이를 통해 컨트롤 플레인과 워커 노드가 Kubernetes 클러스터를 구성
#token값과 sha256값은 랜덤하게 생성되므로 실제 클러스터 구성 시에 출력되는 값을 복사하여야 함

[사진 5] Kubeadm init을 통해 컨트롤 플레인 구성을 성공하면 위와 같이 컨트롤 플레인 노드와 워커 노드에 입력해야할 명령어를 확인할 수 있음

컨트롤 플레인 구성이 끝나면 중간에 3줄의 명령어를 컨트롤 플레인 노드에 입력하여 일반 사용자 권한으로 Kubernetes 클러스터를 사용할 수 있도록 해주어야 한다.

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

 

  (2) 워커 노드 Join

 주의: 워커 노드에서 작업할 것!

이제 각 워커 노드에 ssh를 통해 접속하여 마스터 노드에 출력된 명령어를 입력해 주어야 한다. 이때 주의해야 할 점은 반드시 워커 노드에서 sudo를 붙여서 실행해야 한다는 것이다. 이 부분에서 실수하지 않도록 주의해야 한다.

#각 워커 노드에 아래 명령어 입력

sudo kubeadm join 192.168.0.150:6443 --token tnbij4.klamn95e7dqb9koq \
        --discovery-token-ca-cert-hash sha256:c3012bb5ec60782b30dfc01b14a44774d0fb21ef8a41d47dbd5b03db581ab235
        
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

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.

 이때, 입력하는 token값과 sha256 값은 랜덤 하게 부여되므로, 실제 설치 시 컨트롤 플레인 노드의 화면에 출력되는 값을 복사하여 붙여 넣어야 한다.

 

  (3) Kubernetes 클러스터 구성 확인

 이제 다시 컨트롤 플레인 노드로 돌아와 아래와 같은 명령어를 입력한다.

kubectl cluster-info

#CoreDNS is running at https://192.168.0.150:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

#To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

kubectl get nodes

NAME      STATUS     ROLES           AGE     VERSION
k8s-cp    NotReady   control-plane   19m     v1.28.2
k8s-w01   NotReady   <none>          3m21s   v1.28.2
k8s-w02   NotReady   <none>          3m13s   v1.28.2
k8s-w03   NotReady   <none>          2m59s   v1.28.2
#Kubernetes 클러스터를 구성하는 노드들의 정보를 조회

[사진 6] Kubernetes Cluster 구성이 완료 된 상태

 kubectl get nodes 명령어를 통해 위와 같이 각 노드들이 조회가 되면 Kubernetes 클러스터 구성이 완료가 되었다. 그러나 각 노드의 상태값이 NotReady로 표시되는데 이는 바로 CNI(Container Network Interface)가 구성되지 않았기 때문이다. CNI란 Kubernetes 클러스터 운영에 필수 Plug-in으로 반드시 설치해야 한다. CNI와 그 설치 방법에 대해서는 다음 포스팅에서 다루어 보도록 하겠다.

[사진 7] CNI가 설치 되지 않아 Kubernetes Cluster 내의 coredns pod가 Pending 상태

 

2. 마무리

 다소, 많은 명령어를 입력해야 해서 복잡해 보일 수 있으나, 실제 작업을 진행해 보고, 또 익숙해지면 생각보다 그렇게 난이도가 높은 것은 아니다. 다음 포스팅에서는 CNI가 무엇인지에 대해 알아보고, CNI 설치를 위한 Helm 차트와 본격적인 설치 방법에 대해서도 알아보도록 하겠다.