[k8s] VM에 쿠버네티스 클러스터 구축하기

2022. 8. 22. 19:27[ DevOps ]/[ k8s ]

쿠버네티스 클러스터 구성 도구

1. kubeadm

- 쿠버네티스에서 '공식으로 제공'하는 클러스터 생성, 관리 도구
- single master, multi master를 구축하는데 모두 적합

2. kubespray

- 쿠버네티스 클러스터를 배포하는 오픈소스
- 다양한 형식으로 쿠버네티스 구성 가능(멀티 마스터 구성에 적합)
- 온프레미스에서 상용 서비스 클러스터 운영시 유용
- 다양한 CNI 제공

cf. CNI (Container Network Interface)


- 컨테이너(Pod) 간에 네트워킹을 지원해 주는 '소프트웨어'
- VxLAN, Pod Network 이라고도 부름
- 쿠버네티스를 사용하기 위해서는 반드시 필요함
- 다양한 플러그인이 존재함(플라넬, 칼리코, 위브넷 등)

구성할 요소

1. Control Plane (master node)

- worker node들의 상태를 관리하고 제어
- single master 또는 multi master (3, 5개)로 구축 가능(본 포스팅은 Single master를 기준으로 함)
- single master로 구축 예정

2. worker node

- Docker 플랫폼을 통해 컨테이너를 동작하며 실질적인 서비스가 구동됨
- 2대의 worker node 구축 예정

구축할 쿠버네티스 클러스터 구성도

설치 순서

이번 예시에서는 쿠버네티스에서 공식으로 제공하는 kubeadm을 이용하여 온프레미스 형태로 클러스터를 구축할 것이다.

사전 준비

VM과 네트워크 구성, 도커(또는 컨테이너 런타임) 설치는 미리 구축한 상태로 진행한다. (VM 구축 관련 포스팅: https://jh-labs.tistory.com/473)

Kebernetes 설치


kubeadm을 기반으로 쿠버네티스를 설치한다.
- 링크1: https://kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
- 링크2: http://pwittrock.github.io/docs/setup/independent/install-kubeadm/



사항 확인

- 2core CPU
- 2GB memory
- ubuntu 16.04+
- swap disable: kubelet이 제대로 작동하게 하려면 반드시 스왑을 사용하지 않도록 설정


1. 설치 전 환경 설정

1) swap disable
kubelet이 제대로 작동하게 하려면 반드시 스왑을 사용하지 않도록 설정해야 한다.

$ swapoff -a && sed -i '/swap/s/^/#/' /etc/fstab

 

2) 브릿지 네트워크

master, worker 노드가 브리지 네트워크를 listen 할 수 있도록 설정하기

$ cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

$ sudo sysctl --system

 

만약 아래에서 kubeadm init 명령이수행시 네트워크 브릿지 설정이 되지 않은 이유로 에러가 발생한다면 아래 명령으로 수행한다.

$ vim /etc/sysctl.conf
# net.ipv4.ip_forward=1행 주석 제거
$ sysctl -p
$ cat /proc/sys/net/ipv4/ip_forward  # 1인지 확인

$ modprobe br_netfilter
$ echo 1 > /proc/sys/net/bridge/bridge-nf-call-iptables

 

3) 방화벽, 포트 확인

사용하게 될 포트들은 열어두어야 한다. 방화벽이 설정되 있는지 확인한다.

- Master node

프로토콜 방향 포트범위 목적 used by
TCP inbound 6443 kubernetes API server ALL
TCP inbound 2379 ~ 2380 etcd server client API kube-apiserver, etcd
TCP inbound 10250 kubelet API self, control plane
TCP inbound 10251 kube-scheduler self
TCP inbound 10252 kube-controller-manager self

 

- Worker Node

프로토콜 방향 포트범위 목적 used by
TCP inbound 10250 kubelet API ALL
TCP inbound 30000 ~ 32767 Default port range for NodePort Services. Typically, these ports would need to be exposed to external load-balancers, or other external consumers of the application itself. ALL


