카테고리 없음

[K8S Deploy 7주차] RKE2 & Cluster API

yu3papa 2026. 2. 18. 12:31

 

RKE2

https://docs.rke2.io/

 

Introduction | RKE2

RKE2 is Rancher's enterprise-ready next-generation Kubernetes distribution. It has also been known as RKE Government.

docs.rke2.io

RKE2 는 공식 문서상에 아래와 같이 정의하고 있습니다.

RKE2 is Rancher's enterprise-ready next-generation Kubernetes distribution. It has also been known as RKE Government.

위 정의에서 Goverment란 용어가 핵심이라고 생각합니다.  미국 연방정부 부분의 보안 및 규정 을 만족하는 K8S 배포판입니다.

특징

  • Rancher 에서 개발한 엔터프라이즈 K8S 배포판, 미국 연방 정부 부문의 보안 및 규정 준수
  • 운영자의 개입을 최소화하면서 클러스터가 CIS Kubernetes Benchmark v1.7 또는 v1.8 을 통과할 수 있도록  설정 및 구성 옵션을 제공합니다 .
  • RKE2는 컨트롤 플레인 구성 요소를 kubelet이 관리하는 정적 Pod로 실행합니다. 내장 컨테이너 런타임은 containerd 입니다.
  • 이 배포판은 독립 실행형으로 실행되거나 Rancher 에 통합되어 실행될 수 있습니다.

https://docs.rke2.io/architecture

  • RKE2는 경량 Kubernetes 배포판인 K3S를 개발하고 유지 관리하면서 얻은 교훈을 바탕으로, K3s의 사용 편의성을 유지하면서도 엔터프라이즈 환경에 적합한 배포판을 구축한 제품입니다.
  • RKE2는 가장 간단하게 말하면 Kubernetes 클러스터에 참여할 모든 노드에 설치 및 구성해야 하는 단일 바이너리 파일입니다.
  • 시작되면 RKE2는 각 노드의 역할에 맞는 에이전트를 부트스트랩하고 관리하는 동시에 네트워크에서 필요한 콘텐츠를 가져올 수 있습니다.

RKE2 구성요소는 아래와 같으며, K8S 상의 이미 검증된 오픈소스를 최대한 활용합니다.

k3s    
  Helm Controller GitOps 스타일 경량 배포기 - RKE2 클러스터가 올라올 때 필수 Helm 차트를 자동으로 설치·유지하는 내장 배포 컨트롤러
/var/lib/rancher/rke2/server/manifests 디렉토리에 (HelmChart) YAML 파일만 떨어뜨리면 Helm Controller에 의해 자동으로 Helm chart를 다운로드/설치/업데이트.
K8s API Server
Controller Manager
Scheduler
Proxy
Kubelet
 
etcd
runc
containerd
   
CNI Canal (Calico & Flannel) 네트워크 연결은 Flannel, 보안 정책은 Calico가 담당하는 하이브리드 CNI
canal DaemonSet 파드 내 컨테이너 : flannel (vxlan overlay 담당) , calico-node (network policy, iptables)
CoreDNS
Ingress NGINX Controller and/or Traefik
Metrics Server
Helm
   

 

실습환경 구성

Rocky Linux 9 버전의 2대의 머신을 Vagrant를 이용하여 구성합니다.

 

  • Vagrant 관련 파일
더보기
  • Vagrantfile
# Base Image  https://portal.cloud.hashicorp.com/vagrant/discover/bento/rockylinux-9
BOX_IMAGE = "bento/rockylinux-9" # "bento/rockylinux-10.0"
BOX_VERSION = "202510.26.0"
N = 2 # max number of Node

Vagrant.configure("2") do |config|

# Nodes 
  (1..N).each do |i|
    config.vm.define "k8s-node#{i}" do |subconfig|
      subconfig.vm.box = BOX_IMAGE
      subconfig.vm.box_version = BOX_VERSION
      subconfig.vm.provider "virtualbox" do |vb|
        vb.customize ["modifyvm", :id, "--groups", "/RKE2-Lab"]
        vb.customize ["modifyvm", :id, "--nicpromisc2", "allow-all"]
        vb.name = "k8s-node#{i}"
        vb.cpus = 4
        vb.memory = 4096
        vb.linked_clone = true
      end
      subconfig.vm.host_name = "k8s-node#{i}"
      subconfig.vm.network "private_network", ip: "192.168.10.1#{i}"
      subconfig.vm.network "forwarded_port", guest: 22, host: "6000#{i}", auto_correct: true, id: "ssh"
      subconfig.vm.synced_folder "./", "/vagrant", disabled: true
      subconfig.vm.provision "shell", path: "init_cfg.sh" , args: [ N ]
    end
  end

end

 

  • init_cfg.sh
#!/usr/bin/env bash

echo ">>>> Initial Config Start <<<<"


echo "[TASK 1] Change Timezone and Enable NTP"
timedatectl set-local-rtc 0
timedatectl set-timezone Asia/Seoul


echo "[TASK 2] Disable firewalld and selinux"
systemctl disable --now firewalld >/dev/null 2>&1
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config


echo "[TASK 3] Disable and turn off SWAP & Delete swap partitions"
swapoff -a
sed -i '/swap/d' /etc/fstab
sfdisk --delete /dev/sda 2 >/dev/null 2>&1
partprobe /dev/sda >/dev/null 2>&1


echo "[TASK 4] Config kernel & module"
cat << EOF > /etc/modules-load.d/k8s.conf
overlay
br_netfilter
#vxlan
EOF
modprobe overlay >/dev/null 2>&1
modprobe br_netfilter >/dev/null 2>&1
#modprobe vxlan >/dev/null 2>&1

cat << EOF >/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
sysctl --system >/dev/null 2>&1


echo "[TASK 5] Setting Local DNS Using Hosts file"
sed -i '/^127\.0\.\(1\|2\)\.1/d' /etc/hosts
for (( i=1; i<=$1; i++  )); do echo "192.168.10.1$i k8s-node$i" >> /etc/hosts; done


echo "[TASK 6] Install Helm"
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | DESIRED_VERSION=v3.20.0 bash >/dev/null 2>&1
# echo "[TASK 6] Delete default routing - enp0s9 NIC" # setenforce 0 설정 필요
# nmcli connection modify enp0s9 ipv4.never-default yes
# nmcli connection up enp0s9 >/dev/null 2>&1


echo "[TASK 7] Setting SSHD"
#echo "root:qwe123" | chpasswd

cat << EOF >> /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes
EOF
systemctl restart sshd >/dev/null 2>&1


echo "[TASK 8] Install packages"
dnf install -y conntrack python3-pip git >/dev/null 2>&1


echo "[TASK 9] NetworkManager to ignore calico/flannel related network interfaces"
# https://docs.rke2.io/known_issues#networkmanager
cat << EOF > /etc/NetworkManager/conf.d/k8s.conf
[keyfile]
unmanaged-devices=interface-name:flannel*;interface-name:cali*;interface-name:tunl*;interface-name:vxlan.calico;interface-name:vxlan-v6.calico;interface-name:wireguard.cali;interface-name:wg-v6.cali
EOF
systemctl reload NetworkManager


echo "[TASK 11] Install K9s"
CLI_ARCH=amd64
if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
wget -P /tmp https://github.com/derailed/k9s/releases/latest/download/k9s_linux_${CLI_ARCH}.tar.gz  >/dev/null 2>&1
tar -xzf /tmp/k9s_linux_${CLI_ARCH}.tar.gz -C /tmp
chown root:root /tmp/k9s
mv /tmp/k9s /usr/local/bin/
chmod +x /usr/local/bin/k9s


echo "[TASK 12] ETC"
echo "sudo su -" >> /home/vagrant/.bashrc


echo ">>>> Initial Config End <<<<"

 

RKE2  설치

RKE2 서버 노드 설치

