TT Lab
요금제

한국어

시작하기
블로그

블로그

k3s·RKE2·k0s 구조 비교: 컨트롤 플레인은 어떻게 뜨고 무엇이 기본으로 들어오나

상태: 초안 (2026-10-13), 공식 문서 기준 + 일부 실측. 구조와 기본값은 공식 문서와 GitHub 저장소를 2026-10-13 에 열어 확인했습니다(k3s v1.37.1+k3s1, RKE2 v1.37.1+rke2r1, k0s v1.36.4+k0s.1 이 그날의 최신 안정판). 「실측」 표시가 붙은 값은 TT Lab 코스의 실습 VM 에서 직접 세워 본 결과입니다(2026-08~09, 판을 함께 적음). 작성 규칙: 실측 데이터가 기본이고, 못 잰 것은 「미실측」, 문서에서 찾지 못한 것은 「미확인」으로 표시합니다.

목차

1. 왜 세 개를 같이 보나

쿠버네티스를 직접 세우려는 팀이 가장 먼저 만나는 이름이 k3s, RKE2, k0s 입니다. 셋 다 「바이너리 하나 받아서 서비스로 등록하면 클러스터가 뜬다」는 같은 약속을 합니다. 그래서 겉으로는 비슷해 보입니다.

그런데 운영을 시작하면 차이가 드러납니다. 장애가 났을 때 로그를 어디서 봐야 하는지, etcdctl 을 쓸 수 있는지, 처음부터 인그레스가 깔려 있는지, 노드 목록에 컨트롤 플레인이 보이는지가 모두 다릅니다. 이 차이는 기능 목록이 아니라 설계와 기본값에서 나옵니다. 이 글은 그 설계를 그림으로 비교합니다. 보안·운영은 2편, 고르는 법은 3편에서 다룹니다.

2. 출신과 목표

k3s RKE2 k0s
처음 만든 곳 Rancher(지금은 SUSE) Rancher(SUSE) Mirantis
다른 이름 — RKE Government —
CNCF Sandbox(2020-08-19 채택) CNCF 프로젝트 아님(인증 배포판 목록에만 있음) Sandbox(2025-01-19 채택)
겨냥한 곳 엣지·IoT·ARM 보드·CI·개발 보안·규정 준수, 특히 미국 연방 정부 부문 설치·운영의 마찰을 없앤 범용 배포판
2026-10-13 최신 안정판 v1.37.1+k3s1 (2026-09-30) v1.37.1+rke2r1 (2026-09-30) v1.36.4+k0s.1 (2026-09-19)

세 배포판의 성격을 한 줄씩 정리하면 이렇습니다.

3. 컨트롤 플레인은 어떻게 뜨나

가장 큰 차이는 컨트롤 플레인 구성요소를 어떤 모양으로 띄우는가입니다.

k3s 서버 한 대 · v1.37 문서 기준 k3s server — 한 프로세스 안에서 함께 돈다 kube-apiserver scheduler controller-manager kubelet kube-proxy flannel(CNI) kine → SQLite 네트워크 정책 helm-controller 따로 띄우는 자식 프로세스 containerd(번들) 애드온(파드): CoreDNS · Traefik · ServiceLB · local-path · metrics-server RKE2 서버 한 대 · v1.36~1.37 문서 기준 rke2 server — 감독만 한다 containerd(번들) kubelet kubelet 이 띄우는 정적 파드 etcd kube-apiserver scheduler controller-manager 애드온(헬름 차트): Canal · CoreDNS · Traefik(1.36+) · metrics-server k0s 컨트롤러·워커 분리 · v1.36 문서 기준 k0s controller 각각 따로 뜨는 프로세스 etcd kube-apiserver scheduler controller-manager konnectivity-server kubelet·containerd 없음 k0s worker 다른 노드, 프로세스 containerd(번들) kubelet 파드로 뜨는 것 kube-router(CNI) kube-proxy CoreDNS · metrics-server k0s 는 인그레스·서비스 LB·스토리지를 번들하지 않는다. RKE2 도 ServiceLB 는 기본 꺼짐, 스토리지 클래스는 기본으로 없다.
그림: 세 배포판의 구성요소 구조 비교

k3s: 한 프로세스에 묶는다

k3s server 하나가 kube-apiserver, 스케줄러, 컨트롤러 매니저, kubelet, kube-proxy, flannel, 네트워크 정책 컨트롤러, kine, helm-controller 를 같은 프로세스 안에서 돌립니다. README 가 메모리 절감의 주된 이유로 꼽는 설계입니다. 구성요소마다 따로 들고 있을 런타임과 캐시를 한 번만 쓰기 때문입니다.

