ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

CentOS 7/8快速部署Kubernetes:kubeadm+containerd实战避坑指南

CentOS 7/8快速部署Kubernetes:kubeadm+containerd实战避坑指南

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 chronyd

4.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 containerd

4.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 kubelet

4.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/config

4.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 NotReadycgroup驱动不匹配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、主机名这些做成变量,换环境只改变量就行。

返回列表