☰
CentOS 8 部署 Kubernetes 实战:从系统准备到集群跑通
2026/10/13 10:15:24 网站建设 项目流程

1. 为什么现在还有人要在 CentOS 8 上部署 Kubernetes

CentOS 8 这个系统版本挺有意思,官方在 2021 年底就把维护模式给切了,按道理说大家应该往 Rocky、Alma 或者 Ubuntu 上迁。但现实情况是,大量企业内部的老业务、老硬件、老运维习惯还卡在 CentOS 8 上,尤其是那些 2020 到 2022 年之间上的项目,装机镜像就是 CentOS 8,重新换系统意味着要重新做安全基线、重新过一遍内部流程,成本太高。所以“在 CentOS 8 上把 Kubernetes 跑起来”这件事,到今天依然有非常实际的需求。

我自己前前后后在 CentOS 8 上部署过不下十套 K8s 集群,有小到三台虚拟机的测试环境,也有十几台物理机的中等规模生产环境。踩过的坑从 yum 源失效、内核模块没加载、cgroup 驱动不匹配,到容器运行时和 kubelet 版本对不上,基本都经历了一遍。这篇文章就是把这些经验完整地梳理出来,从系统准备到集群跑通,再到常见问题排查,给你一套可以直接照着做的方案。

这篇文章适合谁看?如果你是运维工程师、后端开发,或者正在做私有化交付的技术人员,需要在 CentOS 8 上从零搭一套 K8s,那这篇内容基本能覆盖你 90% 的场景。如果你只是想了解一下 K8s 部署的大致流程,也可以看,因为我会把每一步“为什么这么做”讲清楚,而不是只丢一堆命令让你复制。

需要提前说明的是,CentOS 8 的官方 yum 源已经不可用了,这是部署过程中第一个必须解决的问题。另外 K8s 从 1.24 版本开始移除了 dockershim,容器运行时的选择也跟以前不一样了。这些变化都会直接影响你的部署方案,我会在正文里逐一拆解。

2. 部署方案的整体设计与选型思路

2.1 版本组合怎么选才不踩坑

在 CentOS 8 上部署 K8s,版本组合是第一个要定下来的事情。我的建议是:K8s 用 1.26 或 1.27,容器运行时用 containerd,网络插件用 Calico 或 Flannel。这套组合是我实测下来最稳的,社区资料也最全。

为什么不推荐 Docker 作为运行时?因为 K8s 1.24 之后已经彻底移除了 dockershim,虽然你可以额外装 cri-dockerd 来兼容,但多一层适配就多一层故障点。containerd 本身就是 CRI 标准的实现,kubelet 直接调它,链路更短,出问题也更好排查。

至于 K8s 版本,不建议追最新。1.28 之后的版本对内核和系统组件的要求更高,CentOS 8 默认的 4.18 内核虽然能跑,但某些特性会受限。1.26 和 1.27 在 CentOS 8 上的兼容性经过大量验证,属于“闭眼选”的范围。

组件推荐版本选择理由
操作系统CentOS 8.5最后一个相对稳定的 CentOS 8 小版本
容器运行时containerd 1.6.xCRI 原生支持,无需额外适配层
K8s1.26.x / 1.27.x与 CentOS 8 内核兼容性好,社区资料丰富
网络插件Calico 3.26支持 NetworkPolicy,性能稳定
内核4.18(默认)满足 K8s 最低要求,无需升级

2.2 集群架构的取舍:单 Master 还是多 Master

测试环境用单 Master 就够了,但生产环境一定要上多 Master。K8s 的控制平面组件 kube-apiserver、etcd、kube-scheduler、kube-controller-manager 如果只跑在一个节点上,那这个节点挂了整个集群就失控了。

多 Master 架构下,通常用 3 个 Master 节点加 N 个 Worker 节点。etcd 会以集群模式跑在这 3 个 Master 上,kube-apiserver 前面挂一个负载均衡器(可以用 HAProxy + Keepalived,也可以用云厂商的 LB)。这样任意一个 Master 挂了,集群还能正常工作。

我自己的习惯是:3 Master + 3 Worker 起步,Worker 节点根据业务负载再扩。Master 节点配置不用太高,2C4G 就够,但 etcd 对磁盘 IO 比较敏感,建议用 SSD。Worker 节点就看你要跑什么业务了,CPU 和内存按需配。

2.3 网络方案的选择逻辑

