[Kubernetes] Cluster: RKE2를 이용해 클러스터 구성하기 - 2. 에이전트 노드 조인

11 분 소요

서종호(가시다)님의 On-Premise K8s Hands-on Study 7주차 학습 내용을 기반으로 합니다.


TL;DR

이번 글의 목표는 RKE2 에이전트 노드를 클러스터에 조인하고 멀티 노드 클러스터를 완성하는 것이다.

  • 에이전트 설치: 동일한 설치 스크립트를 INSTALL_RKE2_TYPE=agent로 실행
  • 설정: 서버 URL(포트 9345), 토큰, NIC 지정 (config.yaml)
  • 조인 과정: 에이전트 프로세스 라이프사이클 확인
  • 결과 확인: 에이전트 노드 디렉터리 구조, server/가 없는 이유
  • 클러스터 검증: 샘플 파드 배포로 멀티 노드 클러스터 동작 확인


들어가며

이전 글에서 RKE2 서버 노드에서 생성된 디렉터리와 설정 파일을 상세히 확인했다. server/token에 에이전트 조인용 토큰이 있고, 에이전트 노드는 이 토큰과 서버 URL을 config.yaml에 설정해서 클러스터에 합류한다는 내용을 짚었다.

이번 글에서는 그 조인 과정을 직접 진행한다. 에이전트 설치와 서비스 기동은 서버보다 훨씬 단순하다. 설치 방법 외에도 에이전트 라이프사이클이 서버와 어떻게 다른지, 조인 후 만들어지는 디렉터리 구조가 서버 노드와 어떻게 다른지를 함께 확인한다.


RKE2 에이전트 노드 설치

토큰 확인

에이전트 노드가 클러스터에 조인하려면 서버 노드의 토큰이 필요하다. 서버 노드(node1)에서 토큰을 확인한다.

# node1에서 실행
cat /var/lib/rancher/rke2/server/node-token
# K1037f1b1f84d631265adcff239308d8b19ae073480250e9fcded6330c97452ad8d::server:158adc28471c9fd7122146cb86bfb5a5

토큰 형식은 K1<sha256>::<type>:<secret> 구조다. server가 타입이고 뒤의 hex 문자열이 실제 secret이다.

서버 토큰은 서비스 시작 시 자동으로 생성되고 만료되지 않는다. kubeadm의 bootstrap token은 24시간 후 만료되어 재발급이 필요했다. RKE2의 서버 토큰은 영구적이라 관리 부담이 없다.

서버 노드에서 9345 포트가 열려 있는지도 확인한다. 에이전트가 조인할 때 사용하는 RKE2 supervisor 등록 포트다.

# node1에서 실행
ss -tnlp | grep 9345
# LISTEN 0  4096  192.168.10.11:9345  0.0.0.0:*  users:(("rke2",pid=133127,fd=6))
# LISTEN 0  4096      127.0.0.1:9345  0.0.0.0:*  users:(("rke2",pid=133127,fd=7))
# LISTEN 0  4096          [::1]:9345     [::]:*  users:(("rke2",pid=133127,fd=8))


에이전트 설치

에이전트 노드(node2)에 접속해서 설치한다. 설치 스크립트는 서버와 동일하지만 INSTALL_RKE2_TYPE="agent"를 지정하는 것이 차이점이다.

# node2에서 실행
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE="agent" INSTALL_RKE2_CHANNEL=v1.33 sh -
[INFO]  using stable RPM repositories
[INFO]  using 1.33 series from channel stable
...
Installing:
 rke2-agent              aarch64  1.33.8~rke2r1-0.el9  rancher-rke2-1.33-stable  8.3 k
Installing dependencies:
 rke2-common             aarch64  1.33.8~rke2r1-0.el9  rancher-rke2-1.33-stable   25 M
 rke2-selinux            noarch   0.22-1.el9            rancher-rke2-common-stable 22 k
