1. 项目概述为什么选择 Kubeadm 部署 K8s 集群如果你正在接触云原生或者你的团队正从单体应用向微服务架构转型那么搭建一个属于自己的 Kubernetes 集群几乎是绕不开的“成人礼”。市面上部署 K8s 的方式五花八门从一键脚本到各大云厂商的托管服务再到像 Kubeadm、Kubespray、RKE 这样的工具。今天我想和你深入聊聊Kubeadm这种方式。为什么是 Kubeadm简单来说它是 Kubernetes 官方出品、用于引导最佳实践集群的“脚手架”工具。它不像 Minikube 或 Kind 那样只为单节点学习设计也不像直接二进制部署那样繁琐到让人望而却步。Kubeadm 在“完全控制”和“部署复杂度”之间找到了一个绝佳的平衡点。它帮你处理了证书生成、核心组件静态 Pod 清单生成、etcd 集群初始化等一堆令人头疼的“脏活累活”但又把网络、存储、负载均衡器这些与具体环境强相关的组件选择权完全交还给你。这意味着你通过 Kubeadm 搭建的集群其架构和组成与生产环境中许多经过验证的集群是一致的你学到的每一个组件、看到的每一个配置文件都具有直接的参考价值。这个项目就是带你从零开始用 Kubeadm 搭建一个高可用的生产就绪或至少是准生产环境的 Kubernetes 集群。我们会涵盖从环境准备、组件安装、集群初始化、网络插件部署到将工作节点加入集群并验证核心功能的完整流程。无论你是运维工程师、开发人员还是架构师只要你想深入理解 K8s 的底层骨架而不仅仅是会敲kubectl命令那么跟着走一遍这个过程收获会远超你的预期。2. 集群架构设计与前置准备在动手敲命令之前清晰的架构设计和周全的环境准备是成功的一半。盲目开始往往会在中途遇到各种版本冲突、端口占用或配置错误导致推倒重来。2.1 集群节点规划与角色定义我们计划搭建一个包含三个节点的集群一个控制平面节点Master和两个工作节点Worker。在生产环境中控制平面通常需要至少三个节点以实现高可用但为了简化初次搭建的复杂度我们先从单 Master 开始理解核心流程。后续扩展到多 Master 高可用架构原理是相通的。控制平面节点 (k8s-master)角色 运行集群的管理组件如 API Server、Scheduler、Controller Manager 以及 etcd默认情况下Kubeadm 会将 etcd 也部署在此节点上形成 Stacked etcd 拓扑。对于生产环境更推荐将 etcd 部署为独立集群。资源建议 至少 2核 CPU4GB 内存20GB 磁盘。实际生产环境需要根据集群规模大幅增加。关键任务 初始化集群持有集群的“大脑”和“状态存储”。工作节点 (k8s-node1, k8s-node2)角色 运行用户部署的实际工作负载Pod。上面有 Kubelet 和容器运行时如 Docker 或 Containerd。资源建议 根据你计划运行的业务负载决定。学习环境至少 1核2G。关键任务 执行来自控制平面的调度指令管理容器的生命周期。所有节点需要满足以下共同前提条件操作系统 推荐使用 Ubuntu 20.04/22.04 LTS 或 CentOS 7/8 Stream 等主流 Linux 发行版。本文以 Ubuntu 22.04 为例。网络各节点间网络互通且主机名解析正确可通过/etc/hosts或内部 DNS 配置。确保所需端口开放如 API Server 的 6443kubelet 的 10250 等。如果节点间有防火墙需预先放行。关闭 Swap 交换分区。Kubernetes 认为 Swap 的启用会导致性能和稳定性问题尤其是内存不足时的调度行为不可预测。sudo swapoff -a # 临时关闭 # 永久关闭注释掉 /etc/fstab 中 swap 相关的行容器运行时 K8s 1.24 版本后默认不再捆绑 Docker你需要自行安装兼容的容器运行时如 Containerd 或 Docker Engine。我们选择Containerd它更轻量是未来趋势。Kubernetes 组件 在所有节点上安装kubeadm、kubelet和kubectl并确保版本一致。2.2 系统环境初始化实操假设我们有三台全新的 Ubuntu 22.04 虚拟机IP 分别为 192.168.1.10 (master), 192.168.1.11 (node1), 192.168.1.12 (node2)。第一步基础配置所有节点执行# 1. 设置主机名并更新 hosts 文件以 master 节点为例 sudo hostnamectl set-hostname k8s-master # 在 node1 和 node2 上分别设置为 k8s-node1, k8s-node2 # 编辑 /etc/hosts在所有节点上添加以下内容 cat EOF | sudo tee -a /etc/hosts 192.168.1.10 k8s-master 192.168.1.11 k8s-node1 192.168.1.12 k8s-node2 EOF # 2. 关闭 Swap sudo swapoff -a sudo sed -i / swap / s/^\(.*\)$/#\1/g /etc/fstab # 永久关闭注释相关行 # 3. 加载内核模块并修改系统参数 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 sudo sysctl --system注意net.ipv4.ip_forward 1对于 Pod 网络通信至关重要。很多网络插件如 Calico、Flannel都依赖于此。第二步安装容器运行时 Containerd所有节点执行# 1. 安装依赖 sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common # 2. 添加 Docker 官方 GPG 密钥Containerd 由 Docker 项目维护 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository deb [archamd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable # 3. 安装 Containerd sudo apt-get update sudo apt-get install -y containerd.io # 4. 配置 Containerd 使用 systemd 作为 cgroup 驱动与 K8s 保持一致 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml # 编辑配置文件将 SystemdCgroup 设置为 true sudo sed -i s/SystemdCgroup false/SystemdCgroup true/g /etc/containerd/config.toml # 5. 重启并设置开机自启 sudo systemctl restart containerd sudo systemctl enable containerd实操心得这里最容易出错的是cgroup驱动不一致。Kubeadm 默认使用systemd作为cgroup驱动而 Containerd/Docker 的默认驱动可能是cgroupfs。如果不一致kubelet将无法启动。通过上面的配置我们强制 Containerd 也使用systemd。3. 核心组件安装与集群初始化环境就绪后我们开始安装 Kubernetes 的核心“三件套”kubeadm、kubelet、kubectl。3.1 安装 Kubeadm、Kubelet 和 Kubectl所有节点执行# 1. 添加 Kubernetes 源 sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg echo deb [signed-by/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main | sudo tee /etc/apt/sources.list.d/kubernetes.list # 2. 安装指定版本这里以 1.28.0 为例请根据实际情况选择稳定版本 sudo apt-get update sudo apt-get install -y kubelet1.28.0-00 kubeadm1.28.0-00 kubectl1.28.0-00 sudo apt-mark hold kubelet kubeadm kubectl # 防止被意外升级为什么锁定版本生产环境中集群内所有节点的组件版本必须严格一致尤其是kubelet。apt-mark hold可以防止系统自动更新时破坏版本一致性。安装后kubelet服务会处于不断重启的状态这是正常的因为它还在等待kubeadm初始化集群时提供的配置文件。3.2 使用 Kubeadm 初始化控制平面仅在 Master 节点执行这是最核心的一步。kubeadm init命令会做很多事情生成 CA 证书和各类组件的证书、生成kubeconfig文件、启动控制平面组件的静态 Pod通过kubelet来管理并输出将工作节点加入集群的命令。在初始化前我们需要决定一个关键的参数Pod 网络 CIDR。这是分配给集群中每个 Pod 的 IP 地址段不能与节点物理网络冲突。我们选择10.244.0.0/16这是 Flannel 网络插件的默认网段。# 在 k8s-master 节点上执行 sudo kubeadm init \ --apiserver-advertise-address192.168.1.10 \ # 指定 API Server 监听的地址 --image-repository registry.aliyuncs.com/google_containers \ # 使用国内镜像源加速 --kubernetes-version v1.28.0 \ # 与安装版本一致 --service-cidr10.96.0.0/12 \ # Service 网络的 CIDR默认即可 --pod-network-cidr10.244.0.0/16 # Pod 网络的 CIDR必须与后续安装的网络插件匹配如果一切顺利你会看到如下成功信息并包含两条非常重要的指令Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config Alternatively, if you are the root user, you can run: export KUBECONFIG/etc/kubernetes/admin.conf You should now deploy a pod network to the cluster. Run kubectl apply -f [podnetwork].yaml with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.1.10:6443 --token some-token \ --discovery-token-ca-cert-hash sha256:a-very-long-hash立即执行第一条指令配置kubectl访问集群mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config现在在 Master 节点上你已经可以运行kubectl命令了。用kubectl get nodes查看节点状态应该是NotReady因为 Pod 网络插件还没装。3.3 部署 Pod 网络插件CNIKubernetes 本身不负责网络需要第三方 CNI 插件实现 Pod 间通信。我们选择最经典的Flannel。# 在 Master 节点执行 kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml这个 YAML 文件会创建一系列资源ServiceAccount、ClusterRole、ConfigMap、DaemonSet 等。DaemonSet 确保在每个节点上运行一个flanneldPod负责维护节点上的网络规则。等待片刻运行kubectl get pods -n kube-system你应该能看到kube-flannel-ds-xxxxx的 Pod 状态变为Running。再次执行kubectl get nodes节点状态应变为Ready。注意事项CNI 插件选择众多如 Calico功能强大支持网络策略、Cilium基于 eBPF性能和安全特性突出。Flannel 的特点是简单、稳定适合学习和测试环境。如果选择其他插件务必确保其 YAML 配置中的net-conf.json部分指定的Network与kubeadm init时的--pod-network-cidr参数一致。4. 工作节点加入与集群验证4.1 将 Worker 节点加入集群回到初始化成功时输出的那条kubeadm join命令。分别在k8s-node1和k8s-node2上以 root 权限执行它。# 在 k8s-node1 和 k8s-node2 上分别执行命令中的 token 和 hash 是示例请使用你初始化时得到的真实命令 sudo kubeadm join 192.168.1.10:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef执行成功后节点会从 Master 拉取必要的镜像和配置启动kubelet。在 Master 节点上运行kubectl get nodes -w可以实时观察节点加入和状态变化的过程。稍等一两分钟两个节点都应该显示为Ready状态。实操心得kubeadm join命令中的 token 默认 24 小时有效。如果过期了可以在 Master 节点上使用kubeadm token create --print-join-command生成新的加入命令。discovery-token-ca-cert-hash也可以通过openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2/dev/null | openssl dgst -sha256 -hex | sed s/^.* //命令重新计算获取。4.2 核心功能验证与初体验集群搭建完成我们来跑几个测试确保一切正常。测试一查看集群状态和组件健康kubectl get nodes # 所有节点 Ready kubectl get pods -n kube-system # 所有系统 Pod Running kubectl get cs # 查看组件状态新版本已弃用健康检查集成到 API Server # 更推荐使用 kubectl get --raw/readyz?verbose # 检查 API Server 健康测试二部署一个简单的应用# 1. 部署一个 Nginx Deployment kubectl create deployment nginx-test --imagenginx:alpine --replicas2 # 2. 将 Deployment 暴露为 NodePort 类型的 Service kubectl expose deployment nginx-test --port80 --typeNodePort # 3. 查看创建的资源 kubectl get deployments, pods, services你会看到两个nginx-test-xxxx的 Pod 被创建并调度到节点上可能是 master 和 node1。同时创建了一个nginx-test的 Service它有一个集群IP和一个随机分配的NodePort例如32145。测试三访问应用由于我们还没有安装 Ingress 控制器可以通过NodePort直接访问。在任何一台节点上使用节点的 IP 和上面查到的NodePort端口用curl或浏览器访问curl http://任一节点IP:NodePort端口如果返回 Nginx 的欢迎页面恭喜你一个最基本的 K8s 集群已经成功运行起来了5. 生产环境考量与进阶配置我们搭建的只是一个最基础的集群。要用于生产还有几个关键点需要考虑和配置。5.1 移除 Master 节点的调度污点默认情况下Master 节点被打上了node-role.kubernetes.io/control-plane:NoSchedule污点以防止工作负载调度到控制平面影响稳定性。在资源紧张的学习或测试环境你可能希望 Master 也能跑 Pod。kubectl taint nodes k8s-master node-role.kubernetes.io/control-plane:NoSchedule- # 注意末尾的减号警告在生产环境请谨慎执行此操作。控制平面节点应专注于运行集群管理组件。5.2 配置镜像拉取策略与私有仓库从 Docker Hub 拉取镜像可能受限于网络和速率。我们可以配置 Containerd 使用镜像加速器或私有仓库。编辑/etc/containerd/config.toml在[plugins.io.containerd.grpc.v1.cri.registry.mirrors]和[plugins.io.containerd.grpc.v1.cri.registry.configs]部分进行配置。例如添加阿里云镜像加速[plugins.io.containerd.grpc.v1.cri.registry.mirrors.docker.io] endpoint [https://你的镜像加速器ID.mirror.aliyuncs.com]修改后需要重启 Containerdsudo systemctl restart containerd。5.3 高可用HA控制平面单 Master 有单点故障风险。生产环境需要部署多 Master 节点。Kubeadm 支持两种高可用拓扑Stacked etcd etcd 与控制平面组件共存于同一节点。部署简单但故障域耦合。External etcd etcd 运行在独立于控制平面的节点集群上。更复杂但解耦了故障域可靠性更高。使用kubeadm init时通过--control-plane-endpoint参数指定一个负载均衡器如 HAProxy Keepalived的 VIP 或 DNS 名称然后使用kubeadm join命令的--control-plane参数将其他节点加入为控制平面。5.4 日志与监控基础集群不包含日志收集和监控。你需要额外部署日志 EFK 栈Elasticsearch, Fluentd, Kibana或 Loki。监控 Prometheus Grafana 是云原生领域的事实标准。你可以部署kube-prometheus-stack原prometheus-operator它会自动抓取 K8s 集群、节点、Pod 的指标。6. 常见故障排查与维护技巧搭建和运维过程中难免会遇到问题。这里记录几个典型场景和排查思路。6.1 节点 NotReady这是最常见的问题。按以下顺序排查检查kubelet服务状态sudo systemctl status kubelet。如果没运行sudo systemctl start kubelet并sudo journalctl -xeu kubelet查看日志。检查容器运行时sudo systemctl status containerd。确保它正在运行并且cgroup驱动配置正确。检查网络插件 Podkubectl get pods -n kube-system -o wide | grep flannel。查看 Flannel Pod 是否运行在问题节点上以及其日志kubectl logs -n kube-system flannel-pod-name。检查节点信息kubectl describe node node-name。在Conditions部分会明确提示是MemoryPressure、DiskPressure、PIDPressure还是NetworkUnavailable。6.2 Pod 一直处于 Pending 状态这通常是因为调度失败。kubectl describe pod pod-name查看Events部分。常见原因Insufficient cpu/memory 节点资源不足。node(s) didn‘t match Pod’s node affinity/selector 节点选择器或亲和性规则不匹配。0/3 nodes are available: 3 node(s) had taint {node-role.kubernetes.io/control-plane: } 所有节点都有污点Pod 无法容忍。6.3 无法通过 kubectl 访问 API Server检查$HOME/.kube/config文件是否存在且内容正确。可以对比/etc/kubernetes/admin.conf。检查 Master 节点防火墙是否放行了 6443 端口。检查kube-apiserverPod 日志kubectl logs -n kube-system kube-apiserver-k8s-masterPod 名可能不同。6.4 重置节点与清理集群如果搭建过程混乱想重来可以在每个节点上执行sudo kubeadm reset -f sudo rm -rf /etc/cni/net.d $HOME/.kube /etc/kubernetes sudo iptables -F sudo iptables -t nat -F sudo iptables -t mangle -F sudo iptables -X # 如果安装了 Docker还需要 docker system prune -a -f # 对于 Containerd: sudo crictl rmi --prune这个命令会清除 Kubeadm 安装的所有痕迹让节点恢复如初。搭建 Kubernetes 集群就像拼装一台精密的仪器Kubeadm 提供了标准的零件和说明书。走通一遍这个流程你对 K8s 各个组件如何协同工作的理解会深刻得多。下次再遇到 Pod 调度失败或者网络不通你脑子里浮现的将不再是一个黑盒而是一幅清晰的组件交互图知道该从哪个环节入手排查。这才是动手搭建最大的价值。