从零开始手动构建Kubernetes集群,很多人以为只要执行三条命令就能完成,实际操作中却经常卡在镜像拉取超时、kubelet起不来、网络插件一直Pending这些问题上。要稳定地把集群跑起来,必须先理解Kubernetes对操作系统和容器运行时的底层要求。本文不使用云厂商托管服务,也不使用一键脚本,而是从干净的Linux节点开始,逐步完成控制平面和工作节点的配置。整个过程涉及节点规划、运行时准备、Kubernetes组件安装、网络方案部署以及集群可用性验证。

在开始之前,先明确实验环境。测试使用三台Ubuntu 22.04服务器,一台作为控制平面节点,两台作为工作节点。主机名分别为k8s-master、k8s-node1、k8s-node2。控制平面节点建议至少2核CPU和2GB内存,工作节点至少1核CPU和1GB内存。生产环境可以适当提高配置,但学习用途下这些资源足够完成基础部署。
节点规划与基础环境准备
手动构建集群的第一步不是安装Kubernetes,而是把操作系统状态调整到符合kubelet和容器运行时的要求。最重要的一项操作是关闭swap。Kubernetes的调度和资源管理假设节点内存是真实可用的,swap会导致Pod被调度到内存不足的节点后性能急剧下降。执行下面的命令关闭当前会话中的swap,并修改/etc/fstab让重启后依然生效。
# 临时关闭swap sudo swapoff -a # 确认swap已经被关闭 free -m # 永久关闭swap:注释掉/etc/fstab中包含swap的行 sudo sed -i '/ swap / s/^/#/' /etc/fstab
接下来加载内核模块并调整网络参数。Kubernetes需要节点开启IP转发,并且让iptables能够正确处理经过网桥的流量。具体做法是加载overlay和br_netfilter两个模块,然后写入sysctl配置。overlay模块用于容器分层文件系统,br_netfilter用于让内核在网络桥接设备上调用iptables规则。
# 加载内核模块 sudo modprobe overlay sudo modprobe br_netfilter # 持久化内核模块配置 cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF # 设置网络转发和桥接过滤参数 cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF # 立即应用sysctl配置 sudo sysctl --system
容器运行时选择containerd,这是当前Kubernetes社区使用最广泛的运行时。安装containerd后需要修改它的配置文件,确保cgroup驱动与kubelet保持一致。containerd默认使用systemd作为cgroup驱动,但部分旧版本可能使用cgroupfs。如果两个组件驱动不一致,kubelet无法正确读取容器资源使用情况,节点会出现不可调度或Pod反复重启的问题。
# 安装containerd sudo apt-get update sudo apt-get install -y containerd # 生成默认配置 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml >/dev/null # 修改cgroup驱动为systemd sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml # 重启containerd sudo systemctl restart containerd sudo systemctl enable containerd
所有基础配置需要在每一台节点上重复执行,包括控制平面和工作节点。做完之后可以用sudo systemctl status containerd确认运行时状态。如果containerd启动失败,优先检查config.toml格式是否被sed命令破坏。另一个容易忽略的点是镜像仓库代理,国内网络环境建议配置registry mirror,但本文不展开,因为不同用户可用的加速地址差异较大。
安装Kubernetes组件并初始化控制平面
基础环境准备好以后,才能安装kubeadm、kubelet和kubectl三个核心包。kubeadm负责初始化集群和加入节点,kubelet是每个节点上运行的代理,kubectl用于与集群交互。安装时需要指定Kubernetes的软件源。这里以1.28版本为例,实际搭建时可以替换成目标版本。
# 添加Kubernetes软件源 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.28/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.28/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list # 安装组件 sudo apt-get update sudo apt-get install -y kubelet kubeadm kubectl # 锁定版本,避免意外升级导致集群版本不一致 sudo apt-mark hold kubelet kubeadm kubectl
安装完成后,先不要着急执行kubeadm init。可以先通过kubeadm config images list查看需要的镜像列表,然后使用kubeadm config images pull预拉取镜像。很多用户失败在init阶段,就是因为镜像拉取缓慢或者被阻断。预拉取可以提前暴露网络问题,也方便在控制平面节点上做针对性的镜像导入。
# 查看需要使用的镜像 kubeadm config images list # 预拉取控制平面所需镜像 sudo kubeadm config images pull
接下来初始化控制平面。--pod-network-cidr参数必须与后续选择的网络插件网段一致。使用Calico时通常设置为10.244.0.0/16,使用Flannel时也是这个网段。如果这个参数和网络插件不匹配,Pod之间无法正常通信,集群虽然启动但实际不可用。--control-plane-endpoint参数在单控制平面场景下可以省略,但生产环境建议设置为主机IP或负载均衡地址。
# 初始化控制平面 sudo kubeadm init \ --pod-network-cidr=10.244.0.0/16 \ --kubernetes-version=v1.28.0 \ --upload-certs
初始化成功后会输出一段包含kubeadm join命令的文本。这段文本需要保存下来,因为工作节点加入时会用到其中的token和discovery-token-ca-cert-hash。如果token过期,可以通过kubeadm token create --print-join-command重新生成。接着管理员需要配置kubectl访问集群。对于普通用户,把admin.conf复制到用户目录并修改权限即可。
# 配置当前用户的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
此时执行kubectl get nodes会看到控制平面节点处于NotReady状态,这是因为还没有部署网络插件。NotReady并不是控制平面组件有问题,而是kubelet发现节点缺少CNI网络插件,无法为Pod分配网络。下一步需要安装Calico或者Flannel来解决这个问题。
部署网络插件并加入工作节点
Kubernetes本身不提供容器网络实现,CNI插件负责Pod IP分配、跨节点路由和网络策略。Calico是生产环境常用的方案,它支持BGP路由和网络策略,性能也比较稳定。部署Calico可以使用官方清单文件,但需要确保清单中的Pod网段与kubeadm init时设置的--pod-network-cidr一致。
# 部署Calico网络插件 kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml # 查看网络插件Pod状态 kubectl get pods -n kube-system
Calico的控制平面组件包括calico-node和calico-kube-controllers。calico-node以DaemonSet方式运行在每个节点上,负责管理路由和网络接口。等待所有calico-node进入Running状态后,控制平面节点会从NotReady变为Ready。如果网络插件Pod一直Pending或CrashLoopBackOff,需要检查节点是否有多余的路由规则,以及kubelet的CNI配置目录/opt/cni/bin是否包含必要的二进制文件。
控制平面就绪后,切换两台工作节点,执行之前保存的kubeadm join命令。加入过程会自动拉取工作节点需要的镜像,并启动kubelet。工作节点不需要安装kubectl,但kubeadm和kubelet必须存在。加入成功后返回控制平面节点执行kubectl get nodes,应该能看到三台节点都在Ready状态。
# 在工作节点执行加入命令,以下为示例 sudo kubeadm join 192.168.1.10:6443 \ --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxx # 回到控制平面查看节点状态 kubectl get nodes -o wide
如果加入命令提示token错误或证书校验失败,不要重复使用过期的token。在控制平面执行kubeadm token create --print-join-command可以生成新的完整命令。如果工作节点之前加入过其他集群,需要先执行sudo kubeadm reset清理旧配置,否则会发生证书冲突。
集群验证与常见问题排查
所有节点Ready之后,手动部署流程并没有结束,还需要通过实际业务Pod验证集群网络和服务发现是否正常。最简单的方式是创建一个nginx Deployment,并暴露NodePort服务,然后用节点IP加端口访问。如果访问失败,问题可能出在CNI插件、防火墙或者kube-proxy上。
# 创建测试Pod kubectl create deployment nginx-test --image=nginx # 扩展为多个副本,验证跨节点Pod通信 kubectl scale deployment nginx-test --replicas=2 # 暴露NodePort服务 kubectl expose deployment nginx-test --port=80 --type=NodePort # 查看服务端口 kubectl get svc nginx-test
拿到NodePort端口后,在浏览器访问任意节点IP:端口,应该看到nginx欢迎页面。如果Pod一直处于ContainerCreating状态,先执行kubectl describe pod查看事件。常见原因包括镜像拉取失败、CNI插件未就绪、节点资源不足。如果Pod已经Running但服务无法访问,可以检查kube-proxy是否正常运行,以及iptables规则是否被本地防火墙拦截。
手动构建的集群还需要关注证书和升级问题。Kubernetes控制平面证书默认有效期为一年,超过后kubeadm需要执行续期操作。可以通过kubeadm certs check-expiration查看证书到期时间。集群升级时,应先升级控制平面,再升级工作节点,并且不允许跨多个次要版本直接跳级。这些维护操作虽然不在初次构建范围内,但知道它们的存在可以让集群生命周期管理更清晰。
# 查看证书有效期 sudo kubeadm certs check-expiration # 续期所有证书 sudo kubeadm certs renew all # 重启控制平面组件使新证书生效 sudo systemctl restart kubelet
整个手动构建过程下来,真正困难的不是执行kubeadm init这一步,而是前期对系统环境、容器运行时和网络插件的统一配置。很多教程只给出三条核心命令,忽略了那些导致失败的环境细节。把swap关闭、内核模块加载、cgroup驱动统一、镜像预拉取这几件事做好,再配合网络插件的正确网段,集群搭建的成功率会大幅提高。完成后可以继续部署Dashboard、Metrics Server或者Ingress Controller,逐步扩展集群的观测和入口能力。
Kubernetes集群手动部署kubeadm修改时间:2026-09-19 03:05:37