...
Installed:
  rke2-agent-1.33.8~rke2r1-0.el9.aarch64  rke2-common-1.33.8~rke2r1-0.el9.aarch64  rke2-selinux-0.22-1.el9.noarch

서버 설치와 비교하면 rke2-server 대신 rke2-agent가 설치된다. rke2-commonrke2-selinux는 서버와 공통이다. rke2-agent 패키지에는 rke2-agent.service systemd 유닛 파일이 포함된다.


config.yaml 작성

에이전트 노드에서도 설정 파일 경로는 /etc/rancher/rke2/config.yaml로 동일하다. 서버와 달리 컨트롤 플레인 관련 설정은 없고, 어느 서버에 조인할지와 인증 정보만 필요하다.

# node2에서 실행
TOKEN=K1037f1b1f84d631265adcff239308d8b19ae073480250e9fcded6330c97452ad8d::server:158adc28471c9fd7122146cb86bfb5a5

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

cat /etc/rancher/rke2/config.yaml
# server: https://192.168.10.11:9345
# token: K1037f1b1f84d631265adcff239308d8b19ae073480250e9fcded6330c97452ad8d::server:158adc28471c9fd7122146cb86bfb5a5
# node-ip: 192.168.10.12

server 항목의 포트가 9345인 점이 눈에 띈다. API 서버 포트(6443)와 다르다.

포트 용도 접근 주체
6443 Kubernetes API 서버 kubectl, 파드, 외부 클라이언트
9345 RKE2 supervisor 등록 엔드포인트 에이전트 노드 조인 시

에이전트가 조인할 때 Kubernetes API 서버(6443)가 아니라 RKE2 supervisor가 노출하는 등록 엔드포인트(9345)에 먼저 접속한다. 클러스터 메타데이터(CA 인증서, API 서버 엔드포인트, 초기 설정 등)를 받아온 뒤 kubelet 등록을 진행하는 방식이다. kubeadm의 kubeadm join은 처음부터 6443에 접속했다. RKE2는 supervisor 레이어가 존재하기 때문에 별도 등록 포트가 있다.

node-ip192.168.10.12로 지정하는 이유는 서버 노드에서와 같다. VirtualBox VM에 NAT NIC(enp0s3, 10.0.2.15)와 호스트 전용 NIC(enp0s9, 192.168.10.12)가 함께 있기 때문에, 클러스터 내부 통신에 사용할 NIC를 명시적으로 지정해야 한다. 이 설정이 없으면 노드가 조인되더라도 INTERNAL-IP가 NAT IP로 등록된다.


서비스 시작

# node2에서 실행

# 서비스 시작 후 로그 확인
systemctl enable --now rke2-agent.service
journalctl -u rke2-agent -f

node1에서 kube-system 네임스페이스를 관찰하면 node2 조인과 함께 관련 파드가 생성되는 과정을 볼 수 있다. 노드 상태가 NotReady에서 Ready로 변화하는 모습도 확인할 수 있다.

rke2-agent-join

에이전트가 뜨는 과정을 프로세스 트리로 보면 서버와 유사하지만 훨씬 단순하다.

# 1단계: rke2 supervisor만 보임
rke2

# 2단계: containerd spawn
rke2
└── containerd

# 3단계: kubelet spawn
rke2
├── containerd
└── kubelet

# 4단계: containerd-shim들이 나타남 (kube-proxy static pod, canal DaemonSet Pod 기동)
rke2
├── containerd
├── kubelet
containerd-shim-runc-v2  ← kube-proxy
containerd-shim-runc-v2  ← canal

서버와 비교하면 etcd, kube-apiserver, kube-controller-manager, kube-scheduler가 없다. 에이전트 노드는 컨트롤 플레인 컴포넌트 없이 kubelet과 kube-proxy, 그리고 DaemonSet으로 배포되는 CNI만 실행한다.


서비스 상태와 프로세스 트리를 확인한다.