containerd 와 runc 는 바이너리에 함께 묶여 오지만, 실행은 따로 합니다. k3s 소스(pkg/agent/containerd)를 보면 k3s 가 containerd 를 별도 프로세스로 띄웁니다.

운영에서 이 구조가 뜻하는 것은 이렇습니다.

RKE2: kubelet 이 정적 파드로 띄운다

rke2 server 프로세스는 직접 컨트롤 플레인을 돌리지 않습니다. 하는 일은 감독입니다. containerd 를 띄우고 그것이 죽으면 자기도 끝나며, kubelet 을 띄우고 멈추면 다시 살립니다. 그리고 etcd, kube-apiserver, 컨트롤러 매니저, 스케줄러의 정적 파드 매니페스트를 /var/lib/rancher/rke2/agent/pod-manifests/ 에 씁니다. kubelet 이 그 매니페스트를 읽어 컨테이너로 띄웁니다. kubeadm 과 같은 모양입니다.

kubelet·containerd·kubectl 같은 실행 파일은 rke2-runtime 이미지 안에 들어 있고, 처음 시작할 때 그 이미지에서 꺼내 풉니다. CNI·CoreDNS·인그레스·metrics-server 같은 애드온은 내장 helm-controller 가 헬름 차트로 설치합니다.

이 구조에는 함정이 하나 있습니다. systemctl stop rke2-server 는 정적 파드를 멈추지 않습니다. TT Lab 의 RKE2 실습(v1.36.4+rke2r1)에서 서비스를 멈춘 뒤에도 root 로 도는 etcd 컨테이너가 그대로 남아 있었습니다(실측). 노드를 완전히 내리려면 설치물에 들어 있는 rke2-killall.sh 를 씁니다. 이 차이가 실제로 어떤 사고를 만드는지는 2편에서 봅니다.

k0s: 따로따로 띄우고, 컨트롤러는 노드가 아니다

k0s 는 컨트롤 플레인 구성요소를 컨테이너가 아니라 각각 독립된 프로세스(문서의 표현으로는 「naked」 프로세스)로 띄우고, k0s 바이너리가 그 프로세스들을 감독합니다. etcd, kube-apiserver, 스케줄러, 컨트롤러 매니저, konnectivity-server 가 따로 뜹니다. 이 실행 파일들은 k0s 바이너리 안에 들어 있다가 첫 실행 때 /var/lib/k0s/bin 에 풀립니다.

가장 눈에 띄는 차이는 컨트롤러 노드에 kubelet 도 컨테이너 런타임도 기본으로 없다는 점입니다. 그래서 컨트롤러는 워크로드를 받지 않고, kubectl get nodes 에도 나오지 않습니다. API 서버가 워커의 kubelet 이나 파드에 말을 걸어야 할 때는 Konnectivity 터널을 거칩니다(에이전트 8132 포트).

이 분리를 풀고 싶을 때 쓰는 플래그가 둘 있습니다.

k0s 는 또 컨트롤 플레인 구성요소를 root 가 아닌 별도 시스템 사용자로 돌립니다. 문서는 k0s install 이 시스템 사용자를 만든다는 데까지만 적고, 사용자 이름은 소스(pkg/constant/constant.go, v1.36.4+k0s.1 태그)에서 확인했습니다. etcd 는 etcd, API 서버는 kube-apiserver, 스케줄러는 kube-scheduler, Konnectivity 는 konnectivity-server 사용자입니다. 컨트롤러 매니저의 사용자는 확인하지 못했습니다(미확인).

세 구조를 한 줄로

k3s RKE2 k0s
컨트롤 플레인 모양 한 프로세스 kubelet 이 띄우는 정적 파드 구성요소마다 별도 프로세스
컨테이너 런타임 containerd·runc 번들 containerd 번들(런타임 이미지에서 꺼냄) containerd·runc 번들
서버(컨트롤러)가 워크로드를 받나 받는다 받는다 기본은 받지 않는다
컨트롤 플레인 로그 k3s 서비스 로그 하나 파드 로그(/var/log/pods) k0s 서비스 로그
애드온 설치 방식 매니페스트 폴더 + helm-controller helm-controller 내장 매니페스트, 헬름 확장은 선택

4. 저장소와 HA

