개요
쿠버네티스를 주제로한 온라인 강의들의 대부분은 쿠버네티스를 설치할 때 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 & node)
이제부터 쿠버네티스 클러스터를 본격적으로 설치해보겠습니다.
1. Swap 비활성화
Pod
가 스왑 영역으로 빠지게 되면 성능저하가 발생하기 때문에 쿠버네티스에서는 SWAP을 사용하지 않습니다.
아래 명령을 실행하여 스왑 메모리를 비활성화 해줍시다.
# 스왑 비활성화
sudo swapoff -a && sudo sed -i '/\/swap.img\|\/swap/s/^/#/' /etc/fstab
swap 해제, 꼭 해야해? 🤔
쿠버네티스를 설치하는 글들을 보다보면 swap memory 비활성화
이 대목은 꼭 들어가있죠?
swap memory
는 물리 메모리(RAM)의 용량이 부족할 때, 하드 디스크의 일부 공간을 메모리처럼 사용하는 것 의 기능을 합니다.
swap을 굳이 꺼놓지 않아도 될 것 같은데, 왜 쿠버네티스 클러스터를 구축할 때에는 왜 끄도록 하는걸까요?
쿠버네티스의 컴포넌트인 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/
2. IPv4 Forwarding 기능 활성화
리눅스는 본래 보안상 이슈로 다른 인터페이스로 패킷을 포워딩하지 않습니다.
네트워크간 패킷 통신이 가능하도록 forward를 설정해주겠습니다.
먼저 포워딩이 되어있는지 확인해보겠습니다.
cat /proc/sys/net/ipv4/ip_forward
0
현재 포워딩이 되어있지 않네요.
아래 명령을 실행해 주석 처리된 부분을 해제하여 포워딩을 활성화하겠습니다.
sed -i 's/^#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.conf
관련 설정 적용 후 다시 확인해보세요.
$ sysctl --quiet --system
$ cat /proc/sys/net/ipv4/ip_forward
3. 커널 파라미터 설정
- bridge 네트워크를 통해 송수신되는 패킷(컨테이너 패킷)이 iptables 설정에 따라 제어되도록 설정 해야함.
- 관련 링크
컨테이너 환경에서는 브리지 네트워크를 통해 패킷이 원활하게 포워딩되고,
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
이후에는 시스템 설정(sysctl
)을 업데이트해주겠습니다.
# 여기서는 몇 가지 중요한 파라미터를 설정. 예를 들어,
# net.bridge.bridge-nf-call-iptables는 iptables가 브리지 트래픽을 처리할 수 있도록 하는 설정임.
cat <<EOF | sudo tee /etc/sysctl.d/kubernetes.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 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
모듈 / 시스템 설정 | 설명 |
---|---|
overlay |
- 오버레이 파일시스템을 지원하는 모듈 - 컨테이너 런타임(예: Docker, containerd)에서 사용하는 스토리지 드라이버로, 컨테이너의 계층화된 파일시스템을 생성하는 데 필요 |
br_netfilter |
- 리눅스 브리지 네트워크 스택에서 iptables를 사용한 네트워크 필터링을 지원 - Kubernetes의 네트워킹 플러그인(예: Calico, Flannel)이 정상적으로 작동하기 위해 필요 |
net.bridge.bridge-nf-call-iptables / net.bridge.bridge-nf-call-ip6tables |
- 이 두 설정은 브리지를 통과하는 패킷이 iptables 규칙을 통과하도록 함 - 쿠버네티스의 서비스 네트워크와 파드 간 통신을 위해 필요함 대부분의 쿠버네티스 CNI 플러그인이 iptables를 사용하므로 이 설정이 필요함. |
4. CRI (Container Runtime Interface) 설치
쿠버네티스는 3가지의 도커 컨테이너 인터페이스(CRI) 를 지원합니다.
- Docker / Docker-Shim (kubernetes 1.27 버전 이상부터 지원)
- CRI-O
- Containerd
과거에는 도커 CRI가 많이 사용되었지만, 최신 쿠버네티스는 containerd
를 직접 사용하는 것을 권장합니다.
더 가볍고 효율적이기 때문이죠!
이전 버전의 도커가 설치되어 있다면 제거하겠습니다.
sudo apt-get remove docker docker-engine docker.io containerd runc
의존성 패키지를 설치해줄게요.
apt install -y curl gnupg2 software-properties-common apt-transport-https ca-certificates
도커 GPG 키 추가 및 도커 레포지토리를 활성화 하겠습니다.
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"
이제 containerd
를 설치하겠습니다.
apt update
apt install -y containerd.io
설치가 완료되면 containerd
가 systemd를 cgroup으로 사용하여 시작할 수 있도록 설정해줘야 합니다.
# containerd 설정 디렉토리 생성
sudo mkdir -p /etc/containerd
# containerd 기본 설정 파일 생성
sudo containerd config default | sudo tee /etc/containerd/config.toml >&/dev/null
# cgroup 드라이버를 systemd로 변경 (kubelet과 일치시키기 위함)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
# 변경된 설정 적용을 위해 containerd 재시작
sudo systemctl restart containerd
🤔 SystemdCgroup = true 설정은 왜 필요한가요?
리눅스에서 프로세스의 자원(CPU, 메모리 등)을 그룹화하고 제어하는 메커니즘을 cgroup(Control Group)이라고 합니다.
kubelet과 containerd는 모두 이 cgroup
을 사용하여 컨테이너의 자원을 관리하는데,
두 프로그램이 동일한 cgroup 드라이버를 사용해야만 자원 관리에 충돌이 발생하지 않습니다.
쿠버네티스는 systemd cgroup 드라이버 사용을 강력히 권장하므로, containerd의 설정도 이에 맞춰주는 것입니다.
마지막으로 containerd
서비스를 활성화!
systemctl enable --now containerd
5. 쿠버네티스 구성 요소 설치 (kubeadm
, kubelet
, kubectl
)
이제 쿠버네티스 클러스터를 만들고 관리하는 데 필요한 핵심 도구들을 설치해봅시다!
kubeadm
: 클러스터를 쉽고 빠르게 생성하고 관리(업그레이드, 노드 추가 등)하는 부트스트랩 도구입니다.kubelet
: 모든 노드에서 실행되는 에이전트입니다. 컨테이너의 생성, 실행, 상태 모니터링 등 실질적인 작업을 담당합니다.kubectl
: 사용자가 클러스터와 상호작용(명령 내리기)할 때 사용하는 커맨드 라인 인터페이스(CLI)입니다.
구글 클라우드 공개 서명 키 추가
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
참고: v1.30
부분은 설치하려는 쿠버네티스 버전에 맞게 변경할 수 있습니다.
쿠버네티스 apt
저장소 추가
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
kubeadm
, kubectl
, kubelet
설치
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
# 특정 버전의 패키지가 자동으로 업데이트되는 것을 방지
sudo apt-mark hold kubelet kubeadm kubectl
kubeadm
은 kubelet
또는 kubectl
을 설치 및 관리하지 않으므로
kubeadm
이 설치하려는 쿠버네티스의 Control Plane의 버전과 일치하는지 확인해야 한다.
버전이 맞지 않는 경우 예상치 못한 오류로 이어질 수 있는 버전 차이(skew)가 발생할 위험이 크다.
마지막으로! 설치 확인하기 ~
kubectl version --client && kubeadm version
마스터 노드 설정
- 마스터 노드에서만 아래 작업 진행
6. 컨트롤 플레인 초기화
kubeadm
을 사용하여 컨트롤 플레인(마스터)를 초기화합시다.
이 명령은 클러스터의 핵심 컴포넌트(API 서버, etcd, 스케줄러 등)를 컨테이너 형태로 실행시킵니다.
# --pod-network-cidr 값은 이후에 설치할 CNI 플러그인의 요구사항에 맞춰야 합니다.
# 여기서는 Calico의 기본값인 192.168.0.0/16 을 사용합니다.
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=<마스터 노드 IP>
- CNI 플러그인을 Calico로 사용할 것이기에 Calico의 기본 CIDR인
192.168.0.0/16
으로 설정하였습니다. - 네트워크 플러그인에 맞는 CIDR을 설정하지 아느면 클러스터 내 Pod 간 통신이 정상적이지 않을 수 있어요.
옵션 | 설명 |
---|---|
--pod-network-cidr=192.168.0.0/16 |
이 플래그는 Kubernetes에서 Pod 네트워크의 CIDR (Classless Inter-Domain Routing)를 설정합니다. 이것은 클러스터 내에서 파드 간 통신을 관리하는 IP 주소 범위를 정의합니다. 여기서는 192.168.0.0/16 로 설정되었습니다. |
--apiserver-advertise-address <마스터 노드 IP> |
이 플래그는 API 서버가 사용할 IP 주소를 설정합니다. Kubernetes API 서버는 클러스터의 상태를 관리하고 클라이언트와 통신하는데 사용됩니다. 쿠버네티스 마스터 노드의 IP 주소를 입력해주시면 됩니다. |
위 명령이 성공적으로 실행되면, 화면에 워커 노드를 클러스터에 조인시키는 데 필요한 kubeadm join
명령어가 출력됩니다!
이 결과 값은 절대로 잃어버리시면 안 됩니다! 안전한 곳에 복사해두세요.
하지만 토큰을 잃어버렸다 해도 걱정하실 필요는 없습니다!
마스터 노드에서 아래 명령을 실행해 다시 생성할 수 있어요.
kubeadm token create --print-join-command
7. kubectl
사용을 위한 환경 설정
kubeadm init
이 완료되면, 루트 사용자만 클러스터를 관리할 수 있는 상태가 됩니다.
일반 사용자 계정에서도 kubectl
을 사용하여 클러스터와 상호작용할 수 있도록 관리자 설정 파일을 복사하고 권한을 설정해야 합니다.
# 홈 디렉토리에 .kube 디렉토리 생성
mkdir -p $HOME/.kube
# 관리자 설정 파일을 홈 디렉토리로 복사
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
# 파일의 소유권을 현재 사용자로 변경
sudo chown $(id -u):$(id -g) $HOME/.kube/config
이제 kubectl get nodes
명령어를 실행했을 때, k8s-master 노드가 NotReady
상태로 보이면 정상입니다. (아직 네트워크 설정이 안 되었기 때문입니다.)
8. 컨테이너 네트워크 인터페이스(CNI) 설치
현재 노드들은 서로 통신할 수 있지만, 각 노드 안에서 실행되는 파드(컨테이너 그룹)들은 서로 통신할 방법이 없습니다.
이 파드 간의 통신을 가능하게 해주는 것이 바로 CNI(Container Network Interface) 플러그인이죠.
다양한 CNI 플러그인 (Calico, Flannel 등)이 있지만,
여기서는 성능과 강력한 네트워크 정책 기능으로 널리 사용되는 Calico
를 설치하겠습니다.
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
참고: Calico의 버전은 계속 업데이트되므로, Calico 공식 문서에서 최신 버전의 YAML 파일 주소를 확인하는 것이 좋습니다.
Calico 설치 후 잠시 기다렸다가 다시 kubectl get nodes
를 실행하면,
k8s-master 노드의 상태가 Ready로 바뀐 것을 확인할 수 있습니다!
9. 마스터 노드 격리 해제 (선택 사항)
기본적으로 컨트롤 플레인 노드에는 일반 애플리케이션 파드가 스케줄링되지 않도록 Taint
라는 꼬리표가 붙어 있습니다.
이는 중요한 컨트롤 플레인 역할에만 집중하기 위함이죠.
하지만 테스트 환경이나 자원이 부족한 소규모 클러스터에서는 마스터 노드에도 워커처럼 파드를 실행하고 싶을 수 있겠죠?
이 때 아래 명령어로 Taint
제거가 가능합니다.
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
워커 노드 설정
이제 마지막 단계입니다!
워커 노드에 각각 접속하여 클러스터에 참여시킵시다.
10. 클러스터에 워커 노드 등록
컨트롤 플레인 초기화 단게에서 복사해 두었던 kubeadm join
명령어를 각 워커 노드의 터미널에 붙여넣고 실행하면 됩니다.
(반드시 sudo
를 붙여 루트 권한으로 실행하세요.)
sudo kubeadm join <마스터 노드 IP>:6443 --token <토큰값> \
--discovery-token-ca-cert-hash sha256:<해시값>
"This node has joined the cluster" 라는 메시지가 보이면 성공입니다!
노드 상태 확인
11. 클러스터 노드 상태 확인
다시 마스터 노드로 돌아와서 kubectl
명령어로 클러스터의 전체 노드 상태를 확인해봅시다.
kubectl get nodes -o wide
모든 노드가 전부 Ready
상태로 보이면 성공!
CNCF landscape에서 확인해보면 CNI가 꽤 많이 존재하는 것을 볼 수 있다.
그 중에 이번 설치에서 사용할 CNI는 Cilium
이다. CNCF에서 밀어주는 프로젝트이고 BGP 기반 LB를 사용해 볼 수 있다.
(Calico
또한 BGP 기반의 LB 사용이 가능하다.)
[번외] Helm 설치
Kubernetes에는 여러가지 Add-on들이 존재합니다.
이를 쉽게 설치하고 사용하기 위한 툴인 Helm
이라는 것이 존재하는데 이번 챕터에서는 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
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