systemctl status rke2-agent.service --no-pager
● rke2-agent.service - Rancher Kubernetes Engine v2 (agent)
     Loaded: loaded (/usr/lib/systemd/system/rke2-agent.service; enabled; preset: disabled)
     Active: active (running) since Thu 2026-02-19 01:04:01 KST; 2min 56s ago
   Main PID: 6453 (rke2)
      Tasks: 61
     Memory: 2.2G
     CGroup: /system.slice/rke2-agent.service
             ├─6453 "/usr/bin/rke2 agent"
             ├─6477 containerd -c /var/lib/rancher/rke2/agent/etc/containerd/config.toml
             ├─6531 kubelet --volume-plugin-dir=/var/lib/kubelet/volumeplugins ...
             ├─6597 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 ...
             └─6598 /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin/containerd-shim-runc-v2 ...

서버와 구조가 같다. rke2 agent 프로세스가 Main PID이고, containerd와 kubelet이 자식으로 있다.

pstree -al | grep -A5 'rke2$'
  |-rke2
  |   |-containerd -c /var/lib/rancher/rke2/agent/etc/containerd/config.toml
  |   |   └─11*[{containerd}]
  |   |-kubelet --volume-plugin-dir=/var/lib/kubelet/volumeplugins ...
  |   |   └─11*[{kubelet}]
  |   └─10*[{rke2}]

서버 노드의 pstree 결과와 구조가 같다. containerd-shim들은 rke2 트리 밖에 PID 1의 자식으로 나타난다. canal과 kube-proxy 컨테이너에 각각 shim이 붙어 있다.

pstree -al | grep -A5 'containerd-shim' | head -20
  |-containerd-shim -namespace k8s.io -id 835eea162bfc93d7... -address /run/k3s/containerd/containerd.sock
  |   |-flanneld --ip-masq --kube-subnet-mgr ...
  |   |-pause
  |   |-runsvdir -P /etc/service/enabled
  |   |   ├─runsv felix
  |   |   |   └─calico-node -felix
  ...
  |-containerd-shim -namespace k8s.io -id ca5e31812e3807fe... -address /run/k3s/containerd/containerd.sock
  |   |-kube-proxy --cluster-cidr=10.42.0.0/16 ...
  |   |-pause

shim이 2개다. 서버 노드(8~10개)보다 훨씬 적다. 실행 중인 컨테이너가 kube-proxy와 canal 2개뿐이기 때문이다.


에이전트 노드 디렉터리 구조

조인 후 에이전트 노드에서 생성된 디렉터리를 확인한다. 서버 노드와의 차이가 핵심이다.

# node2에서 실행
tree /var/lib/rancher/rke2 -L 1
/var/lib/rancher/rke2
├── agent
├── bin -> /var/lib/rancher/rke2/data/v1.33.8-rke2r1-1b2872361ec5/bin
└── data

서버 노드(agent/, bin/, data/, server/)와 달리 server/ 디렉터리가 없다. server/에는 etcd 데이터, PKI, 인증서, 조인 토큰 등 컨트롤 플레인 운영에 필요한 모든 상태가 들어 있다. 에이전트 노드는 컨트롤 플레인을 실행하지 않으므로 이 디렉터리 자체가 생성되지 않는다.

tree /var/lib/rancher/rke2/agent/ -L 2
/var/lib/rancher/rke2/agent/
├── client-ca.crt
├── client-kubelet.crt, .key
├── client-kube-proxy.crt, .key
├── client-rke2-controller.crt, .key
├── containerd/
├── etc/
│   ├── containerd/
│   │   └── config.toml
│   ├── crictl.yaml
│   ├── kubelet.conf.d/
│   │   └── 00-rke2-defaults.conf
│   ├── rke2-agent-load-balancer.json        ← 에이전트 노드에만 있는 파일
│   └── rke2-api-server-agent-load-balancer.json  ← 에이전트 노드에만 있는 파일
├── images/
│   ├── kube-proxy-image.txt
│   └── runtime-image.txt
├── kubelet.kubeconfig
├── kubeproxy.kubeconfig
├── logs/
├── pod-manifests/
│   └── kube-proxy.yaml                      ← kube-proxy 하나만 있음
├── server-ca.crt
├── serving-kubelet.crt, .key
└── rke2controller.kubeconfig

