[Kubernetes] Kubernetes Cluster 환경 구축

Cluster 구축을 위한 VM 환경 구성 

현재 Kubernetes Cluster를 VM위에 Ubuntu 22.04 버전을 사용해서 구축할 것이다. Control plane 한대 worker node 2대를 구축해 총 3개의 VM을 구축할 것이다. 로컬 환경은 Mac환경에서 구축하였다. 쿠버네티스 환경을 구축하려면 네트워크 어댑터가 두 개 이상이어야 한다. 만약 쿠버네티스 컴포넌트(API 서버, Kubelet, Pod 네트워크 등)가 사용하는 네트워크가 디폴트 라우트에서 접근할 수 없는 서브넷에 위치한다면, 트래픽이 적절한 어댑터로 전달되지 않을 수 있다. 그래서 VM에 어댑터를 외부통신이 가능한 Nat 어댑터와 내부 통신을 할수 있는 Host Only 어댑터를 192.168.56.0/24 대역으로 설정해주었다.  

먼저 Kubeadm, Kubectl, Kubelet등을 설치하기 전에 쿠버네티스 클러스터가 요구하는 네트워크 설정, 리소스 관리, 컨테이너 런타임 설정 등을 사전에 구성할 것이다. 한번에 스크립트로 구성하였다. 

#!/bin/bash

# 시스템 업데이트
echo "Updating package lists..."
sudo apt -y update

# 방화벽 해제 -> 
echo "Disabling firewall (UFW)..."
sudo ufw disable

# SWAP 해제 (성능 유지)
echo "Disabling SWAP..."
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab

# 노드간 시간 동기화를 위한 NTP(Network Time Protocol) 설치 및 설정
echo "Installing and configuring NTP for time synchronization..."
sudo apt -y install ntp
sudo systemctl restart ntp
sudo systemctl status ntp
sudo ntpq -p

# 노드 간 통신을 지원하기 위해 커널에서 IP 포워딩을 활성화
echo "Enabling IP forwarding for network packet routing..."
sudo -i # 또는 sudo su -
echo '1' > /proc/sys/net/ipv4/ip_forward
cat /proc/sys/net/ipv4/ip_forward

# Containerd 설정을 위한 커널 모듈 로드
echo "Setting up container runtime (Containerd)..."
sudo cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

# 필요한 커널 모듈 로드
echo "Loading required kernel modules..."
sudo modprobe overlay
sudo modprobe br_netfilter

# iptables 브릿지 네트워크 설정 추가
echo "Configuring iptables and enabling bridge network settings..."
sudo cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

# 추가 커널 모듈 설정
sudo cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

# IP 포워딩 및 브릿지 네트워크 규칙 활성화
sudo cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF

# 시스템에 모든 sysctl 설정 적용
echo "Applying all sysctl configurations..."
sudo sysctl --system

Docker 설치

# 패키지 설치 (apt 패키지 및 Docker 관련)
sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common gnupg2

# Docker 공식 GPG 키 추가
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Docker 리포지터리 추가
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

# Docker 및 Containerd 설치
sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Docker 버전 확인
docker version

Container Runtime 설명 

Kubernetes에서의 컨테이너 런타임

Kubernetes는 다양한 컨테이너 런타임으로 Docker, containerd, CRI-O 등이 있다.(Docker는 이제 지원 종료) Kubernetes에서는 기본적으로 컨테이너 런타임 인터페이스(CRI)를 통해 다양한 런타임을 선택하여 사용할 수 있다.

  • Docker: Kubernetes에서 Docker를 사용하려면 Docker가 컨테이너 런타임 역할을 해야 하며, Kubernetes의 kubelet이 Docker 엔진과 통신하여 컨테이너를 관리한다. 
  • containerd: Kubernetes가 Docker 대신 containerd를 직접 컨테이너 런타임으로 사용할 수도 있다. 이 경우, Docker는 런타임으로 사용되지 않고, containerd가 컨테이너 관리 역할을 한다.
  • CRI-O: 또 다른 대안으로, CRI-O는 Kubernetes와 호환되는 경량 컨테이너 런타임이다. 

Docker와 containerd의 관계

