部署Kubernetes这件事,说起来基础概念不难,什么Pod、Deployment、Service,看两遍文档心里都有数。可真要自己从零把一个集群拉起来,哪怕只是三台机器的测试环境,光是版本匹配、证书签发、组件顺序这些就能把人绕晕。我最早是自己写脚本按二进制方式部署,踩了一轮坑之后转到kubeasz,才真正体会到什么叫“把复杂留给工具,把时间留给业务”。这篇就是围绕kubeasz的完整部署指南,把我实际使用的全过程、选型逻辑和踩坑记录都整理出来,给正在折腾集群部署的朋友一个能直接落地的参考。
如果你已经学了一段时间Kubernetes基础,对Pod、调度、网络这些概念有基本了解,但还不清楚一个集群是怎么从裸机变成可用的控制面加工作节点,那这篇特别适合你。文章覆盖的内容从工具选型、节点规划,到离线资源准备、分步部署、组件安装,再到集群可用性检查,基本就是一套完整的操作手册。过程中我会反复强调“为什么这样做”,而不是只甩命令,方便你部署完还能自己排查问题。
1. kubeasz是什么,为什么我选它做K8s部署
1.1 手工部署的痛点,以及kubeasz的解题思路
我第一次部署Kubernetes集群用的是纯手工方式,过程很锻炼人,但也真的很折磨。证书要一个个签发,etcd、kube-apiserver、kube-controller-manager、kube-scheduler、kubelet、kube-proxy这些组件要按顺序装,每个组件的参数还互相依赖,比如apiserver的etcd地址、service网段、pod网段,稍有出入后面的网络插件就起不来。更麻烦的是升级和加节点,手动操作很容易漏掉配置。
kubeasz的做法不一样,它把整套部署过程拆成ansible的playbook,每个阶段对应一个编号:节点初始化、etcd集群、容器运行时、控制平面组件、工作节点组件、网络插件、集群附加组件。每个阶段都可以单独执行,也可以从头到尾一键跑完。这样做的最大好处是“可重入”,哪一步失败了,改完配置重新跑那一个playbook就行,不需要把整个集群推倒重来。对于我这种喜欢反复检查的人来说,这种设计比黑盒的一键脚本安心得多。
还有一点我比较满意:kubeasz默认支持离线部署。整个工具链可以把需要的二进制文件、容器镜像、依赖包全部下载到本地,然后在内网环境分发。这对生产环境来说几乎是刚需,毕竟很多集群所在网络环境访问外网并不方便。
1.2 几种部署方式的风格差异
在决定用kubeasz之前,我把主流的部署方案都过了一遍。下面是这几种方式在我实际体验中的区别:
| 部署方式 | 上手难度 | 适合场景 | 最大特点 | 我个人的顾虑 |
|---|---|---|---|---|
| kubeadm | 中等 | 单集群快速搭建、认证考试环境 | 官方维护,标准化程度高,一条init命令就能拉起控制面 | 高可用和离线场景要自己补不少工作 |
| kubeasz | 中等偏低 | 生产环境、离线环境、多节点扩展 | ansible编排,分层清晰,部署、升级、增删节点都方便 | 需要对ansible有基础了解 |
| Rancher/RKE | 低 | 图形化管理、已有虚机池 | 界面点一点就能建集群,后续还能管理多个集群 | 需要部署管理端,对资源有一定占用 |
| 纯手工二进制 | 高 | 学习原理、特殊定制 | 每个组件都自己控制,最清楚细节 | 费时费力,维护成本极高 |
现在如果让我说,学习阶段用kubeadm把原理跑通,真实环境里直接用kubeasz,是比较舒服的路径。kubeadm虽然也是官方推荐,但它默认拉镜像走的是在线仓库,离线时你需要先拉下来再打tag,多了一堆手工操作。kubeasz的ezdown脚本把这些都处理好了,下载完直接就是一个可用的离线资源目录。
1.3 三种部署拓扑,先想清楚再动手
kubeasz支持三种部署拓扑,这是开始之前必须想清楚的事,因为它决定了后面的配置文件和节点角色划分。
第一种是all-in-one单节点,一个节点同时承担etcd、控制平面和工作负载角色,适合本机学习验证。第二种是单主多节点,一台控制平面加上若干工作节点,这已经能模拟出生产环境的形态了,适合测试和开发环境。第三种是多主高可用,至少三台控制平面节点配合负载均衡器,etcd也是多节点模式,这才是真正面向生产的部署形态。
我个人的建议是,如果你只是学习,第一种就够了,不用追求复杂;但如果你是在公司搭内部服务平台,建议直接上第三种。原因不复杂:控制平面一旦单点,etcd挂了或者apiserver所在机器宕机,整个集群就不可用了。而高可用模式在部署阶段多花的时间,相比后续维护的省心程度,是完全值得的。
2. 部署前的准备,这三件事最容易被忽略
2.1 节点规划和操作系统基线参数
确定拓扑之后,第一步是规划节点。我这里给出一个参考,是我实际部署时用的资源标准:
- 控制平面节点:4核CPU、8GB内存起步,磁盘建议100GB以上SSD
- 工作节点:4核CPU、8GB内存起步,磁盘根据业务情况调整
- etcd节点:如果独立部署,磁盘IO是关键指标,建议使用SSD,内存8GB以上
操作系统方面,我用的是Ubuntu Server和CentOS系的发行版都跑过,kubeasz对这两类支持都算完善。但有几个基线参数是所有节点必须提前处理的,否则后面部署到一半会各种奇怪问题。
第一是关闭swap。Kubernetes默认要求kubelet节点关闭swap,虽然新版本支持了一定程度的swap使用,但生产环境我强烈建议直接关掉,避免性能不可控。第二是加载必要的内核模块,包括overlay、br_netfilter这些容器和网络转发需要的模块。第三是修改sysctl参数,让iptables对桥接流量的处理生效,典型的就是net.bridge.bridge-nf-call-iptables设为1。
提示:这些初始化操作,kubeasz的01.prepare.yml playbook会自动处理一部分。但如果你在云平台上创建的节点自带swap分区或者安全组限制了某些端口,还是建议提前排查一遍,不要完全依赖自动脚本。
2.2 版本选型,别盲目追新
Kubernetes的版本迭代速度很快,几乎每三个月一个大版本。我见过不少朋友一上来就装最新版,结果发现某个CNI插件还不兼容,或者容器运行时版本对不上,然后花大量时间在issue列表里翻解决方案。
版本选型我的经验是“稳定优先、看生态”。所谓稳定优先,是选择已经发布了一段时间、经过社区验证的版本,而不是刚出的版本。看生态的意思是,你要用的网络插件、ingress控制器、监控组件,是否都已经适配了你选的版本。通常来说,选择当前最新稳定版本的前一个或者再前一个版本,是比较稳妥的做法。
kubeasz下载资源包的时候,它会把一组经过测试的组件版本固定下来,包括Kubernetes、etcd、容器运行时、网络插件等。这个组合是项目社区维护时验证过的,可信度比较高。如果你没有特别的理由,直接采用默认的组合就好,不要自己随意替换组件版本,否则很容易出现兼容性问题。
完整离线包下载的命令大概是这样的:
# 下载kubeasz代码和所有离线资源,包括容器镜像、二进制安装包 ./ezdown -D-D参数是完整下载,如果你网络条件有限,可以先只下载最小集合,后面缺什么再补什么。这一步会花一些时间,几百MB到几个GB都有可能,取决于版本和镜像数量。下载完成后,资源会存放在指定的目录里,后续部署脚本会直接从这里读取,不再需要外网访问。
2.3 网络方案定夺,这个选择影响后续运维体验
Kubernetes集群的网络方案是个绕不开的话题。CNI插件决定了Pod之间、Pod与Service之间怎么通信,也决定了你后面能不能配置网络策略。我在实际使用中接触最多的三个方案是flannel、calico和cilium。
flannel的特点是简单,部署快,性能在中小规模下也够用,它用VXLAN隧道技术把不同节点间的Pod网络打通,理解起来很直观。calico则走BGP路由模式,性能更好,支持NetworkPolicy,适合对网络性能和策略有要求的生产环境。cilium是基于eBPF的新一代方案,功能最强,但它对内核版本有要求,部署和排查的门槛也更高。
在kubeasz里,你在集群配置文件里指定用的CNI插件,部署脚本会自动装好。我个人的建议是:学习环境用flannel,生产环境用calico。cilium虽然吸引人,但如果你对eBPF排查手段不熟,遇到问题会比较吃力。网络方案一旦选定,后续要切换是很痛苦的事情,相当于重新部署一遍网络层面,所以开始之前务必想清楚。
3. 核心部署环节:从下载工具到集群正常
3.1 下载kubeasz和初始化集群配置
资源包到位之后,正式部署开始。先要把kubeasz项目代码放到指定目录,一般推荐放在/etc/kubeasz下,方便统一管理。下载完成后,里面会有一系列脚本和目录,其中ezctl命令是用来创建和管理集群的入口。
创建集群实例的命令是:
# 创建名为test的集群配置文件 ./ezctl new test执行完之后,你会得到一个集群目录,里面包含hosts文件和cluster.yml等配置文件。hosts文件定义了节点角色分组,比如etcd节点有哪些、master节点有哪些、node节点有哪些。cluster.yml则定义了集群的详细参数,包括版本号、网段、运行时、网络插件等。
这两个文件是后续一切部署的基础,务必仔细核对。我第一次部署时忽略了hosts文件里一个节点的IP,结果ansible执行到那个节点的时候一直连不上,排查了半天才发现是IP手误。多节点环境里,每个节点的IP、SSH端口、登录用户都要准确无误。
3.2 按阶段推进:从节点初始化到集群附加组件
配置改好之后,正式进入分步部署。kubeasz把整个过程排成了编号playbook,建议按顺序执行。下面是我每次部署时的必经流程:
# 进入项目目录 cd /etc/kubeasz # 1. 初始化所有节点,传公共参数、关闭swap、加载内核模块 ansible-playbook 01.prepare.yml # 2. 部署etcd集群,注意是只在etcd节点上执行 ansible-playbook 02.etcd.yml # 3. 安装容器运行时,根据配置可能是containerd ansible-playbook 03.containerd.yml # 4. 部署控制平面组件,包括apiserver、controller-manager、scheduler ansible-playbook 04.kube-master.yml # 5. 初始化工作节点,安装kubelet和kube-proxy ansible-playbook 05.kube-node.yml # 6. 安装网络插件 ansible-playbook 06.network.yml # 7. 安装集群附加组件,DNS、dashboard等 ansible-playbook 07.cluster-addon.yml每执行完一个playbook,我都建议看一眼输出里的失败任务。ansible的输出虽然啰嗦,但失败信息很直接,通常能看到是哪台机器、哪个任务、什么报错。不要求完全看懂每一行,但至少要注意play recap里有没有failed的数字不为0。
这里面有一个容易踩的坑:02.etcd.yml执行的时候会比较久,因为etcd集群要完成选主和数据同步。如果你的节点硬件配置一般,或者用的是机械硬盘,这里会尤其慢。不要因为慢就中断,等它跑完。还有,如果这步之前网络环境有变动,可能导致etcd节点间通信失败,那就要检查hosts里的节点IP是否还能互通。
3.3 部署过程中最关键的验证时机
很多朋友习惯把七个playbook全部跑完,然后才开始看集群状态。这样不是不行,但如果中途某一步出了问题,后面的步骤全是在一个错误的状态上叠加,排查起来就麻烦了。
我更推荐在每个大阶段结束后做一次小验证。核心的验证时机有三个:
第一个时机是在03.containerd.yml完成之后,检查容器运行时是否在每台机器上都正常运行。你可以登录任意节点,执行crictl info或者查看containerd服务状态,确认没有异常。这个阶段如果运行时没起来,后面的kubelet一定会出问题。
第二个时机是在04.kube-master.yml完成后,立刻在master节点上配置kubectl访问集群,查看控制面组件状态。只需要确认apiserver起来了,etcd集群健康,调度器和控制器管理器都在运行,就可以放心继续。
第三个时机是整个部署流程结束后,做一次完整的状态检查,我会把它单独放在下一节详细说。
如果发现网络插件之后CoreDNS一直pending,优先怀疑是CNI没有部署成功。这时候可以通过kubectl get pods -n kube-system查看pod事件,多半会看到镜像拉取失败或者CNI配置找不到之类的错误。这些问题在这几个验证时机里就能暴露出来,而不是等业务Pod跑不起来才回头查。
3.4 配置kubectl,让管理命令更顺手
集群部署完成后,kubectl命令能不能直接用,取决于你的kubeconfig配置。kubeasz部署时会在master节点生成admin.conf之类的配置文件,你需要让kubectl知道去哪里找它。
一种方式是把配置文件拷贝到默认位置:
mkdir -p ~/.kube sudo cp /etc/kubernetes/admin.conf ~/.kube/config sudo chown $(id -u):$(id -g) ~/.kube/config也可以使用环境变量方式:
export KUBECONFIG=/etc/kubernetes/admin.conf如果集群部署在多台机器上,本地工作机也需要管理集群的话,就把admin.conf下载到本地,同样配置KUBECONFIG或者放到~/.kube/config里。配置文件里面包含了证书信息,注意按保密文件对待,不要随手放到代码仓库里。
4. 添加组件和集群自检,决定“能用”还是“好用”
4.1 附加组件的取舍
Kubernetes集群跑起来之后,离“好用”还有一段距离,这就是附加组件要解决的问题。kubeasz的07.cluster-addon.yml默认会装一部分基础组件,其中最重要的是CoreDNS,它负责集群内部的DNS解析,没有了它Service名字解析不了,很多工作负载起不来。
除了默认组件,你在生产环境几乎还要考虑这几个东西:metrics-server负责采集节点和Pod的资源指标,HPA和kubectl top依赖它;ingress控制器负责把外部流量按域名路由到集群内部Service;仪表盘可以给团队提供一个图形化界面,不过在公网环境要注意访问控制。
kubeasz对附加组件的管理思路是“可插拔”,你在集群配置文件里可以开启或关闭某些组件。我的建议是基础版先装CoreDNS加metrics-server,后面按需再补Ingress和存储相关的组件。一次装太多,组件之间互相干扰的排查成本会变高。
比如Ingress控制器,我遇到过一个情况是同时装了nginx-ingress和traefik,两个控制器都把自己部署成LoadBalancer类型的Service,端口冲突导致流量异常。附加组件这种东西,需要什么装什么,不需要就不要塞进去。
4.2 集群自检:几分钟排查清楚整体状态
部署完成之后,我每次都会跑一遍下面的检查流程,可以快速定位大部分问题:
# 1. 查看节点状态,所有节点都应该是Ready kubectl get nodes -o wide # 2. 查看系统组件,所有pod都应该是Running或者Completed kubectl get pods -n kube-system -o wide # 3. 检查DNS是否正常,临时创建一个pod执行解析 kubectl run test-dns --image=busybox --rm -it --restart=Never -- nslookup kubernetes.default.svc.cluster.local # 4. 查看事件,如果有资源异常会有事件记录 kubectl get events --all-namespaces --sort-by='.lastTimestamp'这套检查逻辑是从下往上看的:先看节点(物理层),再看系统组件(平台层),然后看DNS和事件(服务层)。哪一层出问题,都能比较快定位。
节点层的典型问题是NotReady,系统组件层的典型问题是某个控制器一直CrashLoopBackOff,服务层的典型问题是DNS解析超时。如果你之前按我的建议做了分阶段验证,这里基本不会出现太大意外,这套自检更多是给自己一个确认。
另外还有一个容易被忽略的点:看节点状态时要注意Kubelet版本和apiserver版本是否一致或兼容。版本差异过大的时候,节点虽然显示Ready,但某些资源操作会报错,这种问题在日志里表现不明显,只能在状态检查时发现。
4.3 高可用集群的故障演练
如果你搭的是多主高可用集群,我强烈建议部署完成之后做一次故障演练。方法是挑一台控制平面节点,直接把它关机或者重启服务,观察集群是否仍然可以正常访问和调度。
具体操作可以这样:先在一台正常的master节点上执行kubectl get nodes打开持续观察,然后到另一台master节点上执行sudo systemctl stop kube-apiserver,模拟控制面故障。正常的现象是客户端访问仍然可用,apiserver的请求会被负载均衡转发到健康的节点上,新的Pod调度不受影响。
做完故障恢复之后,把停止的节点重新启动,观察它是否自动重新加入集群并同步状态。如果重新加入之后,节点状态一直NotReady,或者etcd成员列表里有异常,说明你的高可用方案里有隐藏问题。最常见的原因是负载均衡的健康检查没有正确配置,把故障节点依然当作健康节点在转发流量。
这类演练值得养成习惯,每隔一段时间就做一次,避免集群“看起来高可用,实际主节点挂了就瘫痪”。真实的生产故障往往不会挑你准备好的时候发生。
5. 真实环境里最容易翻车的几个坑
5.1 节点NotReady的完整排查链路
不管你用什么方式部署集群,节点NotReady都是最常见的故障。很多人一看到NotReady就百度找答案,改完一个参数再试一下,运气好解决了,运气不好越改越乱。我分享一下我自己的排查链路,下次遇到可以照着走。
第一步,先看节点状态详情。kubectl describe node节点名,重点看Conditions部分,里面会写清楚KubeletReady是False还是Unknown,还会带上原因描述。如果是类似“PLEG is not healthy”这种,多半是容器运行时卡住了。
第二步,直接拉kubelet日志。journalctl -u kubelet -f在节点上实时看日志,比猜更高效。日志里如果出现“failed to get container info”或者“connection refused”,那问题基本在和容器运行时的通信上。
第三步,检查容器运行时本身。比如使用containerd的话,执行systemctl status containerd,确认服务是active状态。如果是运行时没起来,kubelet自然报错。
第四步,确认cgroup驱动一致。这是最容易忽略的坑。kubelet的cgroup驱动和容器运行时的cgroup驱动如果不一致,节点会反复启动失败。具体表现是kubelet日志里一直报类似“failed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs”的错误。解决办法是修改kubelet或者容器运行时的配置,让两边一致,然后重启相关服务。
第五步,确认网络插件状态。有的节点NotReady不是kubelet问题,而是CNI插件没有安装成功。你可以看kube-system命名空间里的网络插件Pod是否Running,如果是调度到了某个问题节点上,那就需要看那个Pod的事件。
这套链路走完,百分之九十以上的NotReady问题都能找到根因。我见过不少人卡在“重启了还是NotReady”,其实是没看日志就盲目操作。
5.2 证书和kubeconfig的“过期焦虑”
Kubernetes的证书问题是运维里绕不开的话题。部署完成后的默认证书有效期各不相同,我见过有的组件证书只有一年。过了这个期限,你的kubectl命令可能突然就报证书过期或者无法验证身份的错误。
证书过期最典型的场景是:某一天你执行kubectl get nodes,发现报错说证书已经过期,但集群里的Pod却还在正常运行。这是因为控制平面的组件之间通信已经建立的连接可能还在维持,但新的连接会因为证书失效而失败。
遇到这种情况,不要硬着头皮去改每个组件的证书,那样太容易出错。kubeasz提供了证书更新的playbook,实际使用中可以通过重新执行证书生成和分发相关任务,一键更新所有证书。更新完之后重启相关组件服务,一般就能恢复。
我个人的经验是,集群搭好之后就在日历上设置一个证书到期提醒。当然现在很多方案支持自动轮换证书,如果你有时间折腾,可以研究一下;如果不想折腾,就老老实实定期检查,把证书有效期当成和磁盘空间一样重要的监控项。
5.3 离线环境镜像导入的坑
在线环境部署和离线环境部署的差距,比你想象的大。在线时你只需要执行脚本,镜像会自动从仓库拉取;离线时你要确保镜像已经导入到目标机器的容器运行时里,少一个镜像,对应的Pod就会一直ImagePullBackOff。
kubeasz在离线部署时,脚本会把解压后的镜像导入到每个节点。但这里有一个实际执行中容易踩的坑:如果你在下载离线包之后,自己手动改了某些组件的版本或者镜像tag,那么离线包里大概率没有你想要的镜像。部署脚本执行到那一步就会卡住,提示镜像拉取失败。
所以我在离线环境里的原则是:完全信任离线包自带的版本组合。除非有明确的安全原因,不要自己更换镜像版本。如果确实要升级某个组件,那就整个离线包重新下载一次,保证镜像和二进制是匹配的。
另外还有一个容易被忽略的环节:节点的容器运行时配置里,如果默认写了镜像仓库地址,离线环境下要确保这个地址可访问,或者直接不配置外部仓库。我曾经遇到过一个问题,节点上明明已经导入镜像了,但容器运行时会优先尝试从远端仓库拉取,导致一直等待超时后才启动本地镜像。这个可以通过检查容器运行时的配置文件来确认。
5.4 kubectl命令失效的常见原因
还有一个我经常被问到的问题:部署完第二天,kubectl突然“失效了”。这里说的失效,很多时候并不是Kubernetes本身出了问题,而是你的客户端配置丢了或者环境变量变了。
比较常见的情况包括:~/.kube/config文件被误删或覆盖、KUBECONFIG环境变量指向了不存在的文件、当前用户切换了导致权限不足。遇到这些情况,先不要怀疑集群挂了,用admin.conf重新配置一下kubectl就好。
如果你跟我一样在多集群之间切换,建议给每个集群起一个清晰的context名字,并用工具管理kubeconfig的切换。这能省掉大量“这个命令打到哪个集群去了”的尴尬时刻。毕竟一条kubectl apply跑错集群,后果可大可小。
6. 集群落地后的第一件维护事
集群部署完成、自检通过之后,还有一件维护上的事我建议尽早做,就是把ansible的部署清单保存好,并且把关键配置纳入版本管理。你可能会觉得部署完就算完成任务了,但后续加节点、升级组件、修改集群参数,大概率都要用到当初的这套配置。
kubeasz的整个部署目录里,集群的hosts文件和cluster.yml就是这套集群的“出生档案”。我自己的习惯是,在把项目初始化到git仓库时,把kubeasz生成的集群配置也单独建一个仓库保存起来,并且写上备注,说明当时选了哪些版本、改了哪些参数、网络方案是什么。等半年后你再看这些文件,会庆幸当初留了这一手。
此外,日常维护时如果改了集群配置,不要直接改节点上的散落文件,尽量回到ansible playbook去改,然后重新执行对应阶段。这样整个变更过程是可追溯的,不会出现“当时改了哪台机器自己也忘了”的情况。把部署工具本身当作集群的一部分长期维护,是我这几年从kubeasz上得到的最实在的经验。