서버 노드의 agent/ 구조와 거의 동일하지만 두 가지 차이가 있다.

pod-manifests

첫 번째는 pod-manifests/ 내용이다.

# 서버 노드 (node1)
tree /var/lib/rancher/rke2/agent/pod-manifests
├── etcd.yaml
├── kube-apiserver.yaml
├── kube-controller-manager.yaml
├── kube-proxy.yaml
└── kube-scheduler.yaml

# 에이전트 노드 (node2)
tree /var/lib/rancher/rke2/agent/pod-manifests
└── kube-proxy.yaml

에이전트 노드의 pod-manifests/에는 kube-proxy.yaml 하나만 있다. etcd, kube-apiserver, kube-controller-manager, kube-scheduler는 서버 노드에서만 실행되는 static pod다.

load balancer JSON

두 번째는 load balancer JSON 파일이다. 서버 노드에는 없고 에이전트 노드에만 생성된다.

cat /var/lib/rancher/rke2/agent/etc/rke2-agent-load-balancer.json
{
  "ServerURL": "https://192.168.10.11:9345",
  "ServerAddresses": [
    "192.168.10.11:9345"
  ]
}

cat /var/lib/rancher/rke2/agent/etc/rke2-api-server-agent-load-balancer.json
{
  "ServerURL": "https://192.168.10.11:6443",
  "ServerAddresses": [
    "192.168.10.11:6443"
  ]
}

rke2-agent-load-balancer.json은 에이전트가 supervisor에 접속하는 9345 엔드포인트를, rke2-api-server-agent-load-balancer.json은 kubelet이 API 서버에 접속하는 6443 엔드포인트를 추적한다. 서버가 여러 대인 HA 구성에서 에이전트가 어떤 서버에 접속할지를 관리하는 데 사용된다.

서버 노드에는 이 파일들이 없다. 서버 노드 자체가 supervisor를 실행하기 때문에 외부 supervisor에 접속할 필요가 없기 때문이다.

kubelet 설정

에이전트 노드의 kubelet 설정도 서버와 차이가 있다.

cat /var/lib/rancher/rke2/agent/etc/kubelet.conf.d/00-rke2-defaults.conf
address: 0.0.0.0                    ← 서버 노드는 192.168.10.11로 고정됨
allowedUnsafeSysctls:               ← 서버 노드에는 없는 설정
- net.ipv4.ip_forward
- net.ipv6.conf.all.forwarding
...

address: 0.0.0.0 — 서버 노드는 192.168.10.11로 특정 인터페이스에 바인딩됐지만, 에이전트 노드는 모든 인터페이스에서 kubelet API를 수신한다.

allowedUnsafeSysctls — 파드 내에서 net.ipv4.ip_forward, net.ipv6.conf.all.forwarding sysctl을 사용할 수 있도록 허용한다. 에이전트 노드는 실제 파드가 실행되는 워커 역할을 하므로, 파드 수준에서 네트워크 포워딩이 필요한 워크로드(ex. 네트워크 관련 시스템 파드)를 수용하기 위한 설정이다.


클러스터 상태 확인

서버 노드(node1)에서 클러스터 상태를 확인한다.

