☰
CentOS 7/8快速部署Kubernetes:kubeadm实战与避坑指南
2026/10/22 4:24:34 网站建设 项目流程

1. 为什么还要聊CentOS 7和8上快速部署Kubernetes

现在一提到在Linux上部署Kubernetes,很多人第一反应就是Ubuntu,或者直接用各种托管服务。但如果你在传统企业、制造业、教育机构或者一些中小型互联网公司待过,就会发现CentOS系列依然是服务器机房里的绝对主力。CentOS 7虽然已经进入维护末期,但大量存量业务还在跑;CentOS 8虽然生命周期有过波折,但不少团队已经把它作为过渡版本用起来了。所以“在CentOS 7和8上快速部署Kubernetes”这件事,不是过时话题,而是很多运维和开发同学绕不开的硬需求。

我自己在过去几年里,帮不同团队在CentOS 7.9和CentOS 8.5上部署过不下二十套Kubernetes集群,从单节点测试环境到十几个节点的小规模生产集群都有。踩过的坑包括但不限于:内核参数没调导致Pod频繁重启、容器运行时选错版本导致kubelet起不来、防火墙规则没处理干净导致节点间通信时通时断、镜像拉取超时导致初始化卡死。这些问题的根源,往往不是Kubernetes本身复杂,而是CentOS的默认配置和Kubernetes的预期之间存在一些“历史遗留差异”。

这篇文章就是把这些经验整理出来,给出一条在CentOS 7和8上快速、稳定部署Kubernetes的路径。我会重点讲清楚每一步为什么这么做,参数怎么算,遇到问题怎么排查。适合有一定Linux基础、想自己动手搭一套可用集群的运维和开发同学。如果你之前用kubeadm部署失败过,或者部署完发现各种奇怪问题,这篇文章应该能帮你省下不少时间。

2. 部署前的整体设计与关键选型

2.1 为什么选择kubeadm而不是二进制或托管方案

在CentOS上部署Kubernetes,常见路径有三条:二进制手动部署、kubeadm工具部署、以及各种第三方脚本或托管服务。二进制部署灵活度最高,但工作量巨大,光是证书签发和组件配置就能耗掉一整天,而且后续升级几乎等于重新部署。托管服务省心,但很多内网环境根本连不出去,或者出于数据合规要求不能把控制面交给外部。

kubeadm是官方维护的部署工具,它把控制面组件的启动、证书生成、kubeconfig文件创建、Token管理这些重复性工作全部自动化了。你只需要准备好机器、装好容器运行时和kubelet,剩下的交给kubeadm init和kubeadm join两条命令。对于CentOS 7和8这种“标准但有点旧”的系统来说,kubeadm的兼容性经过大量验证,社区文档也最全。我实测下来,在CentOS 7.9上从零到集群Ready,熟练的话15分钟以内能搞定。

注意:kubeadm不是用来管理集群日常运维的,它只负责“把集群搭起来”和“把节点加进去”。后续的Pod调度、网络策略、存储挂载,还是得靠CNI插件、CSI插件和你的业务YAML。

2.2 CentOS 7和8的差异对部署的影响

CentOS 7和8虽然都是RHEL系,但底层差异不小,直接影响Kubernetes部署的细节。最核心的几点:

  • 内核版本:CentOS 7默认内核是3.10,CentOS 8是4.18。Kubernetes从1.24开始要求内核至少3.10,但如果你要用一些高级网络特性(比如IPVS模式、eBPF),4.18的内核明显更稳。CentOS 7上我建议至少升级到3.10.0-1160以上,或者直接上ELRepo的长期支持内核。
  • 包管理器:CentOS 7用yum,CentOS 8用dnf。命令基本兼容,但CentOS 8的模块化仓库(AppStream)有时候会干扰containerd或docker的安装,需要额外禁用某些模块。
  • 防火墙:CentOS 7默认firewalld,CentOS 8也是。但CentOS 8的nftables后端在某些内核版本上和Kubernetes的iptables规则有冲突,我遇到过Pod跨节点通信被丢包的情况,后来统一改用iptables后端才稳定。
  • SELinux:两个版本默认都是enforcing。Kubernetes官方文档建议设置为permissive或disabled,但生产环境直接关掉SELinux有安全风险。我的做法是设置为permissive,然后针对容器运行时和kubelet做策略调整。

2.3 容器运行时选containerd还是Docker

Kubernetes从1.24版本开始正式移除dockershim,也就是说kubelet不再直接支持Docker作为容器运行时。虽然你可以继续用cri-dockerd这个适配器,但多一层转换就多一层故障点。containerd本来就是Docker的底层运行时,现在被CNCF独立维护,和Kubernetes的集成更原生。

