쿠버네티스를 공부하다 보면 클러스터를 수십 번 만들었다 부쉈다 반복하게 됩니다. 처음엔 Proxmox 웹 UI에서 VM을 하나하나 클릭해서 만들었는데, 세 번째쯤 되니까 "이걸 왜 손으로 하고 있지?"라는 생각이 들더군요. 결국 Terraform으로 VM 생성을 자동화하고, terraform destroy → terraform apply 한 방이면 깨끗한 실습 환경이 다시 올라오는 구조를 만들었습니다.
이 글에서는 Proxmox 위에 Terraform으로 마스터 1대 + 워커 2대 VM을 자동 생성하고, kubeadm으로 쿠버네티스 클러스터를 구축하는 전 과정을 정리합니다. 도중에 만난 에러도 10가지 넘게 정리해뒀으니, 같은 삽질을 반복하지 않으셨으면 합니다.
시리즈 안내
- #1 VM 자동화 — Terraform으로 Proxmox VM 생성 + kubeadm 클러스터 구축 (이번 글)
- #2 워크로드 배포 — ArgoCD로 GitOps 파이프라인 구성 (예정)
- #3 모니터링 스택 — Grafana + Prometheus로 클러스터 관측 (예정)
1. 환경 정보
| 항목 | 값 |
|---|---|
| Proxmox VE | 8.4.16 (커널 6.8.12-18-pve) |
| 호스트 하드웨어 | Intel Core i5-8500 @ 3.00GHz, RAM 16GB, HDD 512GB |
| Terraform 작업용 VM (tf-admin) | Ubuntu 22.04, 2 vCPU, 2GB RAM, IP: 192.168.20.111 |
| Terraform | 1.14.7 |
| Terraform Provider | bpg/proxmox 0.98.1 |
| Guest OS | Ubuntu 24.04.4 LTS (cloud-init 이미지) |
| Kubernetes | 1.31.14 |
| 컨테이너 런타임 | containerd 1.7.28 |
| CNI | Calico (calico/node) |
📌 스토리지 이름에 대해: 이 글에서는
local-zfs를 사용하고 있습니다. Proxmox 설치 시 ZFS를 선택했기 때문인데, 기본 설치(ext4/LVM)를 하셨다면 스토리지 이름이local-lvm일 겁니다. 아래 Terraform 코드에서datastore_id = "local-zfs"부분을 본인 환경에 맞게local-lvm등으로 바꿔주세요. Proxmox 웹 UI → Datacenter → Storage에서 본인의 스토리지 이름을 확인할 수 있습니다.
전체 구성도
이 글에서 만들 VM은 총 4대입니다. Terraform을 실행할 작업용 VM 1대, 그리고 Terraform이 자동으로 만들어줄 K8s 노드 3대입니다.
┌──────────────────────────────────────────────────────────────┐
│ Proxmox VE 호스트 │
│ │
│ ┌────────────┐ │
│ │ tf-admin │ ◀── 이 VM에서 terraform apply 실행 │
│ │ (VM 100) │ Proxmox API를 통해 아래 VM 3대를 자동 생성 │
│ │ 2 vCPU │ │
│ │ 2GB RAM │ ┌──── API 호출 ────┐ │
│ │ 20GB Disk │ ▼ ▼ │
│ │ .111 │ ┌───────────┐ ┌──────────┐ ┌──────────┐ │
│ └────────────┘ │ k8s-master│ │k8s-worker│ │k8s-worker│ │
│ ↑ 직접 생성 │ (VM 201) │ │ (VM 202) │ │ (VM 203) │ │
│ (웹 UI or CLI) │ 4 vCPU │ │ 2 vCPU │ │ 2 vCPU │ │
│ │ 4GB RAM │ │ 4GB RAM │ │ 4GB RAM │ │
│ │ 30GB Disk │ │ 30GB Disk│ │ 30GB Disk│ │
│ │ .112 │ │ .113 │ │ .114 │ │
│ └───────────┘ └──────────┘ └──────────┘ │
│ ↑ Terraform이 자동 생성 │
│ │
│ 네트워크: 192.168.20.0/24 │
└──────────────────────────────────────────────────────────────┘핵심 포인트는 이렇습니다. tf-admin VM은 한 번 만들어두면 계속 쓰고, K8s 노드 3대는 terraform destroy / terraform apply로 언제든 부수고 다시 만드는 일회용 VM입니다.
2. Terraform 작업용 VM 만들기 (tf-admin)
Terraform은 Proxmox 호스트에서 직접 실행하는 게 아니라, 별도의 리눅스 VM에서 Proxmox API를 호출하는 방식으로 동작합니다. 이 작업용 VM을 먼저 하나 만들어야 합니다.
2-1. Proxmox 웹 UI에서 VM 생성
Proxmox 웹 UI(https://서버IP:8006)에 접속해서 오른쪽 상단 Create VM 버튼을 클릭합니다.
General:
VM ID: 100
Name: tf-admin
OS:
ISO image: ubuntu-24.04-live-server-amd64.iso (미리 다운로드해서 업로드)
System:
기본값 그대로
Disks:
Disk size: 20 GB
CPU:
Cores: 2
Memory:
Memory: 2048 MB (2GB)
Network:
Bridge: vmbr0
기본값 그대로VM을 시작하고 Ubuntu Server 설치를 진행합니다. 설치 중 네트워크 설정에서 고정 IP를 잡아주세요.
IP: 192.168.20.111/24
Gateway: 192.168.20.1
DNS: 8.8.8.8
2-2. Terraform 설치
Ubuntu 설치가 끝나면 SSH로 접속해서 Terraform을 설치합니다.
ssh ubuntu@192.168.20.111
# 필수 패키지 설치
sudo apt-get update
sudo apt-get install -y gnupg software-properties-common curl
# HashiCorp GPG 키 추가
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
# HashiCorp 저장소 추가
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
# Terraform 설치
sudo apt-get update
sudo apt-get install -y terraform
# 설치 확인
terraform version
아래처럼 버전이 나오면 성공입니다.
Terraform v1.14.7
on linux_amd642-3. SSH 키 생성
이 키는 나중에 Terraform이 K8s VM을 만들 때 자동으로 주입됩니다. 그래야 tf-admin에서 K8s 노드들에 비밀번호 없이 SSH 접속할 수 있습니다.
ssh-keygen -t ed25519 -C "k8s-lab" -f ~/.ssh/id_ed25519 -N ""
# 공개키 확인 (나중에 terraform.tfvars에 넣을 값)
cat ~/.ssh/id_ed25519.pub
출력된 ssh-ed25519 AAAA... 값을 복사해두세요.
정리하면: tf-admin VM은 Terraform을 실행하는 "리모컨" 역할입니다. 이 VM에서
terraform apply를 치면, Proxmox API를 통해 K8s용 VM 3대가 자동으로 만들어집니다. tf-admin은 한 번만 만들면 되고, K8s VM은 실습할 때마다 부수고 다시 만들 수 있습니다.
3. Proxmox 사전 준비 (cloud-init 템플릿 + API 토큰)
3-1. cloud-init 템플릿 VM 만들기
Ubuntu cloud 이미지를 다운로드하고 Proxmox에 VM 템플릿으로 등록합니다. Proxmox 호스트 셸에서 아래 명령어를 실행하세요.
# Ubuntu 24.04 cloud 이미지 다운로드
cd /var/lib/vz/template/iso/
wget https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
# VM 생성 (ID 9000번을 템플릿 용도로 사용)
qm create 9000 --memory 2048 --core 2 --name ubuntu-cloud-template --net0 virtio,bridge=vmbr0
# 다운로드한 이미지를 VM 디스크로 import (local-zfs는 본인 스토리지명으로 변경)
qm importdisk 9000 noble-server-cloudimg-amd64.img local-zfs
# 디스크를 SCSI로 연결
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-zfs:vm-9000-disk-0
# cloud-init 드라이브 추가
qm set 9000 --ide2 local-zfs:cloudinit
# 부팅 순서 지정
qm set 9000 --boot c --bootdisk scsi0
# 시리얼 콘솔 활성화 (cloud-init 호환)
qm set 9000 --serial0 socket --vga serial0
# 템플릿으로 변환
qm template 9000

3-2. Terraform용 API 토큰 발급
Proxmox 웹 UI에서 Datacenter → Permissions → API Tokens로 이동합니다.
사용자: root@pam (또는 전용 사용자 생성 권장)
토큰 ID: terraform
Privilege Separation: 체크 해제 (전체 권한 상속)발급된 토큰 값을 안전하게 보관하세요. 한 번만 보여주기 때문에 놓치면 재발급해야 합니다.
보안 팁: 프로덕션 환경이라면 전용 사용자 + 최소 권한으로 구성하는 게 맞지만, 홈랩 실습 용도에서는 root 토큰으로도 충분합니다. 다만
.tfvars파일은 절대 Git에 올리지 마세요.
4. Terraform 코드 작성 (tf-admin VM에서 진행)
디렉토리 구조
k8s-lab/
├── main.tf # Provider 설정
├── variables.tf # 변수 정의
├── terraform.tfvars # 변수 값 (Git 제외!)
├── master.tf # 마스터 노드 VM
├── worker.tf # 워커 노드 VM
└── outputs.tf # IP 주소 출력여기서부터는 tf-admin VM에 SSH 접속한 상태에서 진행합니다. ssh ubuntu@192.168.20.111로 접속하세요.
4-1. main.tf — Provider 설정
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = ">= 0.98.0"
}
}
}
provider "proxmox" {
endpoint = var.proxmox_api_url
api_token = var.proxmox_api_token
insecure = true # 자체 서명 인증서 사용 시
ssh {
agent = true
}
}
4-2. variables.tf — 변수 정의
variable "proxmox_api_url" {
description = "Proxmox API 엔드포인트"
type = string
}
variable "proxmox_api_token" {
description = "Proxmox API 토큰 (user@realm!tokenid=secret)"
type = string
sensitive = true
}
variable "proxmox_node" {
description = "Proxmox 노드명"
type = string
default = "pve" # 본인 Proxmox 노드명으로 변경
}
variable "template_vm_id" {
description = "cloud-init 템플릿 VM ID"
type = number
default = 9000
}
variable "ssh_public_key" {
description = "SSH 공개키"
type = string
}
variable "gateway_ip" {
description = "네트워크 게이트웨이"
type = string
default = "192.168.20.1"
}
variable "worker_count" {
description = "워커 노드 수"
type = number
default = 2
}
4-3. master.tf — 마스터 노드
resource "proxmox_virtual_environment_vm" "k8s_master" {
name = "k8s-master"
node_name = var.proxmox_node
vm_id = 201
clone {
vm_id = var.template_vm_id
full = true
}
cpu {
cores = 4
type = "x86-64-v2-AES"
}
memory {
dedicated = 4096
}
disk {
datastore_id = "local-zfs" # 본인 환경에 맞게 변경 (기본 설치는 "local-lvm")
size = 30
interface = "scsi0"
}
network_device {
bridge = "vmbr0"
}
initialization {
ip_config {
ipv4 {
address = "192.168.20.112/24"
gateway = var.gateway_ip
}
}
user_account {
username = "ubuntu"
keys = [var.ssh_public_key]
}
dns {
servers = ["8.8.8.8", "8.8.4.4"]
}
}
on_boot = true
lifecycle {
ignore_changes = [disk]
}
}
4-4. worker.tf — 워커 노드 (count 활용)
resource "proxmox_virtual_environment_vm" "k8s_worker" {
count = var.worker_count
name = "k8s-worker-${count.index + 1}"
node_name = var.proxmox_node
vm_id = 202 + count.index
clone {
vm_id = var.template_vm_id
full = true
}
cpu {
cores = 2
type = "x86-64-v2-AES"
}
memory {
dedicated = 4096
}
disk {
datastore_id = "local-zfs" # 본인 환경에 맞게 변경 (기본 설치는 "local-lvm")
size = 30
interface = "scsi0"
}
network_device {
bridge = "vmbr0"
}
initialization {
ip_config {
ipv4 {
address = "192.168.20.${113 + count.index}/24"
gateway = var.gateway_ip
}
}
user_account {
username = "ubuntu"
keys = [var.ssh_public_key]
}
dns {
servers = ["8.8.8.8", "8.8.4.4"]
}
}
on_boot = true
lifecycle {
ignore_changes = [disk]
}
}
4-5. outputs.tf
output "master_ip" {
value = "192.168.20.112"
}
output "worker_ips" {
value = [for i in range(var.worker_count) : "192.168.20.${113 + i}"]
}
4-6. terraform.tfvars (예시)
proxmox_api_url = "https://192.168.20.1:8006"
proxmox_api_token = "root@pam!terraform=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
proxmox_node = "pve" # 본인 Proxmox 노드명으로 변경
ssh_public_key = "ssh-ed25519 AAAA... your-key-here"
gateway_ip = "192.168.20.1"
worker_count = 2
⚠️ 보안 주의:
terraform.tfvars에는 Proxmox API 토큰(비밀번호 역할)이 들어있습니다. 이 Terraform 코드를 Git(코드 버전 관리 도구)으로 관리하면서 GitHub 같은 곳에 올릴 계획이라면,.gitignore파일을 만들어서terraform.tfvars를 제외해야 합니다. 그래야 비밀번호가 인터넷에 노출되지 않습니다. Git을 사용하지 않고 tf-admin VM 안에서만 작업하신다면 이 부분은 넘어가셔도 됩니다.
5. Terraform 실행 — VM 생성
cd k8s-lab/
# 초기화
terraform init
# 플랜 확인
terraform plan
plan 결과에서 VM 3대가 생성되는 것을 확인한 뒤 apply합니다.
terraform apply -auto-approve
정상적으로 완료되면 아래와 같은 출력이 나옵니다.
Apply complete! Resources: 3 added, 0 changed, 0 destroyed.
Outputs:
master_ip = "192.168.20.112"
worker_ips = [
"192.168.20.113",
"192.168.20.114",
]
SSH 접속 테스트:
ssh ubuntu@192.168.20.112
ssh ubuntu@192.168.20.113
ssh ubuntu@192.168.20.114
6. kubeadm으로 쿠버네티스 클러스터 구축
여기서부터는 전체 노드(마스터 + 워커)에 공통 작업이 먼저 있고, 그 다음 마스터/워커 별 작업이 있습니다.
6-1. 전체 노드 공통 작업
아래 명령어를 3대 모두에서 실행합니다. 저는 마스터는 수동으로 먼저 진행하고, 워커 2대는 Ansible로 한 번에 돌렸습니다. Ansible 설정 방법은 아래 참고 박스를 확인하세요.
💡 Ansible로 워커 2대 한 번에 설정하기
tf-admin VM에 Ansible을 설치하고 (
pip3 install ansible), 인벤토리 파일에 워커 IP를 등록한 뒤 플레이북으로 일괄 실행할 수 있습니다. 3대 각각 SSH 접속해서 같은 명령어를 치는 것보다 훨씬 편합니다. 저도 처음에 수동으로 하다가 결국 Ansible로 바꿨습니다. Ansible 설정 과정은 별도 포스팅으로 정리할 예정입니다.
# swap 비활성화 (kubeadm 필수)
sudo swapoff -a
sudo sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 커널 모듈 로드
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilter
# sysctl 설정
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
containerd 설치
# containerd 설치
sudo apt-get update
sudo apt-get install -y containerd
# containerd 기본 설정 생성
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# SystemdCgroup = true 로 변경 (중요!)
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd
핵심 포인트:
SystemdCgroup = true를 빠뜨리면 나중에 kubelet이 제대로 동작하지 않습니다. Ubuntu 24.04는 기본적으로 cgroup v2를 사용하기 때문에 이 설정이 필수입니다.
kubeadm, kubelet, kubectl 설치
# 필수 패키지
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
# Kubernetes 패키지 저장소 키 추가 (v1.31 기준)
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.31/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.31/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
# 설치 및 버전 고정
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
6-2. 마스터 노드 초기화
마스터 노드(192.168.20.112)에서만 실행합니다.
sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=192.168.20.112 \
--kubernetes-version=v1.31.14
초기화가 완료되면 아래와 같은 메시지가 나옵니다.
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
Then you can join any number of worker nodes by running the following on each as root:
kubeadm join 192.168.20.112:6443 --token abcdef.1234567890abcdef \
--discovery-token-ca-cert-hash sha256:xxxxxxxxx...kubeconfig 설정:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Calico CNI 설치
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
6-3. 워커 노드 Join
kubeadm init 결과에서 나온 join 명령어를 워커 노드 2대에서 각각 실행합니다.
# 워커 노드에서 실행 (토큰 값은 본인 환경에 맞게)
sudo kubeadm join 192.168.20.112:6443 \
--token abcdef.1234567890abcdef \
--discovery-token-ca-cert-hash sha256:xxxxxxxxx...
토큰을 잊어버렸다면 마스터에서
kubeadm token create --print-join-command로 재생성할 수 있습니다.
6-4. 클러스터 상태 확인
마스터 노드에서 확인합니다.
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 7m3s v1.31.14
k8s-worker-1 Ready <none> 4m10s v1.31.14
k8s-worker-2 Ready <none> 54s v1.31.143대 모두 Ready 상태면 성공입니다.
# 시스템 Pod 상태도 확인
kubectl get pods -n kube-system
모든 Pod이 Running 상태인지 확인하세요. Calico 관련 Pod이 올라오는 데 1~2분 정도 걸릴 수 있습니다.