# node1에서 실행
kubectl get node -o wide
# NAME               STATUS   ROLES                       AGE    VERSION          INTERNAL-IP     OS-IMAGE                      CONTAINER-RUNTIME
# week07-k8s-node1   Ready    control-plane,etcd,master   4h3m   v1.33.8+rke2r1   192.168.10.11   Rocky Linux 9.6 (Blue Onyx)   containerd://2.1.5-k3s1
# week07-k8s-node2   Ready    <none>                      119s   v1.33.8+rke2r1   192.168.10.12   Rocky Linux 9.6 (Blue Onyx)   containerd://2.1.5-k3s1

두 노드 모두 Ready 상태다. INTERNAL-IP가 각각 192.168.10.11, 192.168.10.12로 호스트 전용 NIC IP가 등록됐다. config.yamlnode-ip를 명시적으로 지정한 결과다.

ROLES 컬럼에서 node2는 <none>이다. kubeadm에서는 워커 노드 조인 시 worker Role이 자동으로 붙었다. RKE2는 에이전트 노드에 Role을 자동으로 부여하지 않는다.

# worker Role 라벨 추가
kubectl label node week07-k8s-node2 node-role.kubernetes.io/worker=worker

kubectl get node
# NAME               STATUS   ROLES                       AGE
# week07-k8s-node1   Ready    control-plane,etcd,master   4h4m
# week07-k8s-node2   Ready    worker                      2m

파드 상태도 확인한다.

kubectl get pod -n kube-system -o wide | grep k8s-node2
# kube-proxy-week07-k8s-node2  1/1  Running  0  2m  192.168.10.12  week07-k8s-node2
# rke2-canal-j6h6t             2/2  Running  0  2m  192.168.10.12  week07-k8s-node2

node2가 조인되자 DaemonSet 기반 컴포넌트들이 node2에도 배포됐다.

  • kube-proxy-week07-k8s-node2: 에이전트 노드의 pod-manifests/kube-proxy.yaml에서 kubelet이 기동하는 static pod다.
  • rke2-canal-j6h6t: Canal은 DaemonSet이다. 새 노드가 추가되면 Helm Controller가 배포한 Canal DaemonSet이 자동으로 해당 노드에 Pod를 스케줄한다.

node2에서 crictl로 직접 확인한다.

# node2에서 실행
ln -s /var/lib/rancher/rke2/bin/crictl /usr/local/bin/crictl
ln -s /var/lib/rancher/rke2/agent/etc/crictl.yaml /etc/crictl.yaml

crictl ps
# CONTAINER  IMAGE  CREATED      STATE    NAME           POD ID  POD
# b39af908   fc623  3 min ago   Running  kube-flannel   835eea  rke2-canal-j6h6t     kube-system
# ecbd11e0   3b961  4 min ago   Running  calico-node    835eea  rke2-canal-j6h6t     kube-system
# 692eaa7f   603f9  4 min ago   Running  kube-proxy     ca5e31  kube-proxy-week07-k8s-node2  kube-system

crictl images
# IMAGE                                   TAG                            SIZE
# docker.io/rancher/hardened-calico       v3.31.3-build20260206          217MB
# docker.io/rancher/hardened-flannel      v0.28.1-build20260206          19.8MB
# docker.io/rancher/hardened-kubernetes   v1.33.8-rke2r1-build20260210   187MB
# docker.io/rancher/mirrored-pause        3.6                            253kB
# docker.io/rancher/rke2-runtime          v1.33.8-rke2r1                 91.3MB

서버 노드의 crictl images와 비교하면 이미지 수가 적다. 서버 노드에는 coredns, metrics-server, klipper-helm, etcd 등이 더 있다. 에이전트 노드에는 실제로 실행 중인 컨테이너에 필요한 이미지만 존재한다.


에이전트 노드 삭제와 재조인

에이전트 노드를 클러스터에서 제거하고 다시 추가하는 과정을 확인한다.

노드 삭제 (서버 노드에서)

# node1에서 실행