① k3s 기본 — 서버 한 대, SQLite k3s server kine(etcd API 흉내) SQLite 서버를 늘릴 수 없다 백업: db 폴더 + 토큰 ② k3s HA — 내장 etcd 또는 외부 DB 고정 등록 주소 :6443 고정 등록 주소 :6443 server etcd server etcd server etcd 홀수 대, 최소 3대 첫 서버에 --cluster-init server server DB 2대 이상 + 외부 DB MySQL · PostgreSQL · etcd ③ RKE2 HA — 서버 3대, etcd 정적 파드 고정 등록 주소 — 등록 :9345 · API :6443 server etcd 파드 server etcd 파드 server etcd 파드 agent agent 홀수 대, 3대 권장 · 서버도 워크로드를 받는다 에이전트는 0대여도 된다 ④ k0s HA — 컨트롤러 3대, 워커는 따로 컨트롤러 etcd 컨트롤러 etcd 컨트롤러 etcd 로드밸런서 또는 CPLB 컨트롤러는 노드 목록에 나오지 않는다(kubelet 없음) 워커 워커 워커 워커는 LB 를 거쳐 붙는다 (노드 로컬 LB 도 있음) 저장소는 기본 etcd, kine(SQLite·MySQL·PostgreSQL)도 고를 수 있다.
그림: 세 배포판의 데이터스토어와 HA 토폴로지

k3s: SQLite 로 시작해 etcd 로 옮길 수 있다

k3s 서버 한 대의 기본 저장소는 SQLite 입니다. API 서버는 etcd 와만 말할 줄 알기 때문에, 가운데에 kine 이 들어가 etcd API 를 흉내 내면서 뒤로는 SQLite 에 씁니다. 가볍지만 서버를 늘릴 수 없고, etcd 도구도 쓸 수 없습니다. TT Lab 실습(v1.35.8+k3s1)에서 SQLite 상태로 k3s etcd-snapshot save 를 치면 etcd datastore disabled 로 거절됐습니다(실측).

HA 로 가는 길은 둘입니다.

어느 쪽이든 서버들 앞에 고정 등록 주소(L4 로드밸런서, 라운드로빈 DNS, 가상 IP 중 하나)를 둡니다.

RKE2: 처음부터 etcd

RKE2 는 다른 설정이 없으면 내장 etcd 입니다. HA 에서 서버는 홀수 대로 3대를 권장하고, 서버도 기본으로 워크로드를 받으므로 서버 3대, 에이전트 0대가 최소 HA 구성입니다. 노드는 고정 등록 주소의 9345 포트로 가입하고, API 는 6443 포트를 씁니다.

SQLite 도 쓸 수는 있지만 문서가 「실험적」이라고 적고 서버 한 대에서만 됩니다. 외부 SQL DB 는 kine 을 거쳐 쓸 수 있습니다(문서 인증 판 etcd 3.6.7, MySQL 8.4.8, MariaDB 11.4.10, PostgreSQL 18.3). 다만 그 백업 절차는 「실험적」 절로 안내합니다.

k0s: 여러 대면 etcd, 한 대면 SQLite

k0s 의 저장소 설정(spec.storage.type)은 etcd 와 kine 둘 중 하나이고, 기본 생성 설정은 etcd 입니다. 문서와 README 의 기능 목록은 기본값을 「여러 노드는 etcd, 한 노드는 SQLite」로 적고, kine 뒤에 MySQL·PostgreSQL 도 둘 수 있다고 합니다.

--single 이 SQLite 로 간다는 것은 실습에서 확인했습니다. --single 로 세운 k0s 에서 k0s etcd member-list 를 치면 wrong storage type: kine 으로 거절됩니다(실측). etcd 를 다루려면 --enable-worker 로 세워야 합니다.

HA 는 컨트롤러 3대 또는 5대가 권장입니다. 2대도 k0s 입장에서는 HA 지만 etcd 정족수로는 HA 가 아니라고 문서가 짚습니다. 컨트롤러 앞에는 6443(API)·8132(Konnectivity)·9443(k0s 가입 API)을 모든 컨트롤러로 보내는 TCP 로드밸런서가 필요합니다. 외부 로드밸런서가 없으면 두 가지를 고를 수 있습니다.

5. 기본으로 들어오는 것

설치 직후 무엇이 이미 돌고 있는지가 셋 사이에서 가장 많이 다릅니다.

구성요소 k3s (v1.37 문서) RKE2 (v1.36~1.37 문서) k0s (v1.36 문서)
CNI flannel(VXLAN) canal(flannel VXLAN + Calico 정책) kube-router
CNI 선택지 flannel 백엔드 변경, 또는 끄고 직접 설치 cilium, calico, flannel, none, multus(보조) calico, custom
NetworkPolicy 내장 컨트롤러(kube-router 기반) canal 의 Calico kube-router
인그레스 Traefik v3 v1.36 부터 새 클러스터는 Traefik, 그 전엔 ingress-nginx 없음
서비스 LoadBalancer ServiceLB(klipper-lb) ServiceLB 있으나 기본 꺼짐 번들 없음(문서의 명시 문장은 미확인)
스토리지 클래스 local-path 없음(실측) 없음(CSI 로 직접)
DNS CoreDNS CoreDNS(+오토스케일러) CoreDNS
metrics-server 있음 있음 있음
기본 대역(파드·서비스) 10.42/16 · 10.43/16 10.42/16 · 10.43/16(실측) 10.244/16 · 10.96/12

