Kubernetes集群从零搭建:kubeadm初始化到生产级高可用

发布时间:2026/9/26 5:20:57
Kubernetes集群从零搭建:kubeadm初始化到生产级高可用 第1篇里我把虚拟机、Ubuntu系统、基础网络和主机名解析都准备好了本以为K8s安装能一路顺风结果从敲下第一条命令到集群真正Running前后还是折腾了一下午。这篇把完整的安装过程记录下来包括核心组件安装、kubeadm初始化、CNI网络插件以及Metrics Server、Dashboard、Ingress这些常用插件最后补上三Master高可用和证书续期的规划争取让你从零得到一个能用的集群少走我踩过的弯路。1. 版本选型是安装成败的第一个坑kubeadm、kubelet、kubectl与容器运行时如何配平很多人第一次装K8s失败不是命令敲错而是版本之间互相打架。K8s这个系统对组件版本一致性要求极高你在网上随手搜到一个教程用的可能是K8s 1.23配Docker另一个教程是1.28配containerd如果照着混着装轻则初始化失败重则集群起来后节点反复CrashLoop。1.1 为什么1.24版本之后不用docker也能跑K8s先说一个绕不开的背景K8s在1.24版本正式移除了内嵌的dockershim组件也就是说从这一刻起K8s不再直接兼容Docker作为容器运行时。Docker本身是披着守护进程外衣的容器工具集它内部依赖containerd来真正管理容器而K8s现在选择了直接面对containerd跳过了中间那一层好处是链路更短、开销更小、故障点更少。对我们使用者来说最大的变化是不用再装docker这个庞然大物只需要一个containerd就够了它体积小、占用低和K8s的集成也更顺畅。在我的集群里容器运行时直接选用containerd 1.7.x这是当前最稳妥的版本线和K8s 1.29配合没有任何问题。1.2 版本匹配规则的通俗理解可以把K8s集群想象成一辆赛车kubeadm是总装引导程序负责把整车零件组装起来kubelet是引擎控制单元在每台节点上维持容器存活kubectl是方向盘让你在远端操控集群。这三样东西必须围绕同一个目标版本工作。我把版本对应关系整理成了一张表照着选就不会错组件版本选择说明操作系统Ubuntu 22.04 LTS内核5.15以上兼容性最好容器运行时containerd 1.7.x替代DockerK8s官方推荐kubeadm1.29.x与K8s目标版本一致kubelet1.29.x与K8s目标版本一致kubectl1.29.x客户端版本不低于服务端即可最省事的做法kubeadm、kubelet、kubectl三个二进制全部选择相同的1.29.x小版本不要混用不同的minor版本。K8s官方支持kubelet比控制面低一个小版本但在学习阶段没必要给自己制造这种不确定性统一切齐。1.3 系统准备阶段容易漏掉的三个细节这三个细节如果不做kubeadm init阶段一定会报错而且报错信息比较抽象排查起来很费时间。关闭swap。K8s从1.28开始默认要求节点禁用swap即使你硬开着kubelet的启动参数也得显式允许swap才能跑起来。我在三台节点上都执行了swapoff -a并把/etc/fstab里的swap条目注释掉保证重启后不会卷土重来。检查方式free -hSwap显示为0就对了。加载内核模块并开启转发。containerd和Kubelet都依赖br_netfilter模块来实现iptables规则对桥接流量的过滤还要开启net.ipv4.ip_forward才能让Pod流量正常路由。我写了一段配置到/etc/modules-load.d/containerd.conf和/etc/sysctl.d/k8s.conf执行sysctl --system后永久生效。主机名解析。每台节点的主机名不要叫localhost并且在/etc/hosts里写上本机IP和主机名的映射。这个细节后面会在kubelet注册节点、kubeadm配置API Server地址时用到省略掉会变得非常混乱。2. 安装核心组件containerd配置、软件源、三个二进制工具环境检查通过之后才算进入真正的安装环节。这一阶段要完成三件事把容器运行时装好并校准参数添加K8s软件源安装kubeadm、kubelet、kubectl三个二进制。2.1 containerd安装与cgroup驱动校准安装containerd很简单Ubuntu自带软件源里就有直接apt install containerd。但这里有个隐藏得很深的坑cgroup驱动必须改成systemd。K8s生态里kubelet默认使用systemd作为cgroup驱动而containerd的默认配置是cgroupfs两者不一致会导致节点上的Pod资源管理混乱症状是kubelet健康检查失败、Pod一直处于ContainerCreating。解决办法是重新生成containerd的默认配置然后把SystemdCgroup改成truesudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml /dev/null sudo sed -i s/SystemdCgroup false/SystemdCgroup true/ /etc/containerd/config.toml sudo systemctl restart containerd如果拉取镜像速度不理想可以在config.toml的[plugins.io.containerd.grpc.v1.cri]下配置registry.mirrors填入国内可用的镜像加速地址之后kubeadm init拉取控制面组件镜像时会快很多。2.2 添加K8s apt源并用apt-mark hold固定版本K8s的二进制不在Ubuntu默认源里需要手动添加Kubernetes官方apt镜像源我用的是阿里云镜像站速度和稳定性都可接受sudo apt-get update sudo apt-get install -y apt-transport-https curl curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add - cat EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main EOF sudo apt-get update sudo apt-get install -y kubelet1.29.2-1.1 kubeadm1.29.2-1.1 kubectl1.29.2-1.1三条命令装完后我立刻执行了apt-mark hold kubelet kubeadm kubectl。这一步很多人会漏掉如果不hold住版本后面每次apt upgrade都可能把K8s组件升级到不兼容的版本导致集群悄悄变坏。锁定版本之后升级动作完全由你掌控这在生产环境里尤其重要。2.3 为什么安装完不能直接启动kubelet装完三个二进制后有一个和直觉相反的操作不要手动启动kubelet也不要设置开机自启后再手动start。原因在于kubelet需要从kubeadm生成的配置文件中读取参数而此时集群尚未初始化没有任何配置文件手动启动只会让kubelet进入反复失败的状态还会在/var/lib/kubelet下留下半成品文件干扰后续初始化。正确的姿势是安装好后保持kubelet的enabled状态交给kubeadm init去完成首次启动。你只需要确认systemctl status kubelet能显示inactive没有报错日志就说明环境是干净的。之后集群初始化的那一刻kubeadm会把控制面组件拉起kubelet才正式进入工作状态。3. kubeadm init实战参数设计、镜像预拉取、报错排查核心组件齐全后进入整个搭建过程的关键动作kubeadm init。这一条命令负责把API Server、etcd、Scheduler、Controller-Manager全部启起来输出一段包含Join命令的结果。很多人在这一步卡住这里把我用的参数和遇到过的问题逐一说明。3.1 三个核心参数的作用与选择逻辑我执行的初始化命令如下sudo kubeadm init \ --apiserver-advertise-address192.168.31.10 \ --pod-network-cidr172.16.0.0/16 \ --kubernetes-versionv1.29.2逐个解释参数的用意--apiserver-advertise-address是API Server对外公布的IP地址。在这里填的是控制面节点的内网IPWorker节点会通过这个IP访问API Server。如果机器上有多块网卡这个参数尤其关键不指定的话kubeadm可能选错网卡。--pod-network-cidr是分配给Pod的虚拟网段。这个参数必须和后面CNI插件要求一致。我选择172.16.0.0/16是因为该网段和我实际的物理网段192.168.x.x不重叠。规划Pod网段时务必检查一下路由表避免和公司网络或服务器内网冲突。--kubernetes-version要和你安装的kubeadm版本一致我装的是1.29.2这里也写v1.29.2kubeadm会按照版本去拉取对应镜像。至于--service-cidr默认是10.96.0.0/12除非你有特殊冲突否则不需要动。3.2 config images pull之后你拿到了什么初始化之前我习惯先执行kubeadm config images pull把控制面镜像全部拉到本地。这个命令的用意是把网络延迟和初始化过程解耦等真正执行init时kubeadm就不需要在线等镜像拉取成功率和速度都更有保障。sudo kubeadm config images pull --kubernetes-versionv1.29.2这个命令会拉取大概十几个镜像包括kube-apiserver、kube-controller-manager、kube-scheduler、etcd、coredns、pause等。每个镜像都带了sha256校验拉取完成后可以用crictl images查看。如果这里出现网络超时优先检查镜像加速配置是否正确而不是反复重试。3.3 初始化过程中的高频报错和对应解法就算前面准备齐全初始化依然有概率报错。我把实操中遇到的报错整理了一下方便你对照排查报错现象根本原因处理方式[ERROR Swap]: running with swap on is not supportedswap未关闭swapoff -a并注释fstab对应条目[ERROR CRI]: container runtime is not runningcontainerd配置异常或重启失败检查systemctl status containerd确认SystemdCgrouptruekubelet.service: Failed to startcgroup驱动不一致校准containerd的SystemdCgroup配置后重启connection refused访问6443端口API Server尚未健康通常和镜像拉取慢有关先kubeadm config images pull保证镜像就位failed to create cluster CA存在历史残留执行sudo kubeadm reset清理后重试其中最高频的是第一个和第三个基本上一半的失败案例都源于swap和cgroup驱动。如果你照上面的步骤做这两项应该已经被提前规避掉了。3.3.1 初始化成功后的kubectl配置当初始化输出最后出现Your Kubernetes control-plane has been initialized successfully!第一步是把管理员的kubeconfig复制到~/.kube不然kubectl无法连上APIServermkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config到这里控制面节点已经具备一个未装备网络的K8s集群。此时执行kubectl get nodes会看到控制面节点状态是NotReady这是正常现象因为Pod之间还没有网络通路。下一章解决的就是这个问题。4. CNI网络插件安装Calico的选择理由与验证路径控制面初始化完成只是骨架真正让集群活过来的关键一步是安装CNI网络插件。没有它Pod之间、Pod和Service之间根本无法通信所有工作负载都起不来。4.1 没有网络插件时集群的假Ready状态很多新手看到kubectl get nodes显示NotReady就慌了但这里要区分原因。控制面节点初始完成后kubelet会尝试启动一个叫coredns的Pod但因为节点上没有任何CNI插件Pod网络无法建立coredns会一直处于ContainerCreating或Pending状态。如果你去看systemctl status kubelet还会发现大量网络相关的报错日志。这不是kubelet崩溃也不是初始化失败只是缺少一个负责给Pod分配IP并把流量路由到正确位置的组件。CNI插件的职责就是在每台节点上创建虚拟网卡、维护路由规则、设置iptables让跨节点的Pod通信像在同一个局域网里一样顺畅。4.2 Calico安装与Pod网段对齐CNI插件有很多Flannel、Calico、Cilium是三个主流选择。Flannel最简单但功能也最单薄Cilium功能最强但依赖较新的内核和ebpf特性。我的选择是Calico理由有三个网络策略支持完善性能比Flannel好一个档次部署复杂度适中。安装Calico需要先和初始化时的--pod-network-cidr对齐。我用的是172.16.0.0/16所以需要在Calico的manifest中将CALICO_IPV4POOL_CIDR改成同一个网段否则IP池和Pod CIDR对不上Calico初始化会报错。kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/calico.yaml如果你是按我前面的--pod-network-cidr172.16.0.0/16初始化的这一步不用改任何参数直接apply即可。如果你的Pod网段不同先下载yaml到本地搜索CALICO_IPV4POOL_CIDR改为你的网段再apply。安装后我验证了一下Calico组件的状态kubectl get pods -n calico-system等到所有calico-node和calico-typha都处于Running状态大约需要一两分钟然后再执行kubectl get nodes所有节点都会变成Ready。4.3 从CoreDNS到跨节点Pod的完整网络验证节点变Ready不代表网络完全没问题我习惯按从内到外的顺序做三层验证第一层系统组件。观察kube-system命名空间下coredns的状态是否从ContainerCreating变成Running。这一步能确认核心系统Pod已经能正常通信。第二层Service解析。创建一个临时Pod在Pod内部执行nslookup kubernetes.default.svc.cluster.local检查DNS解析是否正常。如果解析失败多半是coredns问题要去看它的日志。第三层跨节点通信。分别在两个不同节点的Pod里互相ping对端IP确认数据包能跨物理机转发。这一步验证的是Calico的overlay网络是否真正工作。三层验证全部通过后集群的网络基础算是彻底打牢了。接下来可以放心安装各种周边插件。5. 生产环境必备的三件套插件Metrics、Dashboard、Ingress一个只有控制面和网络插件的K8s集群只是个空壳真正日常运维要用到的插件至少有三个Metrics Server提供资源指标Dashboard提供可视化界面Ingress Controller收口外部流量。这一章把每个插件安装时容易踩的坑说透。5.1 Metrics Serverkubectl top和HPA的前提Metrics Server的作用是采集每个节点和Pod的CPU、内存使用率。没有它kubectl top node和kubectl top pod会直接报错Horizontal Pod AutoscalerHPA也无法工作因为HPA依赖Metrics API的数据。安装命令非常简单kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml但安装后我在测试时发现Pod报了一个x509 certificate错误。原因是Metrics Server访问kubelet时kubelet的证书是自签的没有经过CA信任。网上的常见解法是给Metrics Server的Deployment加一个--kubelet-insecure-tls参数。这个参数在测试学习环境完全够用但如果你追求更严谨的安全配置应该把kubelet的CA证书挂载进去而不是禁用TLS校验。改完参数后执行kubectl top nodes能看到每台节点的CPU和内存利用率说明Metrics Server正常工作。5.2 Dashboard管理员令牌与安全访问Kubernetes Dashboard是官方Web UI能可视化查看工作负载、各种资源的YAML、日志和事件。安装同样是一句applykubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml默认情况下Dashboard绑定的是ClusterIP外部访问不便我把它改成了NodePort方式通过kubectl -n kubernetes-dashboard edit svc kubernetes-dashboard把type改为NodePort然后就能用http://节点IP:NodePort访问了。访问时最让人一头雾水的是登录认证。Dashboard默认会跳转到登录页要求输入Kubeconfig或Token。我用了一个只读的admin-user账号来获取永久有效的Tokenkubectl -n kubernetes-dashboard create serviceaccount admin-user kubectl -n kubernetes-dashboard create clusterrolebinding admin-user --clusterrolecluster-admin --serviceaccountkubernetes-dashboard:admin-user kubectl -n kubernetes-dashboard create token admin-user最后一条命令的输出就是登录Token粘贴到Dashboard的Token登录框里即可。注意这个Token有有效期过期后重新执行create token就能拿到新Token不需要重建ServiceAccount。5.3 Ingress Controller入口流量的统一收口当集群内部跑的服务越来越多你肯定不希望每个服务都暴露一个NodePort或LoadBalancer这时候就需要Ingress Controller。它在集群里扮演流量闸门的角色通过域名或路径规则把外部请求转发到不同Service。我安装的是ingress-nginx这是最主流的方案。裸金属环境用baremetal版本的部署文件kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/baremetal/deploy.yaml安装后需要确认ingress-nginx-controller这个Pod处于Running状态。它默认会占用节点的80和443端口相当于把所有对外流量统一收口。之后你只要创建Ingress资源并配置host规则就能用域名访问集群内服务了。比如我给一个测试应用配了一条host: app.example.com的Ingress请求到达Ingress Controller后就会被转发到对应的ClusterIP Service上。6. 面向未来的两步规划三Master高可用与证书自动续期如果你搭集群只是本地学习单控制面节点完全够用。但一旦想让集群承载真实业务或者哪怕只是认真练一下生产部署三台Master的高可用架构和证书续期问题就应该提前想清楚否则后面迁移架构的代价比现在装一百个插件都大。6.1 一台控制面迟早会遇到的问题单控制面节点的最大问题不是性能而是单点故障。控制面节点上运行着etcd、API Server、Scheduler和Controller-Manager一旦这台机器宕机整个集群的管理面就瘫痪了虽然已经运行的Pod还在但调度、自愈、滚动更新全部停止这在生产上是不可接受的。要解决这个问题标准方案是部署三台Master节点配合一个虚拟IP作为所有Master的入口。工作节点统一访问VIP而不是直接访问某一台Master的IP。VIP背后的流量转发可以用keepalived加haproxy实现也可以使用外部负载均衡器。不过手动搭建keepalived加haproxy的链路比较繁琐如果你的目标是快速搭建高可用集群可以试试KubeKey这个工具它内部封装了高可用方案一条命令就能拉起多Master集群省去手工配置存活探活和负载均衡的步骤。6.2 三台Master的初始化命令与节点加入如果你选择手动搭建核心思路是第一台Master初始化时通过--control-plane-endpoint指定VIP后面的Master用kubeadm join配合--control-plane参数加入。注意一个大前提三台Master的系统环境必须保持一致包括版本、containerd配置、主机名解析。任何不一致都会在join阶段暴露出来。第一台Master初始化后输出里会有一段kubeadm join VIP:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx。第二台和第三台Master只需要在这条命令后面加上--control-plane即可sudo kubeadm join 192.168.31.100:6443 \ --token your_token \ --discovery-token-ca-cert-hash sha256:your_hash \ --control-planeWorker节点不加--control-plane直接join即可。整个集群加入完毕后用kubectl get nodes应该看到三台Master和一个或多个Worker全部处于Ready状态。6.3 证书续期的自动化和常见误区K8s控制面证书的有效期默认是一年包括etcd证书、API Server证书、kubelet证书等。新版本的kubeadm会在证书到期前自动续期控制面证书但前提是集群正常运行、kubelet能连上API Server且主节点在线。如果你的集群长期停机超过一年恢复时就会遇到证书过期导致的认证失败。我通常会做两手准备一方面依赖kubeadm的自动续约机制另一方面通过systemd timer每月强制执行一次kubeadm certs renew all。注意续期只是重签证书真正要生效还需要重启kubelet和静态Podsudo kubeadm certs renew all sudo kubeadm certs check-expiration sudo systemctl restart kubelet另外一个常见的误区是kubeadm certs renew后旧的Cluster CA没有更新这会有什么影响实际上只要集群的CA/etc/kubernetes/pki/ca.crt没有过期控制面各服务端的证书续期是由同一个CA重新签发客户端依然信任这个CA所以无需滚动重建集群。只有当CA自身即将到期时处理起来才真正麻烦需要手动替换CA并将新CA分发到所有节点这属于另一个量级的运维操作。顺便提醒etcd证书也和控制面证书一起管理不要单独手动删除/etc/kubernetes/pki/etcd下的文件那会让整个集群的存储层失联。如果集群里后续要跑GPU推理或大规模监控还可以在此基础上扩展部署NVIDIA device plugin和Prometheus Operator但这些都属于业务插件的范畴和本次基础安装已经不在同一个阶段了。先把这套最小可用集群跑稳再一步步往里加东西才是不容易翻车的节奏。