개요
쿠버네티스를 주제로한 온라인 강의들의 대부분은 쿠버네티스를 설치할 때 Vagrant
를 사용하여 설치한다.
수업 환경이 수강생들마다 다르기 때문에 이를 통일해야 원활한 강의 진행이 되기 때문에 이해가 안되는 것은 아니다.
하지만 초심자라면 자동화 스크립트를 작성하여 한 번에 설치하는 것보다,
하나하나 수동으로 직접 설치해 보는 것이 좋지 않을까라는 생각이 든다.
구축 환경
호스트 이름 | IP 주소 | OS | 역할 |
---|---|---|---|
kube-cp |
192.168.219.151 | Ubuntu 22.04 | Control Plane |
kube-wk01 |
192.168.219.152 | Ubuntu 22.04 | Worker Node |
kube-wk02 |
192.168.219.153 | Ubuntu 22.04 | Worker Node |
- 1. 메모리: 2GB 이상
- 2. CPU 2GHZ 이상
구성도
구성도의 경우 3대의 Ubuntu 22.04 서버를 기반으로 Master 1대, Node 2대로 구성
설치 진행
Master & Worker
- 마스터와 워커 노드에서 1~8번 작업 진행
1. 패키지 최신화
패키지를 최신 버전으로 갱신 후 리부팅
sudo apt-get update -y && sudo apt-get upgrade -y
2. 타임존 설정
sudo timedatectl set-timezone Asia/Seoul
3. /etc/hosts 설정
sudo bash -c "echo -e '192.168.219.151 kube-cp\n192.168.219.152 kube-wk01\n192.168.219.153 kube-wk02' >> /etc/hosts"
4. Swap 해제
# 스왑 비활성화
sudo swapoff -a && sudo sed -i '/\/swap.img\|\/swap/s/^/#/' /etc/fstab
swap 해제 하는 이유
쿠버네티스를 설치하는 글들을 보다보면 swap memory 비활성화
이 대목은 꼭 들어가있다.
swap memory
는 물리 메모리(RAM)의 용량이 부족할 때, 하드 디스크의 일부 공간을 메모리처럼 사용하는 것 의 기능을 한다.
스왑 메모리를 켜놔도 나쁠건 없어 보이는데
쿠버네티스 클러스터를 구축할 때에는 왜 끄도록 하는걸까?
쿠버네티스의 컴포넌트인 kubelet
은 이러한 상황을 처리하도록 구현되지 않았기 때문이다.
쿠버네티스는 Pod를 생성할 때, 필요한 만큼의 리소스를 할당 받아서 사용하는 구조인데
이 때 메모리 swap을 전혀 고려하지 않고 설계되었기 때문에 클러스터 노드들은 모두 swap 메모리를 비활성화 해줘야 한다.
스왑 기능은 본래 가용된 메모리보다 더 큰 메모리 할당을 가능하도록 하기 위함이고,
쿠버네티스는 주어진 노드의 자원을 100%에 가깝게 사용하는 것이 목적이기에 스왑 메모리 사용은 노드 자원이 일관되지 않기 때문에
쿠버네티스에서 swap off
를 권장하고 있는 것이다.
애초에 swap off
를 하지 않으면 kubelet
이 정상 실행되지 않기 때문에 쿠버네티스를 사용할 수 없다.
kubelet
의 기본 동작에서 노드에서 스왑 메모리가 감지되면 시작에 실패하기 때문이다.
# 스왑 메모리를 활성화 했을 때 kubelet 로그
journalctl -u kubelet.service | grep 'swap'
May 07 04:45:30 k8s-master kubelet[1371]: E0507 04:45:30.439443 1371 run.go:74] \
"command failed" err="failed to run Kubelet: running with swap on is not supported, \
please disable swap! or set --fail-swap-on flag to false. /proc/swaps contained: [Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority /swap.img\t\t4194300\t\t0\t\t-2]"
그러나 쿠버네티스 v1.22
이상 버전부터 리눅스용 스왑 기능을 알파 단계로 도입하였다.
리눅스 사용자는 스왑 메모리 지원을 노드 단위로 활성화 할 수 있으나,
cgroup v2
에 대한 부적절한 지원, 불충분한 메트릭 요약 및 API 통계, 부적절한 테스트 등을 포함한 많은 문제 등이 발생하였다.
https://kubernetes.io/blog/2021/08/09/run-nodes-with-swap-alpha/
v1.28
이상 버전부터 swap을 사용하기 위한 베타 버전을 지원하고 있고
알파 단계와 비교했을 때 스왑이 활성화된 실행에 대해 kubelet
은 알려진 단점들을 많이 보완했다고 한다.
적용 방법은 아래 문서를 참고해보도록 하자.
https://kubernetes.io/blog/2023/08/24/swap-linux-beta/
5. SELinux 비활성화 (pass)
SELinux
미사용 환경에서는 넘어가세요.
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
6. 커널 파라미터 추가
- bridge 네트워크를 통해 송수신되는 패킷(컨테이너 패킷)이 iptables 설정에 따라 제어되도록 설정 해야함.
- 관련 링크
# 부팅 시에 overlay와 br_netfilter 라는 두 가지 모듈을 로드하도록 설정한다.
cat << EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
# overlay 커널 모듈을 즉시 로드. 컨테이너 오버레이 파일 시스템에 사용됨.
sudo modprobe overlay
# br_netfilter 커널 모듈을 즉시 로드. Linux 브리지 네트워크와 관련된 네트워크 필터링에 사용됨.
sudo modprobe br_netfilter
# 여기서는 몇 가지 중요한 파라미터를 설정. 예를 들어,
# net.bridge.bridge-nf-call-iptables는 iptables가 브리지 트래픽을 처리할 수 있도록 하는 설정임.
# sysctl params required by setup, params persist across reboots
cat <<EOF | sudo tee /etc/sysctl.d/kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 설정했던 값을 적용.
sudo sysctl --system
정상적으로 설정되었는지 확인
$ lsmod | grep "^br_netfilter"
# Module Size Used by
br_netfilter 28672 0
$ lsmod | grep overlay
# Module Size Used by
overlay 151552 54
# 다음의 세가지가 모두 1 로 되어 있는 지 확인
$ sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
7. CRI (Container Runtime Interface) 설치
쿠버네티스는 3가지의 도커 컨테이너 인터페이스(CRI) 를 지원한다.
- Docker / Docker-Shim (kubernetes 1.27 버전 이상부터 지원)
- CRI-O
- Containerd
이 중에서 원하는 컨테이너 런타임(CRI)을 설치하면 되는데,
필자의 경우 containerd 로 설치하도록 하겠다.
7-1. ContainerD (권장)
Containerd
로 설치하는 경우
의존성이 있는 패키지 먼저 설치
sudo apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates
이후에 도커 레포지토리를 활성화 해주자.
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
그리고 containerd.io
를 설치한다.
sudo apt update
sudo apt install -y containerd.io
설치가 완료되면 containerd
가 systemd를 cgroup으로 사용하여 시작할 수 있도록 설정해주자.
# defualt cgroupfs에서 systemd로 변경 (kubernetes default는 systemd)
containerd config default | sudo tee /etc/containerd/config.toml 1>/dev/null 2>&1
sudo sed -i 's/ SystemdCgroup = false/ SystemdCgroup = true/' /etc/containerd/config.toml
마지막으로 containerd
서비스를 재시작하고 자동 실행 등록을 해주자.
sudo systemctl restart containerd
sudo systemctl enable --now containerd
7-2. Docker CE
Docker CE
로 설치하는 경우
쿠버네티스 컨테이너 런타임을 위해 도커 설치
sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $USER
도커 구동 확인
docker ps
컨테이너의 cgroup 관리에 systemd를 사용하도록 도커 데몬 구성
sudo mkdir /etc/docker 2>/dev/null
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
8. kubelet, kubeadm, kubectl 설치
kubernetes repository 추가
# 최신 버전 사용시 v1.n으로 변경
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
정상적으로 레포지토리가 추가되었다면, kubelet
, kubeadm
, kubectl
을 설치해주자
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
그리고 kubelet
, kubeadm
, kubectl
에 대한 버전을 고정해주자.
sudo apt-mark hold kubelet kubeadm kubectl
kubeadm
은 kubelet
또는 kubectl
을 설치 및 관리하지 않으므로
kubeadm
이 설치하려는 쿠버네티스의 Control Plane의 버전과 일치하는지 확인해야 한다.
버전이 맞지 않는 경우 예상치 못한 오류로 이어질 수 있는 버전 차이(skew)가 발생할 위험이 크다.
설치 확인
kubectl version --client && kubeadm version
Client Version: v1.29.11
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
kubeadm version: &version.Info{Major:"1", Minor:"29", GitVersion:"v1.29.11", GitCommit:"960a2f019319ab5f7ac1c256efcc180a4113343a", GitTreeState:"clean", BuildDate:"2024-11-20T13:44:59Z", GoVersion:"go1.22.8", Compiler:"gc", Platform:"linux/amd64"}
마스터 노드
- 마스터 노드에서만 9~13번 작업 진행
9. 컨트롤 플레인(Control Place) 구성
kubelet 서비스를 enable로 변경
sudo systemctl enable --now kubelet
클러스터 초기화 (Pod Network 설정)
sudo kubeadm init --pod-network-cidr=<원하는-pod-subnet> --apiserver-advertise-address <컨트롤-플레인-주소>
sudo kubeadm init --pod-network-cidr=172.24.0.0/24 --apiserver-advertise-address 192.168.219.150
sudo kubeadm token create --print-join-command > ~/join.sh
~/join.sh
파일에 노드 조인용 토큰 및 커맨드가 생성된다.
옵션 | 설명 |
---|---|
--pod-network-cidr=172.24.0.0/24 |
이 플래그는 Kubernetes에서 Pod 네트워크의 CIDR (Classless Inter-Domain Routing)를 설정합니다. 이것은 클러스터 내에서 파드 간 통신을 관리하는 IP 주소 범위를 정의합니다. 여기서는 172.24.0.0/24 로 설정되었습니다. |
--apiserver-advertise-address 192.168.219.150 |
이 플래그는 API 서버가 사용할 IP 주소를 설정합니다. Kubernetes API 서버는 클러스터의 상태를 관리하고 클라이언트와 통신하는데 사용됩니다. 여기서는 192.168.219.150 으로 쿠버네티스 마스터 서버 IP가 설정되었습니다. |
10. 쿠버네티스 클러스터 권한 설정
아래 명령어를 실행하여 일반 사용자 권한으로 쿠버네티스 클러스터를 사용할 수 있도록 허용하자.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
11. CNI(Container Network Interface) 설치
CNI란?
쿠버네티스 클러스터에는 수많은 Pod들이 생성과 삭제를 반복한다.
어떠한 이유로 인해 Pod가 작동하지 않는 상태가 되면 쿠버네티스 클러스터는 이를 감지하고
해당 Pod를 폐기한 후 새로운 Pod를 생성시킨다.
Pod는 고유한 IP를 가지고는 있지만, 이는 고정적이지 않기 때문에 새로운 Pod가 생성되면 주소 역시 새롭게 변경 된다.
이러한 유동적인 IP를 관리자가 일일이 관리할 수 있을까?
게다가 쿠버네티스는 멀티 노드 환경에서 구현되기 때문에 각 pod들 간의 통신에 있어 노드를 넘나들어야 하는 경우도 생긴다.
이런 경우에는 각 Pod들이 서로 통신하기 위한 오버레이(Overlay) 네트워크가 필요하게 된다.
이러한 것들을 해결하기 위해 만들어진 컨테이너 네트워킹 제어를 위한 표준이 바로 CNI(Container Network Interface)라고 보면 된다.
CNCF landscape에서 확인해보면 CNI가 꽤 많이 존재하는 것을 볼 수 있다.
그 중에 이번 설치에서 사용할 CNI는 Cilium
이다. CNCF에서 밀어주는 프로젝트이고 BGP 기반 LB를 사용해 볼 수 있다.
(Calico
또한 BGP 기반의 LB 사용이 가능하다.)
설치 진행
1. Helm 설치
Kubernetes에는 여러가지 Add-on들이 존재한다.
이를 쉽게 설치하고 사용하기 위한 툴인 Helm
이라는 것이 존재하는데, 이를 이용해 Cilium 설치를 간단히 수행해보려고 한다.
먼저 Helm을 설치해주자.
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
Helm chart 설치는 비교적 간단하다. 위 명령어를 실행함으로써 Helm 설치가 완료된다.
2. Cilium repo 추가 및 설치
위 공식 문서를 참고하여 Cilium 설치가 가능하다.
아래 코드는 공식 문서 설치 방법을 정리한 것이다.
# helm의 cilium repo 추가
helm repo add cilium https://helm.cilium.io/
# cilium 설치
helm install cilium cilium/cilium --version 1.14.2 \
--namespace kube-system
NAME: cilium
LAST DEPLOYED: Sun Aug 8 05:56:57 2023
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
...
cilium
컨테이너가 모두 배포되는데까지 시간이 조금 걸린다.
시간이 지난 후 kubectl get pods -A
명령어를 실행하면 cilium
컨테이너들이 설치된 것을 볼 수 있고
core-dns
Pod들 또한 정상 작동하는 것을 확인 가능하다.
3. Cilium CLI 설치
Cilium의 더 많은 기능을 사용하기 위해서는 Cilium CLI를 설치해야 한다.
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
위 명령을 입력하면 설치가 완료된다.
설치가 정상적으로 되었는지 테스트해보자.
cilium status --wait
12. 마스터 격리 해제 (선택)
- Control Place에 pod 가 설치가 가능하도록 격리를 해제한다.
- Worker Node 없이 Control Plane으로만 클러스터를 구축하는 경우 해당 명령어를 사용하도록 함
마스터 노드 격리 해제
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
격리 해제 확인
# STATUS가 Ready로 바뀌어야 함.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-node Ready control-plane 19m v1.27.6
워커 노드
- 워커 노드에서 14~15번 작업 진행
14. 워커노드 등록
컨트롤 플레인(Control Place) 구성에서 얻은 토큰 및 명령어(~/join.sh
)를 참고하여 워커 노드 서버에 토큰을 등록
# 워커 노드가 될 서버에서 실행
sudo kubeadm join 192.168.121.56:6443 --token o99wfs.****** \
--discovery-token-ca-cert-hash sha256:*****
마스터 노드에서 다음과 같이 토큰을 다시 얻을 수 있다.
kubeadm token create --print-join-command
15. 워커노드 확인
Control Place으로 이동해 아래 명령어 실행하여 노드들이 등록되었는지 확인
kubectl get nodes -o wide
Clean UP
- 쿠버네티스 삭제
- 워커노드, 마스터노드 전부 진행
쿠버네티스 삭제
sudo kubeadm reset
sudo apt-get -y purge kubeadm kubectl kubelet kubernetes-cni kube*
sudo apt-get -y autoremove
sudo rm -rf ~/.kube
잔여 CNI 캐시 삭제
sudo rm /etc/cni/net.d
# clean up iptables rules or IPVS tables.
sudo apt install ipvsadm
sudo ipvsadm --clear
셸 스크립트로 한방에!
위 모든 과정을 쉘 스크립트로 작성하여 자동화해보자.
참고로 해당 스크립트들은 root 권한/계정에서 실행하도록 한다.
1. 모든 노드에서 실행하는 스크립트
install_default.sh
#!/usr/bin/bash
#=========================================#
# All Node Script #
#=========================================#
echo "============== [1] 마스터 노드 & 워커 노드 기본 설정 =============="
echo -e "====== [1-1] 패키지 최신화 ======\n"
apt-get update -y
echo -e "====== [1-2] 타임존 설정 ======\n"
timedatectl set-timezone Asia/Seoul
echo -e "====== [1-3] hosts 설정 ======\n"
bash -c "echo '192.168.219.150 kube-cp\n192.168.219.151 kube-wk01\n192.168.219.152 kube-wk02' >> /etc/hosts"
echo -e "====== [1-4] 스왑 비활성화 ======\n"
swapoff -a && sudo sed -i '/ swap / s/^/#/' /etc/fstab
echo -e "====== [1-5] SELinux 비활성화 ======\n"
echo "" # pass
echo -e "====== [1-6] 커널 파라미터 추가 ======\n"
cat << EOF | tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
cat << EOF | tee /etc/sysctl.d/kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
echo -e "====== [1-7] CRI (Container Runtime Interface) 설치 ======\n"
apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmour -o /etc/apt/trusted.gpg.d/docker.gpg
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
apt update
apt install -y containerd.io
containerd config default | tee /etc/containerd/config.toml 1>/dev/null 2>&1
sed -i 's/ SystemdCgroup = false/ SystemdCgroup = true/' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable --now containerd
echo -e "====== [1-8] kubelet, kubeadm, kubectl 설치 ======\n"
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | 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/ /' | tee /etc/apt/sources.list.d/kubernetes.list
apt update
apt install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
kubectl version --client && kubeadm version
2. 마스터 노드에서만 실행하는 스크립트
master-node.sh
#!/usr/bin/bash
#=========================================#
# Master Node Script #
#=========================================#
echo "============== [2] 마스터 노드 설정 =============="
echo -e "====== [2-9] 컨트롤 플레인 구성 ======\n"
systemctl enable --now kubelet
kubeadm init --pod-network-cidr=172.24.0.0/24 --apiserver-advertise-address 192.168.219.150
kubeadm token create --print-join-command > ~/join.sh
echo -e "====== [2-10] 쿠버네티스 클러스터 권한 설정 ======\n"
USER_NAME="$(getent passwd 1000 | cut -d ':' -f 1)"
USER_HOME="/home/$USER_NAME"
USER_UID=1000
USER_GID=1000
mkdir -p $USER_HOME/.kube
cp -i /etc/kubernetes/admin.conf $USER_HOME/.kube/config
chown $USER_UID:$USER_GID $USER_HOME/.kube/config
echo -e "====== [2-11] CNI (Container Network Interface) 설치 ======\n"
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | tee /usr/share/keyrings/helm.gpg > /dev/null
apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | tee /etc/apt/sources.list.d/helm-stable-debian.list
apt-get update
apt-get install helm
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --version 1.14.2 \
--namespace kube-system
echo "============== [2] 마스터 노드 설정 완료!=============="
3. 모든 스크립트가 정상적으로 설치되었다면 워커 노드로 이동하여 worker-node 등록해주도록 하자.