RKE2 는 systemd 기반 시스템에 서비스로 편리하게 설치할 수 있는 설치 스크립트를 제공(https://get.rke2.io/) 합니다.

설치에 적용할 수 있는 아래 환경변수를 이용하면 커스터마이징이 가능합니다. 환경변수 설정이 없으며 디폴트 값으로 설치됩니다.

INSTALL_RKE2_VERSION GitHub에서 다운로드할 RKE2 버전 - stable(기본값)
INSTALL_RKE2_TYPE 생성할 systemd 서비스의 유형 - server(기본값), agent
NSTALL_RKE2_CHANNEL_URL RKE2 다운로드 URL을 가져오기 위한 채널 URL - https://update.rke2.io/v1-release/channels (기본값)
INSTALL_RKE2_CHANNEL RKE2 다운로드 URL을 가져오는 데 사용할 채널 - stable(기본값), latest, testing
INSTALL_RKE2_METHOD rpm(RPM 기반 시스템 경우 기본값), tar(그외 경우 기본값)

 

  • k8s-node1 에서 RKE2 서버노드 설치를 진행합니다.
# systemd 기반 시스템에 서비스로 편리하게 설치할 수 있는 설치 스크립트를 제공 : 서비스와 바이너리 파일이 컴퓨터에 설치
https://docs.rke2.io/install/methods
curl -sfL https://get.rke2.io --output install.sh
chmod +x install.sh

# 실제 K8S 클러스터를 올리는게 아니라, 필요한 실행파일, 디렉토리 구조, Repo 구성을 진행합니다.
INSTALL_RKE2_CHANNEL=v1.33 ./install.sh
[INFO]  using stable RPM repositories
[INFO]  using 1.33 series from channel stable
...(생략)...
Installed:
  rke2-common-1.33.7~rke2r3-0.el9.aarch64             rke2-selinux-0.22-1.el9.noarch             rke2-server-1.33.7~rke2r3-0.el9.aa

# rke2 버전 확인
rke2 --version
rke2 version v1.33.7+rke2r3 (7e4fd1a82edf497cab91c220144619bbad659cf4)
go version go1.24.11 X:boringcrypto

# repo 추가 확인
dnf repolist
repo id                       repo name
appstream                     Rocky Linux 9 - AppStream
baseos                        Rocky Linux 9 - BaseOS
extras                        Rocky Linux 9 - Extras
rancher-rke2-1.33-stable      Rancher RKE2 1.33 (v1.33)
rancher-rke2-common-stable    Rancher RKE2 Common (v1.33)

tree /etc/yum.repos.d/
/etc/yum.repos.d/
├── rancher-rke2.repo
├── rocky-addons.repo
├── rocky-devel.repo
├── rocky-extras.repo
└── rocky.repo

cat /etc/yum.repos.d/rancher-rke2.repo
[rancher-rke2-common-stable]
name=Rancher RKE2 Common (v1.33)
baseurl=https://rpm.rancher.io/rke2/stable/common/centos/9/noarch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://rpm.rancher.io/public.key
[rancher-rke2-1.33-stable]
name=Rancher RKE2 1.33 (v1.33)
baseurl=https://rpm.rancher.io/rke2/stable/1.33/centos/9/aarch64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://rpm.rancher.io/public.key

# 디렉터리 생성 확인
tree /etc/rancher/
/etc/rancher/
└── rke2

tree /var/lib/rancher/
/var/lib/rancher/
└── rke2
    ├── agent
    │   ├── containerd
    │   │   └── io.containerd.snapshotter.v1.overlayfs
    │   │       └── snapshots
    │   └── logs
    ├── data
    └── server

# rke2 명령 확인
https://docs.rke2.io/install/configuration#running-the-binary-directly
# rke2 server : Run the RKE2 management server, which will also launch the Kubernetes control plane components such as the API server, controller-manager, and scheduler. Only Supported on Linux.
# rke2 agent : Run the RKE2 node agent. This will cause RKE2 to run as a worker node, launching the Kubernetes node services kubelet and kube-proxy. Supported on Linux and Windows.
rke2 --h
COMMANDS:
   server           Run management server
   agent            Run node agent

# RKE2 설정 : cni 플러그인(canal) 등
https://docs.rke2.io/install/configuration
https://docs.rke2.io/advanced
cat << EOF > /etc/rancher/rke2/config.yaml
write-kubeconfig-mode: "0644"
debug: true
cni: canal
bind-address: 192.168.10.11
advertise-address: 192.168.10.11
node-ip: 192.168.10.11
disable-cloud-controller: true
disable:
  - servicelb
  - rke2-coredns-autoscaler
  - rke2-ingress-nginx
  - rke2-snapshot-controller
  - rke2-snapshot-controller-crd
  - rke2-snapshot-validation-webhook
EOF

# canal cni 플러그인 helm chart values 파일 작성
https://docs.rke2.io/networking/basic_network_options
https://github.com/rancher/rke2-charts/blob/main-source/packages/rke2-canal/charts/values.yaml
mkdir -p /var/lib/rancher/rke2/server/manifests/
cat << EOF > /var/lib/rancher/rke2/server/manifests/rke2-canal-config.yaml
apiVersion: helhttp://m.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-canal
  namespace: kube-system
spec:
  valuesContent: |-
    flannel:
      iface: "enp0s8"
EOF

# coredns 의 autoscaler 미설치를 위한 helm chart values 파일 작성
https://docs.rke2.io/add-ons/helm#customizing-packaged-components-with-helmchartconfig
https://github.com/rancher/rke2-charts/tree/main/charts/rke2-coredns/rke2-coredns/1.45.200
cat << EOF > /var/lib/rancher/rke2/server/manifests/rke2-coredns-config.yaml
apiVersion: helhttp://m.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: rke2-coredns
  namespace: kube-system
spec:
  valuesContent: |-
    autoscaler:
      enabled: false
EOF
    
# RKE2 시작 : 2분 정도 소요 -> coredns 파드까지 정상화 대략 1~2분 추가 소요
systemctl enable --now rke2-server.service
systemctl status rke2-server --no-pager
● rke2-server.service - Rancher Kubernetes Engine v2 (server)
     Loaded: loaded (/usr/lib/systemd/system/rke2-server.service; enabled; preset: disabled)
     Active: active (running) since Wed 2026-02-18 13:06:23 KST; 25min ago
       Docs: https://github.com/rancher/rke2#readme
    Process: 6931 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS)
    Process: 6932 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS)
   Main PID: 6933 (rke2)
      Tasks: 162
     Memory: 2.1G
        CPU: 5min 17.531s
     CGroup: /system.slice/rke2-server.service
             ├─ 6933 "/usr/bin/rke2 server"
             ├─ 6954 containerd -c /var/lib/rancher/rke2/agent/etc/containerd/config.toml
             ├─ 7018 kubelet --volume-plugin-dir=/var/lib/kubelet/volumeplugins --file-check-frequency=5s --sync-frequency=30s --config-dir=/var/lib/rancher/rke2/ag…
             ├─ 7072 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id c8191abea8c642f41e03e6a5afd743157a2b62…
             ├─ 7077 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id 7a6b2e479dd5125c2398ad241abc1d669e4723…
             ├─ 7292 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id 64a8a94a463ec77ae7e80b6a18f82514362e3e…
             ├─ 7376 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id 393d8367a845629f737d5728a98d179094ed95…
             ├─ 7394 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id c9ee6d3c4d425f649f999b33c7707c9e947d61…
             ├─ 8071 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id c6816d83b0816a22324fa1dd4afedc4acfb23e…
             ├─ 9625 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id fb6e36730fc3e84d053418e300161adde2dd6e…
             ├─ 9652 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id 4d8761f0eff2e77e9de3dacb19d03f95e757d9…
             └─10039 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 -namespace k8s.io -id aea63f507d68b9942bc602ddabe0dbe35ad4c3…

#(참고) RKE는 하위 프로세스들이 실행되면, systemd가 Parent로 되게 설정함
https://tech-recipe.tistory.com/52



# 프로세스 확인

pstree -a | grep -v color | grep 'rke2$' -A5
  |-rke2
  |   |-containerd -c /var/lib/rancher/rke2/agent/etc/containerd/config.toml
  |   |   `-14*[{containerd}]
  |   |-kubelet --volume-plugin-dir=/var/lib/kubelet/volumeplugins --file-check-frequency=5s --sync-frequency=30s...
  |   |   `-16*[{kubelet}]
  |   `-11*[{rke2}]

pstree -a | grep -v color | grep 'containerd-shim ' -A2
  |-containerd-shim -namespace k8s.io -idc8191abea8c642f41e03e6a5afd
  |   |-kube-proxy --cluster-cidr=10.42.0.0/16 --conntrack-max-per-core=0 --conntrack-tcp-timeout-close-wait=0s...
  |   |   `-7*[{kube-proxy}]
