拓冰建站拓冰建站
首页 / 资讯中心 / 正文

CentOS 7 搭建 Kubernetes 集群:kubeadm 实操指南

自己动手在 CentOS 7 上搭一套 Kubernetes 集群这事我前前后后做过不少次。最初照着线上文档一步步敲命令翻车的次数多得不好意思提后来把细节抠明白了再看别人报错基本一眼就能定位是哪个环节出了问题。这篇围绕“在 CentOS 7 部署 K8s 搭建 Kubernetes 集群”这个主题从环境规划、系统初始化、容器运行时选型到控制平面初始化、Worker 节点加入、网络插件部署再到常见问题排查一次讲清楚。内容面向两类人一是刚接触 Kubernetes、想在自己虚拟机里完整跑通集群的入门者二是在测试环境反复折腾、准备整理出一套标准操作流程的运维或开发同学。看完以后你至少能有一套可以照着敲的命令清单以及遇到报错时的不踩坑思路。整个集群的核心组件就四块控制平面kube-apiserver、kube-controller-manager、kube-scheduler、etcd集群状态存储这里由 kubeadm 默认内置部署、Worker 节点上的 kubelet 和容器运行时以及把各节点 Pod 网络打通的数据面插件。Kubernetes 集群的部署方式很多生产环境常见的有二进制部署、kubeadm 部署和运维平台自动化部署但要说兼顾学习成本、可维护性和社区资料丰富度kubeadm 绝对是最适合起步的方案。它把 etcd、API Server、Controller Manager 这些组件都容器化通过一套清单统一管理对新手非常友好对老手来说效率也高。下面直接按实操顺序展开。1. 环境规划与版本选型1.1 集群拓扑与硬件配置建议我这次用的是 3 台 CentOS 7 虚拟机拓扑结构是 1 个 Master 节点加 2 个 Worker 节点这是目前最常用、也最能体现集群调度能力的最小规模。三台机器配置建议至少 2 核 CPU、2GB 内存磁盘 20G 以上。如果你只是在自己笔记本上用 VMware 或 VirtualBox 跑记得给 Master 节点稍微多留一点资源因为控制面组件加 etcd 都压在上面。实际验证下来2C4G 的 Master 跑起来会比较从容Worker 节点 2C2G 就够用。节点角色和 IP 规划参考如下节点角色主机名IP 地址配置建议Masterk8s-master192.168.1.112C4GWorkerk8s-node1192.168.1.122C2GWorkerk8s-node2192.168.1.132C2G这里有个经验主机名和 IP 规划一定要在最开始就定好并且写进每台节点的 /etc/hosts。Kubernetes 集群内部组件之间对主机名的依赖非常强主机名一乱后续证书签发、kubelet 注册、日志检索全都会跟着出问题。主机名里不要出现大写字母和下划线中划线可以这是官方推荐的命名规范。1.2 版本选型Kubernetes、容器运行时与网络插件版本选型是整个部署过程中最容易被人忽略、但后患最多的环节。Kubernetes 社区发布节奏很快每个月都有 patch 版本但作为部署方第一原则是不要追新选一个稳定且彼此兼容的版本组合。我这次选用 Kubernetes v1.28.2容器运行时使用 containerd 1.7.x网络插件使用 Calico v3.26。这三个版本组合经过大量社区验证兼容性基本不用操心。关于容器运行时这里稍微展开说一句。Kubernetes 在 1.24 版本之后把 dockershim 从 kubelet 里移除了如果你继续用 Docker 作为运行时需要额外安装 cri-dockerd 这样的适配层来翻译多一层组件就多一个故障点。所以新部署集群的运维同学直接上 containerd 就行。containerd 就是 Docker 底层的容器运行时镜像使用方式、日志查看思路都和 Docker 高度相似学习成本并不高。网络插件我这里选 Calico主要看中它支持 NetworkPolicy网络策略对多租户隔离和微服务安全管控场景很实用如果你只是追求最大兼容性、想最快跑通Flannel 也可以后面会讲两者的选择权衡。2. 系统初始化CentOS 7 基础环境准备2.1 关闭防火墙、SELinux 与 SwapCentOS 7 默认开启 firewalld 和 SELinux这两样东西在单机上是好用的安全工具但在 Kubernetes 集群网络环境下往往会成为“隐形杀手”。防火墙规则会影响 kubelet、Calico 的 VXLAN/BGP 流量SELinux 对容器文件系统的访问控制也会导致各种诡异的权限报错所以我们先在每台节点上统一关闭保证集群网络通畅。# 关闭防火墙 systemctl stop firewalld systemctl disable firewalld # 关闭 SELinux setenforce 0 sed -i s/^SELINUXenforcing/SELINUXpermissive/ /etc/selinux/config # 关闭 Swap swapoff -a sed -i /swap/s/^/#/ /etc/fstab这里特别强调 swapoff 的意义。kubelet 默认要求节点关闭 Swap因为 Kubernetes 的 Pod 内存配额和驱逐机制都是基于 CGroup 的内存限制来工作的一旦允许 Swapkubelet 无法准确判断 Pod 的真实内存占用很可能导致节点内存耗尽、Pod 被 OOM Kill或者出现调度不稳定。swapoff -a只是临时关闭重启后又会生效所以必须同步注释 /etc/fstab 中的 swap 行这才是永久生效的做法。2.2 内核参数与模块加载Kubernetes 集群要能承载高并发容器对 Linux 内核的网络参数有特定要求。最核心的是让 iptables 能正确过滤桥接流量因为容器跨 Pod 通信、Service 负载均衡都严重依赖 netfilter 机制。如果不打开 br_netfilter 相关参数Pod 之间甚至在多数情况下连 DNS 解析都会失败。# 加载必要内核模块 cat EOF /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter # 设置内核参数 cat EOF /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 --systemoverlay模块是容器镜像分层存储的驱动基础containerd 默认使用它来做镜像层挂载。net.ipv4.ip_forward控制 IP 转发这个参数非常关键它决定了容器内的网络请求能否路由出去。你可以把整个 Kubernetes 集群想象成一栋写字楼Pod 是楼里的一个个小房间Service 是前台而内核转发就是连接各层走廊的门门不打开房间之间就串不通。2.3 时间同步与 hosts 解析时间不同步是 Kubernetes 集群最常见的隐性故障源。Kubernetes 大量使用证书进行组件间通信证书有效期校验依赖比较精确的时钟。如果节点间时间偏差过大kubelet 连接 API Server 时会出现 x509 certificate has expired or is not yet valid 报错哪怕你离线部署、证书刚刚签发时间错乱也会让你怀疑人生。# 安装并启用 chrony 时间同步 yum install -y chrony systemctl enable --now chronyd timedatectl set-timezone Asia/Shanghai chronyc sources然后配置 hosts 解析。三台节点上都要写入同样的 IP 和主机名映射cat /etc/hosts EOF 192.168.1.11 k8s-master 192.168.1.12 k8s-node1 192.168.1.13 k8s-node2 EOF这一步做完三台机器的基础环境就统一了。建议做一个快照后面万一部署过程中把系统搞坏了直接回滚到这里从头再来也就几分钟的事。3. 容器运行时与 kubeadm 工具安装3.1 安装 containerd 并配置镜像加速CentOS 7 上安装 containerd最省事的方式是通过 Docker 官方仓库因为这个仓库会同时提供 docker-ce、containerd.io 这些包。我们这里只装 containerd不装 Docker 本体。镜像加速这块我使用的是国内能稳定访问的公共镜像源配置在 /etc/containerd/config.toml 里后面会具体说明。# 安装 yum-utils用于获取 yum-config-manager yum install -y yum-utils # 导入 Docker 官方仓库这里提供的地址在国内网络环境下可以访问 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装 containerd yum install -y containerd.io # 生成默认配置文件 mkdir -p /etc/containerd containerd config default /etc/containerd/config.toml生成默认配置后需要改两个关键位置。第一个是 SystemdCgroup把它从 false 改成 true这一步决定了容器使用的 CGroup 驱动和 kubelet 是否一致不一致的话 kubelet 会拒绝启动并报 cgroup driver mismatch。第二个是 sandbox_image也就是 Pod 的基础暂停容器镜像默认指向的是 gcr.io国内拉取非常困难需要改成国内公共镜像仓库地址。sed -i s/SystemdCgroup false/SystemdCgroup true/ /etc/containerd/config.toml sed -i s#registry.k8s.io/pause:3.6#registry.aliyuncs.com/google_containers/pause:3.9# /etc/containerd/config.toml systemctl daemon-reload systemctl enable --now containerd这里给新手解释一下 pause 容器的作用每个 Pod 里除了业务容器还会先启动一个 pause 容器它负责持有这个 Pod 的网络命名空间和 IPC 命名空间业务容器再加入进来。只要 Pod 还活着pause 就不会退出。它相当于一栋宿舍楼的公共走廊住在里面的各个“室友”共享这个走廊但每个房间依然是独立的。3.2 安装 kubeadm、kubelet、kubectlkubeadm 是集群初始化的核心工具kubelet 是运行在每个节点上的“agent”kubectl 是控制面客户端。这三个工具的版本必须保持一致避免版本漂移带来的协议不兼容。这里通过阿里云的开源镜像站配置 yum 源国内节点拉取速度非常稳。cat EOF /etc/yum.repos.d/kubernetes.repo [kubernetes] nameKubernetes baseurlhttps://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled1 gpgcheck1 repo_gpgcheck0 gpgkeyhttps://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF yum install -y kubelet-1.28.2 kubeadm-1.28.2 kubectl-1.28.2 # 设置 kubelet 开机自启但先不启动等初始化后再启动 systemctl enable kubelet安装完成后可以用kubeadm version和kubelet --version确认版本一致。这里有个容易犯迷糊的地方systemctl enable kubelet之后马上 start 的话大概率会看到它反复重启日志里报找不到 /etc/kubernetes/pki/ca.crt。不用担心这是正常现象kubelet 在等待 kubeadm init 生成证书和配置文件属于“待命状态”初始化完成后再启动就不会再报这个错了。3.3 镜像预拉取与版本校验kubeadm init 初始化时默认要从镜像仓库拉取十几个组件镜像包括 kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd、pause、coredns 等。如果网络不好初始化过程会卡在拉镜像阶段表现是等了很久没有反应超时报错。所以建议先把镜像手动拉到本机做一次“预热”。kubeadm config images list --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.28.2 kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.28.2kubeadm config images pull这个命令在初始化前跑一次非常有必要。它会把所有需要的镜像全部拉下来默认从 registry.aliyuncs.com/google_containers 这个国内公共仓库拉取。我在测试环境实测过这个仓库的拉取速度稳定很少出现超时。拉完之后可以用crictl images查看本机已有镜像确认列表里 resgistry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2 这些核心镜像都已就位。4. 控制平面初始化与 kubectl 配置4.1 kubeadm init 参数解析与执行准备工作全部就绪后回到 Master 节点执行集群初始化。这是整个部署过程最关键的一步参数含义必须搞清楚不能照着抄完不知道在干嘛。kubeadm init \ --apiserver-advertise-address192.168.1.11 \ --control-plane-endpoint192.168.1.11 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.28.2 \ --pod-network-cidr10.244.0.0/16 \ --service-cidr10.96.0.0/12各参数的作用--apiserver-advertise-address指定 API Server 对外广播的地址也就是 Master 节点的 IP。Worker 节点和其他客户端都会通过这个地址访问集群。--control-plane-endpoint控制平面端点单节点部署时可以直接用 Master IP。如果以后要做高可用这里会换成负载均衡器的虚拟 IP。--image-repository指定镜像仓库地址必须和前面 kubeadm config images pull 用的一致。--pod-network-cidrPod 网络的 CIDR 网段这个地址要和你后面选的网络插件匹配。我选 10.244.0.0/16 是 Flannel 的默认网段但这次用 CalicoCalico 默认支持这个网段所以也可以直接使用后面部署 Calico 时不需要特别改配置。如果你要自定义 Pod 网段记得同步修改 Calico 的 manifest。--service-cidrService 的虚拟 IP 网段默认 10.96.0.0/12一般不用改。执行成功后输出末尾会有一段完整的 kubeadm join 命令以及配置 kubectl 的操作提示。这段 join 命令是后续 Worker 节点加入集群的凭证入口里面包含 token 和证书哈希一定要复制保存好。如果当时忘了记也没关系后面在 Master 上执行kubeadm token create --print-join-command可以重新生成。4.2 kubectl 配置与验证控制平面kubeadm init 只是把集群的各种组件拉起但当前用户还没有权限通过 kubectl 访问 API Server。按提示执行下面三条命令将集群的 admin 证书复制到当前用户目录下mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config为什么需要这一步因为 Kubernetes 默认没有开启匿名访问所有请求都要走证书认证。admin.conf 文件里包含了一个 cluster-admin 权限的客户端证书把它放到用户默认的 kubeconfig 路径中kubectl 才知道怎么用哪个证书去访问 API Server。这就好比进门要刷门禁卡admin.conf 就是那张万能卡而~/.kube/config就是放卡的地方。配置完成后执行kubectl get nodes可以看到 Master 节点的状态是 NotReady这是正常的因为网络插件还没装。执行kubectl get pods -n kube-system能看到 kube-apiserver、etcd 等 Pod 已经在 Running。这里要留意一下 etcd 的状态如果一直 CrashLoopBackOff大概率是 Master 节点的磁盘性能太差或者时钟不同步需要优先排查。5. 网络插件 CNI 配置与 Worker 节点加入5.1 网络插件选型与部署集群控制平面起来了但 Pod 之间还不能互通因为缺少 CNI 网络插件。我这次选用 Calico先解释一下 Calico 和 Flannel 的差异方便你按场景选型。Flannel 的优点是简单、轻量、组网快使用 VXLAN 或 host-gw 模式将各节点组成一个大二层网络非常适合小规模集群和刚入门学习的场景。但 Flannel 基本不支持 NetworkPolicy你很难通过 Kubernetes 原生的方式限制 Pod 之间的访问。Calico 则采用 BGP 协议做路由分发天然支持 NetworkPolicy性能更好功能更丰富但部署出来的组件更多且依赖 IP 池规划。如果你后面要做微服务网格、多租户安全策略直接上 Calico 准没错如果你只是自己学习、跑个 demoFlannel 完全够用。这里以 Calico v3.26 为例部署# 在 Master 节点先下载 manifest 文件 wget https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml kubectl apply -f calico.yaml如果网络不稳定导致文件下载失败可以换用 GitHub 加速下载或找一台网络可达的机器下载后拷贝过去。执行 apply 之后Calico 会在每个节点上启动 calico-node 守护进程和 calico-kube-controllers 控制器。查看状态kubectl get pods -n kube-system -o wide等所有 kube-system 下的 Pod 都进入 Running控制平面的三个节点状态会从 NotReady 变成 Ready。这个过程通常需要一到两分钟如果超过三分钟还没 Ready就要去看 calico-node 的日志了。5.2 Worker 节点 kubeadm join 与 token 管理Worker 节点加入集群本质上就是把新节点的 kubelet 注册到 Master 的 API Server然后拉取必要的镜像、加入集群网络。在 Worker 节点上执行 Master 初始化结束时输出的 join 命令即可kubeadm join 192.168.1.11:6443 --token xxxx --discovery-token-ca-cert-hash sha256:xxxx如果 join 命令找不到了在 Master 上执行这条重新获取kubeadm token create --print-join-commandtoken 的有效期默认是 24 小时如果你是在初始化之后几天再添加 Worker 节点旧 token 会失效。用kubeadm token list可以查看当前有效的 token过期的就删掉重建。除了 token--discovery-token-ca-cert-hash参数也很关键它用来验证 API Server 的身份防止中间人攻击。可以把它理解成第一次见面时两人对暗号token 是进门凭证certificate hash 是对身份的确认两者缺一不可。Node 节点加入成功后再回到 Master执行kubectl get nodes可以看到三个节点都已经 READY。到这里一套“从零开始”的多节点 Kubernetes 集群就算搭起来了。6. 集群功能验证与日常运维命令6.1 部署一个测试应用验证集群集群搭建完成第一件事不是急着写业务而是先跑一个测试应用把 Pod 调度、Service 暴露、DNS 解析这几个核心链路全部验证一遍。# 创建测试命名空间 kubectl create namespace test # 部署一个 nginx 应用副本数 3 kubectl create deployment nginx-test -n test --imagenginx:1.25 --replicas3 # 暴露为 NodePort 类型的 Service kubectl expose deployment nginx-test -n test --port80 --target-port80 --typeNodePort --namenginx-svc然后查看 Pod 和 Service 状态kubectl get pods -n test -o wide kubectl get svc -n test正常情况下三个副本会分别调度到三个节点上这能证明 Worker 已经真正承担了业务负载。Service 的 NodePort 端口会在 30000-32767 之间随机分配比如 31567。在浏览器里访问任意一个节点 IP 加这个端口比如http://192.168.1.12:31567如果能弹出 nginx 欢迎页说明容器网络、Service 负载均衡、节点端口映射全链路都通了。6.2 常用命令与日志排查思路# 查看所有节点状态 kubectl get nodes # 查看节点详细信息包括 kubelet 版本、操作系统、容器运行时 kubectl describe node k8s-master # 查看集群事件排查异常非常有用 kubectl get events --sort-by.lastTimestamp # 查看某 Pod 日志 kubectl logs -f pod-name -n namespace # 进入 Pod 终端调试 kubectl exec -it pod-name -n namespace -- /bin/bash # 删除 deployment 和 service kubectl delete deployment nginx-test -n test kubectl delete svc nginx-svc -n test排查日志是 Kubernetes 运维里最频繁的操作。Pod 的容器日志用kubectl logs查看kubelet 本身的日志则通过journalctl -u kubelet -f查看。系统组件比如 kube-apiserver都是容器化运行在 kube-system 命名空间下同样可以通过kubectl logs -n kube-system kube-apiserver-k8s-master查看。遇到问题第一件事是看事件而不是盲目重启kubectl describe pod输出的 Events 字段里往往直接写了故障原因比如镜像拉取失败、端口占用、存储卷挂载失败等。7. 常见问题与排查技巧实录7.1 初始化失败类问题现象一kubeadm init 卡在等待 kubelet 启动最后超时报错。这个我在第一次部署时遇到过原因基本有两个一个是 Swap 没有真正关闭另一个是 kubelet 的 cgroup driver 和 containerd 不一致。排查思路先执行free -h确认 swap 是 0再执行kubectl describe node不行因为没有节点那就直接看 kubelet 日志journalctl -u kubelet -f。日志里如果出现failed to run Kubelet: failed to create kubelet component manager: cgroup driver cgroupfs doesnt match with systemd那就是 cgroup 驱动不一致回到 /etc/containerd/config.toml 确认 SystemdCgroup true 然后重启 containerd再执行kubeadm reset后重新 init。这里分享一个经验kubeadm reset是个好东西它会把前面 init 产生的配置、证书、etcd 数据全部清掉恢复到 init 之前的状态。任何初始化失败先不要慌乱解决问题后执行kubeadm reset -f然后重新 init不用重新装系统。现象二镜像拉取失败。报错信息一般是failed to pull image registry.aliyuncs.com/google_containers/kube-apiserver:v1.28.2。先看报错的镜像名和版本号是否存在于仓库里。如果仓库中这个 tag 不存在最常见的原因是版本号写错了检查--kubernetes-version v1.28.2与你安装的 kubeadm 版本是否完全一致。另外kubeadm 默认不会给 Pod 网段之外的流量走代理所以如果你配置了 HTTP 代理要在 init 前把代理地址设为 no_proxy 白名单否则可能会出现能拉镜像但组件之间通信失败的问题。7.2 节点 NotReady 类问题现象kubectl get nodes 显示 NotReadyMaster 和 Worker 都是。节点 NotReady 有相当高的概率是 CNI 网络插件没装或者装错。检查方法kubectl get pods -n kube-system看 calico-node或 flannel的 Pod 是否 Running。如果 calico Pod 一直 CrashLoopBackOff日志里提示BIRD is not ready to propagate routes通常是节点之间的 BGP 端口默认 179被防火墙挡了或者节点处在不同 VLAN 导致 BGP 无法建立邻居。CentOS 7 上先确认systemctl status firewalld是关闭状态然后 telnet 测试节点之间的 179 端口是否通。另一种常见情况是节点 IP 变化导致 CNI 缓存失效。如果你用的是虚拟机设置成了 DHCP重启后 IP 变了网络插件还是按旧 IP 记录路由节点自然 NotReady。解决办法固定每台节点的 IP重启网络服务后重新初始化 CNI Pod或者干脆kubectl delete pods -n kube-system -l k8s-appcalico-node让 Calico 自动重建。7.3 镜像拉取与网络相关问题现象业务 Pod 一直 ImagePullBackOff。这个最容易出现在那些习惯了 Docker Hub 默认镜像的同学身上。比如刚才测试的 nginx:1.25从 Docker Hub 拉取有时会遇到超时尤其是海外镜像。解决办法有两个方向一是给 containerd 配置镜像加速器在 /etc/containerd/config.toml 里的[plugins.io.containerd.grpc.v1.cri.registry.mirrors]段下面增加国内公共加速地址二是把 Docker Hub 上的公共镜像先拉下来再重新打 tag 推到私有 Harbor 仓库。这里说明一下如果只是学习 demo用 containerd 配置国内公开加速地址就能解决大部分问题不涉及任何特殊网络手段。[plugins.io.containerd.grpc.v1.cri.registry.mirrors] [plugins.io.containerd.grpc.v1.cri.registry.mirrors.docker.io] endpoint [https://docker.m.daocloud.io, https://dockerhub.icu]配置完成后记得systemctl restart containerd再重新创建 Pod 验证。7.4 其他实战避坑清单问题原因解决办法kubectl 无法执行报 certificate error节点时间不同步安装并启用 chronydchronyc makestep校准时间后重启 kubeletjoin 时提示 token 过期token 有效期 24 小时在 Master 执行kubeadm token create --print-join-command重新生成Pod 可以创建但无法访问外网未开启 IP 转发检查/proc/sys/net/ipv4/ip_forward是否为 1重新加载 sysctl 配置Service 无法访问后端 Pod标签选择器配置错误检查 Service 的 selector 是否与 Pod 的 label 完全匹配kubelet 频繁重启缺少 /etc/kubernetes/pki/ca.crt这是正常待命状态kubeadm init 完成后会自动恢复etcd Pod 一直 CrashLoopBackOff磁盘 IO 性能过差检查 Master 节点磁盘是否打成快照导致 IO 不稳定避免在机械盘上跑 etcd这套集群我从 CentOS 7 的环境准备一路写到常见问题排查步骤不算少但回头想一想真正核心的就那几件事环境统一、版本锁定、镜像可拉、网络可通。Kubernetes 搭建本身不是目的重要的是通过这个过程理解控制平面和数据平面是怎么协作的组件之间靠什么通信出了问题要从哪个日志切入。我个人在实际操作中的体会是不要急着在生产环境硬上先在虚拟机上把这套流程完整跑三遍第一遍照着命令敲第二遍尝试破坏后重装第三遍不看文档直接排错经历过这三轮你对集群的理解会完全不一样。后面如果有时间可以继续往集群里加 Dashboard、Prometheus 监控、Ingress Controller甚至升级到高可用多 Master 架构那时候你已经手里的知识足以应付这些扩展了。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门