Docker는 containerd를 내부적으로 사용하여 컨테이너를 실행한다. 즉, Docker는 자체적으로 컨테이너를 관리할 수 있는 엔진이고, 그 내부에서 실제 컨테이너 실행은 containerd가 담당한다고 보면 된다. 

  • Docker: Docker는 containerd 위에서 동작하는 더 높은 수준의 애플리케이션으로, 컨테이너의 빌드, 실행, 네트워킹, 볼륨 등을 관리한다. 
  • containerd: containerd는 실제로 컨테이너를 실행하고 관리하는 하위 시스템으로, Kubernetes와 같은 시스템에서 컨테이너를 실행하기 위해 Docker 없이도 독립적으로 동작할 수 있다. 

Containerd 설치  

Docker는 18.09 버전부터 Containerd를 내장하고 있다. Docker를 설치하면 사실상 Containerd도 함께 설치되며, 두 런타임이 동시에 존재하는 상황이 된다. 쿠버네티스(Kubernetes)는 컨테이너 런타임으로 여러 옵션을 지원하지만, 기본적으로 한 번에 하나의 런타임만 사용해야 한다. 만약 Docker와 Containerd가 모두 설치된 상태에서 kubeadm을 실행하면, Docker가 우선적으로 감지된다. 이는 Docker가 Containerd를 관리하는 상위 레이어로 동작하기 때문이다. 

공식문서 : https://kubernetes.io/ko/docs/setup/production-environment/container-runtimes/
공식 문서: https://kubernetes.io/ko/docs/setup/production-environment/container-runtimes/#systemd-cgroup-driver

# Containerd 설정 (기본 설정 파일 생성)
sudo sh -c "containerd config default > /etc/containerd/config.toml"
sudo vi /etc/containerd/config.toml
	disabled_plugins = [] # [] CRI 제거 확인

# Containerd 설정 수정 (SystemdCgroup을 true로 설정)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml

# Containerd 서비스 재시작
sudo systemctl restart containerd.service

# Docker 데몬 설정
echo '{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}' | sudo tee /etc/docker/daemon.json

# Docker 서비스 설정 및 재시작
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo usermod -aG docker dj
sudo systemctl daemon-reload
sudo systemctl enable docker
sudo systemctl restart docker

# Containerd 서비스 재시작
sudo systemctl restart containerd.service

# Docker 및 Containerd 서비스 상태 확인
sudo systemctl status docker
sudo systemctl status containerd.service

# 시스템 재부팅 (필요시)
# sudo reboot

# Docker 정보 확인
docker info

containerd 설정

설정에서 containerd config default > /etc/containerd/config.toml 명령어로 containerd의 기본 설정 파일을 /etc/containerd/config.toml에 복사하고, 이 파일에서 SystemdCgroup = true로 변경하는 부분은 Kubernetes에서 systemd를 cgroup 관리 드라이버로 사용하도록 설정하는 것이다. systemd cgroup 드라이버를 사용하면 Kubernetes가 리소스를 보다 일관되게 관리할 수 있다.

Kubernetes에서 Docker 설정 파일을 수정하는 이유

설정에서 daemon.json을 수정하는 이유는 Docker가 컨테이너 런타임으로 사용되지 않더라도, Docker 데몬이 클러스터에서 여전히 설치되어 있기 때문이다. Docker는 기본적으로 containerd를 내장하고 있기 때문에, Docker 데몬이 실행되고 있다면, 이 설정은 Docker가 관리하는 컨테이너의 동작 방식을 설정하는 것이다. 

  • Docker를 직접 Kubernetes 런타임으로 사용하지 않더라도, 시스템에 설치된 Docker 데몬이 containerd를 사용하여 컨테이너를 실행하고 관리할 수 있게끔 설정을 맞춰 주는 것이다. 
  • daemon.json을 통해 Docker의 cgroup 드라이버를 systemd로 설정하는 것은, Docker가 사용될 경우 시스템 리소스를 systemd 방식으로 관리하도록 하기 위해서다. 이는 containerd도 마찬가지로 systemd로 cgroup을 설정하고 있기 때문에, 리소스 관리 방식의 일관성을 유지하려는 목적이다. 

Kubernetes 설치 

kubeadm 설치 공식 문서: https://kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

 

kubelet kubeadm kubectl 설치 (버전 1.29)

