상태: 초안 (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) |
세 배포판의 성격을 한 줄씩 정리하면 이렇습니다.
- k3s 는 「작게」가 목표입니다. 저장소 README 는 여러 구성요소를 한 프로세스에서 돌려 메모리를 줄였고, 상위 쿠버네티스에서 인트리 스토리지 드라이버와 클라우드 프로바이더를 뺐다고 설명합니다. 상위 코드에 대한 자체 패치는 1,000줄이 안 된다고 밝혀, 포크가 아니라 배포판이라는 점을 강조합니다.
- RKE2 는 k3s 의 설치·운영 방식에, 예전 RKE1 의 「상위 쿠버네티스에 가깝게」를 합친 것입니다. 목표가 분명합니다. 운영자가 거의 손대지 않아도 CIS 벤치마크를 통과하고, FIPS 140-2 를 지원하고, 빌드 과정에서 Trivy 로 CVE 를 꾸준히 검사합니다. Docker 에 기대던 RKE1 은 2025-07-31 에 지원이 끝났고, SUSE 는 RKE2 나 k3s 로 옮기라고 안내합니다.
- k0s 는 「제로」를 내세웁니다. 홈페이지가 말하는 제로는 셋입니다. 마찰 없는 설치(Zero Friction), 호스트 커널 말고는 의존이 없는 단일 바이너리(Zero Deps), Apache 2 라이선스의 무료(Zero Cost)입니다. 흔히 인용되는 「zero overhead」라는 표현은 공식 페이지에서 찾지 못했습니다(미확인).
3. 컨트롤 플레인은 어떻게 뜨나
가장 큰 차이는 컨트롤 플레인 구성요소를 어떤 모양으로 띄우는가입니다.
k3s: 한 프로세스에 묶는다
k3s server 하나가 kube-apiserver, 스케줄러, 컨트롤러 매니저, kubelet, kube-proxy, flannel, 네트워크 정책 컨트롤러, kine, helm-controller 를 같은 프로세스 안에서 돌립니다. README 가 메모리 절감의 주된 이유로 꼽는 설계입니다. 구성요소마다 따로 들고 있을 런타임과 캐시를 한 번만 쓰기 때문입니다.
containerd 와 runc 는 바이너리에 함께 묶여 오지만, 실행은 따로 합니다. k3s 소스(pkg/agent/containerd)를 보면 k3s 가 containerd 를 별도 프로세스로 띄웁니다.
운영에서 이 구조가 뜻하는 것은 이렇습니다.
- 로그가 한곳에 모입니다.
journalctl -u k3s하나에 apiserver 와 kubelet 의 로그가 섞여 나옵니다. crictl ps로 컨트롤 플레인 컨테이너를 찾을 수 없습니다. 애초에 컨테이너가 아니기 때문입니다.- kubeadm 에서 익힌 「
/etc/kubernetes/manifests의 정적 파드 고치기」가 통하지 않습니다. 플래그는 k3s 설정 파일이나--kube-apiserver-arg같은 인자로 넘깁니다.
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 포트).
이 분리를 풀고 싶을 때 쓰는 플래그가 둘 있습니다.
--enable-worker: 컨트롤러가 워커 역할도 합니다. FAQ 는 시험·개발·PoC 용으로만 권합니다.--single: 노드 한 대짜리 모드입니다.--enable-worker를 포함하지만 여러 노드에 필요한 기능을 꺼 두기 때문에 나중에 노드를 늘릴 수 없습니다.
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 로 시작해 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 로 가는 길은 둘입니다.
- 내장 etcd. 첫 서버를
--cluster-init으로 띄우고 나머지를 같은 토큰으로 붙입니다. 홀수 대, 최소 3대가 필요합니다. SQLite 로 돌던 서버도--cluster-init으로 다시 시작하면 etcd 로 옮겨 갑니다. 실습에서는 재시작 12초 만에 API 가 돌아왔고,state.db가state.db.migrated로 이름이 바뀌었으며, 옮기기 전에 만든 ConfigMap 의 UID 가 그대로 남아 있었습니다(실측, v1.35.8+k3s1). - 외부 DB. 서버 2대 이상이 같은 MySQL·MariaDB·PostgreSQL·etcd 를 바라봅니다. 문서가 인증한 판은 etcd 3.5.21, MySQL 8.0·8.4, MariaDB 10.11·11.4, PostgreSQL 15.12·16.7·17.3 입니다(2026-10-07 갱신 문서). 이때도 kine 이 SQL DB 앞에서 etcd API 를 흉내 냅니다.
어느 쪽이든 서버들 앞에 고정 등록 주소(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 로드밸런서가 필요합니다. 외부 로드밸런서가 없으면 두 가지를 고를 수 있습니다.
- CPLB(Control Plane Load Balancing): 컨트롤러들이 keepalived 로 가상 IP 를 나눠 갖습니다.
--single과는 함께 쓰지 못합니다. - NLLB(Node-Local Load Balancing): 워커마다 작은 로드밸런서(기본 Envoy)를 두어 컨트롤러로 나눠 보냅니다. 기본은 꺼져 있습니다.
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 판입니다.
- k3s 문서: https://docs.k3s.io/, 구조 https://docs.k3s.io/architecture, 데이터스토어 https://docs.k3s.io/datastore, 내장 etcd HA https://docs.k3s.io/datastore/ha-embedded, 외부 DB HA https://docs.k3s.io/datastore/ha, 네트워크 https://docs.k3s.io/networking/basic-network-options, 기본 서비스 https://docs.k3s.io/networking/networking-services, 패키지 구성요소 https://docs.k3s.io/installation/packaged-components
- k3s 저장소: https://github.com/k3s-io/k3s (README,
pkg/agent/containerd/containerd.go), CNCF 프로젝트 페이지 https://www.cncf.io/projects/k3s/ - RKE2 문서: https://docs.rke2.io/, 구조 https://docs.rke2.io/architecture, 내장 etcd https://docs.rke2.io/datastore/embedded, 외부 DB https://docs.rke2.io/datastore/external, HA https://docs.rke2.io/install/ha, 네트워크 https://docs.rke2.io/networking/basic_network_options, 기본 서비스 https://docs.rke2.io/networking/networking_services, v1.37 릴리스 노트 https://docs.rke2.io/release-notes/v1.37.X
- RKE1 지원 종료 공지(SUSE): https://suse.com/c/rke-end-of-life-by-july-2025-replatform-to-rke2-or-k3s
- k0s 문서: https://docs.k0sproject.io/stable/, 구조 https://docs.k0sproject.io/stable/architecture/, 구성 https://docs.k0sproject.io/stable/configuration/, HA https://docs.k0sproject.io/stable/high-availability/, CPLB https://docs.k0sproject.io/stable/cplb/, NLLB https://docs.k0sproject.io/stable/nllb/, 네트워크 https://docs.k0sproject.io/stable/networking/, FAQ https://docs.k0sproject.io/stable/troubleshooting/FAQ/
- k0s 저장소: https://github.com/k0sproject/k0s (README,
pkg/constant/constant.go), 홈페이지 https://k0sproject.io/, CNCF 프로젝트 페이지 https://www.cncf.io/projects/k0s/ - 실측: TT Lab 「쿠버네티스 배포판 — 직접 세운다」 코스의 실습 VM 기록(k3s v1.35.8+k3s1, RKE2 v1.36.4+rke2r1, k0s v1.36.4+k0s.0, 2026-09-15 등)