# 노드 drain: 파드를 안전하게 이동시킨 뒤 새 스케줄 차단
kubectl drain week07-k8s-node2 --ignore-daemonsets --delete-emptydir-data
# node/week07-k8s-node2 cordoned
# Warning: ignoring DaemonSet-managed Pods: kube-system/rke2-canal-j6h6t
# node/week07-k8s-node2 drained

kubectl get nodes
# NAME               STATUS                     ROLES                       AGE
# week07-k8s-node1   Ready                      control-plane,etcd,master   4h10m
# week07-k8s-node2   Ready,SchedulingDisabled   <none>                      8m16s

# 클러스터에서 노드 오브젝트 삭제
kubectl delete node week07-k8s-node2
# node "week07-k8s-node2" deleted

kubectl delete node는 Kubernetes API에서 노드 오브젝트를 제거하는 것이다. 에이전트 프로세스(rke2-agent)는 node2에서 계속 실행 중이다.

RKE2 제거 (에이전트 노드에서)

RKE2 패키지를 설치하면 rke2-uninstall.shrke2-killall.sh가 함께 제공된다.

# node2에서 실행

# 서비스 중지
systemctl stop rke2-agent

# 언인스톨 스크립트 실행
# 컨테이너 종료, 네트워크 인터페이스 정리, RPM 제거, 디렉터리 삭제까지 처리
rke2-uninstall.sh

스크립트가 완료되면 /etc/rancher, /var/lib/rancher 디렉터리가 모두 삭제된다.

tree /etc/rancher
# /etc/rancher [error opening dir]
tree /var/lib/rancher
# /var/lib/rancher [error opening dir]

재조인

재조인은 최초 조인과 동일한 절차다. 토큰은 서버 노드에서 바뀌지 않았으므로 그대로 사용할 수 있다.

# node2에서 실행
curl -sfL https://get.rke2.io | INSTALL_RKE2_TYPE="agent" INSTALL_RKE2_CHANNEL=v1.33 sh -

TOKEN=K1037f1b1f84d631265adcff239308d8b19ae073480250e9fcded6330c97452ad8d::server:158adc28471c9fd7122146cb86bfb5a5

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

systemctl enable --now rke2-agent.service

RKE2 토큰이 만료되지 않기 때문에 기존 토큰으로 재조인이 가능하다. kubeadm에서는 토큰이 만료된 경우 kubeadm token create로 새 토큰을 생성하고 kubeadm token list로 확인해야 했다. RKE2는 이 과정이 불필요하다.


서버 노드 vs 에이전트 노드 비교

클러스터를 완성하고 나서 두 노드의 차이를 정리한다.

항목 서버 노드 (node1) 에이전트 노드 (node2)
systemd 서비스 rke2-server.service rke2-agent.service
실행 명령 rke2 server rke2 agent
컨트롤 플레인 etcd, kube-apiserver, kcm, scheduler 없음
static pod 수 5개 (etcd, apiserver, kcm, scheduler, kube-proxy) 1개 (kube-proxy)
server/ 디렉터리 있음 (PKI, etcd, token, manifests) 없음
rke2.yaml (kubeconfig) /etc/rancher/rke2/rke2.yaml 없음
조인 설정 없음 (최초 서버) server:, token: 필요
load balancer JSON 없음 rke2-agent-load-balancer.json 생성
allowedUnsafeSysctls 없음 net.ipv4.ip_forward 등 허용
Node ROLES control-plane,etcd,master <none> (수동 지정 필요)


kubeadm의 kubeadm join과 비교하면:

항목 kubeadm join RKE2 agent
조인 방식 kubeadm join <host>:6443 --token ... --discovery-token-ca-cert-hash ... config.yamlserver:, token: 설정 후 systemctl start
토큰 만료 24시간 만료 없음
서버 접속 포트 6443 (API 서버 직접) 9345 (supervisor 등록 엔드포인트)
kube-proxy DaemonSet static pod
CNI 배포 별도 kubectl apply 필요 Helm Controller가 자동 처리


샘플 파드 배포

