ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

生产级Kubernetes集群搭建实战:从Docker到容器编排的完整复盘

生产级Kubernetes集群搭建实战:从Docker到容器编排的完整复盘 如果你已经用 Docker 把公司的应用都打成了镜像会发现这只是万里长征第一步。镜像能构建、能启动可一旦机器数量从一台变成十台问题立刻来了谁决定容器跑在哪台机器某台机器突然宕机谁来把容器重新拉起流量上来的时候怎么凌晨两点不用人肉扩容Docker 擅长解决“一个容器怎么跑”而企业级容器化部署真正缺的是“一堆容器怎么管”。Kubernetes 就是补上这一环的编排系统也正因如此我们在做企业级容器化部署解决方案时把阶段一定义为 Docker 容器化改造阶段二的核心任务就落在了 Kubernetes 集群的搭建与治理上。这篇文章是阶段二的完整复盘从 kubeadm 初始化、CNI 选型、节点纳管到 RBAC、存储、伸缩、排障一条线讲完希望能给正在从“Docker 单机玩”走向“集群生产化”的团队一个可复制的参考。1. 阶段二到底在解决什么问题1.1 阶段一留下的两张成绩单阶段一做完团队通常拿到的成果有两张。第一张是镜像化清单原来靠线下拷贝、靠环境搭建文档才能跑起来的服务现在一个 Dockerfile 加 docker compose 就能在本地跑通从开发到测试的环境不一致问题大幅缓解。第二张是容器化运营基线哪个服务监听哪个端口、哪些配置走环境变量、日志输出到 stdout 还是文件这些都有了统一约定。但这两张成绩单背后隐藏着一个很尴尬的现实Docker 本身是单机工具。docker compose 再方便它也只能编排一台机器上的容器。业务量一旦上来十几台机器同时跑几十个容器没有编排层的话你很快就会遇到三件事——没人知道容器分布在哪些机器上某台机器宕机后上面的容器不会自动跑起来流量高峰期想扩容只能靠运维同学手动在每台机器上执行 docker run。这些问题阶段一解决不了这也是阶段二必须引入 Kubernetes 的根本原因。1.2 Kubernetes 补上的四块拼图对照上面的痛点Kubernetes 在企业级容器化部署里补上的核心能力可以归纳成四块。第一块是调度。你只需要声明“我要跑 3 个 nginx 副本”调度器会自己找合适的节点把 Pod 放上去资源够不够、有没有端口冲突、是否满足节点标签这些都在调度时自动处理。第二块是自愈。Kubelet 会持续检查节点上的容器状态容器挂了就重启节点挂了就把上面的 Pod 重新调度到其他可用节点。这个能力在传统运维里通常要写一堆监控脚本在 K8s 里是控制面内置行为。第三块是水平伸缩。HPAHorizontalPodAutoscaler监控 Pod 的 CPU、内存或自定义指标超过阈值就自动扩副本回落后自动缩容。配合负载均衡业务流量波动就不再需要人工干预。第四块是服务发现与流量入口。每个 Pod 都会动态变化 IPK8s 用 Service 抽象出一层稳定的访问入口再配合 kube-proxy 做负载均衡上游 Pod 挂了也不会影响调用方。1.3 这套方案适合谁看看完能做什么如果你是一名后端开发看完至少能看懂 kubectl get nodes、kubectl logs、kubectl describe pod 这些日常命令知道应用怎么打镜像、怎么被部署成 Deployment出了问题怎么查日志。如果你是运维工程师这套方案就是你落地的核心手册。kubeadm 初始化、节点加入、CNI 网络选型、证书维护、节点故障排查每一条都是生产环境要面对的真实场景。如果你是架构师阶段二更重要的是给你一套评估框架高可用集群应该怎么搭多团队怎么用 Namespace 隔离GPU 节点怎么纳管有状态服务到底适不适合上 K8s。看完你能对“哪些业务能上、哪些业务暂时别上”有明确判断。2. 动手前必须吃透的五组概念2.1 节点和 Pod不再关心“装在哪台机器”在 K8s 里物理机或虚拟机统一叫 Node也就是节点。节点只有两类角色控制面节点Control Plane和工作节点Worker。控制面节点跑集群的管理组件工作节点跑实际业务负载。节点这个概念之所以重要是因为它把“服务器”抽象成了资源池你不再关心某个容器到底在哪台机器上只要声明需求调度器去匹配。调度的最小单位是 Pod不是容器。一个 Pod 可以包含一个或多个容器它们共享同一个网络命名空间和存储卷。这有点像“同一间房里的合租室友”共享 IP、共享端口范围、可以互相通过 localhost 访问。业务上通常把联系紧密、必须部署在同一台机器上的容器放进同一个 Pod比如一个边车容器负责日志采集主容器处理业务请求。刚开始用 K8s 的人最容易犯的错误就是把容器当调度单位恨不得一个容器一个 Pod。其实 Deployment 管理的副本数指的是 Pod 数量你真正该问的是“这个进程适合和谁共享生命周期和网络”。2.2 控制面四件套集群的“管理层”控制面由四个组件组成理解他们就理解了 K8s 的运作方式。kube-apiserver 是整个集群的唯一入口所有 kubectl 命令、所有组件之间的通信最终都要经过它。你执行 kubectl apply -f deployment.yaml这个请求就是发给 apiserver它校验身份、校验资源定义然后把状态写入 etcd。可以把它理解成公司的前台所有进出申请都要走它。etcd 是集群的“记忆中枢”所有资源状态都保存在这里有哪些节点、有哪些 Pod、期望副本数是多少。它是分布式键值数据库但你不直接操作它apiserver 会帮你读写。生产环境里 etcd 的健康直接决定集群可用性这也是为什么高可用集群对 etcd 的要求很高。kube-controller-manager 是“监工”里面跑着各种控制器循环。比如 ReplicaSet 控制器会盯着“期望副本数是 3”和“实际副本数是 2”这个差距然后调用 apiserver 创建缺失的 Pod。控制器模式的核心思想是不断比较期望状态和实际状态并努力让实际向期望收敛。kube-scheduler 负责“分活”。新创建出来的 Pod 该放在哪台节点就是它算出来的。调度要考虑资源请求、节点标签、污点容忍、数据亲和等因素。scheduler 算完只是把结果写回 apiserver真正执行创建的是节点上的 kubelet。2.3 节点上的两个常驻进程和运行时控制面负责决策决策落地靠节点上的 kubelet 和 kube-proxy。kubelet 是每个节点上的“执行者”负责接收 apiserver 分配下来的 Pod 创建任务和容器运行时打交道把容器真正拉起来。它还负责向 apiserver 上报节点状态和 Pod 运行状态。节点出问题第一件事就是看 kubelet 日志。kube-proxy 负责维护节点上的网络规则实现 Service 的负载均衡。它最常见的实现模式是 iptables 或 IPVSPod IP 变化时它负责更新规则让访问 Service 的流量能转发到健康的 Pod。容器运行时是老生常谈的 CRI 接口Kubernetes 通过 CRI 和 containerd、CRI-O 这类运行时通信。注意K8s 从 1.24 之后正式移除了对 Docker 作为运行时直接支持现在企业中更常见的是 containerd底层虽然仍是容器技术但不再通过 Docker 的守护进程来管理容器生命周期。2.4 kubeadm、kubelet、kubectl 怎么分工这三个工具名字很像很多新手搞混。一句话总结kubeadm 负责搭建集群kubelet 负责维持节点上的容器kubectl 负责向 apiserver 下发指令。kubeadm 是集群初始化工具它帮你在控制面节点上启动 apiserver、etcd、controller-manager、scheduler 这些静态 Pod还会生成节点加入集群的 token 和证书。你可以把 kubeadm init 理解成“一键初始化考场”考场的硬件和监考老师都给你配好考生等着入场。kubelet 是每个节点上需要提前安装并常驻的“监考老师”它不在 kubeadm 的管理范围里需要你用系统服务方式启动。kubectl 则是你的“指挥器”平时操作集群基本都是它。2.5 版本选择与运行时选型企业做版本选型切忌追新。我们在阶段二选择了 Kubernetes v1.26.0 这条稳定线。为什么是老版本而不是最新版因为围绕 1.26 的生态组件兼容性已经验证充分Calico、Dashboard、metrics-server 等都有成熟版本可以对应。新版本往往伴随着组件适配风险生产环境要的是确定性。运行时统一使用 containerd不再使用 Docker 作为容器运行时。理由很直接containerd 更轻少一层 Docker daemon 意味着少一个故障点和一层资源开销同时 K8s 社区已经默认 containerdCRI 适配最顺畅。当然镜像构建还是可以继续用 Docker构建和运行时解耦是完全可行的。3. 从零搭建一套单控制面生产级集群3.1 环境准备与前置检查搭建集群前最重要的是环境基线。我们当时用的是 3 台 Ubuntu 22.04 服务器一台做控制面节点两台做工作节点规格是 4 核 8G、100G 系统盘。这个配置对测试环境完全够想要跑大量业务负载的话工作节点建议上 8 核 16G。节点上需要做的系统配置逐条列在这里。关闭 swap。K8s 要求节点必须关闭交换分区否则 kubelet 会在 pre-flight checks 直接报错。命令是 swapoff -a同时要注释掉 /etc/fstab 里的 swap 行重启后不再生效。加载内核模块。需要 overlay 和 br_netfiltermodprobe 加载后再写入 /etc/modules-load.d/k8s.conf 保证开机自动加载。配置内核参数。关键是 net.bridge.bridge-nf-call-iptables1 和 net.ipv4.ip_forward1。前者保证 iptables 规则能作用于桥接流量后者是容器对外通信的前提。主机名和 hosts 解析也建议提前规划好。控制面节点设置成 k8s-master工作节点设置成 k8s-node1、k8s-node2每台机器的 /etc/hosts 都写上三台机器的 IP。容器运行时安装 containerd。这一步最容易出问题的是配置默认安装完成后/etc/containerd/config.toml 里的 SystemdCgroup 是 false如果不改成 true后面 kubelet 会和 containerd 的 cgroup 驱动不一致节点会一直 NotReady。3.2 初始化控制面看懂 kubeadm 输出工作节点准备完毕控制面节点执行初始化命令。我们当时的命令是kubeadm init \ --apiserver-advertise-address192.168.10.10 \ --pod-network-cidr192.168.0.0/16 \ --kubernetes-versionv1.26.0apiserver-advertise-address 是控制面节点的 IP工作节点要通过这个地址访问 apiserver。pod-network-cidr 是给 Pod 分配的网段这里必须和后面装的 CNI 插件保持一致。我用的是 Calico 默认的 192.168.0.0/16如果你准备用 Flannel就改成 10.244.0.0/16。初始化过程中最显眼的两行日志我截下来解释一下。第一行是[init] Using Kubernetes version: v1.26.0说明 kubeadm 从镜像仓库拉取各组件镜像时会按 v1.26.0 这个版本去拉这一步如果卡住多半是镜像仓库网络问题。企业里建议预先在私有镜像仓库把 kubeadm 需要的镜像准备好或者在 init 时用 --image-repository 指定内网仓库地址。第二行是[preflight] Running pre-flight checks这是 kubeadm 在逐项体检swap 是否关闭、端口是否被占用、CRI 是否可用、内核模块是否加载。任何一项不满足都会在这里直接报错。所以 pre-flight checks 是新手排查问题的第一现场。初始化成功后控制面会输出三部分内容kubeconfig 配置指引、join 命令、token 哈希。先把 kubeconfig 配置好否则 kubectl 没法访问集群mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config执行 kubectl get nodes 会发现 master 节点处于 NotReady 状态这是正常的因为网络插件还没装。3.3 工作节点加入集群工作节点加入集群用的是 kubeadm join 命令kubeadm init 输出末尾会给出完整命令和参数类似这样kubeadm join 192.168.10.10:6443 \ --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxtoken 的作用是让工作节点有资格注册到集群里证书哈希则是让工作节点验证 apiserver 的身份防止中间人。这两个参数都很敏感建议加密保存泄露意味着别人可以往你集群里塞节点。如果 token 已经过期不用慌在控制面节点上重新生成kubeadm token create --print-join-command这条命令会输出一条可以直接执行的新 join 命令token 有效期默认 24 小时。join 过程其实就是工作节点拿着 token 去 apiserver 自报家门apiserver 校验通过后会把证书和配置下发给 kubelet。等个一两分钟在控制面节点执行 kubectl get nodes能看到三个节点都处于 NotReady原因依然是缺网络插件。3.4 安装 CNI 网络插件节点从 NotReady 变 Ready没有 CNI 插件Pod 分配不到可用 IPkubelet 也没法把节点标记为 Ready。为什么 Pod 之间一定要靠 CNI因为 K8s 要求每个 Pod 都有独立的 IP且任意两个 Pod 之间可以直接通信这个“扁平网络”必须由网络插件来实现。Flannel 和 Calico 是最常见的两个选择。我们最终选了 Calico原因有三点一是支持 NetworkPolicy可以在集群内做精细化网络访问控制Flannel 不支持二是性能比 VXLAN 模式的 Flannel 好生产流量大时优势明显三是它不依赖额外的分布式存储部署简单。如果你的场景只是开发测试Flannel 就够省资源且组网简单。安装命令kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yamlCalico 部署完成后控制面会自动检测到 Pod 网段配置建设各节点的网络路由。这段时间观察 calico-system 命名空间下的 Pod 是否 Running全部 Running 后kubectl get nodes 应该看到三个节点全部 Ready。这一步是整个阶段二里最让人舒心的时刻。3.5 集群健康检查与最小验证清单节点 Ready 不等于集群“健康”我习惯按下面这组命令验收。kubectl get nodes -o wide kubectl get pods -n kube-system -o wide kubectl get pods -n calico-system -o wide kubectl get svc -A重点检查四件事所有节点状态 Readykube-system 下的 CoreDNS 处于 Runningcalico-system 下的 Pod 没有 CrashLoopBackOff集群服务列表里有 kubernetes 服务。然后再做一次业务级冒烟测试创建一个 Deployment 试试kubectl create deployment nginx --imagenginx kubectl expose deployment nginx --port80 --typeNodePort kubectl get svc nginx用任意节点 IP 加上 NodePort 端口访问如果页面能打开说明整个链路已经打通网络、DNS、调度、服务发现都正常。这一步通过阶段二的集群底座就算立住了。4. 企业级使用必做的四件事4.1 控制面高可用从单控制面到 HA 的路径阶段二默认是单控制面集群因为学习成本最低、资源需求最小。但如果你要拿这套方案直接支撑生产业务我必须提醒你单控制面等于整个集群的“控制权力”只有一个点apiserver 所在机器宕机你连 kubectl 都执行不了。高可用的标准做法是至少 3 台控制面节点通过负载均衡器把请求分发到多台 apiserver。etcd 有两种部署方式堆叠模式也就是 etcd 跑在控制面节点上外部模式也就是 etcd 单独跑在专门的机器上。生产环境一般建议堆叠模式因为节点数量少、运维简单只要控制面节点不大量宕机就能保证 etcd 仲裁。kubeadm 初始化 HA 集群时会用到 --control-plane-endpoint 参数指向负载均衡器的地址。工作节点全部通过这个地址访问 apiserver主备切换对你来说是透明的。这块在项目的阶段三我们才重点深化阶段二先把单控制面跑熟再谈 HA步子别迈太大。4.2 Namespace 与 RBAC多团队共存的安全基线集群搭完第一个要做的是“分房间”。我们团队按业务线划分了 Namespace比如 zabbix、doris、ai-service各自业务的数据、配置、资源配额都在自己的房间里。Namespace 隔离了资源也隔离了权限和企业级部署必须的资产归属。Namespace 只是逻辑隔离真要控制“谁能干什么”必须靠 RBAC。RBAC 在 K8s 里有三件事ServiceAccount 是可操作集群的身份Role 或 ClusterRole 定义权限集合RoleBinding 或 ClusterRoleBinding 把身份和权限绑定起来。最危险的操作是直接把集群管理员证书发给项目组。正确的做法是给每个团队创建独立的 ServiceAccount只授予他们所在 Namespace 的读写权限。举个例子给 zabbix 团队创建一个只能操作 zabbix 命名空间下 Pod、Service、ConfigMap 的 Role不给他们 delete 权限防止误删。4.3 怎么把 Pod 放到该放的机器节点选择、污点与伸缩集群里有不同规格的机器业务对资源的需求也不同。比如 GPU 服务器专门跑 AI 推理高性能磁盘节点专门跑数据库。K8s 控制 Pod 落点的工具主要有三件套nodeSelector、亲和性和污点容忍。nodeSelector 最简单给节点打标签后Pod 通过 nodeSelector 指定标签。但企业场景里更常用的是“污点加容忍”给 GPU 节点打上污点只有声明了容忍这个污点的 Pod 才能调度上去普通业务 Pod 根本不会沾到 GPU 机器。我们在集群里接入 GPU 机器时就采用了这套逻辑。节点加上 nvidia.com/gputrue 的标签和 nodegpu:NoSchedule 的污点AI 服务部署时显式声明 tolerations 和 resources.limits[nvidia.com/gpu]1。这样普通业务永远不会抢 GPU 资源。现在很火的 DeepSeek 本地部署、Ollama、vLLM 这类大模型服务要跑在 K8s 上都会走这套调度方式配合 nvidia-device-plugin 让节点上报 GPU 资源调度器才能按卡分配。伸缩方面HPA 是最基础的一层。例如kubectl autoscale deployment nginx --cpu-percent70 --min2 --max10CPU 超过 70% 自动扩副本回落自动缩。这套机制依赖 metrics-server 提供监控数据装好 metrics-server 后 HPA 才生效。K8s 默认只支持 CPU 内存指标如果业务需要按消息队列长度、HTTP QPS 伸缩就得引入 KEDA 这类事件驱动伸缩组件。4.4 有状态服务与数据MySQL、Redis 在 K8s 里的正确姿势K8s 里的 Deployment 默认是无状态的Pod 说换就换数据卷说丢就丢。有状态服务必须另做设计核心工具是 StatefulSet 和持久化存储。StatefulSet 和 Deployment 最直观的差别是 Pod 的标识它会给每个副本一个稳定的序号和主机名比如 mysql-0、mysql-1。配合 Headless Service数据库节点能通过固定的 DNS 名称互相访问。这是主从复制软件能正常工作的前提。但真正让 MySQL 主从、Redis 主从在 K8s 里跑得顺的不是 StatefulSet 本身而是存储和配置。存储必须用 PV/PVC 打通持久化推荐用 StorageClass 动态供给底层是 Rook-Ceph 或者云厂商的云盘。配置方面主从复制需要的初始化脚本、账号密码通过 ConfigMap 和 Secret 挂载进去Pod 重建后配置不会丢。我们的建议是中小团队优先把这些有状态服务跑在 K8s 之外的独立环境或者用成熟的 Operator 来管理比如 KubeBlocks、Zalando Postgres Operator。Operator 做的事情远比手动配一个 StatefulSet 复杂它负责故障转移、备份恢复、扩缩容时自动协调主从关系。艺术家可以在文档里“抄作业”但生产环境要交给经过验证的方案。5. 从“能跑”到“好用”的进阶能力5.1 镜像从哪来私有仓库与拉取策略集群建好后业务镜像怎么分发到所有节点是一个前置问题。Kubic的操作如果都直接依赖 Docker Hub既慢又不可控万一镜像仓库抽风Pod 就拉不起。生产环境一定要有私有镜像仓库我们用的是 Harbor因为它有镜像扫描、复制、保留策略这些企业功能。节点拉取私仓镜像时需要在每台节点的 containerd 配置里配置好认证信息或者在集群里创建 ImagePullSecret。拉取策略有三种IfNotPresent 是本地有镜像就不拉默认推荐Always 是每次重新拉适合开发测试持续看最新镜像Never 是只使用本地镜像离线环境会用。很多人遇到 docker 安装 mysql 失败这类问题查到最后多半不是镜像本身的问题而是镜像名写错、tag 不存在、仓库地址没配对。在私有仓统一管理后这类问题会少很多。5.2 滚动更新与回滚别让发布变成午夜惊魂Deployment 发布新版本时默认走滚动更新。就是说新 Pod 先起来一个确认 Available 后旧 Pod 才下线一个业务不中断。滚动更新有两个关键参数maxSurge 控制最多能临时超出期望副本多少个maxUnavailable 控制最多能有几个副本同时不可用。生产发布我们习惯这样设maxSurge1maxUnavailable0。意思是先起一个全新的 Pod等它 Ready再下线一个旧 Pod。这样发布过程副本数不会低于期望值业务流量一直有地方接收。发布出错要回滚不需要重打镜像再部署直接用上一版本kubectl rollout undo deployment/nginx如果想知道回滚到哪个版本kubectl rollout history deployment/nginx 可以查看历史版本列表。回滚操作本身也是滚动更新同样安全。5.3 服务暴露从 ClusterIP 到 IngressPod 的 IP 是易变的Service 是稳定的访问入口。Service 的类型决定暴露范围。ClusterIP 是集群内部访问NodePort 是通过节点 IP 加端口访问外部访问LoadBalancer 是云平台提供负载均衡器Ingress 则是 HTTP 层的统一入口。从外部访问业务企业里最常用的组合是 Ingress Controller 加 Ingress 对象。Ingress Controller 类似于 nginx负责承载真正的流量Ingress 对象只是声明“哪个域名对应哪个 Service”。一套 Ingress Controller 可以服务所有 Namespace避免了每部署一个服务就开一堆 NodePort 的混乱。这里要提醒一句很多“docker 网络不通”的排查思路在 K8s 里要升级。K8s 里网络不通先分清是 Pod 之间不通还是 Service 访问不通还是 Ingress 到 Service 不通。一层层用 curl 验证不要一上来就抓包。5.4 配置管理ConfigMap 和 Secret 的正确用法把配置写死在镜像里是容器化最容易犯的错。K8s 提供了 ConfigMap 存普通配置、Secret 存敏感信息。部署 Pod 时可以把 ConfigMap 挂载成文件也可以把某个 key 注入成环境变量。这里有几个实战注意点。Secret 默认只是 base64 编码不是加密生产环境需要开启加密存储比如用云厂商的 KMS 方案。ConfigMap 和 Secret 更新后已经在运行的 Pod 不会自动拿到新配置必须滚动重启一次 Pod。如果有“配置热更新”的需求可以考虑把配置项挂载成文件再配合应用自己监听文件变化。另外不应该把数据库密码直接写进 Deployment 的 YAML更不应该提交到 Git。实践上我们会用 Sealed Secrets 或者外部密钥管理工具比如 Vault把敏感信息和集群资源定义解耦。6. 部署和排障中的高频坑位实录6.1 pre-flight checks 失败的逐条对账kubeadm init 卡在 pre-flight checks是阶段二出现频率最高的问题。我按实战中遇到的概率排个序。第一swap 没关干净。很多人执行了 swapoff -a但没注释 /etc/fstab重启后 swap 又挂上了。kubeadm 有个参数 --ignore-preflight-errorsSwap 可以绕过但这不是正道因为即使集群初始化成功kubelet 的 QoS 判断也会出问题。第二cgroup 驱动不一致。kubelet 默认使用 systemd 作为 cgroup 驱动如果 containerd 没有把 SystemdCgroup 改成 true启动就报 “failed to run Kubelet” 或节点 NotReady。这个问题要回到 containerd 配置去改改完重启 containerd 和 kubelet。第三端口被占用。apiserver 要监听 6443etcd 要监听 2379 和 2380kubelet 要监听 10250。如果你之前在同一台机器上起过 Docker 容器尤其是那种直接用 host network 的容器很容易把端口占掉。第四CRI 没被识别。kubeadm 默认找 containerd如果装的是其他版本的 containerdUNIX socket 路径不对就会报 “CRI v1 runtime API is not implemented”。这时候先看 containerd 版本再去检查 /var/run/containerd/containerd.sock 是否存在。6.2 节点 NotReady 的排查路径工作节点 join 进来不代表万事大吉节点长期 NotReady 的案例非常典型。我建议从头到尾按这条路查。先看节点本身和 kubelet。systemctl status kubelet 和 journalctl -u kubelet -f 是必查的。kubelet 起不来多半是配置或 cgroup 问题这部分日志里会直接写原因。再看运行时。用 crictl ps 确认 containerd 是否在正常工作。如果 crictl 连不上 containerd说明运行时有问题kubelet 也拉不起 Pod。然后是网络插件。节点 Ready 的判定需要 CNI 网络插件就绪。如果 calico 的 Pod 处于 CrashLoopBackOff大概率是 Pod 网段和初始化参数不一致或者某个镜像拉不下来。还有一种情况节点资源耗尽。kubelet 会主动给节点打上 MemoryPressure 或 DiskPressure同时驱逐部分 Pod。看 kubectl get node -o wide 和 kubectl describe node 里的 Conditions能看到明确的状态。6.3 CoreDNS 起不来、跨节点网络不通如果集群 Nodes 都 Ready但 CoreDNS 一直 Pending 或 CrashLoopBackOff先不要怀疑网络插件而是先看调度和镜像。Pending 多半是资源不足比如节点被污点挡住CrashLoopBackOff 多半是镜像或配置问题kubectl logs 看具体报错。等 CoreDNS 变成 Running再用它做一次 DNS 健检。起一个临时 Pod执行kubectl run busybox --rm -it --imagebusybox -- sh nslookup kubernetes.default.svc.cluster.local能解析出 ClusterIPDNS 基本正常。解析不出来检查 kube-dns Service 是否有后端 Endpoints以及网络插件是否把 DNS 流量转发对了。跨节点网络不通的场景先分别验证三个层面节点到节点 IP 通不通Pod IP 到另一个 Pod IP 通不通Service IP 到 Pod IP 通不通。Calico 网络下隧道模式或 BGP 模式的排障手法不太一样但只要记住“逐层 ping 一遍”这个思路很快能定位到是主机防火墙、路由缺失还是 iptables 规则冲突。6.4 Token 过期与证书过期两个时间炸弹Token 过期是最常见的“第二天就找不到 join 命令”的问题。处理方法前面已经给了kubeadm token create --print-join-command。注意 token 默认 24 小时过期批量加入节点应该在初始化后赶紧加完或者一次创建多个。证书过期才真正棘手。K8s 控制面证书默认有效期一年很多集群跑着跑着突然有一天 kubectl 报 “certificate has expired or is not yet valid”。遇到这种情况先不要慌登录控制面节点执行kubeadm certs renew allrenew 完之后需要重新加载 kubeconfigcp /etc/kubernetes/admin.conf $HOME/.kube/config然后重启相关组件一般是重启 kubelet 或直接重启节点上这几个静态 Pod。证书续期无法替代定期巡检建议用 cron 每个月查一次证书剩余时间最省心的方式是把证书监控接进 Prometheus。6.5 资源不足、时区、镜像拉取等细节坑Pod 长时间 Pendingkubectl describe pod 会显示 “0/3 nodes are available”下面列出每个节点不满足的条件。最常见的是两种节点没有足够 CPU/内存满足 requests或者节点有污点而 Pod 没有对应容忍。时区问题很隐蔽。容器默认时区是 UTC业务系统如果日志时间全差 8 小时那就尴尬了。解决办法是在 Pod 的 spec 里挂载 /etc/localtime或者更规范地在构建镜像时把时区设置好。业务日志和监控告警才能对得上时间轴。镜像拉取失败除了网络问题还有私有仓库证书不被信任导致的 TLS 报错。这种问题建议直接在 containerd 配置里加 registry 的 CA 证书而不是把验证禁掉。我们见过有人为了图省事把 skip_verify 打开结果整个集群的镜像拉取链路都不安全了。6.6 个人电脑上的 Docker Desktop 问题虚拟化检查失败很多朋友并不是在 Linux 服务器上搭集群而是在自己电脑上用 Docker Desktop 内嵌的 Kubernetes 功能遇到报错Docker Desktop failed to start because virtualization support wasnt detected这个报错的原因是电脑 CPU 的虚拟化能力没有被 BIOS 开启或没被系统正确识别。解决办法是进 BIOS/UEFI 开启 VT-xIntel或 SVMAMD开启后在 Windows 任务管理器里确认“虚拟化已启用”。但这个坑要说明白这只是个人电脑上跑 Docker Desktop 时的问题和你在 Linux 服务器上用 kubeadm 搭建企业级集群完全是两条路线。企业级部署不要考虑用它来承载业务真正的集群必须跑在数据中心或云主机的 Linux 操作系统上。Docker Desktop 更适合本地开发联调别让这条报错误导了你的技术选型。7. 阶段二之后怎么走7.1 阶段三优先补哪些能力阶段二交付了一个能跑业务、能扩缩容、有基础权限的集群。但要把它做成“企业级解决方案”阶段三至少还有四块硬骨头要啃。监控告警是优先级最高的。Prometheus 加 Grafana 负责指标采集和可视化结合 Alertmanager 做告警。比如节点 CPU 持续高位、Pod 频繁重启、证书即将过期都要能主动发声。没有监控的集群等于在开一辆没有仪表盘的车。日志系统是第二个。容器是养不住的日志必须外置。常见的方案是 Loki 或者 ELK通过 DaemonSet 在每个节点上采集日志统一汇入日志平台。这样 Pod 崩溃重造日志也不会丢。CI/CD 自动化是第三个。推荐 GitLab CI 加 Argo CD 的组合。GitLab CI 负责构建镜像并推到 HarborArgo CD 监听 Git 仓库里的资源定义变化自动同步到集群。部署操作从“人手敲 kubectl”变成“代码提交就发生”可审计性也有了。服务网格可以放在第四位考虑。Istio 这类方案能给你服务治理能力比如灰度发布、故障注入、流量镜像。但我要泼一盆冷水服务网格的复杂度对中小团队来说往往比收益更突出非必要不建议第一批上。7.2 把 AI 负载和边缘设备放进集群阶段二之后如果还有余力可以关注两类负载上集群的实践。一类是 AI 推理和训练任务。现在 DeepSeek 本地部署、Ollama、vLLM 本地部署越来越火这类应用的特点是吃 GPU、显存和模型存储。接入 K8s 集群的核心是 GPU 资源抽象nvidia-device-plugin 让节点上报 GPU调度器才能按卡分配。不同模型对显存的需求差异很大企业里通常会把 GPU 节点按卡型和显存打上标签再用污点隔离。另一类是边缘设备。比如 RK3588 部署 YOLOv8、Jetson Orin 这类边缘推理设备理论上也可以用 KubeEdge 等方案把边缘节点并入集群统一做任务下发。这个赛道还很新但方向上就是让“云”和“边”用同一套 API 管理。不过对大多数团队来说先把 AI 负载在标准集群里跑稳比碰边缘场景更实际。阶段二的最后一段“收尾”往哪里走我自己的体会是阶段二最大的价值不在于搭出一个集群而在于逼着团队把“容器到底怎么运维”这件事想清楚。很多人觉得集群建好就结束了实际上这恰恰是运维工作的起点。我们之后又把这套集群重装过两次第一次是发现最初用的网络插件选型有问题第二次是调整了节点的资源预留和污点配置。每次重装都比上一次更接近“企业级”这三个字。如果你正准备搭自己的第一个集群我给你一个建议不要照着 HA 的教科书一步到位先用单控制面跑熟把滚动更新、回滚、RBAC、HPA 这些操作在实际业务里用两个月踩过几次 NotReady 和 CrashLoopBackOff 以后再去考虑控制面高可用的事。只有你亲手动过 kubectl describe、看过 kubelet 日志才知道书上那些“最佳实践”到底解决的是什么问题。再分享一个小技巧把所有部署用的 YAML 都收进 Git 仓库和代码一样做版本管理。每次集群出了奇怪的问题先翻一翻上一次提交了什么往往能最快定位这也是从“会部署”走向“会治理”的第一步。
返回列表