Ubuntu上kubeadm搭建Kubernetes集群全流程与排错

发布时间:2026/10/21 12:54:08
Ubuntu上kubeadm搭建Kubernetes集群全流程与排错 第一次看到 k8s 这个缩写的时候很多人都会下意识去猜它到底是什么意思。其实就是 Kubernetes 中间隔了几个字母的简写这个来自希腊语的单词原意是“舵手”或者“掌舵的人”用来描述容器编排平台非常贴切。我最近在测试环境里用 Ubuntu 22.04 LTS 加 kubeadm 手动搭了一套多节点 k8s 集群从系统初始化到节点全部 Ready前后折腾了整整一个周末。中间踩了不少坑也把整个流程彻底理顺了。这篇内容把我在 Ubuntu 上搭建 k8s 环境的完整过程、关键选型逻辑和排错方法都写下来适合两类人看一类是刚接触容器编排、想弄清楚 k8s 到底是什么的初学者另一类是准备在 Ubuntu 上从零搭一套能用的多节点集群、需要避开常见坑的实践者。整个过程完全基于物理机或虚拟机不依赖云平台自带托管版搭出来的集群结构更透明也更适合深入学习。1. 先搞清楚k8s到底在解决什么问题1.1 从“部署几台机器”到“管理一堆容器”早期部署一个应用直接在一台物理机上装环境、跑进程就够了。后来服务多了开始用虚拟机做隔离一台机器上跑多个虚拟机每个虚拟机里面放不同的应用。再往后容器技术普及镜像打完包之后在任何环境里都能跑部署这件事确实变轻了但新的麻烦也跟着来了几十个容器分散在多台机器上怎么互相发现某个容器挂了怎么自动起新的流量多了以后如何横向扩展发新版的时候怎么做到不中断服务这些问题单靠 Docker 命令已经回答不了需要一个“调度和编排中枢”来统一管理。k8s 就是干这个的。它把服务器资源抽象成一个集群帮你在里面安排容器的位置、维护副本数量、处理服务发现和负载均衡还负责滚动更新和故障自愈。1.2 k8s的核心工作原理从架构上看k8s 集群整体分成两部分控制面和数据面。控制面负责做决定核心组件包括 API Server、etcd、Scheduler 和 Controller Manager。API Server 是所有请求的入口相当于公司的前台etcd 是集群状态的数据库存着“当前有哪些节点、哪些 Pod、各自什么状态”Scheduler 负责决定一个新的 Pod 应该放到哪台机器上Controller Manager 负责持续维护期望状态比如某个 Deployment 要 3 个副本少了它就自动补。数据面负责干活每台节点机器上运行 kubelet、kube-proxy 和容器运行时。kubelet 是节点上的代理人跟 API Server 通信确保本机上的容器真实运行kube-proxy 负责实现 Service 的网络转发规则容器运行时就是真正跑容器的软件常见的是 containerd。用一个生活化的类比整个集群就像一个酒店客人是各种应用容器客人想住什么房间、房间要几个你只需告诉酒店前台API Server酒店会自己分配客房节点、安排入住调度、检查房间状态kubelet 汇报客人不需要自己去挑楼层。1.3 什么样的场景才值得上k8sk8s 看起来很强大但并不是所有场景都该上。如果你的环境里只有两三个单体应用用 Docker Compose 管理一台机器就够了硬上一套集群反而会把简单问题复杂化。比较适合上 k8s 的场景有这么几类第一应用需要多副本和高可用机器故障时希望 Pod 自动迁移第二需要根据负载自动扩缩容第三微服务数量比较多互相之间需要服务发现和负载均衡第四你想要有一套统一的发布流程比如金丝雀发布、滚动更新。判断标准其实很简单当单机方案让你频繁手工干涉部署和运维时就轮到 k8s 登场了。2. 动手搭建前先确认选型和Ubuntu环境准备2.1 版本选型kubeadm加containerd搭建 k8s 的方式不止一种。开发环境可以用 Minikube一条命令就能拉起单节点生产或接近生产的环境最主流的手动方案是用 kubeadm。kubeadm 是官方提供的集群初始化工具它不会帮你装容器运行时也不会自动装网络插件但会把控制面组件、证书、kubeconfig 这些基础工作全部自动化是学习底层原理和搭建多节点集群的最佳起点。容器运行时我选了 containerd 而不是 Docker。原因很简单k8s 从 1.24 版本开始就已经不再内置对 Docker 的适配而是通过 CRI 接口直接对接 containerd。containerd 更轻量也更贴近 k8s 的原生运行模型。这里要特别提醒一下kubeadm、kubelet、kubectl 这三个工具的版本必须与目标 Kubernetes 版本保持一致。比如你计划搭 1.28 版本的集群那这三个组件的版本也要选 1.28 对应的小版本不能混搭否则初始化的时候大概率会报版本不匹配的错误。2.2 硬件配置与节点规划在 Ubuntu 上搭集群之前先把硬件账算清楚。控制面节点至少需要 2 核 CPU、2GB 内存但真实使用下来如果后续还要部署监控、管理界面这类组件4GB 内存会更稳妥。工作节点的配置取决于你的业务负载最小建议也是 2 核 2GB。磁盘建议预留 20GB 以上系统盘和镜像存储放在一块也可以但有条件的话最好给容器数据单独挂一块盘。下面是搭建过程中比较常见的节点规划可以直接参考角色主机名示例CPU内存磁盘系统版本控制面k8s-master2核4GB40GBUbuntu 22.04 LTS工作节点1k8s-node014核8GB80GBUbuntu 22.04 LTS工作节点2k8s-node024核8GB80GBUbuntu 22.04 LTS如果你手里只有一台机器可以先用 kubeadm 搭一个单节点集群再移除控制面节点的污点让普通 Pod 也能调度上去这样学习是完全够用的。网络方面有几个硬性要求所有节点之间要能互通建议提前配置静态 IPDNS 最好统一用公司或局域网内稳定的 DNSEtcd 的通信延迟要低跨地域集群不在本次讨论范围内。另外所有节点的时间必须同步建议安装 chrony 或 systemd-timesyncd时间不同步会导致证书校验失败这种非常隐蔽的问题。2.3 Ubuntu上必须提前处理的系统配置Ubuntu 系统默认状态离 k8s 的运行要求还有一段距离主要有三个地方必须处理。第一关闭 swap。k8s 的 kubelet 默认不允许使用交换分区开着 swap 会导致节点状态异常。执行swapoff -a只是临时关闭还要编辑/etc/fstab把 swap 那一行注释掉保证重启后仍然关闭。第二加载内核模块。容器网络需要 overlay 和 br_netfilter 模块这两个模块不加载后续 Pod 之间的通信会出现莫名其妙的问题。第三防火墙和端口放行。对于学习环境我建议直接把 ufw 停掉省得排查网络问题的时候分不清是防火墙还是 CNI 插件的问题。生产环境则必须按端口清单精确放行比如控制面节点的 6443、2379、2380、10250、10251、10252 等端口需要开放。3. Ubuntu里的完整搭建流程从初始化到集群正常3.1 系统初始化这几步不能省搭建流程的第一步是在所有节点上做统一的系统初始化。我按顺序做了以下操作每一步都不想跳过先用hostnamectl set-hostname给每台节点设置主机名控制面节点叫 k8s-master另外两台叫 k8s-node01、k8s-node02。修改好之后把主机名和 IP 的对应关系写入/etc/hosts避免集群内部通过主机名互相访问时解析失败。然后执行 swap 关闭、内核模块加载和 sysctl 参数设置。这里的内核参数比较关键需要让 iptables 能看到网桥流量swapoff -a sed -i /swap/s/^/#/ /etc/fstab modprobe overlay modprobe br_netfilter cat /etc/sysctl.d/k8s.conf EOF net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sysctl --system设置完成后可以确认一下lsmod | grep br_netfilter是否有输出有输出说明模块已经加载成功。3.2 安装并配置containerd容器运行时我装的是 containerd。Ubuntu 的软件源里通常自带 containerd 包直接执行apt install -y containerd就可以装上。装完先别急着用必须生成并修改配置。containerd 默认配置里有两个地方会影响 k8s 运行一是SystemdCgroup的设置二是 sandbox 镜像的地址。生成默认配置并编辑mkdir -p /etc/containerd containerd config default /etc/containerd/config.toml编辑config.toml找到SystemdCgroup默认是 false改成 true。因为 Ubuntu 系统默认使用的就是 systemd 作为初始化进程systemd 的 cgroup 驱动和容器运行时必须保持一致否则 kubelet 会报 cgroup 驱动不匹配的错。同时检查sandbox_image这一项它指定了 k8s 每个 Pod 里都会用到的 pause 容器镜像主要用于为 Pod 提供网络和命名空间基础设施。如果默认的镜像地址拉不回来提前把它改成你环境中可访问的镜像仓库地址避免后面沙箱一直创建不了。改完重启 containerd 并验证systemctl restart containerd systemctl enable containerd crictl version3.3 安装kubeadm、kubelet和kubectl接下来在所有节点上安装 k8s 三个核心工具。先在/etc/apt/sources.list.d/下添加 Kubernetes 软件源然后执行apt update apt install -y kubeadm kubelet kubectl装完之后一定要执行apt-mark hold kubeadm kubelet kubectl把这些工具锁定版本防止系统升级时无意间把 kubelet 或 kubeadm 升级到不兼容的版本。k8s 集群组件版本一旦混搭排查问题会非常痛苦。3.4 初始化控制面节点在控制面节点上执行kubeadm init是整套搭建过程中最关键的一步。我使用的命令是kubeadm init \ --apiserver-advertise-address192.168.10.10 \ --pod-network-cidr10.244.0.0/16--apiserver-advertise-address要填控制面节点的内网 IP--pod-network-cidr是 Pod 网段这个地址段必须和你后面要装的 CNI 网络插件要求的段保持一致。这一步如果填错后面网络全部要返工。初始化过程一般要等几分钟期间 kubeadm 会先做环境预检然后生成证书、启动 etcd、部署控制面组件。看到结尾出现类似kubeadm join ...的命令时把完整输出保存下来这是工作节点加入集群的凭证里面包含了 Token 和证书哈希。初始化完成后还要配置 kubectl 的访问权限mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config此时控制面节点已经初始化完成但集群还是“半残废”状态因为还没有网络插件节点会一直处于 NotReady。3.5 安装CNI网络插件并让节点Ready初始化完成后必须在集群里安装一个 CNI 网络插件。CNI 负责解决 Pod 之间的跨节点通信问题。可选方案有很多Flannel、Calico 是我用得最多的两种。Flannel 配置简单适合快速搭建学习环境Calico 支持网络策略生产环境更常用。安装 CNI 的方式通常是下载对应的 YAML 清单然后执行kubectl apply -f。在安装网络插件之前我建议先确认清单文件里的 Pod 网段和你初始化时设定的--pod-network-cidr一致不一致会导致 Pod 分配不到可用 IP。安装完成后等两分钟左右再执行kubectl get nodes这时可以看到控制面节点的状态变成 Ready。如果一直 NotReady可以参考后面第 4 节的排查流程。3.6 工作节点加入集群工作节点加入集群分为两步。第一步把第 3.1 到 3.3 节的系统初始化、containerd 安装、kubeadm 安装操作在每台工作节点上重复执行。第二步登录每台工作节点执行你在控制面节点保存下来的 join 命令kubeadm join 192.168.10.10:6443 --token 你的token \ --discovery-token-ca-cert-hash sha256:证书哈希join 成功之后回到控制面节点执行kubectl get nodes -o wide应该能看到所有节点陆续出现在列表中。新加入的节点需要一些时间拉镜像、启动组件状态会从 NotReady 慢慢变成 Ready。3.7 验证集群能正常工作集群 Ready 不代表一切都通了我通常还会做一轮基础功能验证。先看所有核心组件是否正常kubectl get pods -A重点关注 kube-system 命名空间下的 coredns它必须是 Running 状态。接着启动一个临时测试 Pod访问里面已有的服务或者直接在集群里创建一个 Deployment检查 Pod 能否相互通信。这里我习惯用两个测试 Pod一个打另一个的服务 IP能通说明 CNI 网络没问题Service 的转发规则也生效了。跑完测试记得把临时资源删掉保持集群干净。4. 我搭k8s环境时踩过的坑和排查链路搭建过程中我踩了好几个坑有些问题光看错误提示完全摸不着头脑必须结合日志一步步倒推。下面这四个坑是我认为最有代表性的。4.1 坑一初始化一直卡在等待kubelet启动第一次执行kubeadm init的时候命令停在[kubelet-check] It seems like the kubelet isnt running很久最后直接超时。当时第一反应是 kubelet 服务出了问题用systemctl status kubelet查看服务确实一直在重启。继续看日志journalctl -u kubelet -f里面有很多关于 CRI 的报错大意是 kubelet 无法通过 CRI 连接到容器运行时。问题出在我没有修改 containerd 的SystemdCgroup配置。kubelet 默认用 systemd 驱动containerd 用的还是旧的 cgroupfs 驱动两边对不上。解决办法就是前面说的把config.toml里的SystemdCgroup改成 true然后彻底重启 containerd。改完之后建议用crictl ps随手验证一下确保 containerd 能正常通信再重新执行kubeadm reset后重新 init。4.2 坑二sandbox镜像拉取一直失败执行 init 时发现控制面组件能起来但 Pod 一直处于ContainerCreating。看 pod 的事件时报的错是拉取 sandbox 镜像失败。这个问题的根子在 containerd 配置里的sandbox_image默认地址。每个 Pod 在创建之前都要先起一个 pause 容器来共享命名空间如果这个 pause 镜像在当前网络环境里拉不下来所有 Pod 都会卡住。方法很朴素修改sandbox_image把它换成环境里能正常拉取的镜像地址然后重启 containerd。改完配置后强烈建议先手动执行crictl pull 镜像地址验证一下是否能成功拉取别等创建 Pod 的时候才被发现。4.3 坑三节点加入后一直是NotReady工作节点成功 join 进集群但状态迟迟不 Ready。用kubectl describe node k8s-node01可以看到节点上有一个条件叫Ready Unknown或者NetworkReadyfalse。这种问题八成是网络插件没装好或者工作节点上没有 CNI 插件所需的二进制文件。我检查了/opt/cni/bin目录发现是空的Flannel 的二进制根本没有被部署上去。CNI 插件清单一般通过 DaemonSet 方式运行它会自动把二进制挂载到每个节点上但如果节点上连/opt/cni/bin目录都不存在挂载就会失败。解决方法是确保每个节点上都有这个目录或者手动安装containernetworking-plugins软件包补齐基础二进制。然后重新应用 CNI 清单等待 Pod 在对应节点上启动节点状态就会恢复。4.4 坑四CoreDNS一直处于Pending状态初始化完成后其他组件都 Running 了唯独 coredns 一直 Pending。看事件信息写的是没有可用节点看起来像是调度不上。当时我搭的是单节点集群控制面节点默认带有node-role.kubernetes.io/control-plane:NoSchedule污点普通 Pod 不允许调度到控制面节点上。CoreDNS 虽然没有设置容忍度但它属于集群必备组件按理说应该能跑起来。真正的原因是节点 NotReady 导致调度器认为没有可用的工作节点。先修好网络插件让节点变 Ready再执行kubectl rollout restart deployment coredns -n kube-system让它重新调度这样 CoreDNS 就正常 Running 了。如果确实只有控制面节点可用也可以给控制面节点去掉污点但学习环境下我更建议先等网络恢复正常。5. 集群搭完之后我建议先做的几件事集群从 NotReady 变 Ready很多人就觉得大功告成了其实后面还有几件事建议尽快做不然用起来很别扭。5.1 安装一套管理和监控能力裸 k8s 的 kubectl 只能看到状态看不到资源使用情况。想执行kubectl top node看节点负载必须提前安装 metrics-server。这是一个轻量组件负责从 kubelet 收集指标数据。没装它之前kubectl top会直接报错提示 Metrics API 不可用。如果希望有一个可视化界面可以考虑部署 Kubernetes Dashboard 或类似的管理界面。部署完以后它默认创建的账号只有基础权限正式使用前需要创建 ServiceAccount 并绑定 RBAC 权限。这里要特别小心不要图省事给账号绑定 cluster-admin权限敞开会很危险。5.2 尽早规划命名空间和资源配额测试集群阶段只用一个 default 命名空间没问题但一旦多人共用最好建几个独立命名空间比如 dev、test、prod给每个命名空间设置资源配额和限制范围。这一步比想象中更重要因为 k8s 默认情况下不对资源做限制某个测试任务可能把整台机器内存吃满影响其他应用。我之前遇到过同事在测试环境跑了一个没有资源限制的批处理任务结果把节点的内存消耗到了临界点其他 Pod 被 OOMKilled。后来给每个命名空间都加了 ResourceQuota 和 LimitRange这类问题基本绝迹。5.3 对etcd做备份并了解升级路径k8s 集群里真正需要持续备份的是 etcd它保存了集群几乎所有状态。如果没做备份某天 etcd 数据损坏整个集群可能直接瘫痪。实际操作中可以直接使用 etcd 的 snapshot 功能把快照文件保存到安全位置并定时执行。建议在节点上写一个 cron 脚本定期备份定期恢复测试也顺手做一次确保备份真的能恢复。升级方面要记住两条原则一是版本升级不能跨级跳要一步步来二是升级顺序应该是先控制面节点再工作节点不能反着来。每升一个小版本之前先执行kubeadm config images pull把新版本的镜像拉到本地确认没问题再开始。6. 在Ubuntu上折腾k8s我最想告诉你的一些经验整个流程走下来我有一个很深的体会手动搭一套 k8s 集群真正困难的不是执行那些命令而是在出错之后能不能有清晰的排查思路。所有的错误日志都有它的逻辑kubelet 日志里写 CRI 连不上就去检查容器运行时Pod 事件里写网络不正常就去检查 CNI 插件和内核模块。把“报错 - 查日志 - 定位假设 - 验证修改”这条链路跑熟了以后遇到任何组件问题都不会慌。再分享一个特别实用的小技巧执行kubeadm init或者kubeadm join之前先手动跑一遍镜像预拉取命令是kubeadm config images pull。这一下能提前暴露所有镜像拉取问题避免在正式流程中途卡住。我当时就是靠这个把 sandbox 镜像的问题提前发现的省了不少时间。最后还有一句话想说学习 k8s 最有效的路径不是看文档也不是看视频而是自己在 Ubuntu 上从零搭一套集群然后把集群故意弄坏几个地方再自己修好。这个过程恍恍惚惚会让你觉得繁琐但等你能不看参考就说出控制面各个组件是干什么的、Pod 启动的生命周期是怎样的“k8s 是什么”这个问题的答案就不再是书上的一句话而是你亲手验证过的经验了。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询