위에서 명시된 포트에 대해서 방화벽을 열어두어야 한다. 하지만 필자는 현재 Private 망에서 실습 중이기 때문에 방화벽을 아예 실행하지 않은 채로 설정한다. 만약 방화벽을 설치해서 사용 중이라면 아래 명령어를 입력해서 모든 포트에 대해 방화벽을 해제한다.

$ systemctl stop firewalld 
$ systemctl disable firewalld

 

2. kubeadm, kubectl, kubelet 설치

- kubeadm: 쿠버네티스 클러스터 전체를 관리해주는 커맨드
- kubelet: 컨테이너나 pod를 실행시키고 관리하며 master와 통신할 때 사용되는 데몬 프로세스
- kubectl: 각 클러스터에서 실행할 명령어

* 3개 모두 master, worker에 설치한다.

$ sudo apt-get update
$ sudo apt-get install -y apt-transport-https ca-certificates curl

$ mkdir /etc/apt/keyrings

$ curl -fsSL https://dl.k8s.io/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg

$ echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

$ sudo apt-get update
$ sudo apt-get install kubelet=1.22.8-00 kubeadm=1.22.8-00 kubectl=1.22.8-00 
$ sudo apt-mark hold kubelet kubeadm kubectl

cf. 공식문서 출처: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

 

 

데몬 프로세스 시작

$ systemctl daemon-reload
$ systemctl restart kubelet

 

 

3. control-plane (master node) initialize

- kubeadm을 init하기 전에 container runtime을 설치해야 한다: 링크

- kubeadm으로 single master 구성하기: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/

* control plane을 시작하기 위해서 아래 명령어를 반드시 'master node'에서 실행해야 한다.

$ kubeadm init

// 그 외 옵션 설정 가능
// kubeadm init --pod-network-cidr=10.244.0.0/16

- 위 명령을 'master node'에서 실행하면 master node에 각종 컴포넌트들(API, controller, scheduler, etcd, coreDNS)이 생성된다

- Pod CIDR과 호스트의 CIDR이 겹치지 않도록 --pod-network-cidr를 명시하는걸 권장함. Pod CIDR과 호스트 CIDR이 겹칠 경우, DNS lookup이 안되거나 IP 충돌 발생 가능.

 

 

kubeadm init 커맨드 분석

1) worker 노드 접속 정보

kubeadm init의 가장 아래에 나오는 kubeadm join ~~~ sha256:~~ 부분까지 복사해서 보관해 둔다. 해당 키 값은 worker 노드들이 join 하기 위해 사용된다.

kubeadm join 10.100.0.104:6443 --token ysnpz.....an853h1 \
        --discovery-token-ca-cert-hash sha256:f4ae06949ad8edf25a939aed.....8a462c9b939fb988b47

- 10.100.0.104:6443 : master의 ip와 포트번호
- ysnpz.....an853h1 : master의 토큰
- sha256:f4ae06949ad8edf25a939aed.....8a462c9b939fb988b47 : master로 접근할 때 사용할 인증서

 

2) regular user에게 kubectl 커맨드 허용

kubeadm init 커맨드 중간에 나오는 로그 중 일부는 아래와 같다.

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.conf

root가 아닌 다른 유저 (regular user)들이 kubectl 커맨드를 실행할 수 있도록 지원하기 위해 설정해야 할 사항을 안내하는 것이다.

하지만 아래와 같이 kubectl 커맨드를 사용할 수 없는 문제 발생

root@master:~# kubectl get nodes
The connection to the server 10.100.0.104:6443 was refused - did you specify the right host or port?

 

해결방법!!

왠진 모르겠으나 kubeadm init 부터 다시 해보니 되었다. init시 --pod-network-cidr 옵션을 주고 실행해야 한다. 그리고 그전에 docker, kubeadm을 초기화해야 한다.

# Docker 초기화
$ docker rm -f `docker ps -aq`
$ docker volume rm `docker volume ls -q`
$ sudo umount /var/lib/docker/volumes
$ sudo rm -rf /var/lib/docker/
$ sudo systemctl restart docker

# kubeadm 초기화
$ sudo kubeadm reset
$ sudo systemctl restart kubelet
$ sudo reboot

