1. 为什么现在还要聊CentOS 7/8上快速部署Kubernetes
CentOS 7和CentOS 8这两个系统版本,在不少中小团队和自建机房里依然是主力。CentOS 7虽然已经进入维护尾声,但存量极大,很多业务跑得稳稳当当,没人愿意为了一个新集群去动底层系统;CentOS 8虽然生命周期也结束了,但Stream版本和大量已装好的环境还在服役。所以“在CentOS 7/8上快速部署Kubernetes”这件事,不是过时话题,而是实打实的日常需求。
我自己在最近一年里,用CentOS 7.9和CentOS 8.5分别搭过好几套Kubernetes集群,有单master的测试环境,也有三master的高可用生产环境。踩过的坑从内核参数、容器运行时选型,到镜像拉取、网络插件冲突,基本都经历了一遍。这篇文章就把这些经验完整地整理出来,目标很明确:让你在一台干净的CentOS 7或8机器上,用最短的时间、最少的弯路,把Kubernetes集群跑起来,并且知道每一步为什么这么做。
适合谁看?如果你是运维工程师、后端开发,或者刚接触容器编排想自己搭一套环境练手,这篇文章都能直接用。我不假设你已经是Kubernetes专家,但假设你会基本的Linux操作,能看懂systemctl、yum这些命令。整个部署过程我会给出完整的命令和配置文件,你可以直接抄作业,同时我会解释每个关键参数背后的逻辑,这样遇到问题你能自己判断,而不是只会复制粘贴。
部署Kubernetes的方式有很多种,kubeadm、二进制、minikube、k3s等等。在CentOS 7/8这个场景下,我强烈推荐kubeadm,原因是它是官方维护的集群引导工具,版本迭代跟得紧,社区文档最全,出问题最容易搜到答案。二进制部署虽然灵活,但组件多、证书管理复杂,不适合“快速部署”这个目标。minikube和k3s更适合本地开发或边缘场景,跟我们要聊的生产级集群不是一回事。所以整篇内容围绕kubeadm展开,配合containerd作为容器运行时,这是目前最主流也最稳的组合。
2. 部署前的整体设计与关键选型
2.1 容器运行时为什么选containerd而不是Docker
这是很多人第一个纠结的点。早期Kubernetes默认用Docker作为容器运行时,但后来Kubernetes引入了CRI(容器运行时接口)标准,Docker因为架构原因需要一层dockershim来适配,性能和维护成本都不理想。从Kubernetes 1.24开始,dockershim被正式移除,Docker不再是默认选项。
containerd是从Docker内部剥离出来的容器运行时,它更轻量、更专注,直接实现了CRI,调用链更短。实测下来,同样配置的机器上,containerd启动Pod的速度比Docker快一些,资源占用也更低。而且containerd的配置文件清晰,排查问题方便。所以现在搭新集群,我基本不犹豫,直接上containerd。
选containerd还有一个实际好处:它的镜像管理跟Docker兼容,你原来用docker pull拉的镜像,containerd通过配置也能用同样的镜像仓库。迁移成本很低。
2.2 网络插件怎么选:Calico还是Flannel
网络插件决定了Pod之间怎么通信。Flannel配置简单,适合小规模集群,但它不支持网络策略(NetworkPolicy),也就是说你没法做Pod级别的访问控制。Calico功能更全,支持网络策略,性能也好,但配置稍微复杂一点。
我的建议是:如果是测试环境、学习用,Flannel够用,五分钟就能跑起来;如果是生产环境,或者你以后可能要做多租户隔离、访问控制,直接上Calico,省得以后迁移。Calico在CentOS 7/8上跑很稳,我用的最多的是Calico的IPIP模式,配置简单,跨节点通信没问题。
2.3 系统层面的准备工作
不管CentOS 7还是8,部署前有几件事必须做,否则后面一定出问题。
第一,关闭Swap。Kubernetes的设计假设是节点内存可预测,Swap会让内存管理变得不可控,kubelet默认不允许Swap开启。命令是swapoff -a,然后编辑/etc/fstab把Swap那行注释掉,重启后也不会自动挂载。
第二,关闭SELinux。SELinux会拦截容器对文件系统的访问,导致各种权限错误。虽然可以通过配置策略让SELinux和容器共存,但配置复杂,快速部署场景下直接关掉最省事。临时关闭用setenforce 0,永久关闭改/etc/selinux/config里的SELINUX=disabled。
第三,配置内核参数。主要是开启网桥模式下的iptables转发,让Pod的流量能正确经过宿主机网络栈。需要加载br_netfilter模块,并设置net.bridge.bridge-nf-call-iptables和net.bridge.bridge-nf-call-ip6tables为1。这些参数不设置,Pod跨节点通信会失败,而且报错信息很隐晦,不容易排查。
第四,时间同步。集群里所有节点的时间必须一致,否则证书验证、日志时间戳都会出问题。CentOS 7/8自带chronyd,启动并设置开机自启就行。
第五,主机名和hosts解析。每个节点要有唯一的主机名,并且所有节点都能通过主机名互相解析。可以在/etc/hosts里手动写,也可以配DNS。我习惯手动写,简单直接。
3. CentOS 7与CentOS 8的差异处理
3.1 防火墙和iptables的差异
CentOS 7默认用firewalld,CentOS 8也是firewalld,但底层iptables版本不同。Kubernetes需要大量的iptables规则来做服务转发和网络隔离,firewalld会干扰这些规则。所以部署前建议直接停掉firewalld,用iptables或者干脆让Kubernetes自己管理。
命令是systemctl stop firewalld和systemctl disable firewalld。然后确保iptables的FORWARD链默认策略是ACCEPT,否则Pod跨节点流量会被丢弃。可以用iptables -P FORWARD ACCEPT临时设置,永久生效需要写进配置文件或者用kubelet的参数。
CentOS 8上还有一个坑:nftables。CentOS 8默认用nftables作为iptables的后端,虽然兼容大部分iptables命令,但在某些Kubernetes版本上会出现规则不生效的情况。解决办法是设置update-alternatives --set iptables /usr/sbin/iptables-legacy,切回传统的iptables。这个坑我在CentOS 8.5上遇到过,Pod之间ping不通,排查了半天才发现是nftables的问题。
3.2 软件源和依赖包的差异
CentOS 7的默认yum源里软件版本比较老,containerd和kubeadm这些包需要额外添加源。CentOS 8的源里软件新一些,但CentOS 8已经停止维护,默认源可能连不上,需要换成vault源或者国内镜像源。
具体操作上,CentOS 7需要先安装yum-utils,然后添加Docker的源(虽然我们不用Docker,但containerd的包在Docker源里)和Kubernetes的源。CentOS 8类似,但要注意podman和containerd的冲突,CentOS 8默认装了podman,需要先卸载或者确保containerd优先。
还有一个差异是conntrack工具。Kubernetes需要conntrack来管理连接跟踪,CentOS 7默认没装,需要yum install conntrack-tools。CentOS 8一般自带,但版本可能不匹配,建议也手动装一下。
3.3 内核版本的影响
CentOS 7默认内核是3.10,这个版本对容器的支持有限,比如OverlayFS在3.10上不是原生支持,需要额外模块。虽然containerd可以用其他存储驱动,但OverlayFS性能最好。建议把CentOS 7的内核升级到4.x或5.x,可以用ELRepo源安装主线内核。
CentOS 8默认内核是4.18,对容器支持好很多,OverlayFS原生支持,基本不用动内核。这也是我推荐新集群用CentOS 8的原因之一,省事。
升级CentOS 7内核的命令大概是:添加ELRepo源,然后yum install kernel-ml,重启后选择新内核启动。注意升级内核后要重新加载br_netfilter模块,因为新内核可能默认不加载。
4. 完整部署实操流程
4.1 所有节点的基础配置
假设你有三台机器,一台master,两台node,主机名分别是k8s-master、k8s-node1、k8s-node2。所有机器都执行以下步骤。
先设置主机名:
hostnamectl set-hostname k8s-master # 在master上执行 hostnamectl set-hostname k8s-node1 # 在node1上执行 hostnamectl set-hostname k8s-node2 # 在node2上执行然后编辑/etc/hosts,加入所有节点的解析:
192.168.1.10 k8s-master 192.168.1.11 k8s-node1 192.168.1.12 k8s-node2关闭Swap:
swapoff -a sed -i '/swap/s/^/#/' /etc/fstab关闭SELinux:
setenforce 0 sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config配置内核参数,创建/etc/modules-load.d/k8s.conf:
br_netfilter ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh nf_conntrack_ipv4注意CentOS 8上nf_conntrack_ipv4可能叫nf_conntrack,根据系统调整。
创建/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生效。
关闭firewalld:
systemctl stop firewalld systemctl disable firewalld设置iptables FORWARD策略:
iptables -P FORWARD ACCEPT时间同步:
systemctl start chronyd systemctl enable chronyd4.2 安装containerd
所有节点都要装containerd。先添加Docker的yum源(containerd的包在这里):
yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo然后安装containerd:
yum install -y containerd.io安装完成后,生成默认配置:
containerd config default > /etc/containerd/config.toml这里有个关键修改:把SystemdCgroup设置为true。因为kubelet默认用systemd作为cgroup驱动,containerd也要用同样的驱动,否则Pod启动会报错。编辑/etc/containerd/config.toml,找到SystemdCgroup = false改成SystemdCgroup = true。
还有一个可选优化:把sandbox_image改成国内能访问的镜像地址,否则初始化时会卡在拉取pause镜像。默认是registry.k8s.io/pause:3.6,可以改成你自己的镜像仓库地址。
启动containerd:
systemctl start containerd systemctl enable containerd4.3 安装kubeadm、kubelet、kubectl
添加Kubernetes的yum源,创建/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如果访问不了这个源,可以换成国内镜像源,比如阿里云的:
[kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=0然后安装指定版本,我一般用1.26或1.27,比较稳定:
yum install -y kubelet-1.26.0 kubeadm-1.26.0 kubectl-1.26.0安装完成后,设置kubelet开机自启,但先不启动,因为还没初始化:
systemctl enable kubelet4.4 初始化master节点
在master节点上执行初始化命令。这里有几个关键参数需要根据你的环境调整:
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:master节点的IP,其他节点通过这个地址访问API Server。--image-repository:镜像仓库地址,默认是k8s.gcr.io,国内访问不了,换成阿里云的。--kubernetes-version:指定版本,跟前面安装的版本一致。--service-cidr:Service的IP段,不能跟宿主机网段冲突。--pod-network-cidr:Pod的IP段,如果后面用Calico,默认是192.168.0.0/16,用Flannel是10.244.0.0/16。这里我写的是Flannel的段,如果用Calico要改。--cri-socket:指定containerd的socket路径,因为系统里可能同时有Docker和containerd,不指定会报错。
初始化成功后,会输出一段join命令,类似:
kubeadm join 192.168.1.10:6443 --token xxxxx --discovery-token-ca-cert-hash sha256:xxxxx把这段命令保存下来,后面node节点要用。
然后配置kubectl:
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config4.5 安装网络插件
如果选Flannel,直接apply一个yaml:
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml如果选Calico:
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml注意Calico的yaml里默认的Pod网段是192.168.0.0/16,如果你初始化时用了别的网段,要改yaml里的CALICO_IPV4POOL_CIDR。
安装完等几分钟,用kubectl get pods -n kube-system看所有Pod是否Running。
4.6 node节点加入集群
在每个node节点上执行前面保存的join命令:
kubeadm join 192.168.1.10:6443 --token xxxxx --discovery-token-ca-cert-hash sha256:xxxxx如果token过期了,可以在master上重新生成:
kubeadm token create --print-join-command加入后在master上执行kubectl get nodes,应该能看到所有节点,状态是Ready。
5. 常见问题与排查技巧实录
5.1 初始化卡在拉取镜像
这是最常见的问题,原因基本是镜像仓库访问不了。解决办法是在初始化命令里指定--image-repository为国内可访问的地址。如果已经卡住了,可以kubeadm reset后重新初始化。
还有一个情况是containerd的配置里sandbox_image没改,即使kubeadm指定了镜像仓库,pause镜像还是从默认地址拉。所以要改containerd的config.toml。
5.2 node节点加入后NotReady
先看kubelet的状态:systemctl status kubelet。常见原因有几个:
一是cgroup驱动不匹配。kubelet默认用systemd,containerd如果没配SystemdCgroup = true,就会报错。检查containerd的config.toml。
二是网络插件没装好。如果master上网络插件的Pod没Running,node节点会NotReady。用kubectl get pods -n kube-system看。
三是防火墙或iptables规则拦截。确保firewalld关了,FORWARD链是ACCEPT。
5.3 Pod之间ping不通
如果Pod能启动但跨节点通信失败,先检查网络插件的日志。Flannel的话看kubectl logs -n kube-system <flannel-pod>,Calico类似。
常见原因是Pod网段冲突。比如你宿主机网段是10.244.0.0/16,Pod网段也设成这个,就会冲突。初始化时要确保--pod-network-cidr跟宿主机网段不重叠。
还有一个原因是CentOS 8的nftables问题,前面提过,切回iptables-legacy。
5.4 证书过期
Kubernetes的证书默认一年有效期,到期后集群会挂。检查证书有效期:
kubeadm certs check-expiration续期:
kubeadm certs renew all然后重启kubelet和containerd。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 初始化卡在拉镜像 | 镜像仓库不可达 | 指定国内镜像仓库,改containerd配置 |
| node NotReady | cgroup驱动不匹配 | containerd配置SystemdCgroup=true |
| node NotReady | 网络插件未就绪 | 检查kube-system下Pod状态 |
| Pod跨节点不通 | Pod网段冲突 | 重新规划网段,reset后重来 |
| Pod跨节点不通 | nftables干扰 | 切换iptables-legacy |
| kubelet启动失败 | Swap未关 | swapoff -a并改fstab |
| 证书过期 | 超过一年 | kubeadm certs renew all |
5.6 几个独家避坑技巧
第一,初始化前先kubeadm config images pull把镜像拉下来,这样初始化时不会卡住,也能提前发现镜像仓库的问题。
第二,如果机器内存小于2G,kubelet可能因为内存不足启动失败。建议master至少2G,node至少1G。测试环境可以调低kubelet的--kube-reserved和--system-reserved参数。
第三,CentOS 7上升级内核后,记得重新配置br_netfilter模块,新内核可能默认不加载。可以在/etc/modules-load.d/k8s.conf里加上,然后modprobe br_netfilter。
第四,如果用了Calico,默认的MTU是1440,在某些网络环境下需要调整。可以在Calico的yaml里改FELIX_IPINIPMTU环境变量。
第五,kubeadm reset后,记得清理/etc/kubernetes、/var/lib/etcd、/var/lib/cni这些目录,否则重新初始化可能残留旧配置。
6. 集群验证与日常维护要点
6.1 部署后的验证清单
集群跑起来后,别急着上业务,先做几个基本验证。
看节点状态:kubectl get nodes,所有节点应该是Ready。
看系统Pod:kubectl get pods -n kube-system,所有Pod应该是Running,特别是coredns、kube-proxy、网络插件的Pod。
跑一个测试Pod:kubectl run test --image=nginx --restart=Never,然后kubectl get pod test看是否Running。再kubectl exec -it test -- curl localhost看nginx是否响应。
测试跨节点通信:在两个node上各跑一个Pod,互相ping。如果通了,网络没问题。
测试Service:kubectl expose pod test --port=80 --type=NodePort,然后从集群外访问NodePort,看是否能通。
6.2 日常维护的几个关键点
证书续期要提前做,别等过期了才想起来。可以写个定时任务,每月检查一次。
etcd备份很重要。etcd存了集群所有状态,丢了就全没了。可以用etcdctl snapshot save定期备份,备份文件存到别的机器上。
节点扩容很简单,新机器按前面的步骤配好环境,然后kubeadm join就行。缩容的话,先在master上kubectl drain <node>把Pod赶走,然后kubectl delete node <node>,最后在node上kubeadm reset。
版本升级用kubeadm upgrade,先升master,再升node。升级前一定要备份etcd和证书。
6.3 资源规划的建议
master节点建议2核4G起步,生产环境4核8G更稳。node节点根据业务负载定,但至少2核4G。
Pod网段和Service网段要提前规划好,别跟宿主机网段冲突。一个集群的Pod网段一旦定了,后面改很麻烦。
如果集群规模大,比如超过50个节点,建议把etcd独立部署,不要跟master混在一起。etcd对磁盘IO敏感,用SSD最好。
我在实际维护中发现,CentOS 7的集群跑久了,内核日志里会有conntrack表满的告警。可以调大net.netfilter.nf_conntrack_max参数,默认是65536,大集群可以调到262144。
6.4 安全加固的几点考虑
虽然快速部署优先,但安全不能完全不管。几个基本操作:
给API Server开RBAC,别用默认的cluster-admin给所有人。
kubelet的匿名访问关掉,在kubelet的配置里设authentication.anonymous.enabled: false。
如果API Server要暴露到公网,一定要配证书和认证,别裸奔。
网络策略用起来,Calico支持NetworkPolicy,可以限制Pod之间的访问。默认拒绝所有,然后按需放开。
镜像仓库用私有仓库,别直接从公网拉,既慢又不安全。
这套流程我在CentOS 7.9和8.5上都跑过,CentOS 7需要多处理内核和conntrack的问题,CentOS 8需要处理nftables的问题,其他基本一致。整个部署过程顺利的话,半小时能搞定一个单master集群。遇到问题别慌,按上面的排查表一步步来,大部分问题都能解决。最后再分享一个小技巧:把整个部署过程写成一个shell脚本,下次搭环境直接跑脚本,省得重复劳动。脚本里把IP、主机名这些做成变量,换环境只改变量就行。