GCP에 VM으로 직접 쿠버네티스 클러스터 구축하는 방법
게시:
쿠버네티스(Kubernetes)는 컨테이너화된 애플리케이션의 자동 배포, 스케일링, 운영을 위한 강력한 오케스트레이션 플랫폼입니다. 클라우드 네이티브 환경에서는 관리형 쿠버네티스 서비스(GKE, EKS, AKS 등)를 사용하는 것이 일반적이지만, 직접 구축하는 과정을 통해 쿠버네티스의 아키텍처를 알아 봅시다.
이 가이드에서는 Google Cloud Platform(GCP) 가상 머신에 kubeadm을 사용하여 쿠버네티스 클러스터를 구축하는 전체 과정을 설명합니다.
버전 호환성 주의
본 문서는 kubeadm v1.33을 기준으로 작성되었습니다. 버전 차이가 클 경우 반드시 공식 문서를 확인하세요.
알려진 문제점: Ubuntu 22.04 LTS + Kubernetes v1.32
커널 6.8.0-56-generic 이상에서iptables
의 MARK 확장 기능에 회귀(regression) 버그가 존재하여Failed to execute iptables-restore
오류가 발생할 수 있습니다.
Ubuntu 24.04 LTS와 Kubernetes v1.33 이상에서는 정상 동작이 확인되었습니다.
쿠버네티스 클러스터 아키텍처 개요
쿠버네티스 클러스터를 구성하는 노드는 크게 컨트롤 플레인(Control Plane)과 워커 노드(Worker Node)로 구성됩니다.
컨트롤 플레인 구성요소
- kube-apiserver: 모든 클러스터 작업의 프론트엔드로, REST API 엔드포인트 제공
- etcd: 클러스터의 상태 정보를 저장하는 일관성 높은 분산 키-값 저장소
- kube-scheduler: 노드의 자원 상황을 고려하여 새로운 파드를 어떤 노드에 배치할지 결정
- kube-controller-manager: 노드, 레플리케이션, 엔드포인트 등 다양한 컨트롤러 프로세스 실행
- cloud-controller-manager: 클라우드 제공업체별 컨트롤 로직 관리
워커 노드 구성요소
- kubelet: 각 노드에서 실행되며 컨테이너 실행을 관리하는 에이전트
- kube-proxy: 네트워크 규칙을 유지하고 파드 간 통신 및 외부 통신 담당
- 컨테이너 런타임: 컨테이너 실행을 위한 소프트웨어(Docker, containerd 등)
1. 클러스터 사전 준비 및 환경 설정
스왑 비활성화, 방화벽 설정, 커널 모듈 및 네트워크 파라미터 설정, 컨테이너 런타임 설치를 진행합니다.
1.1. 스왑 비활성화
Kubelet은 스왑이 활성화된 시스템을 지원하지 않습니다. 이는 쿠버네티스 자원 관리의 일관성, 효율성 및 안정성을 저해하기 때문입니다.
sudo swapoff -a
sudo sed -i '/swap/s/^/#/' /etc/fstab
위 명령어는 즉시 스왑을 비활성화하고, 부팅 시 스왑이 자동으로 활성화되지 않도록 /etc/fstab
파일을 수정합니다.
쿠버네티스가 스왑을 지원하지 않는 이유
- 리소스 관리 일관성 문제: 쿠버네티스는 물리 메모리만을 기준으로 리소스를 할당하고 스왑 메모리를 포함하지 않습니다. 따라서 스왑이 활성화되어 있으면 메모리 부족을 정확히 감지하지 못할 가능성이 있어 리소스 관리가 일관되지 않게 됩니다.
- 성능 예측 불가능성: 컨테이너 성능이 변동적이게 됩니다. 물리 메모리를 할당받은 컨테이너는 빠르게 동작하고 스왑 메모리를 할당받은 컨테이너는 느리게 동작할 것 입니다. 즉, 어떤 컨테이너가 스왑 메모리를 사용하면서 성능이 저하될지 예측할 수 없게 됩니다.
- 쿠버네티스 철학과 불일치: 쿠버주어진 인스턴스(노드)의 자원을 100%에 가깝게 효율적으로 사용하는 철학에 부합하지 않는다.
- 보안 문제: 네임스페이스로 기껏 격리된 데이터가 디스크로 스왑되어 “격리”로 인해 얻을 수 있는 많은 이점을 포기하게 될 수 있습니다. 또한 시크릿 오브젝트의 내용과 같은 민감한 데이터가 디스크에 스왑될 수 있습니다.
최근(1.22 버전)에는 노드 단위로 스왑 메모리를 지원하는 움직임도 보이고 있지만 아직 알파 단계이며 프로덕션에서 사용하기에는 시기상조 같아 보입니다.
1.2. 방화벽 설정
쿠버네티스 클러스터는 노드 간 통신을 위해 여러 TCP/UDP 포트를 사용합니다. 각 구성 요소별로 필요한 포트는 다음과 같습니다.
테스트 환경 간소화 설정
테스트 목적으로는 방화벽을 완전히 비활성화하여 작업을 간소화할 수 있습니다. 그러나 이 방식은 보안상 프로덕션 환경에서는 절대 권장되지 않습니다.
sudo ufw disable
sudo systemctl disable --now ufw
프로덕션 환경 보안 설정
프로덕션 환경에서는 최소 권한 원칙에 따라 필요한 포트만 선택적으로 개방해야 합니다.
노드 종류 | 필수 포트 | 목적 |
---|---|---|
Control Plane | 6443/TCP | 쿠버네티스 API 서버 |
Control Plane | 2379-2380/TCP | etcd 서버 클라이언트 API |
Control Plane | 10250/TCP | Kubelet API |
Control Plane | 10257/TCP | kube-scheduler |
Control Plane | 10259/TCP | kube-controller-manager |
Worker Node | 10250/TCP | Kubelet API |
Worker Node | 10256/TCP | kube-proxy |
Worker Node | 30000-32767/TCP | NodePort 서비스 |
# Control Plane 노드 예시
sudo ufw allow 6443/tcp # kube-apiserver
sudo ufw allow 2379:2380/tcp # etcd 서버 통신
sudo ufw allow 10250/tcp # kubelet API
sudo ufw allow 10257/tcp # kube-scheduler
sudo ufw allow 10259/tcp # kube-controller-manager
sudo ufw reload
# Worker Node 예시
sudo ufw allow 10250/tcp # kubelet API
sudo ufw allow 10256/tcp # kube-proxy 헬스체크
sudo ufw allow 30000:32767/tcp # NodePort 서비스 범위
sudo ufw reload
1.3. 커널 모듈 및 네트워크 파라미터 구성
쿠버네티스 네트워킹은 Linux 커널의 여러 기능에 의존합니다. 특히 컨테이너 네트워킹을 위해 다음 모듈이 필요합니다.
- overlay: 컨테이너 네트워크를 위한 오버레이 네트워크 지원
- br_netfilter: 브릿지 네트워킹을 통과하는 패킷이 iptables 규칙을 따르도록 함
# 커널 모듈 영구 로드 설정
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
# 즉시 모듈 로드
sudo modprobe overlay
sudo modprobe br_netfilter
# 네트워크 브릿지 파라미터 설정
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
# 설정 적용
sudo sysctl --system
네트워크 파라미터 설명
net.bridge.bridge-nf-call-iptables=1
: 브릿지를 통과하는 패킷이 iptables 규칙을 따르도록 함net.bridge.bridge-nf-call-ip6tables=1
: IPv6 트래픽에 대해 동일한 설정net.ipv4.ip_forward=1
: 패킷 포워딩 활성화로 컨테이너 간 통신 허용
# 설정 검증
lsmod | grep overlay # 모듈 로드 확인
sysctl net.ipv4.ip_forward # 1 출력 시 정상
1.4. 컨테이너 런타임 설치 및 구성
쿠버네티스 v1.24 이후부터 Docker 엔진을 직접 지원하지 않고, CRI(Container Runtime Interface) 호환 런타임을 사용합니다. containerd는 가볍고 안정적인 OCI 호환 런타임으로, 쿠버네티스의 표준 런타임으로 권장됩니다.

