1. 为什么我坚持用 RKE 来做集群交付
聊到 Kubernetes 集群部署,大多数人的第一反应是 kubeadm。这种思路没有错,但在做裸金属交付和离线环境搭建的时候,我更习惯直接用 rke。rke 是一个以配置文件驱动的 Kubernetes 部署工具,它不需要在目标服务器上提前安装 kubelet、kubeadm 或 controller-manager,控制机只要有一个二进制文件和一份 cluster.yml,再通过 SSH 连到目标节点,就能把 etcd、控制平面和 worker 节点全部初始化完成,最后生成一个可以直接使用的集群。用一句大白话总结:它把部署 Kubernetes 变成了执行一条命令的事。
如果只看功能,rke 和 kubeadm 之间最大的区别在交付方式。kubeadm 需要你先在每台节点装好组件,再手动执行 init 和 join,中间还要处理证书、token 过期、高可用负载均衡等一系列问题。rke 则把这些步骤全部收进配置和命令里,只要配置写对,重复部署多套环境的差异会很小。这也是我为什么在多个需要批量交付的项目里选择了它,而不是继续沿用 kubeadm。
1.1 最适合什么样的人和场景
先说清楚适用边界,避免大家盲目跟风。我实际使用下来,rke 最适合这几类场景:
- 手上有若干台裸机或虚拟机,需要快速拉起一个多节点集群,并不想手工去下载每个组件;
- 在离线的内网环境交付,需要提前把所有依赖镜像准备好;
- 对可重复性有要求,希望用一份配置就能重建整个集群;
- 需要自己掌控证书、组件和网络插件,不想绑定某一种发行套件。
如果你是刚开始学 Kubernetes,只有一台本地电脑,其实不必急着用 rke。你可以先用单节点模式熟悉组件,等真正要搭建集群或做实验时,再回到 rke 会顺畅得多。rke 的底层会直接操作 Docker,它对节点环境的整洁度要求比你想的高一些,但换来的是整个部署过程非常可预测。
1.2 部署前的节点与资源规划
Kubernetes 节点角色分为 controlplane、etcd、worker 三种。在 rke 的配置里,一台机器可以同时承担多个角色,角色的拆分完全由你在 cluster.yml 的 nodes 数组里定义。生产环境我建议把角色的边界划得清楚一些:etcd 集群至少三台,控制平面可以复用其中部分机器,worker 单独划分,数据安全性和控制面稳定性都会更好。单节点测试集群最低可以给到 2 核 4G,但运行 etcd 的节点建议 4 核 8G 起步。这个不是随口说的,etcd 会频繁做 fsync 和 Raft 心跳,磁盘和内存不足会直接导致 Leader 频繁切换。
部署前还要提前确认操作系统、Docker 版本和存储驱动的一致性。rke 在执行时通过 SSH 到节点操作 Docker,节点上的 Docker 版本需要与 rke 的兼容矩阵对齐,存储驱动建议统一是 overlay2。先把这些基础项拉齐,可以减少后面大部分安装问题。还有一点容易被忽略:所有节点的系统时间要尽可能一致,最稳妥的做法是统一配置 NTP 同步。时间偏差过大会引发证书验证失败,表现形式和网络超时非常像,排查时特别容易走弯路。
2. RKE 配置文件 cluster.yml 关键字段精读
rke 的全部部署行为都写在 cluster.yml 里。这个文件从表面上看只是一堆 YAML,但每一个字段都会直接影响集群最终长什么样。我在项目里看过不少新同事写的配置,有些改了几十个参数,最后反而把集群搞得不稳定。下面我把实际中用到的关键字段逐一拆开讲。
2.1 nodes 节点定义与角色分配的正确姿势
nodes 是整个配置的核心,结构本身不复杂:
nodes: - address: 192.168.10.11 user: root role: - controlplane - etcd - address: 192.168.10.12 user: root role: - controlplane - etcd - address: 192.168.10.13 user: root role: - worker这段配置表示有两台机器同时承担 controlplane 和 etcd 角色,第三台只作为 worker。这里有几个点要特别注意:
第一,role 至少要有 controlplane。etcd 可以和控制面放在一起,也可以完全分开,但生产环境建议至少有奇数个 etcd 节点,避免 Raft 选主时出现脑裂。
第二,user 如果不使用 root,需要提前给当前用户加入 docker 用户组,并确保该用户可以免密 SSH。很多人在这一步用了普通用户,结果 rke 执行时发现没有权限访问 docker socket,报错信息还不能直接看出原因。
第三,ssh_key_path 默认是 ~/.ssh/id_rsa。如果你的私钥不是默认路径,一定要显式配置。我见过有人把私钥放在项目目录下,忘记配路径,rke 一直卡在 SSH 握手阶段,后来加了一行配置就正常了。
2.2 services 服务参数里最容易踩坑的网段
services 段用来覆盖各组件默认参数,一般会在这里配置 kube-api、kubelet 和 etcd:
services: kube-api: service_cluster_ip_range: 10.43.0.0/16 kubelet: cluster_dns_server: 10.43.0.10 etcd: backup_config: interval_hours: 12 retention: 6这里最需要警惕的是 service_cluster_ip_range。这个 CIDR 决定了 Service 的虚拟 IP 范围,一旦集群创建后基本不能改。选它的时候要满足三个条件:不能和节点所在网段重叠,不能和 Pod 网段重叠,不能和 Docker 默认网桥网段重叠。
如果在生产环境选一个很奇怪的段,后续所有网络策略都要跟着适配,非常痛苦。拿不准的时候直接用默认的 10.43.0.0/16 是最稳妥的。有些同事会把 Service CIDR 设成 172.17.0.0/16,结果和 Docker 默认网桥重叠,Pod 内访问 Service 时表现非常不稳定,有的请求能通,有的直接超时,这类问题很难一次定位。另外,etcd 的 backup_config 是我强烈建议配置的,不需要额外引入备份工具就能获得定时快照备份,interval_hours 控制备份间隔,retention 控制保留数量。
2.3 network 网络插件到底怎么选
cluster.yml 里有一个 network 段,用来指定容器网络插件:
network: plugin: calico常见的选项是 flannel、calico、canal。三者的差别用一个表格就能看明白:
| 插件 | 网络策略 | 性能表现 | 适用场景 |
|---|---|---|---|
| flannel | 不支持网络策略 | 轻量、简单 | 纯内网小规模场景 |
| calico | 支持 NetworkPolicy | 性能和功能均衡 | 生产环境首选 |
| canal | flannel 加网络策略 | 兼顾两者 | 需要简单网络又需要策略 |
我在生产环境里基本都是 calico。如果只是熟悉原理或做功能验证,可以考虑 flannel,部署速度更快,出问题的概率也更低。需要特别注意的是,如果使用 flannel,默认的 Pod 网段是 10.42.0.0/16。假设节点之间已经存在一个重叠的 10.42.x.x 网段,rke 部署后 Pod 之间的通信会非常混乱。这不是 rke 本身的问题,而是容器网络叠加时的互通冲突。所以无论使用哪种插件,都必须先梳理整张网络拓扑,保证 Pod CIDR 不与任何物理网段重叠。
2.4 authentication 与权限模型要注意什么
rke 在生产环境部署时会自动生成一套证书体系,默认生成的 kubeconfig 文件就是当前目录下的 kube_config_cluster.yml。这个文件里有客户端的证书和私钥,权限等同于集群管理员,一定要保护妥当:
chmod 600 kube_config_cluster.ymlKubernetes 默认启用 RBAC 权限模型,这一点不要随便关闭。我理解很多人初学阶段想快速看到效果,但一旦关闭 RBAC,任何用户都可能拿到集群管理权,生产环境这是绝对不可接受的。更合理的做法是测试环境单独创建一个只读权限的 ServiceAccount,并把它的 token 和 kubeconfig 分开管理。如果集群需要新增外部访问域名或 IP,可以在 services.kube-api 中配置额外的 SANs,而不是修改已有证书导致其他客户端全部失效。
3. rke up 完整部署实操记录
3.1 控制机准备与 rke 版本选择
部署前,我习惯把所有工具和配置集中放在一个目录:
mkdir -p ~/rke-cluster && cd ~/rke-cluster wget <rke官方下载地址> chmod +x rke_linux-amd64 mv rke_linux-amd64 /usr/local/bin/rke rke --version这里不建议直接下载最新版本,最好先看目标 Kubernetes 版本对应的 rke 版本支持矩阵。版本选错会导致部署过程中组件镜像版本解析失败,出现比较难排查的拉取超时错误。控制机上不需要安装 Docker,也不需要有 kubeconfig,只要有一把可以和节点 SSH 的私钥就够了。这也是 rke 的明显优点:它把控制节点做得极简,避免部署工具本身的依赖把环境搞乱。
3.2 编写一份可直接使用的 cluster.yml
下面是一份三节点集群的标准配置示例,完全可以直接复制调整:
nodes: - address: 192.168.10.11 user: root role: - controlplane - etcd - address: 192.168.10.12 user: root role: - controlplane - etcd - address: 192.168.10.13 user: root role: - worker services: kube-api: service_cluster_ip_range: 10.43.0.0/16 kubelet: cluster_dns_server: 10.43.0.10 etcd: backup_config: interval_hours: 12 retention: 6 network: plugin: calico建议先通过基础模板生成底稿,再按需修改。不要完全靠手写,因为 rke 对字段校验很严格,稍有不慎就会在 rke up 时因为 YAML 解析失败退出。你以为你是漏了个缩进,实际可能是一个不存在的字段名,报错却会指向 YAML 行号,容易让人白跑半天。
3.3 执行 rke up 的过程与关键日志观察
rke up 一旦开始,全程大概需要几分钟到十几分钟不等,取决于镜像拉取速度和节点数量。执行命令很简单:
rke up --config cluster.yml执行过程中你会看到类似这样的轨迹:先是 dialer 阶段建立 SSH 连接,然后进入 certificates 阶段生成证书,接着拉取 etcd、apiserver、kubelet 等组件镜像,最后运行 addon,比如 calico 和 coredns。如果某个环节失败,日志会是显眼的 Failed 状态,这时候不要盲目重试,先分析具体阶段。
其中最常见的是镜像拉取超时。解决方案有两个方向:如果网络条件不理想,可以提前从外部网络拉好 rke 需要的所有镜像,再导出导入到节点并重新打 tag;更省心的方式是在内网准备好私有镜像仓库,让 rke up 可以顺利拉取。注意,rke 会将大量中间状态写到节点上,比如 /etc/kubernetes、/var/lib/etcd、/etc/cni 等。一旦部署失败需要重试,最好先执行一次清理:
rke remove --config cluster.yml --force这个命令会清除节点上的相关文件和数据,避免残留状态干扰下一次部署。
注意:rke remove 会修改节点上的数据,执行前确认你已经不再需要该集群里的业务数据。
3.4 集群验证、增删节点与证书轮转
执行完成后,当前目录会出现 kube_config_cluster.yml。接下来验证:
export KUBECONFIG=$PWD/kube_config_cluster.yml kubectl get nodes kubectl get pods -A正常情况下,你会看到节点状态都是 Ready,coredns、calico 等系统 Pod 处于 Running 状态。此时集群就绪,可以部署业务应用。如果某个节点长时间处于 NotReady,需要到对应节点上查看 kubelet 容器日志,比如通过 docker logs 关键容器排查,或者用 kubectl describe node 查看事件。我遇到过几次 NotReady 的原因,都和防火墙放行策略有关。Kubelet 需要监听 10250 端口,CNI 插件需要在节点间交换数据,如果这些端口被防火墙挡掉,kubelet 的健康检查就会超时。
集群部署好后,后续维护最频繁的操作就是扩缩容。增加 worker 节点的过程并不复杂,在 nodes 里追加一个节点配置,重新执行一遍 rke up 即可。rke 会对比当前状态,只对新节点执行初始化操作,旧节点不会发生重启。不过这里有个细节:如果 etcd 节点数量发生变化,rke 会发起一次 etcd 成员变更,不要同时删除太多 etcd 节点,否则存在数据安全风险。证书默认有效期大约是 1 年,如果部署时间较长,需要执行证书轮转:
rke cert rotate --config cluster.yml轮转过程中集群会短暂不可用,建议安排在维护窗口内执行。轮转前务必确认还能通过 SSH 登录节点,同时备份好原来的证书目录。如果私钥已经丢失,轮转后部分客户端连接可能会失败,需要重新下发 kubeconfig。
4. 常见故障排查与处置速查
4.1 SSH 握手失败看起来像网络问题
典型报错是:
Failed to dial: ssh: handshake failed: ssh: unable to authenticate排查路径:先手动执行 ssh user@node 验证免密登录,若需要输入密码,说明公钥没配置好;再确认 ssh_key_path 是否为默认路径。很多时候问题出在私钥权限过于开放,SSH 客户端会拒绝使用权限为 644 的私钥,将权限调整为 600 即可:
chmod 600 ~/.ssh/id_rsa还有一种情况是目标节点上 /root/.ssh/authorized_keys 的内容不是当前公钥,而是过期公钥。这种问题最隐蔽,因为它不影响手动 SSH 测试,只会在 rke 内部建立连接时暴露出来。我建议在部署前写一个小脚本,把每个节点的 authorized_keys 都刷成控制机的公钥,避免先后顺序导致遗漏。
4.2 镜像拉取失败与版本不匹配
如果在部署中段看到 ImagePullBackOff 或 manifest unknown,多半是 rke 版本和 Kubernetes 版本不匹配。优先去官方 release 页面看 rke 各版本对应的 Kubernetes 支持矩阵,锁定一个组合后再实施。离线场景下,你可以用下面这条命令列出全部镜像清单,配合私有镜像仓库做预导入:
rke images --config cluster.yml预导入的关键在于镜像的 tag 必须与 cluster.yml 解析出的结果完全一致,少一个 tag 都不行。我习惯把命令输出的镜像列表保存成文本,随后写一个循环脚本完成拉取、打 tag、推送的整套流程。导入完成后,优先在每台节点上用 docker images 抽查几个关键镜像,确认都存在后,再开始 rke up。
4.3 etcd 健康检查偶发失败
出现 etcd cluster is unhealthy 时,除了看节点资源,也要关注磁盘 IO 和系统参数。etcd 对磁盘延迟非常敏感,如果使用机械盘或网络存储,延迟过高必然导致 Raft 心跳超时。常规操作是先在 etcd 容器里执行 endpoint health,确认哪个节点异常,再观察是否由 CPU 抢占引起。
还有一个经常被忽略的因素是 etcd 数据目录空间不足。rke 会把 etcd 数据放在 /var/lib/etcd,如果这个分区没有独立挂载,很容易被其他日志占满。生产环境强烈建议给 etcd 数据单独划分磁盘,并配置好监控告警。磁盘空间告警和健康检查失败经常同时出现,提前做好容量规划可以省掉很多半夜的紧急处理。
4.4 DNS 解析异常与网络策略
集群跑起来后,如果 Pod 之间可以通过 IP 访问,但无法通过 Service 域名访问,需要依次检查 coredns 是否 Running、kube-dns 的 Service 是否存在、Pod 的 /etc/resolv.conf 是否指向正确 DNS。我有一个经验:尽量别把 cluster_dns_server 配置成非默认值,除非你非常清楚整个集群 DNS 转发链路。否则 kubelet 启动参数和 CoreDNS 参数对不上,解析问题会非常难排查。
另外一个常见点是 NetworkPolicy。calico 开启了网络策略后,默认是放行还是拒绝取决于你创建的策略对象。如果业务 Pod 互相访问时有时通有时不通,先检查是否有已生效的 NetworkPolicy。这类问题经常在部署完新的命名空间后出现,建议把网络策略和业务发布模板放在一起评审,避免上线后才暴露问题。
5. 补充几个运维细节作为收尾
5.1 把 cluster.yml 当作代码管理
关于 rke 集群部署,网上资料并不少,但很多只讲了能跑的阶段,没讲生产使用中会遇到的问题。就我个人经验来看,最重要的一件事是保持 cluster.yml 可版本化、可评审。我会把这份配置放到代码仓库里,每次修改都走一次 review,并且把 rke up 之后的输出留档。这样即使半年后集群出了问题,也能根据当时配置快速还原环境。
我还会在 cluster.yml 旁边放一个 README,记录每套集群的地址、节点角色、镜像版本、证书到期时间。这个习惯一开始觉得麻烦,但实际维护多个环境时,它比任何告警系统都可靠。人有记忆偏差,配置仓库不会。
5.2 节点环境一致性值得提前投入
还有一个小技巧是给节点机器设置好一致的主机名和时区。虽然 Kubernetes 对主机名没有硬性要求,但现在很多日志和监控系统都依赖主机名关联数据,部署前统一规范会减少后期运维成本。集群内所有机器的时钟建议通过 NTP 对齐,否则除了证书问题,kubelet 上报的资源数据也可能出现时间偏差。
最后,如果只是学习用途,我强烈建议先在虚拟机里做一次完整的三节点部署,把 rke up 每个阶段都看清楚,再上生产。这样你才会真正理解那些看似无关紧要的配置字段到底影响什么。纸上谈兵十次,不如亲手踩一次坑。等你把整个过程跑顺了,以后看到 cluster.yml 里的任何字段,脑子里都能立刻浮现出它对应的组件和日志位置。