公司动态

从零搭建K8s集群:基于Kubeadm的实战部署与核心原理详解

📅 2026/8/5 6:50:51
从零搭建K8s集群:基于Kubeadm的实战部署与核心原理详解
1. 从零到一为什么选择Kubeadm搭建你的第一个K8s集群如果你正在看这篇文章大概率是刚接触Kubernetes面对官方文档里一堆部署工具kubeadm, kops, kubespray, minikube, kind...有点眼花缭乱不知道从哪下手。我的建议是对于学习和搭建生产级测试环境Kubeadm是那个“刚刚好”的起点。它不是最自动化的也不是最轻量的但它能让你最清晰地理解一个K8s集群到底由哪些“零件”组成以及它们是如何被组装起来的。简单来说Kubeadm是一个官方提供的、用于快速引导一个符合最佳实践的Kubernetes集群的命令行工具。你可以把它想象成一个“集群安装向导”。它帮你完成了最繁琐、最容易出错的那部分工作生成集群所需的各类证书、创建静态Pod清单来启动控制平面组件如API Server、Controller Manager、Scheduler、生成加入集群所需的令牌等。但它不会帮你安装容器运行时、配置网络插件、或者设置高可用——这些“脏活累活”需要你自己动手。这恰恰是它的价值所在逼着你去理解集群的每一个核心依赖和组件而不是当一个“一键安装”的黑盒用户。我见过不少朋友一开始图省事直接用云厂商的托管服务或者高度封装的脚本结果遇到网络问题、证书过期、组件异常时完全无从下手。从Kubeadm开始虽然前期会多踩几个坑但你对集群的掌控力会强得多。接下来我会带你用三台虚拟机一步步搭建一个最经典的单控制平面Master多节点Node的K8s集群。这个过程也是你理解K8s架构的最佳实践课。2. 集群蓝图与基础设施准备规划比动手更重要在敲下任何命令之前我们先画个蓝图。一个最小化的、可用于学习和功能验证的K8s集群通常包含以下角色一个控制平面节点 (Master Node) 这是集群的“大脑”负责管理集群状态、调度Pod、处理API请求。在生产环境为了高可用至少需要三个控制平面节点。但为了学习我们先从一个开始。至少一个工作节点 (Worker Node) 这是“肌肉”真正运行容器化工作负载的地方。我们将准备两个工作节点。一个网络插件 (CNI) Kubernetes本身不负责网络需要额外的插件实现Pod之间的通信。我们将选用最流行的Flannel。硬件与系统要求机器 3台虚拟机或物理机。我使用的是本地VMware Workstation创建的3台CentOS 7.9虚拟机你也可以用VirtualBox、Proxmox或者云服务器。配置 控制平面节点建议至少2核CPU、2GB内存、20GB磁盘。工作节点至少1核CPU、1GB内存、15GB磁盘。磁盘空间主要留给容器镜像和日志。网络 所有节点必须在同一个二层网络即能直接通过IP互通且需要关闭防火墙或配置正确的规则关闭SELinux。我们将使用以下IP规划请根据你的环境调整Master:192.168.31.100Node1:192.168.31.101Node2:192.168.31.102系统 各主流Linux发行版均可。本文以CentOS 7为例其他系统部分命令如包管理工具需做调整。第一步系统基础配置所有节点均需执行这些操作是搭建任何Linux服务的通用前置步骤目的是创造一个干净、网络通畅的环境。主机名与Hosts解析 Kubernetes组件之间经常需要通过主机名通信。为每台机器设置永久主机名并编辑/etc/hosts文件确保所有节点能通过主机名互相解析。# 在Master节点执行 hostnamectl set-hostname k8s-master # 在Node1节点执行 hostnamectl set-hostname k8s-node1 # 在Node2节点执行 hostnamectl set-hostname k8s-node2 # 在所有节点的 /etc/hosts 文件末尾添加使用你的实际IP 192.168.31.100 k8s-master 192.168.31.101 k8s-node1 192.168.31.102 k8s-node2关闭防火墙与SELinux 在实验环境为了避免复杂的网络策略干扰我们直接关闭它们。生产环境需要根据安全策略精细配置。# 关闭防火墙 systemctl stop firewalld systemctl disable firewalld # 关闭SELinux需重启生效或先临时设置 setenforce 0 sed -i s/^SELINUXenforcing$/SELINUXpermissive/ /etc/selinux/config关闭Swap Kubernetes 1.8要求必须禁用Swap以保证调度器性能预测的准确性。swapoff -a sed -i / swap / s/^\(.*\)$/#\1/g /etc/fstab # 注释掉fstab中swap的行使其永久失效配置内核参数与模块加载 这是让Linux内核满足容器运行要求的关键步骤涉及网络转发、桥接流量处理等。cat /etc/sysctl.d/k8s.conf EOF net.bridge.bridge-nf-call-ip6tables 1 net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 EOF sysctl --system # 加载br_netfilter模块 modprobe br_netfilter # 确保开机加载 echo br_netfilter /etc/modules-load.d/k8s.conf注意 以上sysctl配置中net.ipv4.ip_forward 1至关重要它开启了IP转发是Pod跨节点通信和Service网络工作的基础。很多后续的网络问题如Pod无法访问外网都源于此参数未正确设置。完成以上步骤后建议重启所有节点确保配置生效。重启后检查主机名、swap状态free -h查看Swap行应为0、以及sysctl -p或sysctl -a | grep forward确认ip_forward已为1。3. 核心依赖安装容器运行时与Kubeadm三件套Kubernetes集群运行依赖于两大核心容器运行时和Kubernetes自身组件。我们将分别安装它们。3.1 安装容器运行时 - ContainerdKubernetes最初只支持Docker但现在通过CRI容器运行时接口支持更多运行时。Docker本身过于臃肿且其内置的dockershim已在K8s 1.24版本后被移除。因此我们选择安装更轻量、更标准的Containerd。它是Docker的底层运行时也是当前社区的推荐选择。配置yum源并安装# 安装必要的依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 # 添加Docker仓库Containerd包含在其中 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装Containerd yum install -y containerd.io配置Containerd 默认配置可能不适合我们需要生成默认配置并修改。# 生成默认配置文件 containerd config default /etc/containerd/config.toml # 修改配置文件使用systemd作为cgroup驱动与K8s保持一致的关键 sed -i s/SystemdCgroup false/SystemdCgroup true/ /etc/containerd/config.toml # 修改sandbox镜像地址为国内源避免拉取k8s.gcr.io/pause镜像失败 sed -i s|registry.k8s.io/pause:3.9|registry.aliyuncs.com/google_containers/pause:3.9|g /etc/containerd/config.toml启动并设置开机自启systemctl daemon-reload systemctl enable --now containerd systemctl status containerd # 检查状态是否为active (running)实操心得SystemdCgroup true这个配置项是连接Containerd和Kubelet的桥梁。如果这里配置错误比如用了cgroupfs后续kubelet会报错导致节点无法正常注册到集群。这是新手最容易忽略的坑之一。3.2 安装Kubeadm、Kubelet和Kubectl这是Kubernetes的“三剑客”kubeadm 用于初始化集群和加入节点的工具。kubelet 运行在每个节点上的代理负责启动和管理Pod。kubectl 集群命令行管理工具。由于官方源在国内访问较慢我们使用阿里云的镜像源。配置Kubernetes yum源cat /etc/yum.repos.d/kubernetes.repo EOF [kubernetes] nameKubernetes baseurlhttps://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled1 gpgcheck1 repo_gpgcheck1 gpgkeyhttps://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-pgp-key.gpg EOF安装指定版本 建议安装一个稳定的次新版本。安装前可以先yum list kubeadm --showduplicates查看可用版本。# 安装特定版本例如1.28.x系列的最新版 yum install -y kubelet-1.28.8 kubeadm-1.28.8 kubectl-1.28.8启动kubelet并设置开机自启systemctl enable --now kubelet此时systemctl status kubelet可能会显示错误或不断重启这是正常的因为集群还未初始化kubelet缺少必要的配置文件。至此所有节点的基础软件都已安装完毕。接下来我们将在Master节点上“点燃”集群。4. 初始化Master节点集群的“点火”仪式现在我们将在k8s-master节点上执行集群的初始化命令。这是最核心的一步。4.1 生成初始化配置文件直接运行kubeadm init会使用默认参数。为了更灵活例如指定Pod网段、镜像仓库我们先生成一个配置文件。kubeadm config print init-defaults kubeadm-config.yaml编辑这个文件修改关键部分apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.31.100 # 修改为Master节点的IP bindPort: 6443 nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock # 指定Containerd的socket路径 imagePullPolicy: IfNotPresent taints: [] # 移除默认的污点允许Pod调度到Master节点仅限实验环境 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.28.8 # 指定版本与安装版本一致 networking: podSubnet: 10.244.0.0/16 # 设置Pod网络CIDR必须与后续安装的Flannel默认网段匹配 serviceSubnet: 10.96.0.0/12 imageRepository: registry.aliyuncs.com/google_containers # 使用国内镜像仓库加速核心参数解析podSubnet: 10.244.0.0/16 这个网段是Flannel默认的“全局”Pod网段。每个节点会从这个大网段中分配一个小子网如10.244.1.0/24。务必记住这个值后面安装Flannel时需要用到。imageRepository 将镜像源从k8s.gcr.io改为阿里云镜像这是在国内网络环境下成功初始化的关键。criSocket 明确告诉kubeadm我们使用的是Containerd而不是Docker。4.2 执行初始化命令使用我们修改好的配置文件进行初始化kubeadm init --configkubeadm-config.yaml --upload-certs | tee kubeadm-init.log这个命令会做以下几件事进行一系列预检检查内核参数、运行时、端口等。生成自签名的CA证书和各种组件证书存放在/etc/kubernetes/pki。为控制平面组件kube-apiserver, kube-controller-manager, kube-scheduler生成静态Pod清单放到/etc/kubernetes/manifestskubelet会监控这个目录并自动启动这些Pod。为kubelet生成连接API Server的配置文件kubelet.conf。安装核心的DNS插件CoreDNS和代理服务kube-proxy。生成一个加入集群的令牌Token和命令。整个过程大约需要2-5分钟取决于网络拉取镜像的速度。务必保存好最后输出的kubeadm join命令它类似于一个“邀请码”工作节点需要用它来加入集群。输出内容类似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.31.100:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx4.3 配置kubectl按照提示我们需要配置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查看节点状态。应该能看到k8s-master但状态是NotReady因为网络插件CNI还没装。5. 安装Pod网络插件CNI打通集群的“经脉”没有网络插件Pod就是一座座孤岛。我们选择最经典简单的Flannel。为什么是Flannel它部署简单使用Overlay网络VXLAN或host-gw能很好地满足中小规模集群的需求。它默认使用的Pod网段就是10.244.0.0/16这与我们初始化时设置的podSubnet完全一致这是它能正常工作的前提。在Master节点执行# 下载Flannel的配置文件 wget https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml # 如果网络问题无法下载可以手动创建文件内容可以从官方GitHub获取 # 或者直接使用kubectl apply但需要确保能访问到镜像 kubectl apply -f kube-flannel.yml这个YAML文件会在集群中创建一系列资源Namespace、ServiceAccount、ClusterRole、ConfigMap以及最重要的DaemonSet。DaemonSet会确保在每个节点包括Master上都运行一个flanneld的Pod这个Pod负责为本节点的Pod分配IP并处理跨节点网络流量。应用后使用以下命令观察安装进度kubectl get pods -n kube-system -l appflannel等待所有flannelPod的状态变为Running。同时再检查节点状态kubectl get nodes稍等片刻k8s-master节点的状态应该会从NotReady变为Ready。恭喜你的Master节点已经就绪踩坑记录 如果Flannel Pod一直处于Init或CrashLoopBackOff状态最常见的原因是镜像拉取失败。Flannel的镜像默认从quay.io拉取国内可能访问不畅。解决方法有两种一是提前将镜像quay.io/coreos/flannel:v0.23.0下载并导入到每个节点二是修改kube-flannel.yml文件将镜像地址替换为国内镜像仓库的地址如registry.cn-hangzhou.aliyuncs.com/google_containers/flannel:v0.23.0。修改后记得重新kubectl apply。6. 将工作节点加入集群扩展你的“算力”现在回到你的工作节点k8s-node1和k8s-node2。在Master节点初始化成功输出的最后有一个kubeadm join命令。分别在两个工作节点上以root身份执行这个命令。# 在 k8s-node1 和 k8s-node2 上分别执行你的token和hash值会不同 kubeadm join 192.168.31.100:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx执行成功后你会看到类似This node has joined the cluster的提示。加入过程发生了什么节点上的kubelet使用提供的Token和CA证书哈希向Master的API Server发起认证请求。认证通过后Master为该节点生成一个唯一的证书和kubeconfig文件。kubelet使用新的身份凭证启动并开始向API Server汇报节点状态。Master上的kube-controller-manager注意到新节点并为其创建Node资源对象。Flannel的DaemonSet控制器发现新节点调度一个flanneldPod到该节点运行配置节点网络。回到Master节点再次执行kubectl get nodes。稍等一会儿可能需要一两分钟拉取Flannel镜像并启动Pod你应该能看到三个节点状态都是Ready。NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 10m v1.28.8 k8s-node1 Ready none 2m v1.28.8 k8s-node2 Ready none 1m v1.28.8注意工作节点的ROLES显示为none你可以通过命令kubectl label node k8s-node1 node-role.kubernetes.io/workerworker为其打上标签方便管理。7. 验证集群与运行第一个应用点亮你的“Hello World”集群搭建完成是时候验收成果了。我们从最基本的命令开始验证集群核心功能是否正常。7.1 基础状态检查# 查看所有组件Pod状态所有应为Running kubectl get pods -n kube-system # 查看集群事件排查潜在问题 kubectl get events --sort-by.lastTimestamp # 查看集群信息 kubectl cluster-info7.2 运行一个测试应用让我们部署一个最简单的Nginx应用它包含了K8s最核心的几个资源对象Deployment和Service。# 创建一个名为 nginx-deployment 的部署运行2个nginx副本 kubectl create deployment nginx-test --imagenginx:alpine --replicas2 # 为这个部署创建一个Service类型为NodePort便于从集群外部访问 kubectl expose deployment nginx-test --port80 --typeNodePort # 查看创建的资源 kubectl get deployment,svc,pods -o wide执行后你会看到deployment.apps/nginx-test被创建并管理着2个Pod。service/nginx-test被创建有一个集群IP如10.96.x.x和一个随机分配的高位端口如NodePort: 30080/TCP。两个Pod被调度到节点上可能是node1和node2状态为Running。7.3 测试访问现在你可以通过任意节点的IP地址和Service的NodePort端口来访问这个Nginx服务。# 获取NodePort端口号 NODE_PORT$(kubectl get svc nginx-test -o jsonpath{.spec.ports[0].nodePort}) echo $NODE_PORT # 使用curl访问假设节点IP是192.168.31.101 curl http://192.168.31.101:$NODE_PORT如果看到Nginx的欢迎页面恭喜你这证明Pod被成功调度并运行。Pod网络正常Service能发现后端Pod。kube-proxy工作正常将NodePort的流量正确转发到了Pod。跨节点网络Flannel工作正常因为请求可能被转发到了运行在其他节点上的Pod。7.4 深入观察理解流量路径你可以进入一个Pod内部查看它的网络配置加深理解。# 获取一个Pod的名字 POD_NAME$(kubectl get pods -l appnginx-test -o jsonpath{.items[0].metadata.name}) # 进入Pod执行命令 kubectl exec -it $POD_NAME -- sh # 在Pod内查看IP地址 ip addr show eth0 # 你会看到一个类似 10.244.1.2 的IP这就是Flannel分配的Pod IP。 # 尝试ping另一个Pod的IP先退出当前Pod获取另一个Pod的IP exit kubectl get pods -l appnginx-test -o wide # 查看所有Pod的IP这个简单的测试串联了从镜像拉取、Pod调度、服务发现到网络访问的完整链路是验证集群健康度的最佳方式。8. 后续配置与进阶方向让集群更“趁手”一个能跑应用的集群只是开始要让其成为一个好用的开发测试环境还需要一些额外配置。8.1 配置kubectl命令自动补全这能极大提升效率。# bash用户 echo source (kubectl completion bash) ~/.bashrc source ~/.bashrc # zsh用户 echo source (kubectl completion zsh) ~/.zshrc source ~/.zshrc8.2 安装Ingress Controller可选但推荐NodePort Service虽然方便测试但端口管理混乱。Ingress是管理外部HTTP/HTTPS流量进入集群的标准方式配合Ingress Controller如Nginx Ingress Controller或Traefik使用。# 以安装Nginx Ingress Controller为例使用DaemonSet方式便于主机网络访问 kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.9.5/deploy/static/provider/baremetal/deploy.yaml安装后你可以创建Ingress资源来定义路由规则通过统一的入口通常是80/443端口访问集群内不同的服务。8.3 安装仪表盘Dashboard一个Web UI可以直观地查看集群状态。但请注意Dashboard的权限很大务必做好安全配置如设置访问令牌或配置HTTPS。# 下载Dashboard的部署文件 wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aio/deploy/recommended.yaml # 修改Service类型为NodePort便于访问 # 在recommended.yaml中找到Service部分将 type: ClusterIP 改为 type: NodePort sed -i s/type: ClusterIP/type: NodePort/g recommended.yaml # 部署 kubectl apply -f recommended.yaml # 创建管理员ServiceAccount和ClusterRoleBinding生产环境请谨慎授权 cat EOF | kubectl apply -f - apiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard EOF # 获取访问令牌 kubectl -n kubernetes-dashboard create token admin-user复制输出的令牌通过https://节点IP:NodePort访问Dashboard使用令牌登录。8.4 配置镜像拉取策略与私有仓库国内拉取Docker Hub镜像很慢可以配置Containerd使用镜像加速器。编辑/etc/containerd/config.toml在[plugins.io.containerd.grpc.v1.cri.registry.mirrors]部分添加[plugins.io.containerd.grpc.v1.cri.registry.mirrors] [plugins.io.containerd.grpc.v1.cri.registry.mirrors.docker.io] endpoint [https://registry.cn-hangzhou.aliyuncs.com, https://docker.mirrors.ustc.edu.cn]然后重启Containerdsystemctl restart containerd。搭建过程中我最大的体会是耐心和仔细阅读错误信息。Kubeadm的预检错误、kubelet的日志journalctl -xeu kubelet、Pod的描述信息kubectl describe pod pod-name是排查问题的三大法宝。90%的问题都能通过它们找到线索。这个手动搭建的过程就像亲手组装一台精密仪器每一个螺丝配置的位置和力度参数都至关重要。虽然过程繁琐但当你看到kubectl get nodes全部显示Ready第一个应用成功跑起来时那种对集群底层了如指掌的踏实感是任何一键脚本都无法给予的。