1.4.1. Docker 및 Containerd 설치
Docker 엔진을 설치하면 containerd도 함께 설치됩니다. 개발 편의성이나 Docker 도구가 필요한 경우 이 방식이 유용합니다.
# 기존 패키지 제거
for pkg in docker.io docker-doc docker-compose docker-compose-v2 \
podman-docker containerd runc; do
sudo apt-get remove $pkg;
done
# Docker 공식 GPG 키 추가
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Docker 레포지토리 추가
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# Docker 엔진 및 containerd 설치
sudo apt-get install -y docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
# 현재 사용자에게 Docker 권한 부여
sudo usermod -aG docker $USER
1.4.2. Containerd CRI 설정
쿠버네티스는 노드의 리소스 관리를 위해 컨테이너 런타임이 systemd cgroup 드라이버를 사용하도록 권장합니다. 이렇게 하면 kubelet과 컨테이너 런타임이 일관된 cgroup 관리 시스템을 사용하게 됩니다.
# containerd 기본 설정 생성
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
# 설정 파일 수정
sudo vim /etc/containerd/config.toml
설정 파일에서 다음 사항을 확인하고 수정해야 합니다.
-
SystemdCgroup 활성화:
.plugins."io.containerd.grpc.v1.cri"
의.containerd.runtimes.runc.options.SystemdCgroup = true
로 변경 -
CRI 플러그인 활성화:
disabled_plugins
목록에서"cri"
항목이 없어야 함
이러한 설정이 완료되면 containerd를 재시작하여 변경사항을 적용합니다.
# containerd 재시작 및 자동 시작 설정
sudo systemctl restart containerd
sudo systemctl enable containerd
# 상태 확인
sudo ctr -n k8s.io images ls # 이미지 목록 확인
crictl info # CRI 정보 확인
cgroup 드라이버
kubelet과 컨테이너 런타임은 서로 일관된 cgroup 드라이버를 사용해야 합니다. 불일치 시 kubelet이 컨테이너를 관리할 수 없어 파드 생성 실패가 발생할 수 있습니다.
2. 쿠버네티스 구성요소 설치
쿠버네티스 구성요소는 kubeadm, kubelet, kubectl을 포함합니다.
- kubeadm: 쿠버네티스 클러스터 부트스트래핑 도구
- kubelet: 각 노드에서 실행되는 에이전트, 컨테이너 실행 관리
- kubectl: 클러스터를 제어하기 위한 CLI 도구
쿠버네티스 레포지토리 추가 및 패키지 설치
# 필수 패키지 설치
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
# Kubernetes v1.33 GPG 키 추가
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# Kubernetes 레포지토리 추가
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
# Kubernetes 패키지 설치
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl # 자동 업데이트 방지
# kubelet 서비스 시작 및 활성화
sudo systemctl enable --now kubelet
버전을 고정해야 하는 이유
apt-mark hold
명령으로 패키지를 고정하는 이유는 계획되지 않은 업그레이드로 인한 클러스터 불안정성을 방지하기 위함입니다. 쿠버네티스 업그레이드는 신중한 계획과 절차를 따라 진행해야 합니다.
3. 쿠버네티스 Control Plane 초기화
컨트롤 플레인 초기화는 클러스터 토폴로지(단일 또는 다중 컨트롤 플레인)에 따라 다르게 진행됩니다.
3-1. 단일 컨트롤 플레인 구성
단일 컨트롤 플레인 구성은 가장 단순한 토폴로지로, 테스트 및 개발 환경에 적합합니다. 이 경우 단일 노드가 모든 컨트롤 플레인 역할을 수행합니다.
sudo kubeadm init --pod-network-cidr="10.244.0.0/16"
여기서 --pod-network-cidr="10.244.0.0/16"
옵션은 Flannel CNI 플러그인의 기본 네트워크 범위를 지정합니다. 다른 CNI 플러그인을 사용할 경우 해당 플러그인에 맞는 CIDR을 사용해야 합니다.
3-2. 고가용성(HA) 다중 컨트롤 플레인 구성
사전조건: 로드밸런서(LoadBalancer)
다중 컨트롤 플레인 구성에는 API 서버 트래픽을 분산하는 로드 밸런서가 필요합니다. HAProxy를 사용한 로드 밸런서 구성 방법은 별도 문서를 참조하세요.
프로덕션 환경에서는 컨트롤 플레인의 고가용성(HA, High Availability)을 보장하기 위해 최소 3개의 컨트롤 플레인 노드를 구성하는 것이 권장됩니다. 이 구성에서는 로드 밸런서를 통해 API 서버에 대한 액세스를 제공합니다.
sudo kubeadm init \
--control-plane-endpoint="10.128.0.34:6443" \
--upload-certs \
--pod-network-cidr="10.244.0.0/16"
주요 매개변수 설명
--control-plane-endpoint
: API 서버의 엔드포인트 주소(로드 밸런서 IP:포트)--upload-certs
: 인증서를 쿠버네티스 시크릿에 업로드하여 다른 컨트롤 플레인 노드가 이를 안전하게 검색할 수 있도록 함--pod-network-cidr
: 파드 네트워크의 IP 범위 지정
초기화 출력 토큰 관리에 주의
kubeadm 초기화 출력에 표시되는 조인 토큰과 인증서 키는 보안 상 중요한 정보이므로 안전하게 보관해야 합니다. 이 정보는 추가 노드를 클러스터에 조인할 때 필요합니다.
Raft 합의 알고리즘
쿠버네티스는 상태 정보를 저장하는 etcd에 Raft 합의 알고리즘을 사용합니다. 이 알고리즘은 분산 환경에서 모든 노드가 동일한 상태를 유지하게 하고, 일부 노드에 결함이 생기더라도 전체 시스템이 계속 작동하게 해줍니다.
정족수(Quorum) 원칙에 따라, 노드가 n개일 때 최대 (n-1)/2개까지 장애를 허용할 수 있습니다. 예를 들면 노드가 5개인 경우, 최대 2개의 노드가 장애가 발생해도 클러스터는 정상 동작합니다. 즉, 과반수(3개)의 노드가 정상 상태를 유지한다면 클러스터 기능이 계속 유지됩니다.
kubeconfig 설정
클러스터를 관리하기 위해 kubectl이 사용할 kubeconfig 파일을 설정합니다.
mkdir -p $HOME/.kube
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
이 설정을 통해 일반 사용자 계정으로 kubectl 명령을 실행할 수 있습니다.
4. CNI(Container Network Interface) 플러그인 구성
쿠버네티스 네트워킹 모델에서 각 파드는 고유한 IP 주소를 가지며, 모든 파드는 NAT 없이 서로 통신할 수 있어야 합니다. 이를 구현하기 위해 CNI 플러그인이 필요합니다.
Flannel CNI 설치
Flannel은 구성이 간단하고 많이 사용되는 CNI 플러그인입니다. 이 명령은 컨트롤 플레인 노드에서만 실행하면 됩니다. Flannel이 설치되면 DaemonSet으로 각 노드에 자동 배포됩니다.
kubectl apply -f \
https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml
CNI 플러그인 대안
Flannel 외에도 Calico, Cilium, Weave Net 등 다양한 CNI 플러그인이 있습니다. 각 플러그인은 네트워크 정책, 성능, 보안 등에서 서로 다른 장점을 제공합니다. 요구사항에 따라 가장 적합한 플러그인을 선택하면 됩니다.
CNI 설치 후 노드 상태 확인
kubectl get nodes
모든 노드가 Ready
상태가 되면 네트워킹이 정상적으로 구성된 것입니다.
5. 워커 노드 클러스터 조인
워커 노드를 클러스터에 추가하려면 kubeadm init 실행 시 출력된 조인 명령을 사용합니다.
sudo kubeadm join 10.128.0.34:6443 --token <TOKEN> \
--discovery-token-ca-cert-hash sha256:<HASH>
토큰이 만료되었거나 명령을 분실한 경우, 새 토큰을 생성할 수 있습니다.
kubeadm token create --print-join-command
워커 노드가 조인되면 클러스터의 노드 목록을 확인합니다.
kubectl get nodes -o wide
컨트롤 플레인 스케줄링 불가
컨트롤 플레인 노드에는 기본적으로 파드가 스케줄링되지 않습니다. 이는 워크로드와 컨트롤 플레인 컴포넌트를 분리하여 안정성을 높이기 위함입니다. 스케줄링하고자 하는 파드에NoSchedule:node-role.kubernetes.io/control-plane
toleration을 추가하거나 테스트 환경에서는 다음 명령을 실행하여 NoSchedule effect를 제거하면 이 제한을 해제할 수 있습니다.kubectl taint nodes --all node-role.kubernetes.io/control-plane-
6. 클러스터 리셋 및 정리
클러스터를 재구성하거나 문제 해결이 필요한 경우, 기존 클러스터 설정을 완전히 제거하고 다시 시작할 수 있습니다.
클러스터 정리 절차
- 모든 워커 노드의 워크로드를 제거:
kubectl drain <node-name> --delete-emptydir-data --force --ignore-daemonsets
- 노드를 클러스터에서 삭제:
kubectl delete node <node-name>
- 각 노드에서 kubeadm 리셋 실행:
sudo kubeadm reset -f
- 네트워크 인터페이스 및 구성 제거:
sudo ip link delete cni0 # CNI 브리지 삭제 sudo ip link delete flannel.1 # Flannel 인터페이스 삭제 sudo rm -rf /etc/cni/net.d sudo rm -rf $HOME/.kube
- 컨테이너 런타임 및 kubelet 재시작:
sudo systemctl restart containerd kubelet
etcd 및 kubelet 데이터 정리
클러스터 리셋 후에는/var/lib/etcd
및/var/lib/kubelet
디렉터리에 남아 있는 데이터가 있는지 확인하고 필요시 삭제해야 합니다. etcd 데이터베이스에는 클러스터의 모든 상태 정보가 저장되므로 완전한 리셋을 위해서는 이 데이터를 제거해야 합니다.
결론
이 가이드에서는 GCP 가상 머신에 kubeadm을 사용하여 쿠버네티스 클러스터를 구축하는 전체 과정을 살펴보았습니다. 컨테이너 런타임 설정부터 쿠버네티스 구성요소 설치, 클러스터 초기화, 네트워킹 구성에 이르기까지 각 단계를 상세히 설명했습니다.
프로덕션 환경을 위한 추가 고려사항
- 모니터링 및 로깅: Prometheus, Grafana, ELK/EFK 스택 구축
- 인그레스 컨트롤러: Nginx Ingress, Traefik 등 설치
- 스토리지 클래스: 클라우드 제공업체의 스토리지 서비스 통합
- 백업 및 복구: etcd 백업 및 재해 복구 절차 수립
- 네트워크 정책: Calico, Cilium 등을 통한 세분화된 네트워크 보안 정책 구현
쿠버네티스 클러스터를 직접 구축함으로써 각 구성 요소 역할과 상호작용을 이해할 수 있었습니다.
댓글남기기