--
  |-containerd-shim -namespace k8s.io -id7a6b2e479dd5125c2398ad241ab
  |   |-etcd --config-file=/var/lib/rancher/rke2/server/db/etcd/config
  |   |   `-11*[{etcd}]
--
  |-containerd-shim -namespace k8s.io -id64a8a94a463ec77ae7e80b6a18f
  |   |-kube-apiserver --admission-control-config-file=/etc/rancher/rke2/rke2-pss.yaml --advertise-address=192.168.10.11...
  |   |   `-11*[{kube-apiserver}]
--
  |-containerd-shim -namespace k8s.io -id393d8367a845629f737d5728a98
  |   |-kube-controller --permit-port-sharing=true --flex-volume-plugin-dir=/var/lib/kubelet/volumeplugins--terminated-pod-gc-thres
  |   |   `-6*[{kube-controller}]
--
  |-containerd-shim -namespace k8s.io -idc9ee6d3c4d425f649f999b33c77
  |   |-kube-scheduler --permit-port-sharing=true ...
  |   |   `-8*[{kube-scheduler}]
--
  |-containerd-shim -namespace k8s.io -idc6816d83b0816a22324fa1dd4af
  |   |-flanneld --ip-masq --kube-subnet-mgr --iptables-forward-rules=false --ip-blackhole-route
  |   |   |-(timeout)
--
  |-containerd-shim -namespace k8s.io -idfb6e36730fc3e84d053418e3001
  |   |-coredns -conf /etc/coredns/Corefile
  |   |   `-7*[{coredns}]
--
  |-containerd-shim -namespace k8s.io -id4d8761f0eff2e77e9de3dacb19d
  |   |-cluster-proport --namespace=kube-system --configmap=rke2-coredns-rke2-coredns-autoscaler--target=Deployment/rke2-c
  |   |   `-6*[{cluster-proport}]
