반응형

Kubernetes cluster(쿠버네티스 클러스터)를 구축하는 것을 도와주는 여러 도구(kubeadm, kubespray, kops)가 있다.

이 문서에는 그런 kubernetes cluster 구축 도구 중에서 kubeadm을 이용하여 kubernetes cluster를 구축하는 방법을 설명한다.

참고로, 이 문서를 작성하는 시점은 2021년 6월 30일이고, 오늘 Kubernetes cluster를 구축하면서 이 문서에 그 구축 과정을 기록한 것이다.

 

Kubernetes Cluster 구축을 위한 계획하기

CentOS 7.9를 설치한 Master node와 Worker node를 준비한다.

Master Node: 1개 (master-0)

Worker Node: 2개 (worker-0, worker-1)

 

 

Master Node와 Worker Node 준비 작업

master node와 worker node 모두에서 아래의 작업을 수행한다.

kubernetes는 iptables를 이용하여 pod간 통신을 가능하게 한다. 따라서 iptables가 정상 동작하도록 하기 위해 아래와 같이 설정한다.

$  sudo modprobe br_netfilter

$  lsmod | grep br_netfilter
br_netfilter           22256  0
bridge                151336  1 br_netfilter

$  cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

$  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

 

모든 master node, worker node에서 swap 영역을 비활성화한다.

$  sudo sed -i '/swap/d' /etc/fstab
$  sudo swapoff -a
$  free -h
              total        used        free      shared  buff/cache   available
Mem:            15G        1.0G         13G         13M        925M         14G
Swap:            0B          0B          0B
$

 

방화벽(Firewalld)를 비활성화한다.

(원칙은 kubernetes가 사용하는 service port만 allow 설정해야 하지만, 여기서는 간단하게 firewalld를 종료하는 것으로 하겠다)

$  systemctl stop firewalld
$  systemctl disable firewalld

 

Docker Engine 설치 (Installing Container Runtime)

container runtime에는 containerd, CRI-O, Docker 등 다양한 SW가 있지만, 이 문서에서는 Docker 제품을 설치한다.

참고: https://kubernetes.io/docs/setup/production-environment/container-runtimes/#docker

 

Old version의 Docker engine을 제거한다.

$  sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

 

Docker 패키지가 있는 Yum Repository를 추가한다.

 $  sudo yum install -y yum-utils
 
 $  sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

 

Docker engine을 설치하고, 기동한다.

$  sudo yum install -y docker-ce docker-ce-cli containerd.io

$  sudo systemctl start docker

# 아래와 같이 'hello-world' container image를 다운로드해서 docker engine이 정상 동작하는지 확인한다.
$  sudo docker run hello-world

 

Container의 Cgroups에 관한 설정 내용을 추가한다.

$  sudo mkdir /etc/docker
$  cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2", 
# 만약 Private container image registry으로부터 `docker image pull`을 하고 싶다면,
# 아래와 같이 'insecure-registries' 항목에 private container image registry의 주소를 추가한다.
# 이렇게 하면, 별도의 Certificate 없이 docker client가 container image registry에서
# container image를 pulling할 수 있다.
  "insecure-registries": ["10.10.3.33:8080", "my-harbor.sejong.space:8080"]
}
EOF

 

Docker engine을 재시작한다.

$  sudo systemctl enable docker
$  sudo systemctl daemon-reload
$  sudo systemctl restart docker

 

여기까지 설명이  master node, worker node에서 준비해야 할 작업이다.

이 다음 설명부터 실제 kubernetes cluster를 구축하기 위한 작업이다.

 

 


 

kubeadm, kubelet, kubectl 설치

kubeadm: kubernetes cluster를 구축하기 위한 명령 도구

kubelet: master node, worker node에서 데몬 프로세스로 기동되어 있으면서, container와 pod를 생성/삭제/상태를 감시한다.

