요약
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>
'Data science > MLOps' 카테고리의 다른 글
[mlflow] child run id 조회하기 (0) | 2023.09.25 |
---|---|
[Step by Step] Github action으로 CD/CI 배포하기 (예제코드 포함) (0) | 2023.07.31 |
도커 이미지에서 딥러닝 GPU가속 사용하기 (0) | 2022.12.17 |
도커 이미지 푸시하기 (0) | 2022.12.12 |
[쿠버네티스] 서비스(Service) 이용하기, 클러스터IP에 접속이 안될 때, (0) | 2022.12.10 |