--
  |-containerd-shim -namespace k8s.io -idaea63f507d68b9942bc602ddabe
  |   |-metrics-server --secure-port=10250 --cert-dir=/tmp --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname...
  |   |   `-8*[{metrics-server}]


# 자격증명 파일 복사
mkdir ~/.kube
cp /etc/rancher/rke2/rke2.yaml ~/.kube/config

# /etc/rancher 디렉터리 확인
tree /etc/rancher/
/etc/rancher/
├── node
│   └── password
└── rke2
    ├── config.yaml
    ├── rke2-pss.yaml
    └── rke2.yaml

2 directories, 4 files

cat /etc/rancher/node/password
08ce9be15deb8c6c10726f30c20781e8

cat /etc/rancher/rke2/config.yaml
write-kubeconfig-mode: "0644"
debug: true
cni: canal
bind-address: 192.168.10.11
advertise-address: 192.168.10.11
node-ip: 192.168.10.11
disable-cloud-controller: true
disable:
  - servicelb
  - rke2-coredns-autoscaler
  - rke2-ingress-nginx
  - rke2-snapshot-controller
  - rke2-snapshot-controller-crd
  - rke2-snapshot-validation-webhook

cat /etc/rancher/rke2/rke2-pss.yaml 
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1beta1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "privileged"
      enforce-version: "latest"
    exemptions:
      usernames: []
      runtimeClasses: []
      namespaces: []

# 바이너리 파일 확인
tree /var/lib/rancher/rke2/bin/
/var/lib/rancher/rke2/bin/
├── containerd
├── containerd-shim-runc-v2
├── crictl
├── ctr
├── kubectl
├── kubelet
└── runc

# PATH 안 건드리고 표준 위치로 바이너리 노출 설정 : 심볼릭 링크 방식 
ln -s /var/lib/rancher/rke2/bin/containerd /usr/local/bin/containerd
ln -s /var/lib/rancher/rke2/bin/kubectl /usr/local/bin/kubectl
ln -s /var/lib/rancher/rke2/bin/crictl /usr/local/bin/crictl
ln -s /var/lib/rancher/rke2/bin/runc /usr/local/bin/runc
ln -s /var/lib/rancher/rke2/bin/ctr /usr/local/bin/ctr
ln -s /var/lib/rancher/rke2/agent/etc/crictl.yaml /etc/crictl.yaml

runc --version
runc version 1.4.0
commit: v1.4.0-0-g8bd78a99
spec: 1.3.0
go: go1.24.11 X:boringcrypto
libseccomp: 2.5.4

containerd --version
containerd github.com/k3s-io/containerd v2.1.5-k3s1 e77c15f30e5162d6abab671b0d74ca2243e2916e

kubectl version
Client Version: v1.33.8+rke2r1
Kustomize Version: v5.6.0
Server Version: v1.33.8+rke2r1

# 편의성 설정
source <(kubectl completion bash)
alias k=kubectl
complete -F __start_kubectl k
echo 'source <(kubectl completion bash)' >> /etc/profile
echo 'alias k=kubectl' >> /etc/profile
echo 'complete -F __start_kubectl k' >> /etc/profile

# 확인
kubectl cluster-info -v=6
I0218 13:12:41.348931   14784 loader.go:402] Config loaded from file:  /root/.kube/config
I0218 13:12:41.350585   14784 envvar.go:172] "Feature gate default state" feature="WatchListClient" enabled=false
I0218 13:12:41.350835   14784 envvar.go:172] "Feature gate default state" feature="ClientsAllowCBOR" enabled=false
I0218 13:12:41.352096   14784 envvar.go:172] "Feature gate default state" feature="ClientsPreferCBOR" enabled=false
I0218 13:12:41.352123   14784 envvar.go:172] "Feature gate default state" feature="InformerResourceVersion" enabled=false
I0218 13:12:41.352131   14784 envvar.go:172] "Feature gate default state" feature="InOrderInformers" enabled=true
I0218 13:12:41.364128   14784 round_trippers.go:632] "Response" verb="GET" url="https://192.168.10.11:6443/api?timeout=32s" status="200 OK" milliseconds=11
I0218 13:12:41.369158   14784 round_trippers.go:632] "Response" verb="GET" url="https://192.168.10.11:6443/apis?timeout=32s" status="200 OK" milliseconds=2
I0218 13:12:41.393121   14784 round_trippers.go:632] "Response" verb="GET" url="https://192.168.10.11:6443/api/v1/namespaces/kube-system/services?labelSelector=kubernetes.io%2Fcluster-service%3Dtrue" status="200 OK" milliseconds=7
Kubernetes control plane is running at https://192.168.10.11:6443
CoreDNS is running at https://192.168.10.11:6443/api/v1/namespaces/kube-system/services/rke2-coredns-rke2-coredns:udp-53/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

# 노드, 파드 정보 확인
kubectl get node -owide
NAME        STATUS   ROLES                       AGE     VERSION          INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                      KERNEL-VERSION                 CONTAINER-RUNTIME
k8s-node1   Ready    control-plane,etcd,master   6m50s   v1.33.8+rke2r1   192.168.10.11   <none>        Rocky Linux 9.6 (Blue Onyx)   5.14.0-570.52.1.el9_6.x86_64   containerd://2.1.5-k3s1


helm list -A
NAME                    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                                   APP VERSION
rke2-canal              kube-system     1               2026-02-18 04:06:42.787680481 +0000 UTC deployed        rke2-canal-v3.31.3-build2026020600      v3.31.3
rke2-coredns            kube-system     1               2026-02-18 04:06:42.83878489 +0000 UTC  deployed        rke2-coredns-1.45.201                   1.13.1
rke2-metrics-server     kube-system     1               2026-02-18 04:07:35.768630923 +0000 UTC deployed        rke2-metrics-server-3.13.007            0.8.0
rke2-runtimeclasses     kube-system     1               2026-02-18 04:07:35.775136792 +0000 UTC deployed        rke2-runtimeclasses-0.1.000             0.1.0


kubectl get pod -A
NAMESPACE     NAME                                                   READY   STATUS      RESTARTS   AGE
kube-system   etcd-k8s-node1                                         1/1     Running     0          7m26s
kube-system   helm-install-rke2-canal-7nlz9                          0/1     Completed   0          7m23s
kube-system   helm-install-rke2-coredns-2769x                        0/1     Completed   0          7m23s
kube-system   helm-install-rke2-metrics-server-9lcss                 0/1     Completed   0          7m23s
kube-system   helm-install-rke2-runtimeclasses-5xrzr                 0/1     Completed   0          7m23s
kube-system   kube-apiserver-k8s-node1                               1/1     Running     0          7m26s
kube-system   kube-controller-manager-k8s-node1                      1/1     Running     0          7m26s
kube-system   kube-proxy-k8s-node1                                   1/1     Running     0          7m26s
kube-system   kube-scheduler-k8s-node1                               1/1     Running     0          7m26s
kube-system   rke2-canal-ptx7b                                       2/2     Running     0          7m3s
kube-system   rke2-coredns-rke2-coredns-559595db99-fs27b             1/1     Running     0          7m6s
kube-system   rke2-coredns-rke2-coredns-autoscaler-8c8d5f4f4-ll2f9   1/1     Running     0          7m6s
kube-system   rke2-metrics-server-fdcdf575d-42nmb                    1/1     Running     0          6m13s

 

워커 노드 추가

k8s-node2를 클러스터에 워커노드로 Join 하는 실습을 진행합니다.

[k8s-node1]
# control-plane인 k8s-node1 에서 TOKEN 값을 확인

cat /var/lib/rancher/rke2/server/node-token
K10d37597051b8b6a68d072f42350af53c2ea977b253ca435263c87a517002ca756::server:d7bb934b9778853e373e8bf78fcf1aa9

# 노드(서버/에이전트)가 RKE2 클러스터에 조인할 때 사용하는 전용 관리/부트스트랩 API 포트 확인
ss -tnlp | grep 9345
LISTEN 0      4096   192.168.10.11:9345       0.0.0.0:*    users:(("rke2",pid=6348,fd=6))

# 아래는 k8s-node2 에서 수행
[k8s-node2]

# Run the installer
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE="agent" INSTALL_RKE2_CHANNEL=v1.33 sh -

# Configure the rke2-agent service : rke-agent 설정 파일 작성 
## The rke2 server process listens on port 9345 for new nodes to register.
TOKEN=K10d37597051b8b6a68d072f42350af53c2ea977b253ca435263c87a517002ca756::server:d7bb934b9778853e373e8bf78fcf1aa9

mkdir -p /etc/rancher/rke2/
cat << EOF > /etc/rancher/rke2/config.yaml
server: https://192.168.10.11:9345
token: $TOKEN
EOF

# Enabled/Start the service
systemctl enable --now rke2-agent.service

[k8s-node1]
# JOIN 이 완료되면 control-plane인 k8s-node1 에서 확인
kubectl get no
NAME        STATUS   ROLES                       AGE    VERSION
k8s-node1   Ready    control-plane,etcd,master   63m    v1.33.8+rke2r1
k8s-node2   Ready    <none>                      2m9s   v1.33.8+rke2r1

kubectl get po -A -o wide
NAMESPACE     NAME                                                   READY   STATUS      RESTARTS   AGE     IP              NODE        NOMINATED NODE   READINESS GATES
kube-system   etcd-k8s-node1                                         1/1     Running     0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   helm-install-rke2-canal-7nlz9                          0/1     Completed   0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   helm-install-rke2-coredns-2769x                        0/1     Completed   0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   helm-install-rke2-metrics-server-9lcss                 0/1     Completed   0          63m     10.42.0.4       k8s-node1   <none>           <none>
kube-system   helm-install-rke2-runtimeclasses-5xrzr                 0/1     Completed   0          63m     10.42.0.3       k8s-node1   <none>           <none>
kube-system   kube-apiserver-k8s-node1                               1/1     Running     0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   kube-controller-manager-k8s-node1                      1/1     Running     0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   kube-proxy-k8s-node1                                   1/1     Running     0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   kube-proxy-k8s-node2                                   1/1     Running     0          2m26s   192.168.10.12   k8s-node2   <none>           <none>
kube-system   kube-scheduler-k8s-node1                               1/1     Running     0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   rke2-canal-7pjs5                                       2/2     Running     0          2m26s   192.168.10.12   k8s-node2   <none>           <none>
kube-system   rke2-canal-ptx7b                                       2/2     Running     0          63m     192.168.10.11   k8s-node1   <none>           <none>
kube-system   rke2-coredns-rke2-coredns-559595db99-fs27b             1/1     Running     0          63m     10.42.0.2       k8s-node1   <none>           <none>
kube-system   rke2-coredns-rke2-coredns-559595db99-vmtv2             1/1     Running     0          89s     10.42.1.2       k8s-node2   <none>           <none>
kube-system   rke2-coredns-rke2-coredns-autoscaler-8c8d5f4f4-ll2f9   1/1     Running     0          63m     10.42.0.5       k8s-node1   <none>           <none>
kube-system   rke2-metrics-server-fdcdf575d-42nmb                    1/1     Running     0          62m     10.42.0.6       k8s-node1   <none>           <none>

 

샘플 애플리케이션 배포

RKE2 에 샘플 어플리케이션을 배포하고 동작 확인

# 샘플 애플리케이션 배포
cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webpod
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webpod
  template:
    metadata:
      labels:
        app: webpod
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - sample-app
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: webpod
        image: traefik/whoami
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: webpod
  labels:
    app: webpod
spec:
  selector:
    app: webpod
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
    nodePort: 30000
  type: NodePort
EOF

# 확인
kubectl get deploy,pod,svc,ep -owide
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES           SELECTOR
deployment.apps/webpod   2/2     2            2           13s   webpod       traefik/whoami   app=webpod

NAME                          READY   STATUS    RESTARTS   AGE   IP          NODE        NOMINATED NODE   READINESS GATES
pod/webpod-697b545f57-cvkwx   1/1     Running   0          13s   10.42.1.3   k8s-node2   <none>           <none>
pod/webpod-697b545f57-j58n2   1/1     Running   0          13s   10.42.0.7   k8s-node1   <none>           <none>

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
service/kubernetes   ClusterIP   10.43.0.1      <none>        443/TCP        66m   <none>
service/webpod       NodePort    10.43.77.240   <none>        80:30000/TCP   13s   app=webpod

NAME                   ENDPOINTS                   AGE
endpoints/kubernetes   192.168.10.11:6443          66m
endpoints/webpod       10.42.0.7:80,10.42.1.3:80   12s

# [HostPC] 반복 호출 : IP는 node 작업에 따라 변경
while true; do curl -s http://192.168.10.12:30000 | grep Hostname; date; sleep 1; done
Hostname: webpod-697b545f57-cvkwx
Wed Feb 18 14:14:32     2026
Hostname: webpod-697b545f57-cvkwx
Wed Feb 18 14:14:33     2026
...(생략)...

 

실습 리소스 삭제

# 실습 종료후 Vagrant 로 생성한 vm 삭제
vagrant destroy -f

Cluster API

Cluster API는 Kubernetes 클러스터를 프로비저닝, 업그레이드 및 운영하기 위한 선언적 API 및 도구를 제공하는 Kubernetes 하위 프로젝트입니다.

https://cluster-api.sigs.k8s.io/introduction

 

Introduction - The Cluster API Book

Cluster API is a Kubernetes sub-project focused on providing declarative APIs and tooling to simplify provisioning, upgrading, and operating multiple Kubernetes clusters. Started by the Kubernetes Special Interest Group (SIG) Cluster Lifecycle, the Cluster

cluster-api.sigs.k8s.io

  • Kubernetes SIG(Special Interest Group) 클러스터 라이프사이클 에서 시작된 클러스터 API 프로젝트는 Kubernetes 스타일의 API와 패턴을 사용하여 플랫폼 운영자를 위한 클러스터 라이프사이클 관리를 자동화합니다.

  • 가상 머신, 네트워크, 로드 밸런서, VPC와 같은 지원 인프라와 Kubernetes 클러스터 구성은 애플리케이션 개발자가 워크로드를 배포하고 관리하는 방식과 동일하게 정의됩니다. 이를 통해 다양한 인프라 환경에서 일관되고 반복 가능한 클러스터 배포가 가능합니다.

위 그림을 보면 k8s 클러스터를 찍어내듯이 새롭게 구성하는 모습을 형상화하였습니다. 일반적인 환경의 업체보다는 "K8S as a Service"  가 필요한 플랫폼 기업에서 사용하면 효과적이라고 생각됩니다.

 

Cluster API 프로젝트 목표

  • 선언적 API를 사용하여 Kubernetes 표준을 준수하는 클러스터의 수명 주기(생성, 확장, 업그레이드, 삭제)를 관리합니다.
  • 온프레미스 환경과 클라우드 환경 모두에서 작업할 수 있어야 합니다.
  • 공통 연산을 정의하고, 기본 구현을 제공하며, 다른 구현으로 교체할 수 있는 기능을 제공합니다.
  • 기존 생태계 구성 요소의 기능을 중복해서 개발하는 대신 재사용 및 통합합니다(예: 노드 문제 감지기, 클러스터 자동 확장기, SIG-Multi-cluster).
  • Kubernetes 라이프사이클 제품이 Cluster API를 점진적으로 채택할 수 있는 전환 경로를 제공합니다.
  • 특히, 기존의 클러스터 라이프사이클 관리 도구는 여러 릴리스에 걸쳐 단계적으로 Cluster API를 채택하거나 클러스터 API의 하위 집합을 채택할 수 있어야 합니다.

Cluster API 실습

① kind k8s에 관리용 Management 클러스터 설치 init

https://cluster-api.sigs.k8s.io/clusterctl/commands/init

# 작업 디렉터리 생성
mkdir capi-docker && cd capi-docker

docker context ls
NAME        DESCRIPTION                               DOCKER ENDPOINT               ERROR
default *   Current DOCKER_HOST based configuration   unix:///var/run/docker.sock

# kind를 이용하여 k8s 설치
kind create cluster --name myk8s --image kindest/node:v1.35.0 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraMounts:
  - hostPath: /var/run/docker.sock
    containerPath: /var/run/docker.sock
  extraPortMappings:
  - containerPort: 30000     # sample
    hostPort: 30000
  - containerPort: 30001     # kube-ops-view
    hostPort: 30001
EOF

# (옵션) kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 \
  --set service.main.type=NodePort,service.main.ports.http.nodePort=30001 \
  --set env.TZ="Asia/Seoul" --namespace kube-system

# open "http://127.0.0.1:30001/#scale=1.5"



# Install clusterctl
curl -L https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.12.2/clusterctl-linux-amd64 -o clusterctl
sudo install -o root -g root -m 0755 clusterctl /usr/local/bin/clusterctl

## 버전 확인
clusterctl version -o json | jq
{
  "clusterctl": {
    "major": "1",
    "minor": "12",
    "gitVersion": "v1.12.2",
    "gitCommit": "9ee80d1ff529c48ae0b8e022ec01d70ac496e8e5",
    "gitTreeState": "clean",
    "buildDate": "2026-01-20T16:48:19Z",
    "goVersion": "go1.24.12",
    "compiler": "gc",
    "platform": "linux/amd64"
  }
}


# [Docker 프로바이더] Initialize the management cluster : 현재 k8s 를 관리 클러스터로 변환
## Docker 프로바이더는 프로덕션 환경에 사용하도록 설계되지 않았으며 개발 환경 전용
## ClusterTopology관리형 토폴로지 및 ClusterClass 지원을 활성화하는 데 필요한 기능은 다음과 같이 활성화
## https://cluster-api.sigs.k8s.io/tasks/experimental-features/experimental-features
export CLUSTER_TOPOLOGY=true # Enable the experimental Cluster topology feature
clusterctl init --infrastructure docker # Initialize the management cluster
Fetching providers
Installing cert-manager version="v1.19.1"
Waiting for cert-manager to be available...
spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="cluster-api" version="v1.12.3" targetNamespace="capi-system"
spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="bootstrap-kubeadm" version="v1.12.3" targetNamespace="capi-kubeadm-bootstrap-system"
spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="control-plane-kubeadm" version="v1.12.3" targetNamespace="capi-kubeadm-control-plane-system"
spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`.
Installing provider="infrastructure-docker" version="v1.12.3" targetNamespace="capd-system"
spec.privateKey.rotationPolicy: In cert-manager >= v1.18.0, the default value changed from `Never` to `Always`.