kubectl: 사용자가 kubernetes cluster에게 작업 요청하기 위한 명령 도구 (예를 들어, 'pod를 생성해달라!'  'pod의 개수를 늘려달라!' 같은 사용자 명령을 kunernetes API server에게 전달)

 

아래의 명령을 따라 하여 kubeadm, kubelet, kubectl 명령 도구를 설치한다. (모든 장비에서 수행해야 함)

# 아래 명령을 모든 'Master node, Worker node'에서 수행해야 한다.

$  cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.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
exclude=kubelet kubeadm kubectl
EOF

# Set SELinux in permissive mode (effectively disabling it)
$  sudo setenforce 0

$  sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

$  sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

$  sudo systemctl enable --now kubelet

 

Kubernetes Cluster 생성하기

아래 명령을 master-0 node에서 수행한다.

# 반드시 `master-0` node에서만 수행해야 한다.
$  kubeadm init --pod-network-cidr=10.244.0.0/16
...
...

# 위 명령은 2분 ~ 3분 정도 소요된다.
# 꽤 오랜 시간이 걸리니까, 인내심을 가지고 기다려야 한다.
# 
# 그리고 위 명령의 출력 내용 중에서 아래의 내용을 복사하여 명령행에 붙여 넣고 수행해야 한다.
# (참고: 이 문서의 뒷 부분에서, 아래 설정 파일 'admin.conf'를 이용해서 
#       Kubernetes cluster 외부에서 Kubernetes API 서버에 접속하는 방법을 설명할 것이다)

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

위 명령을 수행하고 바로 Container Network Interface(CNI) 설치하는 작업을 수행해야 한다.

 

CNI 설치

CNI에는 많은 종류가 있지만, 사용하기 쉽고 대중적인 Calico 또는 Flannel CNI를 설치하겠다.

(A) CNI로써 Calico를 설치하는 경우  <-- 추천

$  kubectl apply  -f https://docs.projectcalico.org/manifests/calico.yaml

(B) CNI로써 Calico를 설치하는 경우 <-- 나는 개인적으로 별루~  (몇달 운영하다보면, Error  발생한 경험이 있어서)

$  kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

 

위 CNI 설명 명령이 수행된 후, 아래와 같이 Pod의 기동 여부를 확인한다.

$  kubectl get pod -A
NAMESPACE     NAME                                                 READY   STATUS    RESTARTS   AGE
kube-system   coredns-558bd4d5db-4qglz                             1/1     Running   0          117m
kube-system   coredns-558bd4d5db-znw7h                             1/1     Running   0          117m
kube-system   etcd-master-0.kube.namiso.space                      1/1     Running   0          117m
kube-system   kube-apiserver-master-0.kube.namiso.space            1/1     Running   0          117m
kube-system   kube-controller-manager-master-0.kube.namiso.space   1/1     Running   0          117m
kube-system   kube-flannel-ds-wnbjv                                1/1     Running   0          115m
kube-system   kube-proxy-8gsdl                                     1/1     Running   0          117m
kube-system   kube-scheduler-master-0.kube.sejong.space            1/1     Running   0          117m
$

 

※ 참고

위 명령 `kubectl apply -f .......calico.yaml` 을 수행 후 CNI 설치에 문제가 있었다면, Pod 'coredns-xxxxxx'  구동하지 못하고 'pending' 상태로 남게 된다.

그런 경우는 대부분 `kubeadm init --pod-network-cidr=10.244.0.0/16` 명령을 수행했을 때 사용했던 CIDR 값이 master node, worker node의 물리 주소와 겹치는 경우에 문제가 발생한다. 따라서 '10.244.0.0/16' 값이 아닌 다른 값으로 다시 변경해서 kubernetes cluster를 생성해보면 문제가 해결될 수 있다.

 

Worker node joining