K8s 的网络模型有几个硬性要求:每个 Pod 有独立 IP、Pod 之间能直接通信、Pod 和 Node 之间能通信。要满足这些要求,必须装网络插件。

Flannel 是最简单的,配置少,上手快,但它不支持 NetworkPolicy,也就是说你没法做 Pod 级别的网络访问控制。Calico 功能更全,支持 NetworkPolicy,性能也好,但配置稍微复杂一点。如果你只是测试环境,Flannel 够用;如果是生产环境,我强烈建议上 Calico。

还有一个细节:Calico 的默认模式是 IPIP 隧道,跨节点通信会多一层封装,性能有损耗。如果你的节点在同一个二层网络里,可以改成 BGP 模式,性能会好很多。这个后面实操部分我会详细说。

3. 系统层面的准备工作

3.1 解决 CentOS 8 yum 源失效的问题

CentOS 8 官方源在 2021 年底就下线了,你直接yum install会报 “Failed to download metadata for repo ‘AppStream’” 之类的错误。解决办法是把源指向 vault 归档地址。

具体操作是,先把/etc/yum.repos.d/下面的源文件备份一下,然后替换成 vault 源。你可以手动改,也可以用 sed 批量替换。我习惯用 sed,一条命令搞定:

cd /etc/yum.repos.d/ sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* yum clean all yum makecache

执行完之后,yum repolist能看到仓库列表就说明源配好了。这一步看着简单,但很多人卡在这里,因为报错信息不太直观,容易以为是网络问题。

注意:vault 源的下载速度可能比较慢,如果内网有镜像站,优先用内网镜像。另外,替换源之后建议先yum update一次,把系统补丁打全,避免后续装 K8s 组件时出现依赖冲突。

3.2 关闭 Swap 和 SELinux

K8s 要求关闭 Swap,原因是 kubelet 在调度 Pod 时需要准确知道节点还剩多少内存,如果开了 Swap,内存统计就不准了,可能导致 Pod 被调度到内存已经不够的节点上。关闭 Swap 的命令很简单:

swapoff -a sed -i '/swap/s/^/#/' /etc/fstab

第一行是临时关闭,第二行是注释掉 fstab 里的 Swap 挂载项,保证重启后也不会自动挂载。

SELinux 也建议关掉,虽然 K8s 理论上支持 SELinux,但实际部署中它会带来各种权限问题,尤其是容器挂载宿主机目录的时候。关闭方式:

setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config

这里改成 permissive 而不是 disabled,是因为 disabled 需要重启才能生效,而 permissive 是立即生效的,只是记录警告不拦截。等集群跑稳定了,你再决定要不要彻底 disabled。

3.3 内核模块与系统参数调优

K8s 的网络和存储功能依赖一些内核模块,必须提前加载。主要是br_netfilter和overlay这两个。br_netfilter让 iptables 能过滤桥接流量,overlay是 containerd 存储驱动需要的。

modprobe br_netfilter modprobe overlay cat <<EOF > /etc/modules-load.d/k8s.conf br_netfilter overlay EOF

然后是 sysctl 参数。K8s 需要开启 IP 转发,否则 Pod 之间的跨节点通信会失败。另外bridge-nf-call-iptables也要打开,让桥接流量经过 iptables 处理。

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 --system

执行完sysctl --system后,可以用sysctl net.ipv4.ip_forward确认一下,返回 1 就对了。

3.4 主机名与 hosts 解析

每个节点的主机名必须唯一,而且最好能通过主机名互相解析。我习惯在/etc/hosts里把集群所有节点的 IP 和主机名都写进去,这样比依赖 DNS 更可靠。

hostnamectl set-hostname k8s-master-01 cat <<EOF >> /etc/hosts 192.168.1.10 k8s-master-01 192.168.1.11 k8s-master-02 192.168.1.12 k8s-master-03 192.168.1.20 k8s-worker-01 192.168.1.21 k8s-worker-02 192.168.1.22 k8s-worker-03 EOF

主机名不要用下划线,K8s 对主机名有格式要求,只允许字母、数字、连字符和点号。这个细节很多人不注意,等到 kubelet 启动报错才发现。

4. 容器运行时 containerd 的安装与配置

4.1 安装 containerd

containerd 的安装方式有好几种,我推荐用官方二进制包,因为可控性最强,不依赖特定发行版的包管理。先从 GitHub Releases 下载对应版本的压缩包,解压到/usr/local/下面。