공식 문서와 다르게 1.29버전을 설치하려고 다음 방식을 활용하였다. 공식 문서를 따라간 후 kubelet, kubeadm, kubectl을 원하는 버전에 맞게 설치하여도 된다. 

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt update
sudo apt -y install kubelet kubeadm kubectl

Kubernetes 도구 버전 확인

kubeadm version
kubectl version [-o yaml]
kubelet --version

설치된 Kubernetes 도구 업데이트 방지

sudo apt-mark hold kubelet kubeadm kubectl

kubelet 서비스 상태 유지 

sudo systemctl daemon-reload
sudo systemctl restart kubelet.service
sudo systemctl enable --now kubelet.service

현재 구성된 VM을 복제해줘서 Control Plane, Workernode를 각각 구성해주면된다. /etc/hosts 파일을 자신의 설정파일에 맞게 수정해주고 각 vm마다 ssh 통신을 해주어서 ssh timeout 문제를 막아준다. 

Kubernetes Cluster init  작업 

kubeadm init --control-plane-endpoint <control_plane_ip_주소> --pod-network-cidr 172.16.0.0/16 --apiserver-advertise-address <api_server_ip_주소>
dj@k8s-master:~$ sudo kubeadm init --pod-network-cidr=10.96.0.0/12 --apiserver-
     advertise-address=192.168.56.100

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.56.100:6443 --token 4f3xj7.abcw1v2h3s5l6z9m \
    --discovery-token-ca-cert-hash sha256:cd8b7c97b1c7a61b40474c44b7c2ecf4596878a381d4c30cfd8bb8d45e0d58ff

Kubernetes Control Plane 초기화 및 sudo 권한 없이 kubectl 사용하기
admin.conf 구성 파일을 통해 Kubernetes 권한을 부여받아 별도의 권한 지정 없이 실행 가능

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

좀 전에 마지막에 있던 join 값이 있을 것이다. 이 명령어는 kubeadm을 사용하여 Kubernetes 클러스터에 워커 노드를 추가하거나, 추가적인 Control Plane 노드를 연결할 때 사용하는 명령어이다.

kubeadm join 192.168.56.100:6443 --token 4f3xj7.abcw1v2h3s5l6z9m \
    --discovery-token-ca-cert-hash sha256:cd8b7c97b1c7a61b40474c44b7c2ecf4596878a381d4c30cfd8bb8d45e0d58ff
  • 192.168.56.100:6443: 이 부분은 Kubernetes API 서버의 IP 주소와 포트를 나타냅니다. 모든 Kubernetes 구성 요소는 이 API 서버를 통해 클러스터와 통신한다. 
  • --token 4f3xj7.abcw1v2h3s5l6z9m: 클러스터에 노드를 추가하기 위해 필요한 인증 토큰이다. 이 토큰은 kubeadm 초기화 시 생성되며 기본적으로 24시간 동안 유효하다. 토큰이 만료되거나 필요할 경우 아래 명령어로 새로운 토큰을 생성 할 수 있다. 
  • --discovery-token-ca-cert-hash sha256:cd8b7c97b1c7a61b40474c44b7c2ecf4596878a381d4c30cfd8bb8d45e0d58ff: 클러스터의 CA(Certificate Authority) 인증서 해시 값을 포함하여 클러스터의 보안성을 확인하는 데 사용된다. 이를 통해 노드가 클러스터에 연결되기 전에 신뢰할 수 있는 API 서버와의 통신임을 보장한다. 

kubeadm을 사용하여 새로운 토큰을 생성

 kubeadm token create --print-join-command

추가적으로 CA 인증서의 해시를 확인하려면 다음 명령어를 사용할 수 있다. 

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'

이제 token값을 각 노드에 입력해주면 된다. 

dj@k8s-node1:~$ kubeadm join 192.168.56.100:6443 --token 4f3xj7.abcw1v2h3s5l6z9m \
    --discovery-token-ca-cert-hash sha256:cd8b7c97b1c7a61b40474c44b7c2ecf4596878a381d4c30cfd8bb8d45e0d58ff

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

kubectl get po -A 명령을 통해 Control plane에 올라온 파드를 확인해준다. 

