Data science/MLOps

클러스터 구성 / 쿠버네티스 설치(master, worker node)

연금(Pension)술사 2023. 1. 8. 18:29

요약


master node에 쿠버네티스를 세팅하고, 각 노드랑 연결해보려고합니다. 쿠버네티스로 클러스터를 구성하는 것은 1) control-plane을 설치해야합니다(단일 control-plane또는 복수의 control-plane도 가능), 2) 또한, 각 워커노드들을 등로갛여 파드들의 네트웍이 클러스터내에서 잘 통신되게 하는 것입니다.

 

시작전에 앞서


kubeadm을 이용해서 쿠버네티스를 세팅해보려고합니다. kubeadm으로 쿠버네티스를 세팅하는 것이 쿠버네티스 공식홈페이지에서도 "best practice"라고 설명되어있습니다. kubeadm은 쿠버네티스를 간단히 설치해서 사용해보기에 용이합니다. 

 

 

사용 전 확인사항


쿠버네티스를 사용하기전에 장치에 아래의 스펙요구사항을 만족하는지 확인해야합니다.

  • A compatible Linux host. The Kubernetes project provides generic instructions for Linux distributions based on Debian and Red Hat, and those distributions without a package manager.
  • 2 GB or more of RAM per machine (any less will leave little room for your apps).
  • 2 CPUs or more.
  • Full network connectivity between all machines in the cluster (public or private network is fine).
  • Unique hostname, MAC address, and product_uuid for every node. See here for more details.
  • Certain ports are open on your machines. See here for more details.
  • Swap disabled. You MUST disable swap in order for the kubelet to work properly

마지막의 swap을 끄라고 되어있는데 이는 "kubelet"이라는 데몬이 정상적으로 돌게하기위함이라고 합니다. "kubelet"은 파드에서 컨테이너들이 "선언적으로 정의한"(=내가 세팅한 그대로 현 상태를 유지해주는 것)대로 실행되게끔 해주는 데몬입니다. 

 

본 포스팅에서는 카카오클라우드를 이용해서 private network로 구성된 클러스터를 구성해볼 예정입니다. 사설 IP로 구성된 노드들은 아래와 같습니다.

MAC address가 유요한지 product uuid가 각 노드에서 유니크한지 확인합니다. 아래의 명령어를 이용해서, 각 노드에서 MAC adresss, product_uuid가 유일한지 확인해봅니다. 일단은 마스터노드만 확인해봅니다.

ubuntu@k8s-master:~$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback [.........]
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000
    link/ether [..........]
    
    
ubuntu@k8s-master:~$ sudo cat /sys/class/dmi/id/product_uuid
[...........]

 

K8S을 설치하기전에 필수로 설치해야할 것이 docer가 있습니다. Docker은 다음의 링크로 설치하시길바랍니다 [1]

 

kubeadm, kubelet, kubectl을 설치할 것입니다. 각 기능은 아래와같습니다.

  • kubeadm: 클러스터를 구축하고 실해나는 과정을 의미합니다 (=cluster bootstrap). control-plane, worker nodes을 구성하고 각 노드들끼리 올바른 정보들을 가지고있는지, 통신이 한지를 확인합니다. [2] 
  • kubelet: 선언적으로 정의한 대로 파드를 시작하거나, 컨테이너를 시작해주는 기능을 합니다.
  • kubectl: 쿠버네티스를 운용하는 사용자와 인터페이스하는 기능을 합니다.

 

구글 공개키를 다운로드합니다.

sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg

apt 리포지토리에 쿠버네티스를 등록합니다.

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

apt 패키지를 업데이트하고, kubelet, kubeadm, kubectl을 설치합니다. 아래의 apt-mark hold은 다른 패키지들이 ㅅ ㅓㄹ치되면서 자동으로 해당패키지들은 업그레이드 되거나, 제거되지 않게 하기 위함입니다.

sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl # 패키지 버전/삭제/업데이트 고정

 

cgroup 드라이버를 systemd로 일치

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"
}
EOF
  
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker
 
 
kubeadm reset

 

(option) kubeadm init이 문제가 있는 경우 해결방법

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

 

kubeadm init을 하고나면, 아래와 같이 join하기 위한 토큰도 나오고, 일반사용자도 클러스터를 등록하기위해서 사용하라는 명령어도 나옵니다. 