Your management cluster has been initialized successfully!

You can now create your first workload cluster by running the following:

  clusterctl generate cluster [name] --kubernetes-version [version] | kubectl apply -f -


New clusterctl version available: v1.12.2 -> v1.12.3
sigs.k8s.io/cluster-api

# 확인
## 관련 crd 설치
kubectl get crd | grep x-k8s
clusterclasses.cluster.x-k8s.io                              2026-02-18T06:53:00Z
clusterresourcesetbindings.addons.cluster.x-k8s.io           2026-02-18T06:53:00Z
clusterresourcesets.addons.cluster.x-k8s.io                  2026-02-18T06:53:00Z
clusters.cluster.x-k8s.io                                    2026-02-18T06:53:01Z
devclusters.infrastructure.cluster.x-k8s.io                  2026-02-18T06:53:04Z
devclustertemplates.infrastructure.cluster.x-k8s.io          2026-02-18T06:53:04Z
devmachines.infrastructure.cluster.x-k8s.io                  2026-02-18T06:53:04Z
devmachinetemplates.infrastructure.cluster.x-k8s.io          2026-02-18T06:53:04Z
dockerclusters.infrastructure.cluster.x-k8s.io               2026-02-18T06:53:04Z
dockerclustertemplates.infrastructure.cluster.x-k8s.io       2026-02-18T06:53:04Z
dockermachinepools.infrastructure.cluster.x-k8s.io           2026-02-18T06:53:04Z
dockermachinepooltemplates.infrastructure.cluster.x-k8s.io   2026-02-18T06:53:04Z
dockermachines.infrastructure.cluster.x-k8s.io               2026-02-18T06:53:04Z
dockermachinetemplates.infrastructure.cluster.x-k8s.io       2026-02-18T06:53:04Z
extensionconfigs.runtime.cluster.x-k8s.io                    2026-02-18T06:53:01Z
ipaddressclaims.ipahttp://m.cluster.x-k8s.io 2026-02-18T06:53:01Z
ipaddresses.ipahttp://m.cluster.x-k8s.io 2026-02-18T06:53:01Z
kubeadmconfigs.bootstrap.cluster.x-k8s.io                    2026-02-18T06:53:02Z
kubeadmconfigtemplates.bootstrap.cluster.x-k8s.io            2026-02-18T06:53:02Z
kubeadmcontrolplanes.controlplane.cluster.x-k8s.io           2026-02-18T06:53:03Z
kubeadmcontrolplanetemplates.controlplane.cluster.x-k8s.io   2026-02-18T06:53:03Z
machinedeployments.cluster.x-k8s.io                          2026-02-18T06:53:01Z
machinedrainrules.cluster.x-k8s.io                           2026-02-18T06:53:01Z
machinehealthchecks.cluster.x-k8s.io                         2026-02-18T06:53:01Z
machinepools.cluster.x-k8s.io                                2026-02-18T06:53:01Z
machines.cluster.x-k8s.io                                    2026-02-18T06:53:01Z
machinesets.cluster.x-k8s.io                                 2026-02-18T06:53:01Z
providers.clusterctl.cluster.x-k8s.io                        2026-02-18T06:52:20Z

## capd-system, capi-(kueadm-X/Y, system), cert-manager 네임스페이스가 생성
kubectl get pod -A
NAMESPACE                           NAME                                                             READY   STATUS    RESTARTS   AGE
capd-system                         capd-controller-manager-54755cdd6-cjmsf                          1/1     Running   0          89s
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-controller-manager-94d8964d9-v6hs2        1/1     Running   0          91s
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-controller-manager-6796744c76-42d8c   1/1     Running   0          90s
capi-system                         capi-controller-manager-59c5798655-m9bjr                         1/1     Running   0          92s
cert-manager                        cert-manager-598d877b78-pvt4k                                    1/1     Running   0          2m8s
cert-manager                        cert-manager-cainjector-6b5777d564-4gsnt                         1/1     Running   0          2m8s
cert-manager                        cert-manager-webhook-5d9fc6b4ff-v4gk5                            1/1     Running   0          2m8s
kube-system                         coredns-7d764666f9-4rn5m                                         1/1     Running   0          5m33s
kube-system                         coredns-7d764666f9-hsr92                                         1/1     Running   0          5m33s
kube-system                         etcd-myk8s-control-plane                                         1/1     Running   0          5m39s
kube-system                         kindnet-59gbn                                                    1/1     Running   0          5m33s
kube-system                         kube-apiserver-myk8s-control-plane                               1/1     Running   0          5m39s
kube-system                         kube-controller-manager-myk8s-control-plane                      1/1     Running   0          5m39s
kube-system                         kube-ops-view-5c64986f74-5lmd2                                   1/1     Running   0          3m24s
kube-system                         kube-proxy-49jsl                                                 1/1     Running   0          5m33s
kube-system                         kube-scheduler-myk8s-control-plane                               1/1     Running   0          5m39s
local-path-storage                  local-path-provisioner-67b8995b4b-9jzvv                          1/1     Running   0          5m33s

 