wget https://github.com/containerd/containerd/releases/download/v1.6.24/containerd-1.6.24-linux-amd64.tar.gz tar Cxzvf /usr/local containerd-1.6.24-linux-amd64.tar.gz

然后配置 systemd 服务。containerd 官方提供了一个 service 文件模板,下载后放到/usr/local/lib/systemd/system/下面,然后systemctl daemon-reload并启动。

wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -O /usr/local/lib/systemd/system/containerd.service systemctl daemon-reload systemctl enable --now containerd

启动后用systemctl status containerd确认状态是 active 的。如果启动失败,大概率是配置文件有问题,可以看journalctl -u containerd的日志。

4.2 配置 containerd 使用 systemd cgroup 驱动

这是最关键的一步。K8s 在 CentOS 8 上默认用 systemd 作为 cgroup 驱动,而 containerd 默认用的是 cgroupfs。两者不一致会导致 kubelet 报错,Pod 起不来。

需要修改/etc/containerd/config.toml,找到SystemdCgroup这一项,改成 true。如果你是用containerd config default生成的默认配置,这一项默认是 false。

containerd config default > /etc/containerd/config.toml sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml systemctl restart containerd

另外,默认配置里的 pause 镜像地址是registry.k8s.io/pause:3.6,国内环境可能拉不下来。可以换成你自己的镜像仓库地址,或者提前把镜像导入到本地。

实操心得:改完 config.toml 之后一定要重启 containerd,而且重启后最好用ctr version确认一下版本信息,确保服务真的起来了。我有一次改完配置忘了重启,后面 kubelet 一直报错,排查了半天才发现是 containerd 还在用旧配置。

4.3 安装 runc 和 CNI 插件

containerd 需要 runc 来实际创建容器,还需要 CNI 插件来配置 Pod 网络。runc 可以从 GitHub 下载二进制文件,放到/usr/local/sbin/下面。

wget https://github.com/opencontainers/runc/releases/download/v1.1.10/runc.amd64 install -m 755 runc.amd64 /usr/local/sbin/runc

CNI 插件是一组二进制文件,下载后解压到/opt/cni/bin/目录。

wget https://github.com/containernetworking/plugins/releases/download/v1.3.0/cni-plugins-linux-amd64-v1.3.0.tgz mkdir -p /opt/cni/bin tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-v1.3.0.tgz

这两个装完之后,containerd 的基础环境就齐了。可以用runc --version和ls /opt/cni/bin/确认一下。

5. K8s 组件的安装与集群初始化

5.1 安装 kubelet、kubeadm、kubectl

这三个组件是 K8s 的核心。kubelet 跑在每个节点上,负责管理容器;kubeadm 用来初始化集群和加入节点;kubectl 是命令行工具。

在 CentOS 8 上,可以通过 K8s 官方 yum 源安装。先添加源:

cat <<EOF > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.27/rpm/ enabled=1 gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.27/rpm/repodata/repomd.xml.key EOF

然后安装指定版本:

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

注意这里 kubelet 先不要启动,因为还没初始化集群,启动了也会报错。等 kubeadm init 跑完之后,kubelet 会自动起来。

5.2 初始化第一个 Master 节点

初始化命令里需要指定几个关键参数:Pod 网段、Service 网段、API Server 地址、容器运行时。Pod 网段和 Service 网段不能跟宿主机网段重叠,否则会路由冲突。

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

--image-repository指定镜像仓库,国内环境用阿里云的镜像源会快很多。--cri-socket指定 containerd 的 socket 路径,因为现在默认不是 Docker 了,必须显式指定。

初始化成功后,会输出一段 join 命令,类似kubeadm join 192.168.1.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx。这段命令要保存好,后面加 Worker 节点和 Master 节点都要用。

然后配置 kubectl 的 kubeconfig:

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

执行kubectl get nodes,应该能看到 Master 节点,状态是 NotReady,因为还没装网络插件。

5.3 安装 Calico 网络插件

Calico 的安装很简单,用 kubectl apply 一个 yaml 文件就行。但要注意 Pod 网段要跟 kubeadm init 时指定的--pod-network-cidr一致。

kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml

如果你改了 Pod 网段,需要先下载 yaml 文件,修改里面的CALICO_IPV4POOL_CIDR环境变量,再 apply。

等几分钟,kubectl get pods -n kube-system看到 Calico 的 Pod 都 Running 了,kubectl get nodes里 Master 节点变成 Ready,就说明网络插件装好了。

