- Published on
本地轻量级 Kubernetes 选型标准 —— 用数据存储和合规要求来区分 k3s、k0s、RKE2
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 引言 —— "轻量"不是选型标准
- 轴线 1:数据存储 —— 一半的决定在这里就定了
- 轴线 2:默认打包了什么 —— 剥离出去的成本
- 轴线 3:进程部署方式 —— 单一二进制与静态 Pod
- 轴线 4:合规要求 —— RKE2 的 CIS 和 FIPS 到底是给谁用的
- 轴线 5:升级、支持周期,以及在小型节点上的占用
- 三个场景与推荐
- 结语 —— 从最难反悔的那条轴线开始选
- 参考资料
引言 —— "轻量"不是选型标准
新搭建本地集群时,最常被问到的问题是"k3s 和 RKE2 到底哪个更好"。但如果用一张功能对比表来回答这个问题,通常会答错。三个发行版都是经过认证的 Kubernetes,都接近以单一二进制的形式分发,也都正式支持离线安装路径。光看功能清单是区分不出来的。
真正决定选型的大概有五条轴线,其中第一条 —— 数据存储 —— 是事后最难更改的。装完六个月之后才收到"得再加一台服务器节点"的需求,这时候才发现自己当初是用 SQLite 起步的,类似的情况并不少见。
本文把这些轴线逐一摆出来,最后针对三个具体场景给出答案。以下是核实基准。
| 发行版 | 核实的版本系列 | 核实时间 | 主要出处 |
|---|---|---|---|
| k3s | v1.36.2+k3s1 | 2026-07-31 | docs.k3s.io |
| k0s | v1.36.3+k0s.0 | 2026-07-31 | docs.k0sproject.io |
| RKE2 | v1.33.1+rke2r1 (文档示例) | 2026-07-31 | docs.rke2.io |
轴线 1:数据存储 —— 一半的决定在这里就定了
k3s 的默认数据存储是 SQLite。官方 CLI 文档把 --datastore-endpoint 选项描述为"指定 etcd、NATS、MySQL、Postgres 或 SQLite(默认)的数据源名称"。而且在 SQLite 配置下没法扩展服务器节点。如果需要高可用,就必须转向内置 etcd 或接上外部数据存储。
# k3s —— 用内置 etcd 起步(第一台服务器)
k3s server --cluster-init
# k3s —— 外部数据存储(PostgreSQL 示例)
k3s server --datastore-endpoint="postgres://k3s:PASSWORD@10.10.30.5:5432/k3s?sslmode=require"
即便是已经用 SQLite 搭起来的集群,也有迁移路径。官方文档说明,把现有服务器带上 --cluster-init 参数重启,就能迁移到 etcd。不过这不是一个可以在没有备份的情况下在生产环境里尝试的操作。
k0s 通过 spec.storage.type 来指定,有效值是 etcd 和 kine 两个。官方配置文档的示例展示的是 type: etcd;如果用 kine,则通过 spec.storage.kine.dataSource 里的连接字符串来接入 SQLite、MySQL、PostgreSQL 系列的后端。要接入外部管理的 etcd 时,在 spec.storage.etcd.externalCluster 下指定端点和 TLS 文件路径。
# k0s —— 内置 etcd(文档示例的基本形式)
apiVersion: k0s.k0sproject.io/v1beta1
kind: ClusterConfig
spec:
storage:
type: etcd
# k0s —— 接入外部管理的 etcd 集群
apiVersion: k0s.k0sproject.io/v1beta1
kind: ClusterConfig
spec:
storage:
type: etcd
etcd:
externalCluster:
endpoints:
- https://10.10.30.11:2379
- https://10.10.30.12:2379
etcdPrefix: onprem-cluster
caFile: /etc/pki/etcd/ca.crt
clientCertFile: /etc/pki/etcd/client.crt
clientKeyFile: /etc/pki/etcd/client.key
RKE2 的概览文档没有明确说明数据存储。在这次核实范围内,我没能在官方文档正文中确认 RKE2 是否提供 SQLite 数据存储,如果目标是单节点超轻量配置,请在引入之前直接确认这一项。不过按 RKE2 的定位来看,想把这个发行版当成 SQLite 单节点来用的场景本身就很少见。RKE2 是把控制平面以静态 Pod 的形式启动的结构,文档自己给出的目标就是合规。
从离线部署的角度看,这条轴线的结论是这样的:如果一开始就能把服务器节点数定为 3 台,就用内置 etcd 起步。 在离线网络里,日后想加节点,意味着要从导入审核重新走一遍,迁移成本比普通环境高得多。而且内置 etcd 要求奇数节点。2 台配置的容错能力和 1 台一样,却白白多了一台要运维。
轴线 2:默认打包了什么 —— 剥离出去的成本
轻量级发行版为了做到"开箱即用",会预先打包一些组件。这在第一天是优点,到了六个月后想换掉 Ingress 控制器的那天,就变成了缺点。
k3s 在这一点上最直白。根据官方 CLI 文档,可以用 --disable 参数关闭的打包组件有 coredns、servicelb、traefik、local-storage、metrics-server、runtimes。这份清单原样写在文档里,这件事本身在实务中意义很大。
# /etc/rancher/k3s/config.yaml —— 想用别的 ingress 代替 Traefik 时
disable:
- traefik
- servicelb
# 在已经安装好的集群上关闭组件,manifest 可能会残留下来,导致组件复活。
# k3s 会监视 /var/lib/rancher/k3s/server/manifests,所以要一并清理。
sudo ls /var/lib/rancher/k3s/server/manifests/
sudo systemctl restart k3s
sudo k3s kubectl -n kube-system get pods
k0s 通过 spec.network.provider 指定网络提供方,按官方文档,有效值是 calico、kuberouter、custom,默认是 kuberouter。文档明确说明,选择 custom 意味着"用户要为所有 CNI 配置和主机层面的配置负责"。使用其他 CNI 这个决定,本身就等于宣布"一切自己来",这个结构选择很清晰,但没有中间地带。
RKE2 把 CNI 干脆整个拆成独立的镜像归档来分发。离线导入清单在这里就产生了差异。
| RKE2 镜像归档 | 内容 | 离线导入时的判断 |
|---|---|---|
| rke2-images.linux-amd64.tar.zst | 包含默认 CNI Canal 的完整打包 | 默认配置的话,只要这一个 |
| rke2-images-core.linux-amd64.tar.zst | 不含 CNI 的核心镜像 | 替换 CNI 时,核心和 CNI 分开导入 |
| rke2-images-cilium.linux-amd64.tar.zst | Cilium 专用镜像 | 使用 Cilium 时与核心一起导入 |
| rke2-images-vsphere.linux-amd64.tar.zst | vSphere CPI/CSI 镜像 | vSphere 本地部署时追加 |
这种拆分在离线环境里确实有用。没有理由把用不到的 CNI 镜像装进介质、提交审核。反过来,如果导入清单没配对,只带进来了核心却漏掉了 CNI,节点就会卡在 NotReady。
轴线 3:进程部署方式 —— 单一二进制与静态 Pod
k3s 和 k0s 把控制平面组件都放在自己的进程里运行。在节点上敲 ps,也看不到一个叫 kube-apiserver 的独立进程。RKE2 不一样。官方概览文档明确说明"RKE2 把控制平面组件作为由 kubelet 管理的静态 Pod 来运行"。
这个差异带来了三个实际后果。
- 调试路径不一样。 在 RKE2 上,可以把 API 服务器的日志当作 Pod 日志来读,也能直接修改 manifest。在 k3s 和 k0s 上,只能翻查一份服务日志(journalctl)。在离线环境里得不到外部支持时,这个差异的体感会很明显。
- 上游文档能直接套用的程度不一样。 RKE2 自己的文档说,它"从 RKE1 继承了与上游 Kubernetes 的紧密对齐"。在互联网搜索被屏蔽的环境里,手边的 Kubernetes 教材能不能直接照搬使用,是一个比想象中更重要的条件。
- 资源占用不一样。 进程一旦分开,内存也会跟着分开。在只有 2 GB 内存的工控机上,这个差异是决定性的。
轴线 4:合规要求 —— RKE2 的 CIS 和 FIPS 到底是给谁用的
这条轴线,就是 RKE2 存在的理由。它的概览文档把目标领域明确写成"美国联邦政府部门的安全与合规",说明它能实现 FIPS 140-2 合规,并让集群能通过"CIS Kubernetes Benchmark v1.7 或 v1.8"。
CIS 配置文件只需一行配置就能打开。
# /etc/rancher/rke2/config.yaml
profile: 'cis'
# RKE2 v1.29 及以上版本可能需要一并添加的 API 服务器参数
kube-apiserver-arg:
- 'service-account-extend-token-expiration=false'
官方加固指南说明,这个通用配置文件会自动套用与 RKE2 版本相匹配的基准控制项,所以升级时不需要修改配置值。不过主机准备工作必须提前做好,没做好的话,RKE2 会以致命错误中止启动。
# 1) 创建 etcd 用户和用户组
sudo useradd -r -c "etcd user" -s /sbin/nologin -M etcd -U
# 2) 应用内核参数 —— RPM 系列
sudo cp -f /usr/share/rke2/rke2-cis-sysctl.conf /etc/sysctl.d/60-rke2-cis.conf
sudo systemctl restart systemd-sysctl
# 2') 非 RPM 系列(Ubuntu 等)
sudo cp -f /usr/local/share/rke2/rke2-cis-sysctl.conf /etc/sysctl.d/60-rke2-cis.conf
sudo systemctl restart systemd-sysctl
官方文档警告说,这个 sysctl 改动只应该在部署 Kubernetes 之前的全新安装中应用。在已经运行中的集群上重启 sysctl,会产生意料之外的副作用。
打开 CIS 配置文件之后会发生什么变化,文档里也整理清楚了:Pod 安全准入会在整个集群范围内强制设为 restricted(kube-system、compliance-operator-system、tigera-operator 除外),会部署一条把流量限制在同一命名空间内的网络策略,agent 的 manifest 和配置文件权限会从 644 收紧到 600,etcd 静态 Pod 会以 etcd 用户身份运行。
这里有一项大多数团队会漏掉的内容 —— 审计日志。
# RKE2 的默认审计策略什么都不记录。
# 必须把 level 从 None 改成 Metadata 或更高级别,日志才会被保留下来。
sudo vi /etc/rancher/rke2/audit-policy.yaml
sudo systemctl restart rke2-server.service
sudo tail -f /var/lib/rancher/rke2/server/logs/audit.log
在离线环境的审计中,面对"你们开启审计日志了吗"这个问题,回答"我们开了配置文件"是过不了关的,必须直接修改策略文件。
FIPS 的覆盖范围比想象中要窄
读 FIPS 文档时,有一句话必须确认清楚,是关于 CNI 的。官方文档明确说明,在支持的 CNI 里,只有默认的 Canal 是为 FIPS 合规而重新构建的。Cilium、Calico、Multus 都不在此列。
| 领域 | 是否支持 FIPS(按官方文档) |
|---|---|
| API 服务器、控制器管理器、调度器、kubelet、kube-proxy | 用 GoBoring 编译器静态构建 |
| etcd、containerd 及其 shim、crictl、runc | 用启用了 FIPS 的 Go 编译器静态构建 |
| CoreDNS、Flannel、Calico Helm chart | 在范围之内 |
| NGINX Ingress | Go 控制器用 BoringCrypto,C 服务器用经 FIPS 验证的 OpenSSL |
| Canal CNI | 已重新构建(默认项) |
| Cilium、Calico、Multus CNI | 未重新构建 |
所以如果"需要 FIPS"和"想用基于 eBPF 的 CNI"这两个要求同时存在,就必须放弃其中一个。这个决定如果在设计阶段没有做出来,往往会在审计前夕才不得不去更换 CNI。
还有一点值得坦白说清楚。如果合同或审计条款里没有要求 FIPS 和 CIS,就没有必要因为这个理由去选 RKE2。 在国内的金融、政务离线网络里,真正被要求的通常是 CIS 系列的加固和审计日志,而不是 FIPS 140 验证模块本身,这种情况并不少见。应该先确认需求文档里明确写了什么,再区分哪些是 RKE2 会自动帮你做的,哪些是不管选哪个发行版都得自己动手做的。
轴线 5:升级、支持周期,以及在小型节点上的占用
在离线网络里,升级意味着"把新的二进制和新的镜像包带进来,替换掉旧的"。三个发行版在这个骨架上是一样的,但自动化工具的特点不同。
| 发行版 | 离线升级路径 | 多节点自动化 |
|---|---|---|
| k3s | 把新归档放进镜像文件夹并删除旧的,替换二进制,重新运行 install.sh | system-upgrade-controller (需要导入相关镜像) |
| k0s | 部署新的镜像包和二进制之后重启服务 | k0sctl apply,和安装用的是同一条命令 |
| RKE2 | 放置新的制品目录,重新运行 install.sh | system-upgrade-controller 系列 |
k3s 的自动升级在离线环境里需要额外的工作。官方文档明确说明,要使用自动升级,rancher/k3s-upgrade、rancher/system-upgrade-controller、rancher/kubectl 这三个镜像必须在私有仓库里。如果导入清单里没有这三个镜像,自动升级会在第一次尝试时就卡住。
支持周期方面,三个发行版都跟随上游 Kubernetes 的次要版本节奏。在离线环境里,重要的不是支持周期本身,而是导入周期和支持周期之间的匹配度。如果导入审核一个季度才能走一次,而补丁是按月发布的,实质上就变成了每个季度一次性上一堆累积补丁。这种情况下,把次要版本固定得低一档、用一条已经稳定下来的补丁线,事故会更少。
资源占用方面,从轴线 3 的进程部署方式那里就已经拉开差距了。具体数字会因 CNI 选择、节点数量、工作负载而有很大差异,这里就不引用具体数字了。只给出判断标准:如果是内存 4 GB 以下的工控节点,就把静态 Pod 方案从候选里排除。 控制平面组件各自作为独立容器启动的结构,在那种硬件上没有余量。
轴线汇总表
| 轴线 | k3s | k0s | RKE2 |
|---|---|---|---|
| 默认数据存储 | SQLite (文档明确说明) | etcd (按配置文档示例) | 概览文档未明确说明 —— 需要确认 |
| 高可用扩展 | 需要迁移到内置 etcd 或外部数据库 | 内置 etcd 或外部 etcd | 基于静态 Pod 的多服务器 |
| 外部数据库支持 | etcd、NATS、MySQL、Postgres | kine 数据源 | 未能确认 |
| 默认 Ingress | Traefik (可禁用) | 单独部署 | ingress-nginx 系列 (概览文档未明确说明) |
| CNI 选择 | 默认 Flannel,可替换 | 默认 kuberouter,可选 calico、custom | 默认 Canal,Cilium/Calico/Multus 镜像归档分离 |
| 控制平面部署方式 | 单一进程 | 单一进程 | 由 kubelet 管理的静态 Pod |
| 离线镜像导入 | 复制发行版归档 | 复制发行版镜像包,或自行制作 | 按 CNI 选择性导入归档 |
| 私有仓库配置 | registries.yaml (支持 rewrite) | 直接配置 containerd | system-default-registry 配置 |
| 官方多节点自动化工具 | 无 (需用外部工具) | k0sctl | 无 (需用外部工具) |
| 合规支持 | 需要单独加固 | 需要单独加固 | profile cis、FIPS 构建 |
| 小型节点适配性 | 高 | 高 | 低 |
| 社区资料量 | 多 | 一般 | 一般 |
这张表里标为"未能确认"的格子,没有用猜测去填。在离线环境的引入决策中,把未经确认的项目当成已确认的来处理,是代价最高的错误。如果某个格子会影响你的决策,请在引入之前先在预发布环境里直接确认。
三个场景与推荐
场景 1:工厂现场的边缘节点
每条产线一台工控机,4 GB 内存,远程访问只在检修时开放,节点数量从几十到几百台不等。一台出问题,只影响那一条产线,其他产线互不相干。
推荐:k3s。如果节点数量多、需要反复搭建同样的配置,选 k0s 加 k0sctl。
理由有三个。第一,因为这种结构不需要高可用,SQLite 单节点反而是恰当的选择,管理 etcd 法定人数在这个场景里纯粹是成本。第二,4 GB 内存下静态 Pod 部署方式没有余量。第三,用 k3s 的内置仓库镜像功能,够不到内部仓库的节点之间可以互相分享镜像。
# 边缘节点最小配置 —— /etc/rancher/k3s/config.yaml
disable:
- traefik
- servicelb
- metrics-server
write-kubeconfig-mode: '0600'
kubelet-arg:
- 'image-gc-high-threshold=70'
- 'image-gc-low-threshold=50'
调低镜像 GC 阈值的原因是边缘节点的磁盘小。在离线的边缘环境里,一旦磁盘占满,就没有办法重新获取镜像,整个节点就会变得没法用。
如果节点有几百台、需要反复搭建同样的配置,就应该倾向于 k0sctl。用一份 YAML 管理节点列表、用同一条命令一路走到升级的这种结构,在需要重复几百次的场景里,最能减少人为失误。
场景 2:金融或政务离线网络中的内部服务集群
承载内部业务系统的集群。节点 10 到 30 台,属于审计对象,存在安全检查清单,出故障时业务会中断。
推荐:RKE2 —— 但仅限于需求文档里确实包含 CIS 系列条款的情况。
理由有两个。第一,一行 profile: 'cis',就能一并调整 Pod 安全准入、网络策略、文件权限、etcd 运行用户。如果手动去做,每一项都需要单独验证,每次审计都得解释一遍"为什么这么配置"。让发行版通过官方文档来提供依据,明显更有优势。第二,因为是静态 Pod 结构,上游 Kubernetes 的知识和工具能直接套用,在没有外部支持、需要靠内部人员长期维护的情况下更有优势。
# /etc/rancher/rke2/config.yaml —— 离线合规集群的基本形式
profile: 'cis'
system-default-registry: 'registry.internal.example:5000'
tls-san:
- k8s-api.internal.example
- 10.10.20.10
kube-apiserver-arg:
- 'service-account-extend-token-expiration=false'
system-default-registry 在离线环境里特别有用。官方文档明确说明,这个值只接受 RFC 3986 URI authority,也就是说只能用主机名加可选端口,不能带路径。想在这里再拼上一段项目路径而导致失败的案例并不少见。
反过来,如果需求文档里既没有 CIS 也没有 FIPS,只写了"加强安全性"这种笼统的说法,选 RKE2 的理由就站不住脚。这种情况下,直接在 k3s 上应用 Pod 安全标准和网络策略,运维负担会更小。
场景 3:开发者笔记本电脑与 CI
用于功能开发和集成测试。一天要建了又扔好几次,生命周期从几分钟到几小时不等。
推荐:k3s 或 k0s 单节点。只看启动速度和是否方便丢弃。
在这个场景里,数据存储的讨论没有意义。SQLite 是最好的选择,组件也应该尽量都关掉,以缩短启动时间。
# 在 CI runner 上跑 k3s 单节点 —— 把不需要的都关掉
sudo INSTALL_K3S_SKIP_DOWNLOAD=true \
INSTALL_K3S_EXEC="server --disable traefik --disable servicelb --disable metrics-server --disable local-storage --write-kubeconfig-mode 0644" \
./install.sh
# k0s 单节点 —— 不用安装脚本,只靠子命令
sudo k0s install controller --single
sudo k0s start
sudo k0s kubectl get nodes
# 销毁
sudo k0s stop && sudo k0s reset
# 或者用 k3s
sudo /usr/local/bin/k3s-uninstall.sh
离线 CI 有一点需要注意。如果 CI runner 每次都重新建一个全新的集群,镜像导入也会每次都发生一遍。反复解压几百 MB 镜像包的成本会主导整个构建时间,所以更好的做法是把设计改成用一个预先填充好 containerd 存储快照的 runner 镜像,或者干脆复用集群。
结语 —— 从最难反悔的那条轴线开始选
三个发行版之间的功能差异,会随时间推移而缩小。不会缩小的,是初始决策所造成的约束。给一个用 SQLite 起步的集群加服务器节点、在一个后来发现需要 FIPS 的环境里已经用 Cilium 搭好了、在 4 GB 内存的节点上部署了静态 Pod 类型的发行版 —— 这三种情况,事后想修正都得重建集群,而在离线网络里重建集群,就意味着要从导入审核重新走一遍。
所以顺序应该是这样的:先定数据存储,再读合规需求文档,然后确认节点的实际内存容量,最后从同时满足这三条的发行版里选一个。剩下的差异,可以在运维过程中慢慢消化。