尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CentOS7云主机快速搭建K8S集群:kubeadm实操指南

CentOS7云主机快速搭建K8S集群:kubeadm实操指南 聊到K8S集群搭建不少人的第一反应是“这东西太重搞不定”。但如果你手头正好有一批跑着CentOS7的云主机只是想快速拉起一套可用于学习、测试甚至轻度生产的K8S集群整个过程其实就是“环境准备组件安装一条初始化命令一条加入命令”的组合拳。我最近在HoRain云上申请了几台CentOS 7.9的云主机从裸机环境到集群Ready严格记录下来大约用了二十来分钟。这篇就把从云主机购买到集群可用的完整过程、每个参数背后的原因、以及实际踩过的坑一次性讲清楚适合刚接触K8S、想自己动手复刻一套集群的朋友参考。1. 项目概览与方案选型思路1.1 为什么还在用CentOS7部署K8S先说一个现实问题CentOS 7已经在2024年6月停止维护为什么还要选它原因其实非常实际——存量环境太多。很多公司内部的基础组件、自研中间件至今还跑在CentOS7上云主机模板也是现成的。对这些环境来说短时间内完成大规模系统替换并不现实但应用又需要容器化、需要上K8S那只能在CentOS7上先把容器集群跑起来。另一个原因是这套组合对新手的友好度极高。CentOS7的用户基数大网上能搜到的问题案例几乎覆盖了所有安装过程中的报错场景系统本身资源占用也低一台2C4G的机器带Master加两个Worker做学习和功能验证绰绰有余。你在CentOS7上把K8S的整个流程走通了再迁移到Rocky Linux、Ubuntu或其它容器平台几乎是无缝的因为核心的kubeadm流程和内核参数基本一致区别只在于软件源和包管理器。1.2 HoRain云主机环境与集群规模规划我这次是在HoRain云控制台上创建的云主机操作系统选了CentOS 7.9 64位规格按“3台2C4G”来规划。为什么是3台因为一个Master加两个Worker就是最小可用集群既能验证调度、跨节点通信、Pod漂移这些核心功能又不会因为主机数量太多拉高成本。如果你想做高可用后面可以再加两台Master组成三节点控制面这个我在3.6节会单独说。网络方面云平台的安全组或叫防火墙规则需要提前放行K8S相关的几个端口。Master节点的6443、2379-2380、10250-10252Worker节点的10250、30000-32767。这里有个非常经典的坑很多人在集群搭好之后发现NodePort服务从外部访问不了排查半天最后发现是云平台的安全策略拦掉了端口。放行端口的时候建议宁可多放也别等到集群搭完再回来开否则每个节点都要重新配置一遍。1.3 版本选择与组件依赖关系版本锁定是搭建K8S时最容易出问题的点。K8S 1.24是一个明显分水岭从1.24开始Dockershim被移除kubelet不再直接连接Docker。如果沿用老教程里“kubelet直接调Docker”的思路必然失败。我这次选的是K8S 1.23.17 Docker 20.10.x这是CentOS7上兼容性极好的一组组合网上教材多、镜像也全。如果你坚持用更新版本也可以选1.28.x containerd但CentOS7的内核默认是3.10对新版本K8S的某些特性支持不完整踩坑成本会更高。版本对应关系可以简单记kubeadm、kubelet、kubectl三者版本必须完全一致Docker/containerd的版本只影响运行时不需要和K8S版本完全一致但必须在K8S支持的范围内。具体的兼容列表以官方release notes为准别用“大版本差几位没关系”的侥幸心理生产中这种侥幸最容易翻车。1.4 本方案技术路线概览完整的技术路径是3台CentOS7云主机 - 修改内核参数 - 安装Docker - 安装kubeadm/kubelet/kubectl - kubeadm init初始化Master - 部署Calico网络插件 - kubeadm join加入两个Worker - 部署测试应用验证集群。整条链路没有复杂的组件核心就两个动作初始化、加入。我会在下面每个步骤里讲清楚“为什么这么做”而不只是贴命令。比如为什么要关Swap、为什么内核参数里要开ip_forward、为什么网络插件必须尽早部署。这些细节如果你只知道命令换个环境就抓瞎把原理吃透了任何一个环节出错你都能自己判断问题出在哪一层。这也是这篇指南和普通安装教程最大的区别。2. 环境准备与主机基础配置2.1 主机名、hosts与网络互通检查先把三台机器的主机名和hosts固定下来。我在规划里统一命名master01、worker01、worker02。主机名必须唯一且不能有大写字母因为K8S的节点名取自hostname后面证书里的SAN也依赖它。修改命令是hostnamectl set-hostname master01改完用bash重新加载一下终端即可。接着编辑/etc/hosts写三台机器的内网IP和主机名映射。为什么要写hosts因为集群内部节点之间需要通过主机名互相访问如果依赖DNS一旦DNS服务抖动整个集群的组件通信就会中断。把hosts写死是最可靠的做法。IP地址建议使用云平台的内网IP跨节点流量尽量走内网不占用公网带宽也更安全。网络互通检查很简单三台机器互相ping一下主机名能通再继续。有一个容易忽略的点云主机默认开启了实例内部的firewalld即使云平台安全组放行了端口节点本机的防火墙仍然会拦截kubelet流量。所以下面马上处理防火墙。2.2 系统初始化防火墙、SELinux、swap、内核参数这是整个搭建过程中最机械、但也最关键的环节。先说实际操作再说每个操作背后的原因。关防火墙。K8S内部组件之间、Kubelet和API Server之间要建立大量长连接firewalld的动态规则偶尔会误伤这些连接尤其是Calico的BGP模式。一次性关闭比事后加规则省心得多。SELinux必须设为disabled或permissive容器运行时访问文件系统的权限模型和SELinux的强制策略会冲突保持enforcing会出现各种看不懂的权限报错。Swap必须关。Kubernetes的kubelet在默认配置下不允许宿主机开启Swap因为容器的内存配额受cgroup限制如果宿主机用了Swap内存回收的行为就无法精确控制Pod可能被无感知地换出到磁盘直接影响QoS和OOM判断。关闭命令是swapoff -a然后把/etc/fstab里的swap行注释掉否则重启后Swap又被挂载回来。内核参数方面需要保证三个模块和两个参数。br_netfilter模块让Linux网桥设备上的流量也能经过iptables规则这是容器网络和Service转发的基础net.ipv4.ip_forward开启转发让Pod与Pod、Pod与外部网络之间能路由net.bridge.bridge-nf-call-iptables设为1确保Kubernetes Service的iptables规则能作用于桥接流量。cat EOF /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables 1 net.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 EOF sysctl --system执行完sysctl --system后用sysctl net.ipv4.ip_forward确认返回1。这一步如果返回0后面Pod网络必出问题别问我是怎么知道的。2.3 安装Docker并配置cgroup驱动与镜像加速安装Docker我直接使用CentOS7源里的docker包版本是20.10.x。CentOS7官方源并不直接提供docker-ce需要先添加Docker官方仓库文件然后执行yum install -y docker-ce docker-ce-cli containerd.io。装完先把服务启动并设置开机自启systemctl enable docker --now。然后修改/etc/docker/daemon.json把两件事一起办了镜像加速和cgroup驱动。镜像加速地址可以根据你实际环境填写我这边用的是云厂商提供的镜像加速器地址。cgroup驱动必须写成systemd因为K8S官方推荐kubelet和容器运行时使用同一个cgroup驱动否则kubelet会报misconfigured cgroup driver导致节点直接不可用。{ exec-opts: [native.cgroupdriversystemd], registry-mirrors: [https://你的镜像加速地址] }改完配置后记得执行systemctl daemon-reload systemctl restart docker并用docker info确认Cgroup Driver一栏已经变成systemd。这个检查很多人省略我建议不要省因为后面报错会浪费更多时间。2.4 配置K8S软件源并安装kubelet、kubeadm、kubectlK8S组件默认从packages.cloud.google.com分发访问时延较高建议配置为镜像仓库提供的kubernetes yum源。仓库文件内容大致如下cat EOF /etc/yum.repos.d/kubernetes.repo [kubernetes] nameKubernetes baseurlhttps://镜像仓库地址/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled1 gpgcheck1 repo_gpgcheck0 gpgkeyhttps://镜像仓库地址/kubernetes/yum/doc/yum-key.gpg https://镜像仓库地址/kubernetes/yum/doc/rpm-package-key.gpg EOF这里要注意我选的是兼容性最强的1.23.x系列安装时用固定版本号避免自动升级导致三组件版本漂移yum install -y kubelet-1.23.17 kubeadm-1.23.17 kubectl-1.23.17安装完成后有个细节kubelet现在还不能启动因为还没有配置文件。你先执行systemctl enable kubelet设置开机自启但不要start等kubeadm init的时候会生成kubelet的初始配置。如果你手快先启动了通常也没事它会在缺配置时自动退出等到初始化时再重新拉起。2.5 操作心得初始化顺序为什么这么重要我见过很多人把环境准备当走过场参数配完不验证就往下走。结果所有问题集中爆发在kubeadm init那一步报错信息又长又杂只能回头一项项排除。我的习惯是每到一步就做一个轻量验证。配置完hosts后用getent hosts master01确认解析成功关完swap后用free -h和cat /proc/swaps双重确认改完内核参数后用sysctl net.ipv4.ip_forward检查返回值装完Docker后用docker info检查cgroup驱动。全部绿灯再继续后面几乎不会卡壳。3. 核心实操用kubeadm快速搭建集群3.1 在Master节点执行kubeadm init环境准备好后在master01上执行初始化命令。这是整个搭建过程的“临门一脚”。我的命令如下kubeadm init \ --apiserver-advertise-address主节点内网IP \ --image-repositoryregistry.aliyuncs.com/google_containers \ --kubernetes-versionv1.23.17 \ --pod-network-cidr10.244.0.0/16 \ --service-cidr10.96.0.0/16各个参数解释一下。--apiserver-advertise-address一定要填内网IP不要填公网IP因为Worker节点是通过内网连Master的填公网可能导致跨网络回环问题。--image-repository指定镜像仓库K8S组件默认从registry.k8s.io拉镜像经常超时替换成国内镜像源后成功率会高很多。--pod-network-cidr是Pod的网段我写10.244.0.0/16是为了和后续的Calico默认配置匹配。--service-cidr是Service虚拟网段10.96.0.0/16是kubeadm默认值没有特殊需求不要改。Pod网段和Service网段都不能和宿主机网段冲突这是基本原则。初始化成功会输出一大段信息最关键的是最后三部分kubeconfig复制命令、Worker加入命令、以及包含--token和--discovery-token-ca-cert-hash的完整join指令。先把join命令保存下来后面要用。如果初始化失败不要急着重复执行用kubeadm reset清理后查日志具体排查放到第四章。3.2 配置kubectl命令行工具kubeadm init完成后root用户下还不能直接使用kubectl因为kubeconfig文件还没有放到默认位置。按提示执行mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config这里要说明admin.conf是管理员级别的kubeconfig包含API Server的地址和客户端证书。把它放到$HOME/.kube/config后kubectl就会自动找到集群。配置完最好执行kubectl get nodes验证一下此时Master节点状态应该是NotReady这是正常的因为网络插件还没装。如果不报“Unable to connect to the server”说明API Server正常。3.3 部署CNI网络插件Calico为什么必须装CNI因为K8S的Pod网络不是自带的需要第三方插件来分配IP、打通跨节点路由。我用的是Calico它在性能和功能上比较均衡支持网络策略社区也活跃。Flannel更轻量适合纯学习环境Calico多了BGP路由能力生产环境更常见。部署命令通常是先下载Calico的manifest文件然后修改其中的CALICO_IPV4POOL_CIDR配置让它跟--pod-network-cidr保持一致。如果不一致Pod拿到的IP和Calico的路由规则对不上跨节点通信直接失败。这是一个非常典型的“配置不匹配”问题。kubectl apply -f calico.yaml应用之后观察Pod状态大约一两分钟后calico-kube-controllers和calico-node会陆续Running。看到Master节点状态变为Ready说明网络已经通了。如果manifest里的镜像拉取受限可以先在本机确认能正常拉取calico相关镜像或者把镜像地址改到可访问的仓库。这次实测下来用默认方式可以顺利拉取。3.4 Worker节点加入集群Worker节点的操作非常简单环境准备阶段关防火墙、SELinux、Swap、装Docker和kubelet和Master完全一致唯一区别是不用执行kubeadm init直接执行Master初始化时输出的join命令kubeadm join 主节点内网IP:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxxToken默认有效期是24小时如果过期了在Master上执行kubeadm token create --print-join-command重新生成完整指令即可。这个命令很实用也是处理集群加入问题时常用的操作。加入成功后回到Master执行kubectl get nodes能看到新节点但状态先是NotReady需要等CNI组件在这台机器上启动完成通常几十秒后就会变成Ready。3.5 快速验证集群状态三台机器都加入后我会习惯性地跑一遍状态检查kubectl get nodes kubectl get pods -A kubectl get svc -Anodes应该显示三台全部Readypods里coredns和calico相关组件应该Running且READY列是1/1或3/3svc里能看到kubernetes服务CLUSTER-IP是10.96.0.1。到这里一个最小集群就跑起来了。接下来部署一个测试应用比如nginx并暴露NodePort端口验证外部访问链路是否正常。这一步能同时验证网络层和Service层建议不要跳过。kubectl create deployment nginx-test --imagenginx kubectl expose deployment nginx-test --port80 --target-port80 --typeNodePort kubectl get svc nginx-test取到NodePort端口后用Worker节点的公网IP加端口访问能打开nginx默认页面说明整条链路没问题。3.6 扩展思路三台Master高可用与可视化工具前面说的是单Master架构适合学习和开发。生产环境三台Master如何保证高可用思路是三台Master都运行控制面组件但通过云平台负载均衡器或自建KeepalivedHAProxy把API Server的6443端口统一暴露给Worker。worker的kubeadm join地址指向负载均衡器的VIP而不是某一台Master的IP。这样某一台Master挂了另外两台还能继续提供API服务。如果想降低管理成本可以在集群上装KubeSphere。它是封装K8S的可视化运维平台能直接在界面上管理节点、应用、监控告警和日志。对于刚接触K8S的人KubeSphere可以帮你从“命令操作”过渡到“图形化管理”但建议先纯kubectl用熟再依赖图形界面否则底层概念会越来越模糊。4. 常见问题与排查技巧实录4.1 kubelet无法启动cgroup驱动不匹配场景kubelet日志里持续报failed to run Kubelet: misconfigured cgroup driver或Container runtime is not running。核心原因99%是kubelet默认使用systemd cgroup驱动而Docker的cgroup驱动还是cgroupfs。解决方法就是在/etc/docker/daemon.json里把cgroup driver统一改成systemd然后重启Docker和kubelet。这里有一个隐藏坑如果改完Docker配置后没有重启containerdDocker和containerd之间的运行时链路仍然是旧配置kubelet依然报错。执行systemctl restart docker会连带重启containerd所以顺序是先改配置再systemctl daemon-reload再systemctl restart docker最后看到docker info里Cgroup Driver为systemd再去启动kubelet。4.2 镜像拉取超时配置镜像仓库与加速器初始化或join时卡在pulling image registry.k8s.io/... refused是最常见的镜像问题。解决办法有两个层面一是Docker镜像加速给daemon.json配镜像加速地址解决Docker Hub上镜像的拉取二是kubeadm层面初始化时用--image-repository参数指定镜像仓库解决系统组件镜像的拉取。如果集群已经初始化完了再改镜像仓库是来不及的只能通过kubeadm reset重来。所以一定要在init之前就把--image-repository想清楚。对于已创建的Pod拉不到某些镜像可以手动docker pull对应镜像然后再触发Pod重建。4.3 证书过期与自动续签K8S集群默认证书有效期是一年这是很多人“集群突然挂了”的元凶。kubeadm提供了证书更新命令kubeadm certs renew all手动执行后需要重启控制面组件。更省心的做法是利用systemd定时器或cron实现每年自动续签。我自己偏向用systemd timer来做定期续签。要注意的是renew之后kubeconfig文件里的证书也会更新需要把/etc/kubernetes/admin.conf重新拷到用户目录kubectl才能继续使用。这个坑很典型证书明明更新了但kubectl还是提示证书过期。4.4 Pod无法跨节点通信与NetworkPolicy症状是同一节点上的Pod互相能通但跨节点的Pod ping不通。优先检查两件事Calico的BGP邻居是否建立以及云平台安全组是否放行了BGP端口179。很多云平台安全组默认不放这个端口导致跨节点路由无法通告。其次检查宿主机ip_forward是否生效sysctl net.ipv4.ip_forward返回1才算正常。另外如果误加了NetworkPolicy且策略不匹配也会出现“时通时不通”的诡异现象。排查思路是kubectl get networkpolicy -A看一下有没有策略先把策略临时删除再测试能快速确认是不是策略误伤。Pod网络问题最好从下往上查网卡、路由、iptables、策略一层一层排除。4.5 ExternalIP、NodePort与故障转移场景NodePort用于暴露服务到宿主机端口但外部访问还要依赖云平台安全组放行30000-32767端口。ExternalIP则是直接把云主机的公网IP绑到Service上访问ExternalIP:Port就能到达后端Pod。两种方式各有适用场景NodePort适合集群内测试和临时访问ExternalIP适合把服务稳定暴露给外部系统。故障转移场景下需要关注节点的NotReady。当Worker节点宕机时上面运行的Pod不会立刻被调度走默认有个容忍时间toleration大约5分钟节点恢复后调度器会把不满足健康状态的Pod迁移到其它可用节点。这个机制和Kafka三节点集群、Redis Cluster的主从切换逻辑思路类似先通过健康检查识别故障成员再执行数据或工作的搬运。理解这个思想排查集群故障时就不会只看单个Pod。4.6 日常运维常用的K8S命令速查最后给一份我常用的命令清单覆盖节点、Pod、Service、日志、事件五类kubectl get nodes -o wide # 查看节点和IP kubectl get pods -A -o wide # 查看所有Pod及其所在节点 kubectl describe pod pod-name -n ns # 查看Pod事件和调度详情 kubectl logs -f pod-name -n ns # 跟踪日志 kubectl get events --sort-by.lastTimestamp # 查看全局事件调试阶段我最推荐的是describe命令。一个Pod起不来describe会把拉镜像失败、探针失败、资源不足、调度不满足等常见原因直接列出来比一个个容器日志翻快得多。这套命令配合第四章的排查思路基本能覆盖日常问题。集群跑起来之后建议把这些命令记在本地备忘录里大概率每周都会用上。最后再分享一个小技巧每次搭建集群之前我会把Master和Worker的环境初始化脚本写成同一个Shell脚本三台机器跑同一份配置。这样既能保证cgroup驱动、hosts、内核参数完全一致也方便后面扩容Worker时一键准备好环境。集群稳定性的根基在于配置统一而不是版本最新。祝你能一次把集群Run起来。
返回列表