在CentOS 7和8上,containerd的安装都很简单,官方仓库直接有包。我现在的做法是:新集群一律用containerd,老集群如果已经在用Docker且业务稳定,不急着换。containerd的配置文件/etc/containerd/config.toml需要重点调整两个地方:一是SystemdCgroup要设为true,否则kubelet和containerd的cgroup驱动不一致,Pod会反复重启;二是镜像仓库的mirror配置,国内环境不配镜像加速基本拉不动镜像。

2.4 网络插件怎么选

CNI插件决定了Pod之间怎么通信。常见的有Flannel、Calico、Cilium。Flannel最简单,适合小集群和测试环境,但它不支持网络策略。Calico功能全,支持网络策略、BGP路由,生产环境用得最多。Cilium基于eBPF,性能最好,但对内核版本有要求,CentOS 7的3.10内核跑起来比较吃力。

我的建议是:CentOS 7上优先用Calico的VXLAN模式,兼容性好;CentOS 8上可以尝试Calico的IPIP模式或者Cilium。如果你只是搭个测试环境,Flannel足够,一条命令就搞定。但要注意,Flannel的默认网段是10.244.0.0/16,别和你的宿主机网段冲突。

3. 系统准备与核心参数调优

3.1 机器规划与基础环境检查

假设我们要搭一个三节点集群:一台控制面(master),两台工作节点(worker)。配置建议至少2核4G,控制面最好4核8G。操作系统分别是CentOS 7.9和CentOS 8.5。所有节点需要满足:

  • 主机名唯一,且能互相解析。我习惯在/etc/hosts里写死,不依赖DNS。
  • 时间同步。CentOS 7用chronyd,CentOS 8也是chronyd,确保timedatectl显示同步正常。
  • 关闭Swap。Kubernetes要求Swap关闭,否则kubelet启动会报错。临时关闭用swapoff -a,永久关闭要注释掉/etc/fstab里的swap行。
  • 防火墙放行必要端口。控制面需要6443、2379-2380、10250、10259、10257;工作节点需要10250、30000-32767。如果你不想折腾规则,测试环境可以直接systemctl stop firewalld并systemctl disable firewalld,但生产环境必须按需放行。

提示:CentOS 8的firewalld默认使用nftables后端,和Kubernetes的iptables规则可能冲突。如果遇到Pod网络不通,先检查firewall-cmd --state,然后尝试update-alternatives --set iptables /usr/sbin/iptables-legacy切换回legacy模式。

3.2 内核参数与模块加载

Kubernetes需要一些内核模块和参数才能正常工作。在CentOS 7和8上,这些配置基本一致,但CentOS 7的内核较旧,有些模块需要手动加载。

首先创建/etc/modules-load.d/k8s.conf,内容如下:

overlay br_netfilter

然后执行modprobe overlay和modprobe br_netfilter立即加载。接着创建/etc/sysctl.d/k8s.conf:

net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1

执行sysctl --system生效。这三个参数的作用分别是:让桥接流量经过iptables规则、让IPv6桥接流量也经过规则、开启IP转发。少了任何一个,Pod跨节点通信都会出问题。

CentOS 7上还需要确认br_netfilter模块是否真的加载了,有时候内核版本太旧会加载失败。用lsmod | grep br_netfilter检查,如果没有输出,说明模块没加载,需要升级内核或者手动编译。

3.3 关闭SELinux的正确姿势

SELinux是CentOS的安全基石,但Kubernetes的很多操作会被它拦截。官方建议是设置为permissive模式,这样SELinux只记录不拦截。修改/etc/selinux/config:

SELINUX=permissive

然后重启或者执行setenforce 0立即生效。注意,不要直接设为disabled,因为disabled模式下SELinux完全不加载策略,后续想重新启用会很麻烦。permissive模式下,你可以通过ausearch -m avc -ts recent查看被拦截的操作,逐步调整策略。

我在CentOS 8上遇到过containerd被SELinux拦截导致容器无法启动的情况,日志里会有avc: denied记录。临时方案是setenforce 0,长期方案是给containerd和kubelet打上正确的SELinux标签,或者使用container-selinux包提供的策略。

3.4 容器运行时安装与配置

以containerd为例,CentOS 7和8的安装步骤略有不同。

CentOS 7:

yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y containerd.io

CentOS 8:

dnf install -y dnf-utils dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo dnf install -y containerd.io

安装完成后,生成默认配置:

containerd config default > /etc/containerd/config.toml

然后编辑这个文件,找到SystemdCgroup,设为true:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true

这一步非常关键。如果SystemdCgroup是false,kubelet用systemd管理cgroup,containerd用cgroupfs,两者不一致会导致Pod状态异常,日志里会出现failed to create containerd task: cgroup ...之类的错误。