注意:Calico 默认用 IPIP 模式,如果你节点在同一个二层网络,可以改成 BGP 模式提升性能。修改方式是编辑 Calico 的 DaemonSet,把CALICO_IPV4POOL_IPIP改成Off。不过改之前要确认网络设备支持 BGP,否则可能导致跨节点通信失败。

5.4 加入 Worker 节点和 Master 节点

Worker 节点的加入很简单,在 Worker 节点上执行之前保存的 join 命令就行。但前提是 Worker 节点也装好了 containerd、kubelet、kubeadm,并且做了系统层面的准备。

kubeadm join 192.168.1.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx

如果是加 Master 节点,需要加--control-plane参数,并且要先在第一个 Master 上生成证书 key:

kubeadm init phase upload-certs --upload-certs

然后 join 命令里加上--control-plane --certificate-key xxx。

所有节点加完之后,在 Master 上执行kubectl get nodes,应该能看到所有节点都是 Ready 状态。

6. 常见问题排查与避坑指南

6.1 kubelet 启动失败怎么查

kubelet 启动失败是最常见的问题,表现是systemctl status kubelet显示 failed。排查思路是先用journalctl -u kubelet -f看日志,日志里通常会告诉你具体原因。

常见的几个原因:cgroup 驱动不匹配(containerd 和 kubelet 用的驱动不一致)、Swap 没关、SELinux 没关、配置文件路径不对。我遇到最多的是 cgroup 驱动问题,报错信息类似 “failed to run Kubelet: misconfiguration: kubelet cgroup driver: ‘cgroupfs’ is different from docker cgroup driver: ‘systemd’”。解决办法就是改 containerd 的配置,把 SystemdCgroup 改成 true。

还有一个坑是 kubelet 的配置文件/var/lib/kubelet/config.yaml里 cgroupDriver 写的是 cgroupfs,需要改成 systemd。这个文件是 kubeadm init 生成的,如果 init 的时候没指定,默认就是 cgroupfs。

6.2 Pod 一直 Pending 或 CrashLoopBackOff

Pod 一直 Pending,通常是资源不够或者没有可调度的节点。用kubectl describe pod <pod-name> -n <namespace>看 Events,里面会写原因。如果是 “Insufficient cpu” 或 “Insufficient memory”,那就是节点资源不够了。如果是 “node(s) had taint”,那就是 Master 节点有污点,不允许调度普通 Pod。

CrashLoopBackOff 是容器启动后不断崩溃重启。常见原因是镜像里的启动命令有问题、配置文件挂载错了、依赖的服务没起来。也是用kubectl describe pod和kubectl logs排查。

6.3 跨节点 Pod 通信失败

跨节点 Pod 通信失败,大概率是网络插件的问题。先确认 Calico 或 Flannel 的 Pod 是不是都 Running,然后检查节点的 iptables 规则和路由表。

如果是 Calico IPIP 模式,检查ip tunnel show有没有 tunl0 接口。如果是 BGP 模式,检查calicoctl node status看 BGP 邻居是不是 Established。

还有一个容易忽略的点:宿主机的防火墙规则可能把 Pod 网段的流量拦了。CentOS 8 默认用 firewalld,建议在部署 K8s 之前就把 firewalld 关了,或者放行 Pod 网段和 Service 网段。

问题现象可能原因排查命令解决办法
kubelet 启动失败cgroup 驱动不匹配journalctl -u kubelet改 containerd 配置为 systemd
Pod Pending资源不足或节点有污点kubectl describe pod扩容节点或去掉污点
Pod CrashLoopBackOff容器启动命令错误kubectl logs检查镜像和启动参数
跨节点通信失败网络插件异常kubectl get pods -n kube-system重装网络插件或检查防火墙
镜像拉取失败镜像仓库不可达kubectl describe pod配置镜像加速或导入本地镜像

6.4 证书过期导致集群不可用

K8s 的证书默认有效期是一年,到期后 kube-apiserver 会拒绝所有请求,集群直接不可用。查看证书有效期:

kubeadm certs check-expiration

续期命令:

kubeadm certs renew all systemctl restart kubelet

续期之后要重启 kube-apiserver、kube-controller-manager、kube-scheduler 这些静态 Pod,最简单的方式是重启 kubelet,它会自动重建这些 Pod。另外,续期后要更新 kubeconfig 文件,否则 kubectl 还是用旧证书。