② 관리용 Management K8S 클러스터 정보 확인

## Enabling Experimental Features 정보 확인 : ClusterTopology=true , InPlaceUpdates=false 등
kubectl describe -n capi-system deployment.apps/capi-controller-manager | grep feature-gates
      --feature-gates=MachinePool=true,ClusterTopology=true,RuntimeSDK=false,MachineSetPreflightChecks=true,MachineWaitForVolumeDetachConsiderVolumeAttachments=true,PriorityQueue=false,ReconcilerRateLimiting=false,InPlaceUpdates=false,MachineTaintPropagation=false

# 프로바이더 (타입별)확인 : CAPI 구성요소가 설치된 상태
kubectl get providers.clusterctl.cluster.x-k8s.io -A
NAMESPACE                           NAME                    AGE     TYPE                     PROVIDER      VERSION
capd-system                         infrastructure-docker   4m56s   InfrastructureProvider   docker        v1.12.3
capi-kubeadm-bootstrap-system       bootstrap-kubeadm       4m58s   BootstrapProvider        kubeadm       v1.12.3
capi-kubeadm-control-plane-system   control-plane-kubeadm   4m57s   ControlPlaneProvider     kubeadm       v1.12.3
capi-system                         cluster-api             4m59s   CoreProvider             cluster-api   v1.12.3

# CAPI의 핵심 컨트롤러 집합 : Cluster / MachineDeployment / MachineSet / Machine CRD 관리, 전체 reconcile orchestration 담당
kubectl get providers -n capi-system cluster-api -o yaml
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Provider
metadata:
  creationTimestamp: "2026-02-18T06:53:02Z"
  generation: 1
  labels:
    cluster.x-k8s.io/provider: cluster-api
    clusterctl.cluster.x-k8s.io: ""
    clusterctl.cluster.x-k8s.io/core: inventory
  name: cluster-api
  namespace: capi-system
  resourceVersion: "1064"
  uid: 34380fef-226b-413e-8c27-39fda6b19964
providerName: cluster-api
type: CoreProvider
version: v1.12.3

# 노드를 Kubernetes로 부팅시키는 역할 : cloud-init user-data 생성, kubeadm join/init config 생성
kubectl get providers -n capi-kubeadm-bootstrap-system bootstrap-kubeadm -o yaml
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Provider
metadata:
  creationTimestamp: "2026-02-18T06:53:03Z"
  generation: 1
  labels:
    cluster.x-k8s.io/provider: bootstrap-kubeadm
    clusterctl.cluster.x-k8s.io: ""
    clusterctl.cluster.x-k8s.io/core: inventory
  name: bootstrap-kubeadm
  namespace: capi-kubeadm-bootstrap-system
  resourceVersion: "1126"
  uid: a2b58ebb-f52f-47fb-9849-95227a160b1d
providerName: kubeadm
type: BootstrapProvider
version: v1.12.3

# Control Plane 전용 Machine 관리 : KubeadmControlPlane 리소스 관리, Control Plane 노드 스케일링, etcd 포함 업그레이드 관리
kubectl get providers -n capi-kubeadm-control-plane-system control-plane-kubeadm -o yaml
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Provider
metadata:
  creationTimestamp: "2026-02-18T06:53:04Z"
  generation: 1
  labels:
    cluster.x-k8s.io/provider: control-plane-kubeadm
    clusterctl.cluster.x-k8s.io: ""
    clusterctl.cluster.x-k8s.io/core: inventory
  name: control-plane-kubeadm
  namespace: capi-kubeadm-control-plane-system
  resourceVersion: "1200"
  uid: c65b2a94-7eec-499c-8672-9ab6ba2c1f52
providerName: kubeadm
type: ControlPlaneProvider
version: v1.12.3

# 실제 인프라 리소스 생성 담당 : 실제 Docker 컨테이너를 VM처럼 생성, Dev/Test 용도 (CAPD)
kubectl get providers -n capd-system infrastructure-docker -o yaml
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Provider
metadata:
  creationTimestamp: "2026-02-18T06:53:05Z"
  generation: 1
  labels:
    cluster.x-k8s.io/provider: infrastructure-docker
    clusterctl.cluster.x-k8s.io: ""
    clusterctl.cluster.x-k8s.io/core: inventory
  name: infrastructure-docker
  namespace: capd-system
  resourceVersion: "1303"
  uid: a59b4f02-5ec2-4387-b587-4ed288a648b4
providerName: docker
type: InfrastructureProvider
version: v1.12.3

 

③ cert-manger 확인

kubectl get crd | grep cert
certificaterequests.cert-manager.io                          2026-02-18T06:52:25Z
certificates.cert-manager.io                                 2026-02-18T06:52:25Z
challenges.acme.cert-manager.io                              2026-02-18T06:52:25Z
clusterissuers.cert-manager.io                               2026-02-18T06:52:25Z
issuers.cert-manager.io                                      2026-02-18T06:52:26Z
orders.acme.cert-manager.io                                  2026-02-18T06:52:25Z


kubectl get deploy,pod,svc,ep,cm,secret,sa -n cert-manager
NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/cert-manager              1/1     1            1           9m
deployment.apps/cert-manager-cainjector   1/1     1            1           9m
deployment.apps/cert-manager-webhook      1/1     1            1           9m

NAME                                           READY   STATUS    RESTARTS   AGE
pod/cert-manager-598d877b78-pvt4k              1/1     Running   0          9m
pod/cert-manager-cainjector-6b5777d564-4gsnt   1/1     Running   0          9m
pod/cert-manager-webhook-5d9fc6b4ff-v4gk5      1/1     Running   0          9m

NAME                              TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)            AGE
service/cert-manager              ClusterIP   10.96.98.8    <none>        9402/TCP           9m
service/cert-manager-cainjector   ClusterIP   10.96.16.30   <none>        9402/TCP           9m
service/cert-manager-webhook      ClusterIP   10.96.98.31   <none>        443/TCP,9402/TCP   9m

NAME                                ENDPOINTS                          AGE
endpoints/cert-manager              10.244.0.7:9402                    9m
endpoints/cert-manager-cainjector   10.244.0.6:9402                    9m
endpoints/cert-manager-webhook      10.244.0.8:10250,10.244.0.8:9402   9m

NAME                         DATA   AGE
configmap/kube-root-ca.crt   1      9m1s

NAME                             TYPE     DATA   AGE
secret/cert-manager-webhook-ca   Opaque   3      8m33s

NAME                                     AGE
serviceaccount/cert-manager              9m
serviceaccount/cert-manager-cainjector   9m
serviceaccount/cert-manager-webhook      9m
serviceaccount/default                   9m1s


kubectl get issuers.cert-manager.io -A                    
NAMESPACE                           NAME                                           READY   AGE
capd-system                         capd-selfsigned-issuer                         True    8m50s
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-selfsigned-issuer       True    8m52s
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-selfsigned-issuer   True    8m51s
capi-system                         capi-selfsigned-issuer                         True    8m54s

kubectl get certificaterequests.cert-manager.io -A -owide
NAMESPACE                           NAME                                        APPROVED   DENIED   READY   ISSUER                                         REQUESTER                                         STATUS                                         AGE
capd-system                         capd-serving-cert-1                         True                True    capd-selfsigned-issuer                         system:serviceaccount:cert-manager:cert-manager   Certificate fetched from issuer successfully   9m9s
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-serving-cert-1       True                True    capi-kubeadm-bootstrap-selfsigned-issuer       system:serviceaccount:cert-manager:cert-manager   Certificate fetched from issuer successfully   9m11s
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-serving-cert-1   True                True    capi-kubeadm-control-plane-selfsigned-issuer   system:serviceaccount:cert-manager:cert-manager   Certificate fetched from issuer successfully   9m10s
capi-system                         capi-serving-cert-1                         True                True    capi-selfsigned-issuer                         system:serviceaccount:cert-manager:cert-manager   Certificate fetched from issuer successfully   9m12s