NAMESPACE     NAME                                       READY   STATUS    RESTARTS       AGE
kube-system   coredns-76f75df574-7h27l                   0/1     Pending   2 (169m ago)   22h
kube-system   coredns-76f75df574-ln2hh             		 0/1     Pending   2 (169m ago)              12m   22h
kube-system   etcd-k8s-master                            1/1     Running   7 (169m ago)   22h
kube-system   kube-apiserver-k8s-master                  1/1     Running   7 (169m ago)   22h
kube-system   kube-controller-manager-k8s-master         1/1     Running   7 (169m ago)   22h
kube-system   kube-proxy-dj2j5                           1/1     Running   2 (169m ago)   22h
kube-system   kube-proxy-gfwsx                           1/1     Running   2 (169m ago)   22h
kube-system   kube-proxy-nrk68                           1/1     Running   2 (169m ago)   22h
kube-system   kube-scheduler-k8s-master                  1/1     Running   7 (169m ago)   22h

아직 coredns가 안올라온 이유는 CNI(Container Network Interface) 가 존재하지 않아서 이다. CNI를 별도의 yaml파일로 설치해주어야한다. 

CNI(Container Network Interface)

Calico Network Add-On

Control plane에 Calico.yaml 다운로드 

curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml

Calico 리소스 생성

kubectl apply -f calico.yaml

Core DNS 파드가 정상적으로 잘 올라왔고 각 노드에 calico 파드가 생성된 것을 확인 할 수 있다. 

dj@k8s-master:~$ kubectl get po -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS       AGE
kube-system   calico-kube-controllers-658d97c59c-tq2vh   1/1     Running   2 (169m ago)   22h
kube-system   calico-node-c2527                          1/1     Running   2 (169m ago)   22h
kube-system   calico-node-lfpbw                          1/1     Running   2 (169m ago)   22h
kube-system   calico-node-w8zvk                          1/1     Running   2 (169m ago)   22h
kube-system   coredns-76f75df574-7h27l                   1/1     Running   2 (169m ago)   22h
kube-system   coredns-76f75df574-ln2hh                   1/1     Running   2 (169m ago)   22h
kube-system   etcd-k8s-master                            1/1     Running   7 (169m ago)   22h
kube-system   kube-apiserver-k8s-master                  1/1     Running   7 (169m ago)   22h
kube-system   kube-controller-manager-k8s-master         1/1     Running   7 (169m ago)   22h
kube-system   kube-proxy-dj2j5                           1/1     Running   2 (169m ago)   22h
kube-system   kube-proxy-gfwsx                           1/1     Running   2 (169m ago)   22h
kube-system   kube-proxy-nrk68                           1/1     Running   2 (169m ago)   22h
kube-system   kube-scheduler-k8s-master                  1/1     Running   7 (169m ago)   22h

Node의 Status가 정상적으로 Ready 된 모습을 확인할 수 있다. 

dj@k8s-master:~$ kubectl get nodes
NAME         STATUS   ROLES           AGE   VERSION
k8s-master   Ready    control-plane   22h   v1.29.10
k8s-node1    Ready    <none>          22h   v1.29.10
k8s-node2    Ready    <none>          22h   v1.29.10

Worker Node 확장 

사전에 준비한 k8s-node3 vm을 부팅시켜준다. 

토큰값 재확인 

dj@k8s-master:~$ kubeadm token list
TOKEN                     TTL     EXPIRES                USAGES                   DESCRIPTION                                                EXTRA GROUPS
0qjyz4.2ufuh0pl98qj82sy   1h      2024-11-19T18:55:38Z   authentication,signing   <none>                                                     system:bootstrappers:kubeadm:default-node-token
37kgfi.1eec32oo4r9jkpoe   1h      2024-11-19T18:51:12Z   authentication,signing   The default bootstrap token generated by 'kubeadm init'.   system:bootstrappers:kubeadm:default-node-token

만약 토큰이 24시간이 지나 무효화 되었다면 다음 명령어를 통해 토큰을 재발급해준다. 

 kubeadm token create --print-join-command

그 후 node3에 토큰값을 입력해주면 정상적으로 worker node가 확장될 것이다. 

'kubernetes' 카테고리의 다른 글

[Kubernetes] Label, Select & Annotation  (0) 2024.12.03
[Kubernetes] Namespace  (0) 2024.11.29
[Kubernetes] Kubernetes Objects  (0) 2024.11.28
[kubernetes] Pod  (0) 2024.11.27
[Kubernetes] Kubernetes architecture  (0) 2024.11.21