# 클러스터 생성 및 설정 (Kubeadm)

본질적으로 "**Vanilla Kubernetes**"는 광범위한 사용 사례에 적합한 쿠버네티스의 표준 버전을 의미하다. 이 버전은 쿠버네티스의 핵심 기능들을 모두 포함하고, 이번 데모에서는 한 물리 서버에, 가상 머신 3대를 사용하여 쿠버네티스 클러스터를 생성할 예정이다.

💡 *현재 VMWare Workstation에서 실습 진행*

- Master Node: 192.168.1.10
- Worker Node 1: 192.168.1.20
- Worker Node 2: 192.168.1.30

<p class="callout warning">쿠버네티스 클러스터 설정 시 Swap Memory 비활성화를 권장 한다. Swap Memory는 하드 디스크를 이용하여 RAM을 보충하는 가상 메모리이지만, K8s는 많은 자원을 요구하므로 Swap Memory를 사용하면 성능 저하와 스케줄링 지연이 발생할 수 있다. 또한, Swap Memory는 리소스 관리에 문제를 야기할 수 있으므로 안정적이고 효율적인 성능을 위해 스왑 메모리를 비활성화해야 한다. 더 자세한 내용은 여기 블로그 [링크](https://kgw7401.tistory.com/50) 참고.  
</p>

###   


### **CentOS 7에서 K8s 클러스터**

##### **Docker Installation**

시작 전, 이전 버전들을 삭제 한다

```bash
sudo dnf remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
```

yum-utils 설치와 로컬 yum repository (저장소) 지정

```bash
sudo dnf install -y yum-utils
```

```bash
sudo dnf-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
```

도커 최신 버전 설치

```bash
sudo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```

도커 서비스 시작

```bash
sudo systemctl start docker
```

 도커 버전 확인

```bash
docker version
```

##### **Kubernetes Installation**

💡 *Master Node과 Worker Node에서 동일 하게 작업*

[Swap Memory](http://138.2.116.150/books/kubernetes/page/8d7d8) 비활성화

```bash
swapoff -a
```

daemon.json 파일 생성 후 cgroupdriver을 systemd로 설정

```bash
vi /etc/docker/daemon.json

{
    "exec-opts": ["native.cgroupdriver=systemd"]
}
```

daemon 재시작

```bash
systemctl daemon-reload
systemctl restart docker
```

쿠버네티스 로컬 저장소 지정 및 설치

```bash
vi /etc/yum.repos.d/kubernetes.repo

[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
       https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg

# 쿠버네티스 설치
dnf install -y kubelet-1.19.16-0.x86_64 kubectl-1.19.16-0.x86_64 kubeadm-1.19.16-0.x86_64
```

쿠버네티스 설치 완료 확인

```bash
rpm -qa | grep kube
```

쿠버네티스 클러스터 요소들 통신을 위해 방화벽 포트 추가

```bash
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --permanent --add-port=2376/tcp
firewall-cmd --permanent --add-port=2379/tcp
firewall-cmd --permanent --add-port=2380/tcp
firewall-cmd --permanent --add-port=6443/tcp
firewall-cmd --permanent --add-port=8472/udp
firewall-cmd --permanent --add-port=9099/tcp
firewall-cmd --permanent --add-port=10250/tcp
firewall-cmd --permanent --add-port=10251/tcp
firewall-cmd --permanent --add-port=10252/tcp
firewall-cmd --permanent --add-port=10254/tcp
firewall-cmd --permanent --add-port=10255/tcp
firewall-cmd --permanent --add-port=30000-32767/tcp
firewall-cmd --permanent --add-port=30000-32767/udp
firewall-cmd --permanent --add-masquerade
firewall-cmd --reload
```

💡*마스터 노드에서만 진행*

쿠버네티스 클러스터 생성

```bash
kubeadm init --apiserver-advertise-address=192.168.1.10 --pod-network-cidr=10.244.0.0/16

# K8s control plane 생성 완료 화면
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.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.1.10:6443 --token 172vji.r0u77jcmcnccm6no \
    --discovery-token-ca-cert-hash sha256:72b9648c647f724ab52471847cb06c47b23097375f2e67633b745fc69db16e8d 
```

kubectl 활성화 위해 admin.conf 복사 작업

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

네트워크 플러그인 다운로드 ([Flannel](http://138.2.116.150/books/kubernetes/page/8d7d8))

\[ CNI(Container Network Interface)는 컨테이너를 위한 네트워킹을 제어할 수 있는 플러그인을 만들기 위한 표준이라고 한다. 간단하게 설명 하면 CNI플러그인을 쿠버네티스 클러스터에 설치하여 서로 다른 노드에서 실행되는 컨테이너 간의 네트워킹을 할 수 있다 \]

더 자세한 내용은 [링크](https://tommypagy.tistory.com/390) 참고

```bash
curl -O -L https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
```

kube-flannel.yml 파일 안에 --iface=(가상 인터페이스 이름) 지정

```bash
vi kube-flannel.yml

args:
        - --ip-masq
        - --kube-subnet-mgr
        - --iface=ens160
```

네트워크 플러그인 YAML 파일 적용 후 kubelet 재시작

```bash
kubectl apply -f kube-flannel.yml
systemctl restart kubelet
```

💡 *Worker Node에서 작업*

Master Node에서 받은 Discovery Token 값을 join 명령어로 통해 Master과 Worker 노드 묶기

```bash
kubeadm join 192.168.1.10:6443 --token 172vji.r0u77jcmcnccm6no \
    --discovery-token-ca-cert-hash sha256:72b9648c647f724ab52471847cb06c47b23097375f2e67633b745fc69db16e8d 
    
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.
```

Join 작업이 완료 후 마스터 노드에서 쿠버네티스 클러스터 확인

```bash
kubectl get nodes
NAME     STATUS   ROLES    AGE    VERSION
master   Ready    master   107m   v1.19.16
node-1   Ready    <none>   91s    v1.19.16
node-2   Ready    <none>   48s    v1.19.16
```

### **Rocky Linux 9.1에서 K8s 클러스터**

##### **Docker Installation**

업데이트 확인 및 업데이트 있으면 실행

```bash
sudo dnf check-update
sudo dnf update
```

도커 repository (저장소) 추가

```bash
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
```

도커 설치

```bash
sudo dnf install docker-ce docker-ce-cli containerd.io
```

도커 시작 및 확인

```bash
sudo systemctl start docker 
sudo systemctl enable docker

sudo docker version
```

'sudo' 생략 하고 docker 만 쓰고 싶으면

```bash
sudo usermod -aG docker $(whoami)
```

##### **Kubernetes Installation**

💡 *Master Node과 Worker Node에서 동일 하게 작업*

Swap Memory 비활성화

```bash
sudo swapoff -a
```

daemon.json 파일 생성 후 cgroupdriver을 systemd로 설정

```bash
sudo vi /etc/docker/daemon.json

{
    "exec-opts": ["native.cgroupdriver=systemd"]
}
```

daemon 재시작

```bash
sudo systemctl daemon-reload
sudo systemctl restart docker
```

쿠버네티스 로컬 저장소 지정 및 설치

```bash
sudo vi /etc/yum.repos.d/kubernetes.repo

[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
       https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg

# 쿠버네티스 설치
sudo dnf -y kubelet-1.19.16-0.x86_64 kubectl-1.19.16-0.x86_64 kubeadm-1.19.16-0.x86_64
```

쿠버네티스 설치 완료 확인

```bash
sudo rpm -qa | grep kube
```

쿠버네티스 클러스터 요소들 통신을 위해 방화벽 포트 추가

```bash
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --permanent --add-port=2376/tcp
sudo firewall-cmd --permanent --add-port=2379/tcp
sudo firewall-cmd --permanent --add-port=2380/tcp
sudo firewall-cmd --permanent --add-port=6443/tcp
sudo firewall-cmd --permanent --add-port=8472/udp
sudo firewall-cmd --permanent --add-port=9099/tcp
sudo firewall-cmd --permanent --add-port=10250/tcp
sudo firewall-cmd --permanent --add-port=10251/tcp
sudo firewall-cmd --permanent --add-port=10252/tcp
sudo firewall-cmd --permanent --add-port=10254/tcp
sudo firewall-cmd --permanent --add-port=10255/tcp
sudo firewall-cmd --permanent --add-port=30000-32767/tcp
sudo firewall-cmd --permanent --add-port=30000-32767/udp
sudo firewall-cmd --permanent --add-masquerade
sudo firewall-cmd --reload
```

💡*마스터 노드에서만 진행*

슈퍼 유저 (root 계정)으로 전환

```bash
sudo su
```

쿠버네티스 클러스터 생성

```bash
kubeadm init --apiserver-advertise-address=192.168.1.10 --pod-network-cidr=10.244.0.0/16

# K8s control plane 생성 완료 화면
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.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:

sudo kubeadm join 192.168.1.10:6443 --token 1x7qb2.dww3u7mjsrq2hoxt --discovery-token-ca-cert-hash sha256:10a6803e9a45bb029af4ad3c1d0d894dfaee9980d2318c495739296daeffb9eb
```

kubectl 활성화를 위해 admin.conf 복사 작업 (꼭 root계정으로 해야됨!)

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

네트워크 플러그인 다운로드

```bash
curl -O -L https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
```

네트워크 플러그인 YAML 파일 적용 후 kubelet 재시작 (꼭 root계정으로 해야됨!)

```bash
kubectl apply -f kube-flannel.yml
systemctl restart kubelet
```

💡 *Worker Node에서 작업*

Master Node에서 받은 Discovery Token 값을 join 명령어로 통해 Master과 Worker 노드 묶기

```bash
sudo kubeadm join 192.168.1.10:6443 --token 1x7qb2.dww3u7mjsrq2hoxt \
--discovery-token-ca-cert-hash sha256:10a6803e9a45bb029af4ad3c1d0d894dfaee9980d2318c495739296daeffb9eb

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

Join 작업이 완료 후 마스터 노드에서 쿠버네티스 클러스터 확인

```bash
sudo kubectl get nodes
NAME       STATUS   ROLES    AGE    VERSION
master     Ready    master   33m    v1.19.16
worker-1   Ready    <none>   91s    v1.19.16
worker-2   Ready    <none>   48s    v1.19.16
```

### **Ubuntu 20.04.6에서 K8s 클러스터**

##### **Docker Installation**

업데이트 확인 및 실행

```bash
sudo apt update
```

필수 패키지 설치

```bash
sudo apt install ca-certificates curl gnupg lsb-release
```

도커 GPG 키 추가

```bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
```

도커 Repository 등록

```bash
echo \
  "deb [arch=$(dpkg --print-architecture) 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
```

도커 엔진 설치 및 확인

```bash
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

sudo docker version
sudo systemctl status docker

## 서버 재시작 시 docker 가 자동으로 시작되도록 설정 ##
sudo systemctl enable docker
```

##### **Kubernetes Installation**

💡 *Master Node과 Worker Node에서 동일 하게 작업*

Swap Memory 비활성화

```bash
sudo swapoff -a
```

노드간 통신을 위한 iptables에 브릿지 관련 설정 추가

```bash
sudo vi /etc/modules-load.d/k8s.conf

br_netfilter

sudo vi /etc/sysctl.d/k8s.conf

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

# sysctl.conf 파일 맨 아래에도 동일 하게 두 줄 추가
sudo vi /etc/sysctl.conf

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

# sysctl 설정 파일들의 변동을 저장 하기 위해
sudo sysctl --system
sudo sysctl -p
```

daemon.json 파일 생성 후 cgroupdriver을 systemd로 설정

```bash
sudo vi /etc/docker/daemon.json

{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
```

daemon 재시작

```bash
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl enable docker
```

apt 업데이트 및 ca 관련 패키지 다운로드 및 설

```bash
sudo apt update
sudo apt install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
```

도커 gpg 및 소스 리스트 내용 추가 및 apt 업데이

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

kubeadm, kubelet 그리고 kubectl 설치 및 활성화

```bash
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

sudo systemctl start kubelet && systemctl enable kubelet
```

💡*마스터 노드에서만 진행*

쿠버네티스 클러스터 생성을 위해 kubeadm init 실행

```bash
sudo kubeadm init --apiserver-advertise-address=192.168.1.10 --pod-network-cidr=10.244.0.0/16

# K8s control plane 생성 완료 화면
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.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.1.10:6443 --token mu72kx.sn06xzcg3lde7mha \
	--discovery-token-ca-cert-hash sha256:d134654458b474c796667776ab8175adbc0e1dc4bc21a712a19a7bb405ee9273 
```

kubectl 활성화를 위해 admin.conf 복사 작업

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

네트워크 플러그인 다운로드

```bash
curl -O -L https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
```

네트워크 플러그인 YAML 파일 적용 후 kubelet 재시작

```bash
kubectl apply -f kube-flannel.yml
sudo systemctl restart kubelet
```

💡 *Worker Node에서 진행*

Master Node에서 받은 Discovery Token 값을 join 명령어로 통해 Master과 Worker 노드 묶기

```bash
sudo kubeadm join 192.168.1.10:6443 --token mu72kx.sn06xzcg3lde7mha \
	--discovery-token-ca-cert-hash sha256:d134654458b474c796667776ab8175adbc0e1dc4bc21a712a19a7bb405ee9273 

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

Join 작업이 완료 후 마스터 노드에서 쿠버네티스 클러스터 확인

```bash
kubectl get nodes
NAME       STATUS   ROLES    AGE    VERSION
master     Ready    master   33m    v1.19.16
worker-1   Ready    <none>   91s    v1.19.16
worker-2   Ready    <none>   48s    v1.19.16
```

#### **Error Troubleshooting** 

##### <span class="notion-enable-hover" data-token-index="0">\[ERROR CRI\]: container runtime is not running \[Issue Encountered\]</span>

<span class="notion-enable-hover" data-token-index="0">이 문제는 사용되는 CRI가 Containerd인 경우 마스터 노드에서 kubeadm init 명령을 실행할 때 자주 발생한다. 대부분의 경우 config.toml 파일에 문제가 있다.</span>

##### 해결

```bash
# config.toml 파일을 삭제 한다
sudo rm /etc/containerd/config.toml

# containerd 서비스 재시작
sudo systemctl restart containerd
```

##### The connection to the server &lt;IP 주소&gt;:6443 was refused - did you specify the right host or port?

이 에러는 보통 API 서버가 실행 중이지 않거나 네트워킹 문제가 있는 경우 발생할 수 있다. 하지만 이 애러가 마스터 노드 재시작 후 발생 한다면 Swap 메모리 문제일 가능성이 높다. 그런 경우 리소스 제약 때문에 발생한 문제일 가능성이 있다. Swap이 활성화되어 있으면, 커널은 비활성화된 페이지를 디스크로 이동시킬 수 있으며, 이로 인해 API 요청과 같은 지연이 발생할 수 있다. 이러한 시스템 재시작 할때 매번 swap memory를 비활성화 해야 된다.

##### 해결

/etc/fstab 설정 파일안에서 swap을 주석 처리 해준다

```bash
sudo vi /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/sda3 during curtin installation
/dev/disk/by-uuid/05bb7e29-bd2d-4ffb-86c6-8868e48548f4 / ext4 defaults 0 1
# /boot/efi was on /dev/sda2 during curtin installation
/dev/disk/by-uuid/ebbd40d6-5f57-4214-806f-8cf8e929b23d /boot/efi ext4 defaults 0 1
# /swap.img     none    swap    sw      0       0
```