# kubeadm init 부터 다시한다
$ sudo kubeadm init --pod-network-cidr=10.100.0.0/16

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config  # 쿠버네티스 설정 복사
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config   # 권한 부여
$ echo 'export KUBECONFIG=$HOME/.kube/config' >> $HOME/.bashrc
$ source ~/.bashrc

# 잘 되는지 확인
$ kubectl get nodes

만약 계속에서 오류가 난다면 공식문서 링크를 참고해서 kubeadm init에 적절한 옵션을 설정하자.

cf. kubeadm init 실행시 다음과 같은 에러가 발생한다면

[init] Using Kubernetes version: v1.25.0
[preflight] Running pre-flight checks
        [WARNING SystemVerification]: missing optional cgroups: blkio
error execution phase preflight: [preflight] Some fatal errors occurred:
        [ERROR CRI]: container runtime is not running: output: E0825 19:55:05.469644    8043 remote_runtime.go:925] "Status from runtime service failed" err="rpc error: code = Unimplemented desc = unknown service runtime.v1alpha2.RuntimeService"
time="2022-08-25T19:55:05+09:00" level=fatal msg="getting status of runtime: rpc error: code = Unimplemented desc = unknown service runtime.v1alpha2.RuntimeService"
, error: exit status 1
[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`
To see the stack trace of this error execute with --v=5 or higher


해결방법 (X)

$ sudo rm /etc/containerd/config.toml
$ sudo systemctl restart containerd
$ sudo kubeadm init

구글링하면 위 커맨드를 통해 에러를 해결하라고 안내하는 글이 많은데, 사실 containerd 설정파일에 SystemdCroup에 대한 설정이 있기 때문에 해당 파일을 삭제하면 kubeadm init까지는 잘 수행되더라도 kubectl 커맨드가 제대로 수행되지 않을 수 있으니 좋은 방식은 아니다.

 

CNI (Container Neteork Interface, Pod network add-on) 설치

- 참고링크: https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#pod-network

- CNI 플러그인 설치는 반드시 master 노드에서 진행한다.

- CNI 플러그인이 설치되면 노드의 NotReady 상태가 Ready가 되어야 함.

 

[calico]

$ curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -O --insecure

$ vim calico.yaml // 필요에 따라 CALICO_IPV4POOL_CIDR 수정

$ k apply -f calico.yaml

$ sudo systemctl restart containerd

 

- pod 기본 CIDR 192.168.0.0/16로 설정되어 있다. 만약 설정된 Pod CIDR이 해당 값이 아닐 경우 다운 받은 칼리코 yaml 파일에서 'CALICO_IPV4POOL_CIDR' 값을 수정해줘야 한다.

 

[Weave Net]

$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

 

 

 

4. worker node join

이전에 kubeadm init 커맨드 실행시 복사해 둔 'kubeadm join ~~~ sha256:~~' 부분을 그대로 worker 노드에서 실행시킨 뒤, master 노드에서 아래와 같이 worker 노드가 join 된 것을 확인한다.

 

 

 

cf. kubecl 커맨드를 bash 쉘에서 자동완성 설정하기

kubectl 커맨드는 bash 쉘에서 기본적으로 자동완성(tab)이 지원되지 않는다. 아래 커맨드를 입력하여 bash 쉘에서도 자동완성 기능을 사용하도록 설정할 수 있다. (참고링크)

$ source <(kubectl completion bash) # setup autocomplete in bash into the current shell, bash-completion package should be installed first.
$ echo "source <(kubectl completion bash)" >> ~/.bashrc # add autocomplete permanently to your bash shell.

# k alias 설정
$ vim ~/.bashrc
# alias k=kubectl
# complete -o default -F __start_kubectl k

 

 



 

쿠버네티스 컴포넌트 구성도

 

https://ssup2.github.io/theory_analysis/Kubernetes_Architecture/



 

 

Reference

- 따배쿠, https://youtu.be/lheclzO-G7k
- https://github.com/237summit/k8s_core_labs
- 쿠버네티스 컴포넌트 구성도.  https://ssup2.github.io/theory_analysis/Kubernetes_Architecture/