[![logo](https://www.hongsnet.net/images/logo.gif)](https://www.hongsnet.net)

# k8s 설치 및 구성

쿠버네티스의 환경을 구성하는 방법은 다양하지만, 여기서는 **kubeadm 툴**을 이용하여 구성한다.

**kubeadm 툴**은 쿠버네티스에서 제공하는 기본적인 도구이며, 쿠버네티스 클러스터를 가장빨리 구성하기 위한 다양한 기능을 제공한다. 일반적으로 주로 사용되는 명령 옵션은 다음과 같다.

| NO | 명령 옵션 | 설명 |
| ------ | ------ | ------ |
| 1 | **kubeadm init** | 쿠버네티스의 컨트롤 플레인 노드를 초기화한다. 즉, 마스터 Node를 초기화한다. |
| 2 | **kubeadm join** | 쿠버네티스 Worker를 초기화하고, 클러스터에 연결한다. |
| 3 | **kubeadm upgrade** | 쿠버네티스 클러스터를 업그레이드한다. |
| 4 | **kubeadm token** | 부트 스트랩 Token을 사용한 인증에서 사용되며, 클러스터에 참여하는 Node와 컨트롤 플레인 사이에 양방향 신뢰를 설정하는 데 사용된다. |
| 5 | **kubeadm reset** | kubeadm init 혹은 kubeadm join의 변경사항을 최대한 복구한다. |
| 6 | **kubeadm version** | kubeadm 버전을 출력한다. |


# Pre CheckList

- **OS Support**
  - Ubuntu 16.04+
  - Debian 9+
  - `CentOS 7`+
  - RHEL 7+
  - Fedora 25+
  - HypriotOS v1.0.1+
  - 컨테이너 리눅스(1800.6.0+)

> 여기서는 CentOS 7 환경에서 구성한다.

<br>

- **H/W Spec Recommend**
  - Node 당 최소 2GB 이상의 메모리
  - Node 당 최소 CPU 2Core 이상
  - 구성된 클러스터간 상호 네트워크연결이 정상적으로 이루어지는 상태
  - 모든 Node에 대한 고유한 호스트이름 및 MAC주소, product_uuid  존재 (아래 **Node Uniuqe Infomation** 참조)
  - Swap 메모리 비활성화 (아래 **SWAP Memory Disabled** 참조)
  - iptables의 nftables 미사용 환경 (아래 **iptables의 nftables 미사용환경** 참조)
  - 방화벽 포트오픈 필요 (아래 2-4 참조)
<br>

- **Node Uniuqe Infomation**

먼저 **MAC 주소**는 다음과 같이 확인할 수 있다.

```bash
[All Node]# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:8c:73:15 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:2a:35:38 brd ff:ff:ff:ff:ff:ff
4: eth2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 52:54:00:fd:ef:66 brd ff:ff:ff:ff:ff:ff
```

그리고 **product_id**는 다음과 같이 확인할 수 있다.

```bash
[All Node]# cat /sys/class/dmi/id/product_uuid
9D353FB8-9516-4730-AB61-F4D300861059
```

- **SWAP Memory Disabled**

  - POD를 할당하고 제어하는 **kubelet**은 `Swap 메모리를 허용하도록 설계되지 않았다`.
  - 쿠버네티스트의 기존 컨셉이 필요한 리소스만큼 호스트자원을 사용하는 것이다. Swap은 현재 설치된 메모리보다 더 큰, 엄밀히 말하면 가상 메모리할당이 가능하다.
  - `모든 Node의 Swap 메모리는 반드시 비활성화 해줘야 한다`.

먼저 **swapoff -a 명령**으로 비활성화를 진행한다.

```bash
[All Node]# free
              total        used        free      shared  buff/cache   available
Mem:        3880196      365332     3025804       53536      489060     3197136
Swap:       4194300           0     4194300

[All Node]# swapoff -a

[All Node]# free
              total        used        free      shared  buff/cache   available
Mem:        3880196      362436     3028636       53536      489124     3200004
Swap:             0           0           0
```

단, 이 설정은 영구적인 설정이 아니기 때문에 **/etc/fstab의 설정**에 주석처리를 하면 된다.

```bash
[All Node]# cat /etc/fstab |grep swap
#UUID=7498975c-4815-4892-a816-adcd67a10385 swap                    swap    defaults        0 0
```

- **iptables의 nftables 미사용환경**

다음과 같이 **k8s.conf** 파일을 생성한 후 sysctl 명령을 수행한다. 이 과정은 iptables가 우회되어 트래픽이 잘못 라우팅되는 것을 방지하기 위한 설정이다.

```bash
[All Node]# cat /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

[All Node]# sysctl --system
* Applying /usr/lib/sysctl.d/00-system.conf ...
* Applying /usr/lib/sysctl.d/10-default-yama-scope.conf ...
kernel.yama.ptrace_scope = 0
...중략
```

- **방화벽 포트 Open 필요**

| NO | Role | Protocol | 정책 | Port번호 | 용도 | 사용구분 |
| ------ | ------ | ------ | ------ | ------ | ------ | ------ |
| 1 | `MANAGER` | TCP | INBOUND | 6443 | Kubernetes API Server | ALL |
| 2 | `MANAGER` | TCP | INBOUND | 2378, 2380 | etcd Server Client API | kube-apiserver, etcd |
| 3 | `MANAGER` | TCP | INBOUND | 10250 | Kubelet API | Self, Control Plane |
| 4 | `MANAGER` | TCP | INBOUND | 10251 | Kube-scheduler| Self |
| 5 | `MANAGER` | TCP | INBOUND | 10252 | Kube-controller-manager | Self |
| 6 | WORKER | TCP | INBOUND | 10250 | Kubelet API | Self, Control Plane |
| 7 | WORKER | TCP | INBOUND | 30000~32767 | NodePort Service | ALL |


- **구성 시스템에 대한 hosts 설정**

모든 Node의 호스트네임을 설정해야 한다.

```bash
[ALL NODE]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

172.24.0.222 cube01
172.24.0.223 cube02
172.24.0.224 cube03
172.24.0.225 cube04
172.16.0.226 cube05
172.16.0.227 cube06
```


# kubeadm 설치

CentOS 7의 Base Repository에서는 k8s에 대한 Repository를 제공하지 않기 때문에 다음과 같이 별도의 Repository를 설정해야 한다.

```bash
[ALL NODE]# cat /etc/yum.repos.d/kubernetest.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
```

설치는 다음과 같이 모든 Node에서 수행해야 한다.

```bash
[ALL NODE]# yum install kubeadm kubelet kubectl
```

- 구성 패키지 설명
  - **kubeadm** : 클러스터를 시작하기 위한 커맨드 툴
  - **kubelet** : 클러스터의 모든 노드에서 실행되는 컴포넌트이다. POD나 컨테이너를 실행하느 등의 작업을 수행한다.
  - **kubectl** : 클러스터와 통신하기 위한 커맨드 툴 이다. 위의 툴 중 가장 많이 사용되는 툴 이다.


**kubelet** 서비스는 다음과 같이 `활성화만` 진행한다.

```bash
[ALL NODE]# systemctl enable kubelet.service
Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /usr/lib/systemd/system/kubelet.service.
```

# k8s Manager(Master) Node 구성

MasterNode01에서 마스터 노드의 이니셜라이징을 수행한다.

  - **--apiserver-advertise-address** 옵션에 대해 Kubernetes API 서버가 수신하는 IP 주소를 지정합니다.
  - **--pod-network-cidr** 옵션에 포드 네트워크가 사용하는 네트워크를 지정합니다.
 
Pod Network용 플러그인(https://kubernetes.io/docs/concepts/cluster-administration/networking/)이 있으며, 
이 예에서는 **Flannel**을 선택한다. **Flannel**의 경우 --pod-network-cidr=10.244.0.0/16을 지정하여 Pod 네트워크가 정상적으로 작동하도록한다.

```bash
[root@cube01 ~]# kubeadm init --apiserver-advertise-address 172.24.0.222 --pod-network-cidr=10.244.0.0/16
[init] Using Kubernetes version: v1.20.4
[preflight] Running pre-flight checks
        [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
        [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.2. Latest validated version: 19.03
[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'
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [cube01 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 172.24.0.222]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [cube01 localhost] and IPs [172.24.0.222 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [cube01 localhost] and IPs [172.24.0.222 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[kubelet-check] Initial timeout of 40s passed.
[apiclient] All control plane components are healthy after 79.004047 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.20" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node cube01 as control-plane by adding the labels "node-role.kubernetes.io/master=''" and "node-role.kubernetes.io/control-plane='' (deprecated)"
[mark-control-plane] Marking the node cube01 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: 8qooeq.jlogho5870706u5g
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

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 172.24.0.222:6443 --token 8qooeq.jlogho5870706u5g \
    --discovery-token-ca-cert-hash sha256:5cefb514ae202caeb088fafc6aedcf0cea24f909e76e648a9796bcf9ed212951
```

위의 가이드대로, **kubectl**이 이 클러스터에서 동작하도록 다음과 같이 설정한다.

```bash
[root@cube01 ~]# mkdir -p $HOME/.kube
[root@cube01 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@cube01 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
```

위에서 지정한 POD Network 플러그인인 **Flannel** 를 설정한다.

```bash
[root@cube01 ~]# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
```

위와 같이 설정이 완료되었으면, 다음과 같이 Node의 정보를 확인한다.

```bash
[root@cube01 ~]# kubectl get nodes
NAME     STATUS   ROLES                  AGE   VERSION
cube01   Ready    control-plane,master   25m   v1.20.4
```

아래는 Node에 대한 NameSpace를 확인한 내역이다.

```bash
[root@cube01 ~]# kubectl get pods --all-namespaces
NAMESPACE     NAME                             READY   STATUS    RESTARTS   AGE
kube-system   coredns-74ff55c5b-bqvlp          1/1     Running   0          26m
kube-system   coredns-74ff55c5b-sqbsc          1/1     Running   0          26m
kube-system   etcd-cube01                      1/1     Running   0          26m
kube-system   kube-apiserver-cube01            1/1     Running   0          26m
kube-system   kube-controller-manager-cube01   1/1     Running   0          26m
kube-system   kube-flannel-ds-kcvfs            1/1     Running   0          109s
kube-system   kube-proxy-h9d7x                 1/1     Running   0          26m
kube-system   kube-scheduler-cube01            1/1     Running   0          26m
```

# k8s Worker Node 구성

Worker 노드는 위의 Master Node 구성 시 가이드된 **join 명령**을 수행하면 된다.

```bash
[root@cube02 docker]# kubeadm join 172.24.0.222:6443 --token 8qooeq.jlogho5870706u5g \
>     --discovery-token-ca-cert-hash sha256:5cefb514ae202caeb088fafc6aedcf0cea24f909e76e648a9796bcf9ed212951
[preflight] Running pre-flight checks
        [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
        [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.2. Latest validated version: 19.03
[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.
```

이후 다음과 같이 Master Node에서 상태가 "**Ready**" 로 표시되어야 한다.

```bash
[root@cube01 ~]# kubectl get nodes
NAME     STATUS   ROLES                  AGE   VERSION
cube01   Ready    control-plane,master   44m   v1.20.4
cube02   Ready    <none>                 86s   v1.20.4
```

> 참고 : 현재를 기준으로는 ROLES에 "<node>" 이라는 표시된 것은 정상이다.