RKE2 의 인그레스는 판에 따라 답이 다르니 조심해야 합니다. ingress-nginx 의 상위 지원이 2026년 3월에 끝나면서 RKE2 는 v1.36 부터 새 클러스터의 기본을 Traefik 으로 바꿨습니다. 서비스 문서는 v1.36 에서 ingress-nginx 를 사용 중단 상태로 남기고 v1.37 에서 뺀다고 적는데, v1.37 릴리스 노트는 Traefik 과 함께 고르는 이전용 조합만 남겼다고 적습니다(2026-10-13 확인). 이미 돌던 클러스터는 업그레이드해도 쓰던 인그레스를 유지합니다. 그래서 「RKE2 의 인그레스는 무엇인가」에 대한 답은 클러스터를 처음 만든 판을 알아야 나옵니다. TT Lab 실습 VM(v1.36.4+rke2r1)에서도 기본 인그레스 클래스는 traefik, CNI 는 canal, 스토리지 클래스는 없음이었습니다(실측).

k3s 는 반대로 「다 넣어 주고 끄게 한다」 쪽입니다. --disable 에 traefik, servicelb, local-storage, metrics-server, coredns 를 줄 수 있습니다. 흔한 실수는 kubectl delete 로 Traefik 을 지우는 것입니다. k3s 는 시작할 때마다 기본 구성요소의 매니페스트를 다시 쓰고 적용하기 때문에, 재시작하면 Traefik 이 돌아옵니다. 실습에서는 systemctl restart 뒤 20초 만에 Traefik Deployment 가 되살아났습니다(실측, v1.35.8+k3s1). 끄려면 모든 서버에 --disable 을 같이 줘야 합니다.

k0s 는 「아무것도 넣지 않고 고르게 한다」 쪽입니다. README 의 범위 절은 인그레스·서비스 메시·스토리지를 번들하지 않는다고 적고, MetalLB·Traefik·OpenEBS 같은 것은 예제로만 안내합니다. 실제 서비스를 올릴 클러스터에서는 이 선택이 장점입니다. 기본으로 들어온 것은 이미 무언가가 기대고 있을 때에야 눈에 띄고, 그때 걷어내기가 더 번거롭기 때문입니다.

6. 대역은 같은 듯 다르다

마지막 행이 생각보다 큰 사고를 만듭니다. k0s 의 기본 대역(파드 10.244/16, 서비스 10.96/12)은 표준 쿠버네티스에서 흔히 쓰는 값과 같습니다. 서비스 대역은 kubeadm 의 기본값이고, 파드 대역은 flannel 예제가 널리 퍼뜨린 값입니다. 그래서 k0s 를 다른 쿠버네티스 위의 VM 안에 기본값으로 세우면, 바깥 클러스터가 준 DNS 서버 주소(바깥 서비스 대역 10.96/12 안의 주소)를 안쪽 k0s 가 자기 서비스 IP 로 가져갑니다. TT Lab 실습을 만들 때 실제로 이렇게 됐습니다(실측). DNS 가 죽어 CNI 이미지를 못 받고, CNI 가 없어 CoreDNS 가 못 뜨는 교착이었는데, 화면에 보이는 오류는 이미지 받기 시간 초과뿐이었습니다.

k3s 와 RKE2 의 10.42/10.43 은 비표준이라 우연히 비켜 갑니다. 「k3s 로는 됐는데」라는 경험은 설계가 아니라 우연이라는 점을 기억해 두면, 배포판을 바꿀 때 같은 함정을 피할 수 있습니다. 대역은 처음 만들 때 정하고, 세 배포판 모두 만든 뒤에는 바꾸기 어렵습니다.

티티 한마디. 셋 다 「바이너리 하나」라서 쌍둥이 같지만, 열어 보면 하나는 한 몸, 하나는 정적 파드, 하나는 따로따로예요. 장애 때 로그를 어디서 찾을지부터 다르니까, 처음 세울 때 그림 한 장은 꼭 머리에 넣어 두세요.

참고

모든 문서는 2026-10-13 에 열람했습니다. docs.k3s.io 와 docs.rke2.io 의 페이지는 2026-10-07 갱신으로 표시돼 있었고, docs.k0sproject.io 의 stable 은 v1.36.4+k0s.1 판입니다.

로그인하면 좋아요를 누를 수 있습니다

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다