kubectl get certificates.cert-manager.io -A -owide
NAMESPACE                           NAME                                      READY   SECRET                                            ISSUER                                         STATUS                                          AGE
capd-system                         capd-serving-cert                         True    capd-webhook-service-cert                         capd-selfsigned-issuer                         Certificate is up to date and has not expired   9m27s
capi-kubeadm-bootstrap-system       capi-kubeadm-bootstrap-serving-cert       True    capi-kubeadm-bootstrap-webhook-service-cert       capi-kubeadm-bootstrap-selfsigned-issuer       Certificate is up to date and has not expired   9m29s
capi-kubeadm-control-plane-system   capi-kubeadm-control-plane-serving-cert   True    capi-kubeadm-control-plane-webhook-service-cert   capi-kubeadm-control-plane-selfsigned-issuer   Certificate is up to date and has not expired   9m28s
capi-system                         capi-serving-cert                         True    capi-webhook-service-cert                         capi-selfsigned-issuer                         Certificate is up to date and has not expired   9m31s

 

④ 첫 번째 워크로드 Workload 클러스터 생성 및 확인

# 첫 번째 워크로드 구성을 위한 환경 변수 설정 : 필요에 맞게 수정.
## The list of service CIDR, default ["10.128.0.0/12"]
export SERVICE_CIDR=["10.20.0.0/16"]

## The list of pod CIDR, default ["192.168.0.0/16"]
export POD_CIDR=["10.10.0.0/16"]

## The service domain, default "cluster.local"
export SERVICE_DOMAIN="myk8s-1.local"

## PSS Disable
export POD_SECURITY_STANDARD_ENABLED="false"


# Generating the cluster configuration : 워크로드 클러스터 생성 --dry-run
## https://cluster-api.sigs.k8s.io/clusterctl/commands/generate-cluster
clusterctl generate cluster capi-quickstart --flavor development \
  --kubernetes-version v1.34.3 \
  --control-plane-machine-count=3 \
  --worker-machine-count=3 \
  > capi-quickstart.yaml

# Cluster, Machines, Machine Deployments 등과 같은 Cluster API 객체의 미리 정의된 목록이 포함된 YAML 파일이 생성됨.
cat capi-quickstart.yaml

cat capi-quickstart.yaml | grep -E '^apiVersion:|^kind:'
apiVersion: cluster.x-k8s.io/v1beta2
kind: ClusterClass                       #  ClusterClass 정의 (설계도)
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: DockerClusterTemplate
apiVersion: controlplane.cluster.x-k8s.io/v1beta2
kind: KubeadmControlPlaneTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: DockerMachineTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: DockerMachineTemplate
apiVersion: infrastructure.cluster.x-k8s.io/v1beta2
kind: DockerMachinePoolTemplate
apiVersion: bootstrap.cluster.x-k8s.io/v1beta2
kind: KubeadmConfigTemplate
apiVersion: cluster.x-k8s.io/v1beta2
kind: Cluster                           # Cluster (Topology 기반 실제 클러스터)


# Apply the workload cluster
kubectl apply -f capi-quickstart.yaml
clusterclass.cluster.x-k8s.io/quick-start created
dockerclustertemplate.infrastructure.cluster.x-k8s.io/quick-start-cluster created
kubeadmcontrolplanetemplate.controlplane.cluster.x-k8s.io/quick-start-control-plane created
dockermachinetemplate.infrastructure.cluster.x-k8s.io/quick-start-control-plane created
dockermachinetemplate.infrastructure.cluster.x-k8s.io/quick-start-default-worker-machinetemplate created
dockermachinepooltemplate.infrastructure.cluster.x-k8s.io/quick-start-default-worker-machinepooltemplate created
kubeadmconfigtemplate.bootstrap.cluster.x-k8s.io/quick-start-default-worker-bootstraptemplate created
cluster.cluster.x-k8s.io/capi-quickstart created

##### 생성 확인 & kubeconfig 자격 증명 & CNI 플러그인 설치 후 확인

# Accessing the workload cluster
## The cluster will now start provisioning. You can check status with
kubectl get cluster -o wide
NAME              CLUSTERCLASS   AVAILABLE   CP DESIRED   CP CURRENT   CP READY   CP AVAILABLE   CP UP-TO-DATE   W DESIRED   W CURRENT   W READY   W AVAILABLE   W UP-TO-DATE   PAUSED   PHASE         AGE    VERSION
capi-quickstart   quick-start    False       3            2            0          0              2               3           3           0         0             3              False    Provisioned   119s   v1.34.3


## You can also get an “at glance” view of the cluster and its resources by running:
clusterctl describe cluster capi-quickstart
NAME                                                           REPLICAS AVAILABLE READY UP TO DATE STATUS REASON        SINCE  MESSAGE                            
Cluster/capi-quickstart                                        5/6      0         0     5          False  NotAvailable  2m32s  * WorkersAvailable:                
│                                                                                                                                * MachineDeployment capi-quickstart-md-0-nggp5: 0 available replicas, at least 3 required
│                                                                                                                                  (spec.strategy.rollout.maxUnavailable is 0, spec.replicas is 3)
├─ClusterInfrastructure - DockerCluster/capi-quickstart-sxndn                                      True   Ready         2m26s                                     
├─ControlPlane - KubeadmControlPlane/capi-quickstart-t85tp     2/3      0         0     2          True   Available     69s                                       
│ └─2 Machines...                                                       0         0     2          False  NotReady      76s    See capi-quickstart-t85tp-q68rk, capi-quickstart-t85tp-wks7g
└─Workers                                                                                                                                                         
  └─MachineDeployment/capi-quickstart-md-0-nggp5               3/3      0         0     3          False  NotAvailable  2m32s  0 available replicas, at least 3 required (spec.strategy.rollout.maxUnavailable is 0, spec.replicas
    │                                                                                                                          is 3)                              
    └─3 Machines...                                                     0         0     3          False  NotReady      2m15s  See capi-quickstart-md-0-nggp5-ktbr7-qwf9k, capi-quickstart-md-0-nggp5-ktbr7-tfbdr, ...

# docker 프로바이더에 의해서, 워크로드 클러스터에 머신이 컨테이너로 기동
docker ps
CONTAINER ID   IMAGE                                COMMAND                  CREATED              STATUS              PORTS                                                             NAMES
15455de22dd1   kindest/node:v1.34.3                 "/usr/local/bin/entr…"   About a minute ago   Up About a minute                                                                     capi-quickstart-md-0-nggp5-ktbr7-tfbdr
6a0ba5492f8f   kindest/node:v1.34.3                 "/usr/local/bin/entr…"   About a minute ago   Up About a minute                                                                     capi-quickstart-md-0-nggp5-ktbr7-v9wp9
dc5ed6880fcb   kindest/node:v1.34.3                 "/usr/local/bin/entr…"   About a minute ago   Up About a minute                                                                     capi-quickstart-md-0-nggp5-ktbr7-qwf9k
6b0064291f54   kindest/node:v1.34.3                 "/usr/local/bin/entr…"   2 minutes ago        Up 2 minutes        127.0.0.1:32770->6443/tcp                                         capi-quickstart-t85tp-q68rk
13a26eaa4503   kindest/haproxy:v20230606-42a2262b   "haproxy -W -db -f /…"   3 minutes ago        Up 2 minutes        0.0.0.0:32768->6443/tcp, 0.0.0.0:32769->8404/tcp                  capi-quickstart-lb
834590feb6e7   kindest/node:v1.35.0                 "/usr/local/bin/entr…"   28 minutes ago       Up 28 minutes       0.0.0.0:30000-30001->30000-30001/tcp, 127.0.0.1:45647->6443/tcp   myk8s-control-plane


# 컨트롤 플레인 역할 컨테이너 로그 1대 확인 해보기
docker logs -f capi-quickstart-t85tp-q68rk
...
Welcome to Debian GNU/Linux 12 (bookworm)!

Queued start job for default target graphical.target.
[  OK  ] Created slice kubelet.slic… used to run Kubernetes / Kubelet.
[  OK  ] Created slice system-modpr…lice - Slice /system/modprobe.
...