위에서 `kubeadm init --pod-network-cidr=10.244.0.0/16` 명령을 수행했을 때, 출력되었던 메시지 중에 `kubeadm join ....` 과 같은 형태의 메시지가 있었을 것이다. 그 메시지를 복사해서  모든 worker node에서 수행한다.

# worker-0, worker-1에서 아래의 명령을 수행한다.
kubeadm join 10.10.12.39:6443 --token pdfjgjas.psdfjbh kajsdjhasdfv \
    --discovery-token-ca-cert-hash sha256:3nasklj46kj234k5lj12k3j4gkfdjjgdsfh51a3a686

위 명령이 수행된 이후에 master-0 node에서 아래의 명령으로 cluster 구축된 결과를 확인한다.

$  kubectl get node
NAME                         STATUS   ROLES                  AGE    VERSION
master-0.kube.sejong.space   Ready    control-plane,master   3m     v1.21.2
worker-0.kube.sejong.space   Ready    <none>                 1m     v1.21.2
worker-1.kube.sejong.space   Ready    <none>                 1m     v1.21.2

 

Kubernetes cluster 삭제 (Tear down)

만약 깔끔하게 kubernetes cluster를 지우고, 처음부터 다시 구축하고 싶다면 아래와 같이 cluster를 reset 한다.

$  kubeadm reset;   rm -rf  $HOME/.kube  /etc/kubernetes;   rm -rf /etc/cni

 

 

Bastion Node 설정

위 설명에서는 kubectl 명령을 master-0 node에서 수행했다.

그러나 일반적으로 master-0에 직접 SSH 접속해서 kubectl 명령을 수행하는 것을 권장하지 않는다.

kubernetes cluster node는 운영 node이기 때문에 개발자가 접속하는 것이 바람직하지 않다.

(어쩌면, 보안 규정상 개발자가 master node에 SSH 접속하는 것 자체를 허용하지 않는 회사도 있을 것이다)

따라서 master-0 node가 아닌 본인의 PC(예를 들어 MacBook 같은 PC)에서 접속하는 방법을 사용하는 것을 권장한다.

방법은 간단하다.

master-0 node에 있는 /etc/kubernetes/admin.conf 파일을 내 PC(예를 들어 Macbook)에 복사하기만 하면 된다.

# MacOS를 사용한다고 가정하고 설명하겠다.
$  mkdir -p $HOME/.kube
$  cd $HOME/.kube
$  master-0 node의 '/etc/kubernetes/admin.conf' 파일을 내 PC로 내려받는다.
$  mv  admin.conf  config

# 내 PC에서 Kubernetes cluster의 API 서버로 잘 접속하는지 아래와 같이 명령을 수행해본다.
$  kubectl get node
NAME                         STATUS   ROLES                  AGE    VERSION
master-0.kube.sejong.space   Ready    control-plane,master   128m   v1.21.2
worker-0.kube.sejong.space   Ready    <none>                 126m   v1.21.2
worker-1.kube.sejong.space   Ready    <none>                 126m   v1.21.2
$

 


 

이 아래 부분에서 설명하는 작업 절차는 Kubernetes를 운영하는 데 있어서 꼭 필요한 것은 아니고, Web Dashboard로 좀 더 예쁘게 Kubernetes cluster를 모니터링하고 싶은 경우에 아래 Web Dashboard 설정 작업을 해주면 좋다.

 

Kubernetes Web Dashboard 설치 및 설정

