
1. 先把场景说清楚为什么需要这份流程清单搞过k8s的人都清楚集群搭建这件事十次有九次不是挂在技术上而是挂在自己都没想到的细节上。我记得之前带一个项目时部署环境是ubuntu 20.04硬件还算充裕三台控制节点心想这配置足够了。结果一折腾就花了一整周最后排查出来的原因居然是证书签发时间不对、容器运行时版本不匹配。这种问题你查文档基本查不出来只能靠现场一点点翻日志。也就是从那一次起我开始整理自己的k8s流程创建清单从环境准备、集群初始化、控制面高可用、存储与监控配套一直覆盖到日常排障。现在每次新接项目我都会先把这份清单过一遍能省掉很多不必要的折腾。这份清单主要解决三类问题。第一明确每个阶段该交付什么东西、怎么验证不依赖“感觉好像装好了”这种错觉。第二沉淀版本、参数、网络规划这些容易踩坑的细节让同一个坑不要被连续踩。第三为后续扩容、升级、证书轮换、监控接入这些动作预留好接口而不是等出了问题才临时补。适合谁参考呢如果你是刚入门k8s准备在虚拟机或物理机上搭建集群的运维工程师可以把它当作一份checklist来用。如果你已经有一定基础想梳理一套标准化交付流程里面很多细节也可以直接借鉴。即使你平时主要用kubectl管理应用了解一下集群是怎么被创建和校验的对定位问题也有帮助。2. 环境与前置条件把地基打牢再动手2.1 硬件、操作系统与内核模块k8s对硬件的要求并不苛刻但也不能太随意。生产级配置我一般按控制面节点2核4GB起步来评估工作节点根据业务负载决定。如果你要跑GPU工作负载还得单独预留GPU资源这个放到后面细说。操作系统层面我推荐Ubuntu LTS版本比如20.04或22.04。原因有两个一是内核版本适中对overlay、br_netfilter这些容器相关模块支持友好二是社区案例多遇到问题能快速找到答案。CentOS虽然很多老项目在用但大版本已经走向维护尾声新项目我不建议再往里搭。如果公司内部已经有统一的操作系统基线那就按基线走但必须在动手之前确认内核模块、iptables、IPVS这些底层依赖是否就绪。这里有一个很多人忽略的经验不要在系统刚装完、还没处理内核模块的时候就急着跑kubeadm init。先把overlay和br_netfilter的自动加载确认掉不然很多看起来像flannel或calico引发的报错底层其实是模块没加载。检查方式很简单cat /proc/modules | grep br_netfilter cat /etc/modules-load.d/k8s.conf如果输出为空就手动创建modules-load.d配置把overlay、br_netfilter写进去然后重启或modprobe加载。这一步放进流程清单的最前面能避免后面大量的网络类报错。2.2 网络规划与网段划分网络方面我强烈建议先规划再动手。k8s集群里三个网段特别容易混淆宿主机网络、Pod网络、Service网络。宿主机网络是节点自身的IP地址由现有网络架构决定可能是公司内网或云VPC。Pod网络分配给每个Pod主要由CNI插件管理Calico场景下一般配置成192.168.0.0/16这类网段。Service网络在kube-apiserver启动参数中用--service-cluster-ip-range定义常见配置是10.96.0.0/12。三个网段绝对不能重叠否则路由会乱排查起来非常痛苦。我踩过一次坑机房内网段正好是10.96.0.0/16我却把Service网络也配成了10.96.0.0/12结果集群怎么装都不通最后发现是网段冲突一个下午就这么搭进去了。从那以后我在流程清单里把网段规划设成硬性步骤必须填表确认签字那种。2.3 版本选型与容器运行时版本选择上我不追最新只追稳定。新版本往往伴随新的API特性和兼容性变化对大多数项目来说选择一个已发布三个月以上的版本更合理。当前来看1.28、1.29、1.30这几个系列都算主流具体要根据你使用的部署工具和厂商支持范围来定。容器运行时方面最常见的选择是containerd和Docker。这里有一个老生常谈的话题k8s和docker的区别到底是什么我的理解是Docker是一套容器管理工具负责构建镜像、运行容器、管理镜像生命周期k8s是一个容器编排平台负责调度、伸缩、服务发现、故障恢复。两者本身不在同一层。换句话说k8s完全可以不依赖Docker借助containerd就能完成运行时层面的工作。在流程清单里我统一推荐containerd。原因很直接kubelet通过CRI对接containerd配置链短排查看日志更清晰。自Kubernetes 1.24之后dockershim已经被移除再强行用Docker作为kubelet运行时需要额外适配层纯属给自己添堵。如果团队里还有人问docker和k8s是什么关系可以顺手解释一句docker管单机上的容器k8s管整个集群里的容器前者是工具后者是平台。3. 集群创建的核心阶段控制面立起来才算开始3.1 初始化准备与镜像处理在正式执行kubeadm init之前有两件事必须先做确认kubeadm、kubelet、kubectl三个组件版本一致提前处理镜像拉取问题。版本不一致是我见过最多的低级翻车点。kubeadm、kubelet、kubectl必须在同一个minor版本内否则初始化时kubelet和apiserver之间的通信会产生版本不匹配报错。这类报错往往藏得很深表面看起来是token失效或TLS握手失败实际查版本才发现对不上。镜像处理上如果你在默认网络环境下直接用registry.k8s.io大概率会卡住。更稳妥的方案是先把镜像清单拉出来换到可用镜像源再导入。用kubeadm config images list能看到所需镜像通过--image-repository参数指定替代仓库。这一步我在清单里标记为“必做”否则初始化卡在拉镜像这一个环节就能耗掉大半天。3.2 三台master的高可用方案选型所谓高可用核心是避免控制面单点故障。最常见的方案有两种kubeadm自带的stacked etcd模式以及外部etcd集群模式。对大多数中小型集群stacked etcd就够用。它把etcd和控制面组件放在同一批master节点上部署简单维护成本低推荐优先考虑。三台master如何保证高可用简单说需要两个层面的配合。控制面组件层面kube-apiserver是无状态的可以多副本运行通过负载均衡对外提供服务kube-controller-manager和kube-scheduler通过leader election机制选主同一时刻只有一个实例真正工作。流量入口层面需要一个虚拟IP或负载均衡器把请求转发到多个apiserver。常见的组合是keepalived加haproxy也可以用云厂商SLB或者用kubekey这类工具做自动化部署。我之前在ubuntu上做高可用k8s部署用的就是三台master加keepalived加haproxy的组合。haproxy负责反向代理6443端口的apiserver流量keepalived负责提供虚拟IP漂移。只要任意一台master挂掉流量会自动切到健康节点对上层用户无感知。有一点必须提醒kubeadm join其他master节点时一定要用负载均衡地址而不是某台master的单独IP。很多人初始化时把apiserver-endpoint写成了第一台master的IP后面第三台master加入时它只连那一台。一旦第一台挂了整个控制面入口就断了。这个细节我在清单里会用高亮标出来。如果你不想手动拼接这些组件kubekey是另一个不错的选择。它能比较顺滑地编排多master配置对国内环境也更友好。但无论用什么工具高可用的本质逻辑是一样的apiserver必须多副本加统一入口etcd数据必须冗余且保持一致。3.3 工作节点加入与集群自检控制面起来后工作节点加入反而简单。核心就是拿到token和证书哈希执行kubeadm join。但有两个点容易被忽略token默认有效期是24小时如果隔几天再扩容需要重新生成token。加入指令里的--discovery-token-ca-cert-hash要用kubeadm token create --print-join-command打印出来不要复制网上的示例。节点加入后自检我从三个维度来看kubectl get nodes kubectl get pods -n kube-system -o wide kubectl get cs第一看节点状态是否Ready第二看coredns、flannel或calico等核心Pod是否Running第三看controller-manager和scheduler是否healthy。这三个命令基本覆盖80%的初始化问题。有一点要注意新版k8s里kubectl get cs可能显示为legacy或不准确因为它读的是ComponentStatus接口实际意义有限。我更建议把多个维度结合起来判断重点还是看核心Pod的状态和节点条件。3.4 GPU资源接入要点GPU接入是AI项目里最常见的硬需求。声明GPU资源的正确方式是在容器资源限制里加nvidia.com/gpu字段resources: limits: nvidia.com/gpu: 1但加上这个并不代表就能直接用整个链路涉及三个环节节点上安装NVIDIA驱动和CUDA保证nvidia-smi能正常输出。部署NVIDIA Container Toolkit让容器运行时能感知GPU设备。在集群中启用NVIDIA Device Plugin把GPU资源暴露给kubelet。如果集群用containerd需要修改/etc/containerd/config.toml在plugins配置里加入nvidia runtime配置然后重启containerd。我第一次做k8s与GPU安装时漏了这一步GPU资源始终显示不出来排查一圈才发现是containerd没有感知到nvidia runtime。从此这份流程清单里“containerd GPU runtime配置”就成了必检项。4. 存储、监控与控制器让应用跑得稳4.1 持久化存储方案生产环境中大多数应用都需要持久化数据存储方案不能等到部署应用时再临时抱佛脚。本地卷适合单机验证但生产环境我更推荐网络存储比如NFS、Ceph或云厂商的存储卷。k8s通过StorageClass管理动态供应应用只需要声明PVC系统会自动创建PV并绑定省去手动创建PV的麻烦。如果你的硬件条件一般NFS是成本最低的入门方案。部署一个NFS服务端然后装nfs-subdir-external-provisioner这个StorageClass Provider之后创建PVC时就会自动在NFS上分配目录。这个方案对中小型项目够用唯一要注意的是NFS服务端的性能和稳定性。NFS自身一旦挂掉所有依赖它的Pod都可能异常所以至少要做服务端监控。4.2 Prometheus监控部署监控这一环我见过太多项目装完集群就不管了全靠肉眼发现问题。实际上部署prometheus监控k8s并不复杂kube-state-metrics暴露集群对象的状态指标node-exporter采集节点指标Prometheus负责抓取和存储Grafana负责展示一套基础监控栈就起来了。如果你不想每个组件单独配置kube-prometheus-stack这个Helm Chart把Prometheus、Alertmanager、Grafana、node-exporter都打包好了一条命令就能部署。部署时核心要改的是serviceMonitor的selector确保命名空间标签和实际业务命名空间对得上否则指标抓不到Grafana面板上一片空白。还有一点经常被忽略监控账号的RBAC权限。默认Helm Chart一般会带上ClusterRole但如果你自定义了配置文件务必确认监控账号有足够的get/list/watch权限。我最开始部署prometheus监控k8s时只顾着serviceMonitor忘了鉴权这一层结果大部分指标能拉到资源相关指标却一直403。这个点清单里必须写。4.3 控制器类型的选型逻辑k8s的控制器很多Deployment、StatefulSet、DaemonSet、Job、CronJob等。不少人一开始只认识Deployment所有工作负载都往里塞但选错控制器会给后续维护埋坑。Deployment适合无状态应用比如Web服务、API服务可以随意替换Pod支持滚动更新和回滚。StatefulSet适合有状态应用比如数据库、缓存中间件每个Pod有稳定的网络标识和存储声明。DaemonSet适合每个节点都要跑一个实例的场景比如日志采集、监控采集Agent、CNI组件本身。举个小例子Deployment这个术语在中文环境里通常就直接叫“部署”或“工作负载”。它做的事情很朴素声明你要的Pod副本数并自动维持这个数量。如果Pod挂了它再拉起一个如果流量大了它配合HPA扩容。而StatefulSet和Deployment的核心差异在于身份和存储的稳定性所以数据库这类应用天生应该用StatefulSet而不是Deployment。5. 常用命令、证书生命周期与高并发落地5.1 高频命令速查命令这块我平时用得最多的整理如下用途命令查看节点状态kubectl get nodes查看所有Podkubectl get pods -A查看Pod详细日志kubectl logs -f -n进入Pod调试kubectl exec -it -n -- /bin/sh查看资源明细kubectl describe pod -n查看集群事件kubectl get events --sort-by.lastTimestamp临时启动调试Podkubectl run -it --rm --restartNever test --imagebusybox -- /bin/sh端口转发调试kubectl port-forward svc/my-service 8080:80很多问题排查的第一步我就直接看events。这个命令能把集群里的异常事件一次性列出来比如镜像拉取失败、探针失败、磁盘压力等比对着单个Pod反复describe高效得多。我见过太多人一遇到问题就describe Pod却忘了先看events白白浪费不少时间。还有一个我常用的组合是kubectl get pod -n某个命名空间 -o wide加kubectl describe node节点名。节点级别的问题比如内存不足、磁盘压力用describe node能看到Conditions里的压力状态再配合kubectl top nodes看实时占用基本能快速定位。5.2 证书过期与自动续签证书过期是k8s运维中的经典定时炸弹。默认情况下集群各组件证书有效期是一年。一年内你可能没什么感觉一旦过期kubelet与apiserver之间的TLS握手直接失败整个集群像被按了暂停键。kubeadm部署的集群证书续签命令是kubeadm certs renew。流程大致是备份旧证书目录比如/etc/kubernetes。执行kubeadm certs renew all。重启控制面组件容器或更新kubeconfig。验证apiserver、controller-manager、scheduler及所有节点的证书是否更新。但这里有个细节很多人忽略kubeadm管理的证书会续签但如果你手动调整过组件启动参数或者证书结构有特殊情况自动续签可能不完整。更省心的方案是上证书自动续签机制。我见过团队用cert-manager结合kubeadm也有人写一个cronjob定时执行检查与续签。无论哪种方案核心原则都是一致的证书续签不是一次性动作必须纳入日常巡检。我在流程清单里会写一行每个月跑一次kubeadm certs check-expiration看看证书状态这比任何警报都直接。5.3 高并发组件优化k8s本身的设计能处理高并发但“能处理高并发”不等于“默认配置就能扛住高并发”。比较典型的优化点在三个位置kube-apiserver是集群所有请求的入口高并发下需要调大内存和限流参数比如--max-requests-inflight和--max-mutating-requests-inflight。kube-scheduler在Pod频繁创建销毁时会成为瓶颈需要根据Pod数量评估调度性能。HPA是k8s处理高并发的核心组件之一基于CPU、内存或自定义指标自动扩缩容配合ingress-nginx或云负载均衡把流量分发到多个副本上。高并发场景里有几个特别容易踩的坑。HPA的指标采集存在延迟业务流量瞬间暴增时Pod扩容会有滞后。如果想快速扩容得提前配置HPA的behavior参数设置较大的scaleUp速率甚至加一层基于自定义指标的触发机制才能应对突刺流量。apiserver限流参数也是重点。如果集群里有大量定时任务或批处理脚本在高峰期频繁创建、删除Pod默认限流很容易触发最终表现为kubectl命令大量超时。这类问题不会直接在业务日志里体现而是在集群元数据层面积累。5.4 管理工具选型kubectl是基础但长时间在终端里敲命令效率还是不够高。这里分享几个我用过的工具。k9s终端UI能直接看Pod、Service、日志、事件不用每次敲一长串命令适合日常巡检。Lens桌面客户端图形化操作多集群管理方便。Kuboard有中文界面适合让开发人员也能看到集群状态。kubekey既能部署也支持管理对国内环境比较友好。工具不是越多越好选一个用得顺手的就行。我个人是k9s加kubectl并行k9s负责快速看状态kubectl负责精细操作。多集群场景下注意context切换可以在~/.kube/config里配置多个集群context然后kubectl config use-context切换。6. 常见问题与排障实录6.1 节点NotReady的最常见原因节点变成NotReady排第一的原因基本是CNI网络插件没有正常工作。比如flannel或calico的Pod没有Running状态节点之间的网络隧道没建立kubelet上报时就会带NetworkPluginNotReady。遇到NotReady我先看这组命令kubectl get pods -n kube-system -o wide kubectl describe node node先确认kube-system里有没有大量CrashLoopBackOff的Pod再看node的Conditions输出。Ready状态里如果reason是KubeletNotReady通常message里会写具体原因指向容器运行时或网络插件。再往下排查可能是磁盘满了也可能是运行时挂了。这里有个很现实的点小容量磁盘集群长期不清理/var/lib/containerd会涨得很快kubelet因为磁盘压力把节点标记为NotReady。所以日常巡检里磁盘空间监控和日志清理要放在前面。我在流程清单里专门加了一步给containerd配置镜像垃圾回收同时把系统目录独立挂盘避免根分区被塞满。6.2 Pod卡在Pending或CrashLoopBackOffPod一直Pending最常见的原因是资源不足其次是调度约束不满足。最快的方法还是看describe Pod的Events部分。如果Events提示0/3 nodes are available说明节点资源不够要么扩容要么降低Pod的requests。如果提示node affinity或taint相关消息就得确认节点标签和污点配置是否符合预期。Pod处于CrashLoopBackOff时思路也直接先看日志再看退出码。退出码137代表被OOM杀掉退出码1通常是应用自己崩溃退出码127是命令找不到。很多情况下问题出在镜像启动命令与容器配置不一致比如镜像里没有entrypoint你在yaml里指定了不存在的参数。这类问题靠单纯重启Pod没用必须改配置源头。6.3 DNS解析与网络策略问题集群内服务互相访问最常遇到的是CoreDNS相关问题。现象就是Pod之间通过Service名解析不通或跨namespace访问失败。先检查CoreDNS Pod是否在跑再看CoreDNS配置里的forward是否正常。如果只影响某个命名空间多半是Service的metadata.name和实际服务名不一致。还有一点容易被忽略Pod里的/etc/resolv.conf是否正确注入了kube-dns的ClusterIP。如果没用上检查Pod的dnsPolicy配置。网络策略方面默认是“允许所有”可一旦引入NetworkPolicy并且配置不当服务间通信会被静默丢弃。排查时先用kubectl get networkpolicy -A看有哪些策略必要时临时调整策略做对比测试。这类问题通常不会在日志里直接报错需要结合抓包或策略配置来定位但大多数场景下确认策略是否一致已经能解决问题。6.4 真实案例一场高并发下的apiserver抖动最后分享一个真实案例。某次业务反馈应用访问数据库偶发性超时持续时间不长但频率很高。一开始怀疑数据库压力后来看数据库集群一切正常SLA指标也没问题。后来我们把视角转到k8s层面发现apiserver在高峰期出现慢请求导致大量Pod在创建或更新时延迟明显。进一步排查时定位到某个团队写了大量短时任务一直在用kubectl创建和删除Podapiserver默认限流参数被顶到了上限。处理办法其实不复杂调整apiserver的--max-requests-inflight和--max-mutating-requests-inflight同时给不同用户配置ResourceQuota和LimitRange限制单个命名空间的Pod数量上限。这个案例给我的启发是k8s的问题往往是系统性的单点排查可能很长时间找不到源头把视角拉高到整个集群的请求链路才能逐步定位。也正因如此流程清单里不能只写“怎么建集群”还得写清“建完后怎么长线观察”。7. 写在最后我的清单迭代体会很多人在做完一次集群搭建后就把文档封存了等到半年后再扩容或升级发现一切都变了又要从零摸索。我的做法是每次项目结束后都把流程清单更新一遍记录踩过的坑、用过的参数、耗时情况以及验证通过的环境。这么做最直接的好处是同一个坑我不会踩第二次。比如本文写到的网段冲突、证书过期、containerd的GPU runtime配置几乎都是团队新人最容易碰到的。而一份真正好用的k8s流程创建清单从来不是从网上复制下来的而是从自己的实践里长出来的。我的建议是先按这套框架搭一份最小可用的清单跑通一个简单项目把环境差异补进去再在后续项目中逐步打磨。它会慢慢变成团队内部真正有价值的运维沉淀比任何现成模板都靠谱。最后再分享一个小技巧把这份清单做成集群交付时的验收依据不管是内部同事还是外部实施方都按清单逐项签字确认。你会发现很多潜在风险在交付阶段就被拦下来了而不是等业务上线后变成故障再补救。