# 워크로드 클러스터 자격증명
clusterctl get kubeconfig capi-quickstart > capi-quickstart.kubeconfig
kubectl --kubeconfig=capi-quickstart.kubeconfig get nodes -owide
NAME                          STATUS     ROLES           AGE     VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION                  CONTAINER-RUNTIME
capi-quickstart-t85tp-q68rk   NotReady   control-plane   5m16s   v1.34.3   172.18.0.4    <none>        Debian GNU/Linux 12 (bookworm)   6.12.0-124.31.1.el10_1.x86_64   containerd://2.2.0

# 노드 NotReady 상태 해결 : CNI 플러그인 설치
kubectl --kubeconfig=capi-quickstart.kubeconfig apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml

kubectl --kubeconfig=capi-quickstart.kubeconfig get nodes -owide
NAME                                     STATUS   ROLES           AGE     VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION                        CONTAINER-RUNTIME
capi-quickstart-md-0-lt794-jfjvx-9wrql   Ready    <none>          113s    v1.35.0   192.168.97.6   <none>        Debian GNU/Linux 12 (bookworm)   6.17.4-orbstack-00308-g195e9689a04f   containerd://2.2.0
capi-quickstart-md-0-lt794-jfjvx-f4pr4   Ready    <none>          113s    v1.35.0   192.168.97.5   <none>        Debian GNU/Linux 12 (bookworm)   6.17.4-orbstack-00308-g195e9689a04f   containerd://2.2.0
capi-quickstart-md-0-lt794-jfjvx-rjfz9   Ready    <none>          113s    v1.35.0   192.168.97.7   <none>        Debian GNU/Linux 12 (bookworm)   6.17.4-orbstack-00308-g195e9689a04f   containerd://2.2.0
capi-quickstart-sn62k-6n9sd              Ready    control-plane   2m14s   v1.35.0   192.168.97.4   <none>        Debian GNU/Linux 12 (bookworm)   6.17.4-orbstack-00308-g195e9689a04f   containerd://2.2.0
capi-quickstart-sn62k-pr4kp              Ready    control-plane   46s     v1.35.0   192.168.97.9   <none>        Debian GNU/Linux 12 (bookworm)   6.17.4-orbstack-00308-g195e9689a04f   containerd://2.2.0
capi-quickstart-sn62k-xhmtw              Ready    control-plane   75s     v1.35.0   192.168.97.8   <none>        Debian GNU/Linux 12 (bookworm)   6.17.4-orbstack-00308-g195e9689a04f   containerd://2.2.0

 

⑤ 첫 번째 워크로드 클러스터 관련 Cluster API CRD 확인

# clusterclasses 확인 : 클러스터의 기본 템플릿 정보 제공, 예) 아래 처럼 etcd 컨테이너 이미지 tag  3.5.3-0
kubectl get clusterclasses quick-start -o yaml
...
  patches:
  - definitions:
    - jsonPatches:
   ...
 - name: etcdImageTag
    required: true
    schema:
      openAPIV3Schema:
        default: ""
        description: etcdImageTag sets the tag for the etcd image.
        example: 3.5.3-0
        type: string
...

kubectl get clusterclasses -owide
NAME          PAUSED   VARIABLES READY   AGE
quick-start   False    True              10m


# cluster 확인 : 아래 Varibles 중 Name에 값이 없는 경우, clusterclasses 템플릿에 variables 값이 반영됨
kubectl get cluster -owide
NAME              CLUSTERCLASS   AVAILABLE   CP DESIRED   CP CURRENT   CP READY   CP AVAILABLE   CP UP-TO-DATE   W DESIRED   W CURRENT   W READY   W AVAILABLE   W UP-TO-DATE   PAUSED   PHASE         AGE     VERSION
capi-quickstart   quick-start    True        3            3            3          3              3               3           3           3         3             3              False    Provisioned   4m37s   v1.35.0

kubectl describe cluster capi-quickstart
...
Spec:
  Cluster Network:
    Pods:
      Cidr Blocks:
        10.10.0.0/16
    Service Domain:  myk8s-1.local
    Services:
      Cidr Blocks:
        10.20.0.0/16
  Control Plane Endpoint:
    Host:  192.168.97.3
    Port:  6443
  ...
  Topology:
    Class Ref:
      Name:  quick-start
    Control Plane:
      Replicas:  3
    Variables:
      Name:   imageRepository
      Value:
      Name:   etcdImageTag
      Value:
      Name:   coreDNSImageTag
      Value:
      Name:   podSecurityStandard
      Value:
        Audit:    restricted
        Enabled:  false
        Enforce:  baseline
        Warn:     restricted
    Version:      v1.34.3
    Workers:
      Machine Deployments:
        Class:     default-worker
        Name:      md-0
        Replicas:  3

# 워크로드 클러스터에서 etcd 이미지 태그 정보 확인
kubectl --kubeconfig=capi-quickstart.kubeconfig get pod -n kube-system -l component=etcd -o yaml | grep image: | uniq
      image: registry.k8s.io/etcd:3.6.5-0


# To verify the first control plane is up:
kubectl get kubeadmcontrolplane -owide
NAME                    CLUSTER           AVAILABLE   DESIRED   CURRENT   READY   AVAILABLE   UP-TO-DATE   PAUSED   INITIALIZED   AGE   VERSION
capi-quickstart-j9fdm   capi-quickstart   True        3         3         3       3           3            False    true          48m   v1.34.3

## kubeadm 에 의해 생성된 컨트롤 플레인 정보
kubectl get kubeadmcontrolplane -o yaml
...
  spec:
    kubeadmConfigSpec:
      clusterConfiguration:
        apiServer:
          certSANs:
          - localhost
          - 127.0.0.1
          - 0.0.0.0
          - host.docker.internal
      initConfiguration:
        nodeRegistration:
          kubeletExtraArgs:
          - name: eviction-hard
            value: nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%
      joinConfiguration:
        nodeRegistration:
          kubeletExtraArgs:
          - name: eviction-hard
            value: nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%
    machineTemplate:
      metadata:
        labels:
          cluster.x-k8s.io/cluster-name: capi-quickstart
          topology.cluster.x-k8s.io/owned: ""
      spec:
        infrastructureRef:
          apiGroup: infrastructure.cluster.x-k8s.io
          kind: DockerMachineTemplate
          name: capi-quickstart-tdwjt
    replicas: 3
    rollout:
      strategy:
        rollingUpdate:
          maxSurge: 1
        type: RollingUpdate
    version: v1.34.3
  status:
    availableReplicas: 3
    ...
    

# kubeadmconfigtemplate 확인
kubectl get kubeadmconfigtemplate
NAME                                           CLUSTERCLASS   CLUSTER           AGE
capi-quickstart-md-0-ln5zl                                    capi-quickstart   37m    # 실제 Cluster 생성 시 자동으로 만들어진 복사본
quick-start-default-worker-bootstraptemplate   quick-start                      37m    # ClusterClass에 속함, 실제 클러스터에 직접 사용되지 않음, '설계도용 템플릿'

## kubeadm 작업 시 템플릿 : 아래는 워커 노드 join 시, kubelet args 설정
kubectl get kubeadmconfigtemplate -l cluster.x-k8s.io/cluster-name=capi-quickstart     # 실제 Cluster 생성 시 자동으로 만들어진 복사본
kubectl get kubeadmconfigtemplate -l cluster.x-k8s.io/cluster-name=capi-quickstart -o yaml # 아래 spec 내용과 동일
...
  spec:
    template:
      spec:
        joinConfiguration:
          nodeRegistration:
            kubeletExtraArgs:
            - name: eviction-hard
              value: nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%
 
kubectl get kubeadmconfigtemplate quick-start-default-worker-bootstraptemplate -o yaml # ClusterClass에 속함, 실제 클러스터에 직접 사용되지 않음, '설계도용 템플릿'
...
spec:
  template:
    spec:
      joinConfiguration:
        nodeRegistration:
          kubeletExtraArgs:
          - name: eviction-hard
            value: nodefs.available<0%,nodefs.inodesFree<0%,imagefs.available<0%

실습 정리

# 첫 번째 워크로드 클러스터(머신 포함) 삭제
kubectl delete cluster capi-quickstart

# 관리 클러스터 삭제
kind delete cluster --name myk8s