내가 참고했던 Web docs(https://waspro.tistory.com/516) 가 있고, 이 문서에서 설명한 3가지 방식 중에서 3번째 방식(Kubernetes API Server 연동 방식)을 사용하는 것을 권장한다.

이 Web Docs에 설명이 잘 되어 있어서 내가 별도 설명할 필요 없을 것이고, 내가 수행했던 명령만 로그로 남겨보겠다.

 

$  kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
$  kubectl proxy   &

# Service Account 생성

$  cat <<EOF | kubectl create -f -
 apiVersion: v1
 kind: ServiceAccount
 metadata:
   name: admin-user
   namespace: kube-system
EOF
$

# ClusterRoleBinding을 생성

$  cat <<EOF | kubectl create -f -
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
   name: admin-user
 roleRef:
   apiGroup: rbac.authorization.k8s.io
   kind: ClusterRole
   name: cluster-admin
 subjects:
 - kind: ServiceAccount
   name: admin-user
   namespace: kube-system
EOF
$

# 사용자 계정의 Token 확인
$  kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') 
Name:         admin-user-token-p9ldd
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name=admin-user
              kubernetes.io/service-account.uid=041cb7ec-946a-49b6-8900-6dc90fc08464

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  11 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6InFmdGVUUnB6QmhoOHhlSDBJLUNLVHlEWWxpd2ZVaDhBVjZOQXE5TElhVWsifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWZ2dG5uIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjOGExZTY3MS00NmY1LTQwZjctODNkYy02YTE4N2NiYzkzYmYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.lKKD4yEvlpFJ7-BNuPTyO3YRuFYYdQMgPX5azXwn4bZiki2Y886k1dnNM16L4YuA_SSahrPtitSzPfevlAoeC5msdDg1DKRdFuGuYkkI_u_KvOv7orMopDDgZs0zuXFrHIZa1-qiWbgvHfgokOMvndchCcMHyo8pKD3vdBAq_AxtGlCIPImkfALM_d41FrBKIXEjdoCcHkPu7Cz13UAxNRBRs-d274g2UNz-MUnNiomDhJlcYFXTXeooKjHhUiyoFLCgP-V6Wh_1QSCwdfYZGQ1bF0QcZINQZdwluyOtP43AjXHxdSBrAGIPfaY7qsBR_b2upuUDnQsA1w7qkaQB0g     <== 이 빨간색 token을 Web dashboard login 화면에 붙여 넣고, "Sign-in" 버튼을 누른다.
$

 

위와 같이 ServiceAccount와 ClusterRole을 설정하고, secret을 생성/등록한 후에 Web Browser에서 접속하면 된다.

 

접속 주소 예시:  http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

 

 

 

 

Troubleshooting & How to clear the issue

kubeadm join  명령이 실패하는 경우.

대부분 master node에서 생성한지 1시간이 초과된 token 값을 이용해서 worker node에서 join하려고 하면 

'kubeadm join' 명령이 실패한다.

worker node 1, 2, ... 9 이런 식으로 순차적으로 작업하다가 보면, 거의 끝 부분에 있는 worker node 9는 이미 1 시간이 지난 뒤에

'kubeadm join'을 하게 되므로 종종 실패하게 된다.

그러나 심각한 문제는 아니고, master node에서 'kubeadm token create ...' 명령을 사용해서 다시 token 값을 생성해주기만 하면 된다.

아래와 같이 master node에서 token create하고, worker node에서 새로 만들어진 token 값으로 `kubeadm join'하면 된다.

## On master node.
$ kubeadm token create --print-join-command
kubeadm join 10.10.3.33:6443 --token z53s7g.aa...zc --discovery-token-ca-cert-hash sha256:372...3a686


## On worker node.
$ kubeadm join 10.10.3.33:6443 --token z53s7g.aa...zc --discovery-token-ca-cert-hash sha256:372...3a686
[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.
$


## 위와 같이 worker node의 joining이 성공하면, 
## 그 동안 activating (auto-restart) 상태였던 kubelet에 아래와 같이 active(running) 상태로 바뀐다.
## On worker node.
$ systemctl status kubelet
● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           └─10-kubeadm.conf
   Active: active (running) since Thu 2021-11-11 15:38:54 KST; 17s ago
   ...
   ...
 $

위와 같이 Active: active (running) 상태로 출력되면, 정상적으로 kubelet이 기동되고 Master node와 연동된 것이다.

 

+ Recent posts