kubeadm init --apiserver-advertise-address 172.16.2.231  # Master의 Host을 CIDR로 사용합니다.
[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.16.2.231:6443 --token y24p4r.7itfeqy96rkjof8q \
        --discovery-token-ca-cert-hash sha256:5e9e43eadba4eb7858033de6fbb4d99cc811f7d49d96f3637435370463c087e

 

클러스터의 실행을 위해서 위의 나온 메시지대로 $HOME/.kube/config 파일을 설정합니다. (일반사용자로 전환후)

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

 

아래의 명령어로 쿠버네티스(kubelet, kubeadm, kubectl)이 설치된 노드에서 클러스터로 참여할 수 있게됩니다. 그리고, 해당 노드는 워커노드가 됩니다. 이 명령어를 잃버어렸다고해서 참여못하는 것은 아닙니다. 

# kubeadm join <kubernetst cluster API:PORT> --token [token] --discovery-token-ca-cert-hash sha256 <hash>

kubeadm join 172.16.2.231:6443 --token y24p4r.7itfeqy96rkjof8q \
        --discovery-token-ca-cert-hash sha256:5e9e43eadba4eb7858033de6fbb4d99cc811f7d49d96f3637435370463c087e

 

token을 잃어버렸을 경우 및 해시를 잃어버린경우

// 명렁어로 토큰 출력
# kubeadm token list 

// 아래의 명령어로 해시 출력
# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

 

 

control-plane에서만 deployment을 실행하면 파드가 생성되지않음을 확인할 수 있습니다. deployment에는 replicas 수를 3으로 지정해놓았는데요. 할당할 worker노드가 없기 때문에 pending으로 나온 것으로 판단됩니다.  실제로 하나의 파드의 상태를 추적해보면 아래와 같습니다.

root@k8s-master:/home/ubuntu# kubectl apply -f controllers-nginx-deployment-yaml
deployment.apps/nginx-deployment created
# watch "kubectl get pods"
Every 2.0s: kubectl get pods                                                                        k8s-master: Sun Jan  8 09:00:15 2023

NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-85996f8dbd-98xlx   0/1     Pending   0          118s
nginx-deployment-85996f8dbd-d9bhg   0/1     Pending   0          118s
nginx-deployment-85996f8dbd-kjk8t   0/1     Pending   0          118s
root@k8s-master:/home/ubuntu# kubectl describe pod nginx-deployment-85996f8dbd-98xlx
Name:             nginx-deployment-85996f8dbd-98xlx
Namespace:        default
Priority:         0
Service Account:  default
Node:             <none>
Labels:           app=nginx
                  pod-template-hash=85996f8dbd
Annotations:      <none>
Status:           Pending
IP:               
IPs:              <none>
Controlled By:    ReplicaSet/nginx-deployment-85996f8dbd
Containers:
  nginx:
    Image:        nginx:1.14.2
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-h2w8s (ro)
Conditions:
  Type           Status
  PodScheduled   False 
Volumes:
  kube-api-access-h2w8s:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  49s   default-scheduler  0/1 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling..

 

 

Worker노드를 할당해보겠습니다.

1. 마스터노드에서 join command을 stdout합니다.

root@k8s-master:/home/ubuntu# kubeadm token create --print-join-command
kubeadm join 172.16.2.231:6443 --token 8yxfot.ktr4irill5hqw6h7 --discovery-token-ca-cert-hash sha256:bd91d60981b5774add11106cd42e1f8d6db18b241b5fd529b0d638e71

2. Worker Node에서 해당 명령어를 copy&paste 하여 사용합니다.

root@k8s-test:/home/ubuntu# kubeadm join 172.16.2.231:6443 --token 8yxfot.ktr4irill5hqw6h7 --discovery-token-ca-cert-hash sha256:bd91d60981b5774add11106cd42e1f8d6db18b241b5fd529b0d638e713522494
[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.

 

control-plane (master node)로 돌아가, 파드가 정상적으로 실행되는지 확인합니다. nginx 서버 3개가 정상적으로 돌아가는 것을 확인할 수 있었습니다.

root@k8s-master:/home/ubuntu# kubectl get pods --all-namespaces
NAMESPACE     NAME                                      READY   STATUS    RESTARTS   AGE
default       nginx-deployment-85996f8dbd-98xlx         1/1     Running   0          30m
default       nginx-deployment-85996f8dbd-d9bhg         1/1     Running   0          30m
default       nginx-deployment-85996f8dbd-kjk8t         1/1     Running   0          30m
kube-system   calico-kube-controllers-7bdbfc669-5qmkf   1/1     Running   0          4h49m
kube-system   calico-node-4ss8x                         0/1     Running   0          86s
kube-system   calico-node-ns4ct                         0/1     Running   0          4h49m
kube-system   coredns-787d4945fb-lgxsp                  1/1     Running   0          5h8m
kube-system   coredns-787d4945fb-xs85r                  1/1     Running   0          5h8m
kube-system   etcd-k8s-master                           1/1     Running   3          5h8m
kube-system   kube-apiserver-k8s-master                 1/1     Running   3          5h8m
kube-system   kube-controller-manager-k8s-master        1/1     Running   3          5h8m
kube-system   kube-proxy-4rkz6                          1/1     Running   0          5h8m
kube-system   kube-proxy-pzbmc                          1/1     Running   0          86s
kube-system   kube-scheduler-k8s-master                 1/1     Running   3          5h8m

 

 

아래와 같이, NodePort와 deployment을 각각 실행합니다. deployment와 nodeport의 manifest(yaml)은 아래와 같습니다.

# nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: nodeport-svc
spec:
  type: NodePort
  clusterIP: 10.100.100.200
  selector:
    app: my-nginx  # deployment에 의해서 생성되는 라벨의 이름과 동일해야합니다.
  ports:
  - protocol: TCP
    port: 80         # 쿠버네티스 서비스의 포트
    targetPort: 80   # POD로 전달될 포트
    nodePort: 30052  # 각 노드에 뚫릴 포트입니다. 외부와 통신합니다.

 

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports :
        - containerPort: 80

 

// 클러스터에서 실행해봅시다.
# kubectl apply -f nodeport.yaml
# kubectl apply -f deployment.yaml

 

리소스들이 제대로 생성되었는지 확인합니다.

// 리소스들이 잘 실행되는지 확인
root@k8s-master:/home/ubuntu/test# kubectl get pods,deploy,svc,endpoints

NAME                            READY   STATUS    RESTARTS   AGE
pod/my-nginx-7c777db54b-26rcz   1/1     Running   0          34m
pod/my-nginx-7c777db54b-4l2wc   1/1     Running   0          34m
pod/my-nginx-7c777db54b-4wtdn   1/1     Running   0          34m
pod/my-nginx-7c777db54b-8l2vg   1/1     Running   0          34m
pod/my-nginx-7c777db54b-dmtjd   1/1     Running   0          34m
pod/my-nginx-7c777db54b-fphk9   1/1     Running   0          34m
pod/my-nginx-7c777db54b-rcql5   1/1     Running   0          34m
pod/my-nginx-7c777db54b-s658f   1/1     Running   0          34m
pod/my-nginx-7c777db54b-tq5t6   1/1     Running   0          34m
pod/my-nginx-7c777db54b-tqd4q   1/1     Running   0          34m

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx   10/10   10           10          34m

NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
service/kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP        34m
service/nodeport-svc   NodePort    10.100.100.200   <none>        80:30052/TCP   34m

NAME                     ENDPOINTS                                                           AGE
endpoints/kubernetes     172.16.2.231:6443                                                   34m
endpoints/nodeport-svc   172.18.137.166:80,172.18.137.167:80,172.18.137.168:80 + 7 more...   34m

 

각 node로 들어가서 listening 중인지 확인합니다. 쿠버네티스 최신버전들은 netstat으로 확인이 불가능하고, iptables로만 확인이 가능합니다.

ubuntu@k8s-master: ssh k8s-test

ubuntu@k8s-master: su

root@k8s-test:/home/ubuntu# iptables -t nat -L KUBE-NODEPORTS | column -t
Chain                      KUBE-NODEPORTS  (1   references)
target                     prot            opt  source       destination
KUBE-EXT-FDIGUWRAAKMFZSYO  tcp             --   anywhere     anywhere     /*  default/nodeport-svc  */  tcp  dpt:30052

 

30052로 제대로 포트로 요청을 보내서 응답이 오는지 확인해봅니다.

root@k8s-test:/home/ubuntu# curl localhost:30052
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

 

localhost말고, 외부에서 해당 노드로도 요청을 보내봅니다.

// 마스터노드에서 노드1의 사설IP:30052포트로 요청
ubuntu@k8s-master:~$ curl 172.16.2.253:30052
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

 

[1] https://confluence.curvc.com/pages/releaseview.action?pageId=98048155#:~:text=kubelet%2C%20kubeadm%2C%20kubectl%20%EC%84%A4%EC%B9%98%20(master%2C%20node),-%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4&text=%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4%EB%A5%BC%20%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0%20%EC%9C%84%ED%95%B4%20Kubernetes%20%EC%A0%80%EC%9E%A5%EC%86%8C%20%EC%B6%94%EA%B0%80%ED%95%9C%EB%8B%A4.&text=%EC%A0%80%EC%9E%A5%EC%86%8C%20%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%20%ED%9B%84%20kubelet%2C%20kubeadm,%EB%A5%BC%20%EC%88%9C%EC%B0%A8%EC%A0%81%EC%9C%BC%EB%A1%9C%20%EC%A7%84%ED%96%89%ED%95%9C%EB%8B%A4.&text=%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4%EB%A5%BC%20%EC%84%9C%EB%B9%84%EC%8A%A4%20%EB%93%B1%EB%A1%9D%20%EB%B0%8F%20%EC%9E%AC%EC%8B%9C%EC%9E%91%EC%9D%84%20%EC%88%98%ED%96%89%ED%95%9C%EB%8B%A4.

[2] https://www.techtarget.com/searchitoperations/tip/Learn-how-to-bootstrap-Kubernetes-clusters-with-kubeadm

반응형