배포 전에 서버 노드의 Taint를 확인한다.

# node1에서 실행
kubectl describe node week07-k8s-node1 | grep -A5 Taints
# Taints:             <none>

kubeadm은 컨트롤 플레인 노드에 node-role.kubernetes.io/control-plane:NoSchedule Taint를 기본으로 설정해 워크로드 파드가 컨트롤 플레인에 스케줄되지 않도록 한다. RKE2의 컨트롤 플레인에는 Taint가 없다. K3s의 설계를 계승한 것으로, 노드 1대만으로도 완전한 클러스터가 동작해야 한다는 철학 때문이다. 서버 노드에도 일반 파드가 스케줄될 수 있다.

명시적으로 Taint를 걸고 싶다면 config.yamlnode-taint를 추가하거나 kubectl taint로 직접 설정하면 된다.

배포 매니페스트는 podAntiAffinity를 사용해 두 파드가 서로 다른 노드에 배포되도록 한다.

# node1에서 실행
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
# deployment.apps/webpod created
# service/webpod created
kubectl get deploy,pod,svc,ep -o wide
# NAME                     READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES           SELECTOR
# deployment.apps/webpod   2/2     2            2           10s   webpod       traefik/whoami   app=webpod
#
# NAME                          READY   STATUS    RESTARTS   AGE   IP          NODE               NOMINATED NODE   READINESS GATES
# pod/webpod-697b545f57-brtbk   1/1     Running   0          10s   10.42.2.2   week07-k8s-node2   <none>           <none>
# pod/webpod-697b545f57-tqkrt   1/1     Running   0          10s   10.42.0.6   week07-k8s-node1   <none>           <none>
#
# NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE     SELECTOR
# service/kubernetes   ClusterIP   10.43.0.1       <none>        443/TCP        4h18m   <none>
# service/webpod       NodePort    10.43.194.214   <none>        80:30000/TCP   10s     app=webpod
#
# NAME                   ENDPOINTS                   AGE
# endpoints/kubernetes   192.168.10.11:6443          4h18m
# endpoints/webpod       10.42.0.6:80,10.42.2.2:80   10s

파드 2개가 각각 node1(10.42.0.6)과 node2(10.42.2.2)에 분산 배포됐다. podAntiAffinity가 정상 동작한 결과다.

endpoints/webpod에 두 파드의 IP가 모두 등록됐다. NodePort 30000으로 접근 테스트를 한다.

# node1에서 실행
curl -s http://192.168.10.11:30000 | grep Hostname
# Hostname: webpod-697b545f57-tqkrt

curl -s http://192.168.10.12:30000 | grep Hostname
# Hostname: webpod-697b545f57-brtbk

두 노드 모두에서 NodePort로 접근이 되고, 각 노드에 배포된 파드가 응답한다. kube-proxy가 iptables 규칙을 통해 NodePort 트래픽을 파드로 전달하는 구조가 node2에서도 정상 동작함을 확인했다.


결과

RKE2 에이전트 노드 조인을 완료하고, 샘플 파드 배포로 멀티 노드 클러스터 동작을 확인했다.

  • 에이전트 설치는 서버와 동일한 스크립트에 INSTALL_RKE2_TYPE="agent"만 추가하면 된다.
  • config.yaml에 서버 URL(포트 9345)과 토큰만 지정하면 조인이 완료된다.
  • 에이전트 노드에는 server/ 디렉터리가 생성되지 않는다. 컨트롤 플레인이 없기 때문이다.
  • Canal DaemonSet은 새 노드 조인 시 자동으로 파드를 배포한다.
  • nginx Deployment를 통해 두 노드에 파드가 분산 배포되고 ClusterIP를 통한 통신이 정상 동작하는 것을 확인했다.
  • RKE2 토큰은 만료되지 않아 언인스톨 후 재조인 시에도 동일한 토큰을 사용할 수 있다.


참고



hit count

댓글남기기