7. 트러블슈팅 모음
이 과정에서 실제로 제가 만난 오류들을 전부 정리합니다. 아마 따라하시다 보면 비슷한 걸 만나실 겁니다.
🔧 Terraform 단계
❌ terraform.tfvars에서 SSH 키 줄바꿈 에러
Error: Invalid multi-line string
on terraform.tfvars line 4:
Quoted strings may not be split over multiple lines.원인: SSH 공개키를 붙여넣을 때 줄이 바뀌면서 닫는 따옴표(")가 다음 줄로 넘어감
해결: ssh-ed25519 AAAA... k8s-lab 전체를 반드시 한 줄 안에 큰따옴표로 감싸야 합니다. 터미널에서 복사할 때 줄바꿈이 들어가지 않도록 주의하세요.
❌ 마스터/워커 IP 서브넷 불일치
terraform plan을 찍어보니 마스터는 192.168.20.x, 워커는 192.168.1.x로 서로 다른 서브넷에 배치되어 있었습니다. 이러면 노드 간 통신이 안 돼서 kubeadm join이 실패합니다.
원인: master.tf와 worker.tf에서 IP 대역을 다르게 적음
해결: master.tf, worker.tf, terraform.tfvars의 IP와 게이트웨이를 전부 같은 서브넷으로 통일합니다. terraform plan으로 미리 확인하는 습관이 중요합니다.
❌ API 토큰 권한 부족 (HTTP 403, VM.Clone)
Error: error cloning VM: received an HTTP 403 response
Reason: Permission check failed (/vms/9000, VM.Clone)원인: API 토큰을 만들 때 Privilege Separation이 체크되어 있으면, 토큰이 사용자 권한을 상속받지 못합니다.
해결: Proxmox 웹 UI → Datacenter → Permissions → API Tokens에서 기존 토큰을 삭제하고, Privilege Separation 체크를 해제한 뒤 새로 발급합니다. 새 토큰 값을 terraform.tfvars에 업데이트하세요.
❌ 스토리지 이름 불일치 (local-lvm does not exist)
Error: received an HTTP 500 response
Reason: storage 'local-lvm' does not exist원인: .tf 파일에 local-lvm이라고 적었는데, 실제 Proxmox 스토리지 이름이 local-zfs였음. disk 블록뿐 아니라 initialization 블록(cloud-init 드라이브)에도 스토리지 이름이 필요합니다.
해결: 모든 .tf 파일에서 스토리지 이름을 실제 환경과 맞춥니다. initialization 블록에 datastore_id를 명시적으로 추가하세요.
# 어디에 잘못된 값이 남아있는지 한 번에 확인
grep -rn "local-lvm" *.tf
🔧 SSH 접속 단계
❌ sudo ssh 하면 Permission denied
ubuntu@192.168.20.112: Permission denied (publickey).원인: sudo ssh를 하면 root 계정의 SSH 키(/root/.ssh/)를 찾습니다. 하지만 SSH 키는 일반 사용자 홈 디렉토리(~/.ssh/)에 만들었기 때문에 키가 안 맞습니다.
해결: sudo 빼고 접속합니다.
# ✕ 이렇게 하면 안 됨
sudo ssh ubuntu@192.168.20.112
# ○ 이렇게 해야 함
ssh ubuntu@192.168.20.112
🔧 Ansible 단계
❌ No module named 'ansible.module_utils.six.moves'
ModuleNotFoundError: No module named 'ansible.module_utils.six.moves'원인: Ubuntu apt로 설치한 Ansible 버전이 너무 오래돼서 대상 서버의 Python 3.12와 호환되지 않음
해결: apt 버전을 제거하고 pip3로 최신 버전을 설치합니다.
sudo apt-get remove -y ansible
pip3 install ansible
❌ ansible 명령어가 안 먹힘 (command not found)
-bash: /usr/bin/ansible: No such file or directory원인: pip3로 설치하면 ~/.local/bin/에 설치되는데, 이 경로가 PATH에 없음
해결:
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
🔧 kubeadm 단계
❌ conntrack not found
[ERROR FileExisting-conntrack]: conntrack not found in system path원인: cloud-init 이미지에는 conntrack 패키지가 기본 설치되어 있지 않음. kubeadm이 네트워크 연결 추적에 필요합니다.
해결: 마스터/워커 모두에 설치합니다.
sudo apt-get install -y conntrack
Ansible로 한 번에:
ansible k8s -i inventory.ini -m apt -a "name=conntrack state=present" --become
❌ kubeadm join에서 "accepts at most 1 arg(s), received 2"
accepts at most 1 arg(s), received 2원인: --token 뒤에 token이라는 단어가 중복으로 들어감 (복사 실수)
해결: --token 바로 뒤에 토큰 값만 와야 합니다.
# ✕ 잘못된 예
sudo kubeadm join 192.168.20.112:6443 --token token abcdef.1234567890abcdef
# ○ 올바른 예
sudo kubeadm join 192.168.20.112:6443 --token abcdef.1234567890abcdef
❌ 워커 노드에서 kubectl get nodes 실행하면 connection refused
The connection to the server localhost:8080 was refused원인: kubectl은 API 서버에 접속하는 클라이언트 도구인데, API 서버는 마스터 노드에만 있습니다. 워커 노드에서 실행하면 로컬(localhost:8080)에 API 서버가 없으니 당연히 거부됩니다.
해결: 반드시 마스터 노드에서 kubectl 명령어를 실행하세요.
ssh ubuntu@192.168.20.112
kubectl get nodes
8. 정리 및 다음 편 예고
전체 소요 시간
| 단계 | 소요 시간 |
|---|---|
| tf-admin VM 생성 + Terraform 설치 (최초 1회) | 약 15분 |
| cloud-init 템플릿 생성 (최초 1회) | 약 10분 |
| Terraform 코드 작성 | 약 30분 |
| terraform apply (VM 3대 생성) | 약 3~5분 |
| kubeadm 클러스터 구축 | 약 15~20분 |
| 합계 | 약 1시간 15분 (최초), 이후 재구축은 20분 |
가장 큰 장점은 재현성입니다. 클러스터를 망쳐도 terraform destroy -auto-approve 한 번이면 깔끔하게 정리되고, 다시 terraform apply로 새 VM을 올리면 됩니다. 쿠버네티스 공부할 때 "부수고 다시 만들기"를 부담 없이 할 수 있는 환경이 되니, 이것저것 과감하게 실험할 수 있게 됐습니다.
다음 편에서는 이 클러스터 위에 ArgoCD를 올려서 GitOps 기반 배포 파이프라인을 구성해보겠습니다.
참고 링크
- bpg/proxmox Terraform Provider — cloud-init 가이드
- Kubernetes 공식 문서 — kubeadm 설치
- Ubuntu 24.04에서 kubeadm으로 K8s 클러스터 구축
- Calico 공식 설치 가이드