另外,配置镜像加速。在config.toml里找到[plugins."io.containerd.grpc.v1.cri".registry.mirrors],添加:

[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"] endpoint = ["https://你的镜像加速地址"]

最后启动containerd并设为开机自启:

systemctl enable containerd systemctl start containerd systemctl status containerd

3.5 kubelet、kubeadm、kubectl安装

这三个组件需要版本一致。添加Kubernetes的yum仓库:

cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg EOF

CentOS 8上把baseurl里的el7改成el8。然后安装指定版本,比如1.26.0:

yum install -y kubelet-1.26.0 kubeadm-1.26.0 kubectl-1.26.0 systemctl enable kubelet

注意,kubelet此时启动会失败,因为还没初始化集群,这是正常的。等kubeadm init之后再检查。

4. 集群初始化与节点加入实操

4.1 kubeadm init的关键参数计算

控制面节点上执行初始化。最简命令是kubeadm init,但实际使用中需要指定一些参数。我常用的命令如下:

kubeadm init \ --apiserver-advertise-address=192.168.1.10 \ --image-repository=registry.aliyuncs.com/google_containers \ --kubernetes-version=v1.26.0 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.244.0.0/16 \ --cri-socket=unix:///var/run/containerd/containerd.sock

逐个解释:

  • --apiserver-advertise-address:控制面的内网IP,工作节点通过这个地址访问API Server。必须写实际IP,不能写0.0.0.0。
  • --image-repository:国内环境必须换镜像仓库,否则拉取kube-apiserver等镜像会超时。
  • --kubernetes-version:指定版本,避免默认拉取最新版导致兼容问题。
  • --service-cidr:Service的虚拟IP段,默认10.96.0.0/12,一般不用改,但要确保不和宿主机网段冲突。
  • --pod-network-cidr:Pod的IP段,必须和CNI插件的配置一致。Flannel默认10.244.0.0/16,Calico默认192.168.0.0/16。这里我写10.244.0.0/16是为了配合Flannel。
  • --cri-socket:指定containerd的socket路径。如果系统里同时装了Docker和containerd,kubeadm可能会检测到多个socket而报错,必须显式指定。

初始化过程会拉取镜像、生成证书、启动控制面组件。如果卡在[kubelet-check]或者[api-check],通常是网络问题或者cgroup驱动不一致。用journalctl -xeu kubelet看日志。

4.2 初始化后的收尾工作

初始化成功后,终端会输出一段提示,告诉你如何配置kubectl:

mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config

然后检查节点状态:

kubectl get nodes

此时控制面节点应该是NotReady,因为还没装CNI插件。这是正常的。

4.3 安装CNI插件

以Flannel为例,一条命令:

kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml

如果网络受限,可以先把YAML下载下来,修改里面的Network字段和--pod-network-cidr一致,再apply。安装完成后等一两分钟,再查节点状态,应该变成Ready。

Calico的安装稍微复杂一点,需要先下载calico.yaml,修改里面的CALICO_IPV4POOL_CIDR和--pod-network-cidr一致,然后apply。Calico的好处是自带网络策略,但资源占用比Flannel高。

注意:CNI插件安装后,如果节点还是NotReady,先检查kubectl get pods -n kube-system,看CNI的Pod是否Running。如果CrashLoopBackOff,多半是镜像拉取失败或者网段冲突。

4.4 工作节点加入集群

在控制面节点上执行:

kubeadm token create --print-join-command

这会输出一条kubeadm join命令,复制到工作节点上执行。命令里包含Token和CA证书的hash,Token默认24小时有效。如果过期,重新生成即可。

工作节点执行join命令后,回到控制面节点kubectl get nodes,应该能看到新节点。如果节点一直NotReady,检查工作节点的kubelet日志和containerd状态。

4.5 验证集群基本功能

部署一个测试Pod:

kubectl run nginx --image=nginx --port=80 kubectl expose pod nginx --port=80 --type=NodePort

然后kubectl get svc查看NodePort,用curl访问任意节点的NodePort,如果能返回Nginx欢迎页,说明集群网络和Service都正常。

再测试跨节点通信:在两个工作节点上各起一个Pod,互相ping对方的Pod IP。如果通,说明CNI工作正常。

5. 常见问题与排查技巧实录

5.1 kubelet启动失败排查思路

kubelet起不来是最常见的问题。排查顺序:

  1. systemctl status kubelet看状态,journalctl -xeu kubelet看详细日志。
  2. 如果报failed to run Kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd",说明cgroup驱动不一致。修改containerd配置里的SystemdCgroup = true,重启containerd和kubelet。
  3. 如果报failed to load kubelet config file,检查/var/lib/kubelet/config.yaml是否存在。kubeadm init之前这个文件不存在是正常的,init之后会自动生成。
  4. 如果报Unit kubelet.service entered failed state,但日志里没有明显错误,检查Swap是否关闭,SELinux是否permissive。

5.2 Pod一直Pending或CrashLoopBackOff

Pod Pending通常是资源不足或者没有可用节点。用kubectl describe pod看Events,如果提示0/3 nodes are available: 3 node(s) had taint,说明控制面节点有污点,工作节点没加进来或者没Ready。

CrashLoopBackOff多半是容器内部错误。kubectl logs看容器日志,如果日志为空,可能是镜像入口命令有问题。kubectl describe pod看Last State的Exit Code,137表示OOM被杀,1表示应用报错。

5.3 跨节点网络不通

先确认CNI Pod是否Running。然后检查宿主机iptables规则:

iptables -t nat -L -n | grep flannel

如果没有相关规则,说明CNI没生效。再检查net.ipv4.ip_forward是否为1,br_netfilter是否加载。CentOS 8上还要确认firewalld的nftables后端是否干扰,必要时切换iptables-legacy。

5.4 镜像拉取超时

国内环境必须配置镜像加速。containerd的配置在/etc/containerd/config.toml,修改后systemctl restart containerd。kubeadm init时用--image-repository指定国内仓库。如果还是超时,检查DNS解析,/etc/resolv.conf里最好有可用的DNS服务器。

5.5 常见问题速查表

问题现象可能原因排查命令解决方案
kubelet启动失败cgroup驱动不一致journalctl -xeu kubelet修改containerd配置SystemdCgroup=true
节点NotReadyCNI未安装kubectl get pods -n kube-system安装Flannel或Calico
Pod Pending控制面有污点kubectl describe node去掉污点或加入工作节点
跨节点不通防火墙拦截iptables -L -n放行端口或关闭firewalld
镜像拉取失败无镜像加速crictl pull配置registry mirror
Token过期超过24小时kubeadm token list重新生成join命令

6. 部署后的调优与日常维护建议

6.1 资源预留与驱逐策略

默认情况下,Kubernetes不会为系统组件预留资源,业务Pod可能把节点资源吃光导致kubelet被OOM。建议在kubelet配置里加上:

evictionHard: memory.available: "200Mi" nodefs.available: "10%" systemReserved: cpu: "200m" memory: "300Mi" kubeReserved: cpu: "200m" memory: "300Mi"

这些配置写在/var/lib/kubelet/config.yaml里,然后systemctl restart kubelet。evictionHard定义驱逐阈值,systemReserved和kubeReserved分别给系统和Kubernetes组件预留资源。

6.2 证书有效期与轮换

kubeadm生成的证书默认有效期一年。到期后集群会不可用。检查证书:

kubeadm certs check-expiration

续期:

kubeadm certs renew all

然后重启控制面组件。建议把续期命令加到定时任务里,每年执行一次。

6.3 备份etcd数据

etcd存着整个集群的状态,必须定期备份。用etcdctl:

ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key

恢复的时候用etcdctl snapshot restore,然后修改etcd的data-dir指向恢复的数据。这个操作比较危险,建议先在测试环境演练。

6.4 节点维护与Pod驱逐

需要重启某个节点时,先驱逐Pod:

kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data

维护完成后:

kubectl uncordon <node-name>

--ignore-daemonsets是因为DaemonSet的Pod不能驱逐,--delete-emptydir-data是允许删除使用emptyDir的Pod。如果不加这两个参数,drain会卡住。

6.5 监控与日志收集

最小化监控方案:部署metrics-server,然后用kubectl top nodes和kubectl top pods看资源使用。日志收集可以用Loki+Promtail,比ELK轻量很多。如果集群规模不大,直接kubectl logs也够用。

我在实际维护中发现,CentOS 7上跑Kubernetes最大的隐患是内核太旧,长时间运行后可能出现网络连接跟踪表满、conntrack丢包的问题。建议定期检查dmesg | grep conntrack,如果出现nf_conntrack: table full, dropping packet,调整net.netfilter.nf_conntrack_max参数,或者升级内核。CentOS 8的内核4.18在这方面表现好很多,但也要注意nf_conntrack的默认值可能不够用。

最后分享一个小技巧:如果你在CentOS 8上遇到containerd启动后kubelet无法连接的问题,检查/etc/containerd/config.toml里的sandbox_image是否可拉取。默认是registry.k8s.io/pause:3.6,国内环境可能拉不动,改成registry.aliyuncs.com/google_containers/pause:3.6即可。这个细节很多教程不会提,但一旦遇到就是卡在kubeadm init的[kubelet-check]阶段,日志里只报超时,不报具体原因,排查起来很费时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询