实操心得:建议在证书到期前一个月就做续期,别等到过期了再处理。过期后 kube-apiserver 起不来,kubectl 也用不了,只能手动用 openssl 或者备份的证书恢复,非常麻烦。我自己的做法是写一个定时任务,每个月检查一次证书有效期,提前告警。

7. 集群跑通后的验证与日常维护

7.1 部署一个测试应用验证集群

集群搭好之后,别急着上业务,先跑一个简单的测试应用验证一下。我习惯用 nginx 或者一个简单的 echo 服务。

kubectl create deployment nginx-test --image=nginx:alpine --replicas=3 kubectl expose deployment nginx-test --port=80 --type=NodePort

然后kubectl get pods -o wide看 Pod 是不是分布在不同节点上,kubectl get svc看 Service 的 NodePort 是多少。用 curl 访问任意节点的 NodePort,能返回 nginx 欢迎页就说明集群基本功能正常。

再测试一下 DNS 解析,在 Pod 里 ping 一下kubernetes.default.svc,能解析就说明 CoreDNS 工作正常。

7.2 日常巡检要看哪些指标

集群跑起来之后,日常巡检主要看几个方面:节点状态、Pod 状态、资源使用率、证书有效期、etcd 健康状态。

节点状态用kubectl get nodes,Pod 状态用kubectl get pods --all-namespaces,资源使用率需要装 metrics-server,然后kubectl top nodes和kubectl top pods。etcd 健康状态用etcdctl endpoint health。

我自己的习惯是每天上班第一件事就是跑一遍这些命令,看看有没有异常。如果集群规模大,建议上 Prometheus + Grafana 做监控告警,靠人工巡检不现实。

7.3 节点扩容与缩容的操作要点

扩容 Worker 节点很简单,新节点做好系统准备、装好 containerd 和 kubelet,然后执行 join 命令就行。缩容的话,先kubectl drain <node-name> --ignore-daemonsets把 Pod 驱逐到其他节点,然后kubectl delete node <node-name>从集群里删掉,最后在节点上kubeadm reset清理环境。

drain 的时候要注意,如果有 Pod 用了 emptyDir 或者 local storage,驱逐后数据会丢。另外 DaemonSet 的 Pod 默认不会被驱逐,需要加--ignore-daemonsets参数。

7.4 备份与恢复的基本策略

etcd 是集群的大脑,所有集群状态都存在 etcd 里,所以 etcd 的备份至关重要。备份命令:

ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot.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

建议写个定时任务,每天备份一次,备份文件保留最近 7 天。恢复的时候用etcdctl snapshot restore,然后修改 etcd 的静态 Pod 配置指向恢复后的数据目录。

除了 etcd,还要备份/etc/kubernetes/目录下的所有证书和配置文件。这些文件丢了,集群就彻底起不来了。

8. 一些零散但重要的经验补充

CentOS 8 上部署 K8s,还有一个容易被忽略的点是时间同步。K8s 的证书验证、Token 有效期都依赖准确的时间,如果节点之间时间差太大,会出现各种莫名其妙的认证失败。建议所有节点都配好 NTP 同步,用chronyd或者ntpd都行。

另外,CentOS 8 的默认内核参数里,net.ipv4.ip_forward是 0,必须手动打开。这个前面提过了,但值得再强调一次,因为很多人会忘。

关于镜像仓库,如果你在内网环境,建议搭一个 Harbor 或者 registry,把 K8s 需要的镜像提前推上去。kubeadm init 的时候用--image-repository指向你的内网仓库,这样初始化会快很多,也不依赖外网。

最后说一个我踩过的坑:CentOS 8 的iptables默认是 nftables 后端,而 K8s 的某些组件对 nftables 的支持还不完善。如果遇到网络规则不生效的问题,可以试试把 iptables 切换成 legacy 模式:

update-alternatives --set iptables /usr/sbin/iptables-legacy update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy

切换之后重启 containerd 和 kubelet,网络问题大概率能解决。这个坑我在两套环境里都遇到过,排查起来很费时间,因为报错信息不会直接告诉你是 nftables 的问题。

这套流程走下来,一套 CentOS 8 上的 K8s 集群基本就能稳定运行了。后续的 Ingress、存储、监控、日志这些,都是在这个基础之上叠加的,基础打牢了,后面的事情就顺了。

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

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

立即咨询