1. 项目概述:在CentOS 7上搭建Kubernetes 1.20集群
如果你正在寻找一份能在生产环境或学习环境中,手把手指导你在CentOS 7系统上部署一个稳定、可用的Kubernetes 1.20集群的详细指南,那么你来对地方了。Kubernetes,也就是大家常说的k8s,已经成为容器编排领域的事实标准,但对于很多刚接触的开发者或运维工程师来说,从零开始搭建一个集群依然是个不小的挑战,尤其是在相对“古老”但依然广泛使用的CentOS 7系统上。这个过程涉及到系统配置、容器运行时安装、核心组件部署、网络插件集成等多个环节,任何一个步骤的疏漏都可能导致集群初始化失败或运行异常。
我结合自己多次在CentOS 7上部署k8s 1.20版本的经验,将整个流程拆解成清晰、可复现的步骤。这篇文章不仅会告诉你“怎么做”,更会解释“为什么这么做”,比如为什么必须关闭Swap,为什么Docker的cgroup驱动最好用systemd,以及遇到镜像拉取失败、节点状态NotReady等常见问题时该如何排查。无论你是为了搭建开发测试环境,还是为生产部署做准备,这份指南都能帮你避开我踩过的那些坑,高效地完成集群搭建。
2. 环境准备与前置条件解析
在开始敲命令之前,充分的准备工作是成功的一半。对于Kubernetes集群部署,我们需要从硬件资源、网络规划、系统版本等多个维度进行考量,确保基础环境满足要求。
2.1 硬件与节点规划
一个最小化的、可用于学习和功能验证的Kubernetes集群至少需要两台机器:一个控制平面节点和一个工作节点。但为了体验高可用和更好的隔离性,我建议使用三台节点,结构如下:
- 控制平面节点:1台。负责集群的管理和控制,运行
kube-apiserver、kube-scheduler、kube-controller-manager、etcd等核心组件。在生产环境中,为了实现高可用,控制平面通常需要3台或更多奇数台机器。 - 工作节点:2台。负责运行实际的应用容器负载。
每台机器的最低配置建议:
- CPU:2核或以上。控制平面节点需要更多计算资源来处理调度和API请求。
- 内存:2GB或以上,建议4GB。内存不足会导致
kubelet或docker进程被系统杀死。 - 磁盘:20GB或以上。需要预留空间存放容器镜像、
etcd数据以及日志。 - 网络:所有节点必须在同一个二层网络内,能够通过IP互相通信,且主机名可以正确解析。
在我的实验环境中,使用了三台虚拟机,具体信息如下表:
| 主机名 | IP地址 | 角色 | 操作系统 |
|---|---|---|---|
| k8s-master | 192.168.110.130 | Control Plane, Master | CentOS 7.9 |
| k8s-node1 | 192.168.110.129 | Worker | CentOS 7.9 |
| k8s-node2 | 192.168.110.128 | Worker | CentOS 7.9 |
注意:确保你的防火墙(如
firewalld)或安全组规则放行了Kubernetes所需的端口,最关键的包括6443(API Server)、10250(kubelet API)、2379-2380(etcd)等。一个简单的做法是在测试环境暂时将防火墙默认区域设置为trusted,但生产环境务必配置精确的规则。
2.2 操作系统统一配置
以下操作需要在所有节点上执行,以确保环境一致性。
2.2.1 设置主机名与Hosts解析
为每个节点设置唯一的主机名,并配置/etc/hosts文件,让节点之间可以通过主机名互相访问。这能避免后续很多因网络解析问题导致的诡异错误。
# 在k8s-master节点上执行 hostnamectl set-hostname k8s-master # 在k8s-node1上执行 hostnamectl set-hostname k8s-node1 # 在k8s-node2上执行 hostnamectl set-hostname k8s-node2 # 编辑所有节点的 /etc/hosts 文件,添加以下内容 cat >> /etc/hosts << EOF 192.168.110.130 k8s-master 192.168.110.129 k8s-node1 192.168.110.128 k8s-node2 EOF配置完成后,可以互相ping一下主机名,测试解析是否正常。
2.2.2 关闭Swap并禁用SELinux
这是Kubernetes的强制要求。Swap的存在会影响kubelet对节点内存使用情况的判断,可能导致Pod调度和运行出现问题。kubeadm在初始化时会直接检查Swap是否关闭,如果未关闭则会报错并中止。
# 临时关闭当前已启用的Swap swapoff -a # 永久关闭,注释或删除 /etc/fstab 中所有包含swap的行 sed -i '/swap/s/^/#/' /etc/fstab # 临时将SELinux设置为permissive模式 setenforce 0 # 永久禁用SELinux sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/configSELinux在某些安全要求严格的场景下需要保持开启并配置正确的策略,但对于学习和测试环境,禁用它可以避免很多权限相关的复杂问题。
2.2.3 配置内核参数与加载模块
Kubernetes需要一些特定的内核参数来支持网络转发和网桥过滤。编辑/etc/sysctl.d/k8s.conf文件:
cat > /etc/sysctl.d/k8s.conf << EOF net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOF # 使配置生效 sysctl --system这里net.ipv4.ip_forward = 1开启了IPv4转发,这是容器跨节点通信的基础。bridge-nf-call-iptables参数是为了让网桥上的流量也经过iptables规则,这是Calico、Flannel等CNI插件正常工作所必需的。
2.2.4 配置时间同步
集群内所有节点的时间必须同步,否则会导致证书错误、日志时间错乱等一系列问题。使用chronyd或ntpd服务进行时间同步。
# 安装chrony yum install -y chrony # 启动并设置开机自启 systemctl enable --now chronyd # 检查时间同步状态 chronyc sources -v3. 容器运行时与Kubernetes组件安装
Kubernetes本身不直接运行容器,它通过CRI接口与容器运行时交互。虽然Kubernetes 1.20版本已经宣布弃用Docker,但通过cri-dockerd适配器,我们依然可以使用熟悉的Docker作为运行时。这里我们选择安装Docker,因为它生态成熟,资料丰富。
3.1 在所有节点安装Docker
首先配置Docker的yum源,然后安装指定版本。不推荐使用系统默认源里过旧的版本。
# 安装yum工具包并设置Docker仓库 yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 安装Docker CE(社区版)及命令行工具 yum install -y docker-ce docker-ce-cli containerd.io # 启动Docker并设置开机自启 systemctl enable --now docker安装完成后,验证Docker版本并测试运行一个hello-world容器:docker run --rm hello-world。如果成功,说明Docker安装正确。
接下来是关键一步:配置Docker使用systemd作为cgroup驱动。Kubernetes默认使用systemd作为cgroup驱动,如果Docker使用cgroupfs,会导致kubelet启动失败。编辑/etc/docker/daemon.json文件(如果不存在则创建):
{ "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "registry-mirrors": ["https://<你的镜像加速器地址>.mirror.aliyuncs.com"] }其中,registry-mirrors可以替换为阿里云、腾讯云等提供的国内镜像加速地址,能极大提升拉取镜像的速度。配置完成后,重启Docker:systemctl restart docker。使用docker info | grep Cgroup命令确认驱动已改为systemd。
3.2 安装kubeadm, kubelet和kubectl
kubeadm是官方推荐的集群引导工具;kubelet是运行在每个节点上的代理,负责管理Pod和容器;kubectl是命令行工具,用于与集群交互。我们需要在所有节点上安装它们。
由于官方源在国内访问较慢,我们使用阿里云的Kubernetes镜像源。
# 1. 添加阿里云Kubernetes Yum源 cat > /etc/yum.repos.d/kubernetes.repo << EOF [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF # 2. 安装指定版本(这里安装1.20.15,一个稳定的1.20子版本) yum install -y kubelet-1.20.15 kubeadm-1.20.15 kubectl-1.20.15 --disableexcludes=kubernetes # 3. 设置kubelet开机自启(先不启动,等kubeadm init后再启动) systemctl enable kubelet注意:
--disableexcludes=kubernetes参数是为了确保在安装时,即使系统有其他仓库排除了Kubernetes包,也能正常安装。此时先不要启动kubelet,因为它需要由kubeadm初始化后生成的配置文件才能正确运行。
4. 使用kubeadm初始化控制平面
准备工作就绪后,我们就可以在规划为Master的节点(k8s-master)上初始化控制平面了。这是搭建集群最核心的一步。
4.1 预拉取镜像与初始化命令
kubeadm init会拉取运行Kubernetes控制平面所需的所有镜像。由于网络原因,直接从k8s.gcr.io拉取很可能失败。我们可以使用阿里云镜像仓库,并先手动拉取镜像,避免初始化过程因镜像问题中断。
# 在k8s-master节点上执行 # 查看kubeadm需要哪些镜像 kubeadm config images list --kubernetes-version v1.20.15 # 使用阿里云镜像仓库拉取镜像(这是一个常用技巧) docker pull registry.aliyuncs.com/google_containers/kube-apiserver:v1.20.15 docker pull registry.aliyuncs.com/google_containers/kube-controller-manager:v1.20.15 docker pull registry.aliyuncs.com/google_containers/kube-scheduler:v1.20.15 docker pull registry.aliyuncs.com/google_containers/kube-proxy:v1.20.15 docker pull registry.aliyuncs.com/google_containers/pause:3.2 docker pull registry.aliyuncs.com/google_containers/etcd:3.4.13-0 docker pull registry.aliyuncs.com/google_containers/coredns:1.7.0 # 为镜像打上符合kubeadm期望的标签 docker tag registry.aliyuncs.com/google_containers/kube-apiserver:v1.20.15 k8s.gcr.io/kube-apiserver:v1.20.15 docker tag registry.aliyuncs.com/google_containers/kube-controller-manager:v1.20.15 k8s.gcr.io/kube-controller-manager:v1.20.15 # ... 其他镜像同理完成镜像准备后,执行初始化命令。这里有几个重要参数需要解释:
--image-repository: 指定镜像仓库,我们使用阿里云仓库。--kubernetes-version: 必须与安装的kubeadm、kubelet版本严格一致,这里是v1.20.15。--pod-network-cidr: 设置Pod网络的IP地址段。这个网段不能与任何主机网络重叠。我们选择10.244.0.0/16,这是Flannel网络的默认网段,也与Calico的默认配置兼容。--apiserver-advertise-address: 指定API Server对外公告的IP地址。如果你的Master节点有多个网卡,需要指定一个其他节点能访问到的IP。这里我们使用192.168.110.130。
kubeadm init \ --image-repository=registry.aliyuncs.com/google_containers \ --kubernetes-version=v1.20.15 \ --pod-network-cidr=10.244.0.0/16 \ --apiserver-advertise-address=192.168.110.130执行这个命令后,kubeadm会进行一系列预检,生成证书和配置文件,最后启动控制平面的各个组件。整个过程大约需要1-3分钟。如果一切顺利,你会在最后看到类似下面的成功信息,其中包含加入集群的令牌命令:
Your Kubernetes control-plane has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config You should now deploy a pod network to the cluster. Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: https://kubernetes.io/docs/concepts/cluster-administration/addons/ Then you can join any number of worker nodes by running the following on each as root: kubeadm join 192.168.110.130:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx4.2 配置kubectl并检查集群状态
根据上面的提示,我们需要配置kubectl的认证信息,这样才能以管理员身份操作集群。
mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config现在,使用kubectl get nodes命令查看节点状态,应该能看到k8s-master节点,但其状态是NotReady。这是因为我们还没有安装Pod网络插件(CNI),集群内部的网络还不通。
[root@k8s-master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master NotReady control-plane,master 2m v1.20.155. 部署Pod网络插件(CNI)与加入工作节点
Pod网络插件是Kubernetes集群的“神经系统”,负责为每个Pod分配IP地址,并实现Pod之间的跨节点通信。没有它,集群就无法正常工作。
5.1 部署Calico网络插件
CNI插件有很多选择,如Flannel、Calico、Cilium等。Calico以其高性能和强大的网络策略功能而闻名,我们选择它。首先,下载Calico的部署清单文件。
# 在k8s-master节点上执行 curl https://docs.projectcalico.org/manifests/calico.yaml -O下载的calico.yaml文件里定义了所有需要的Kubernetes资源。我们需要修改一个关键配置,使其Pod网段与我们初始化时指定的--pod-network-cidr一致。找到文件中配置CALICO_IPV4POOL_CIDR环境变量的地方(大约在第1000行附近),将其值修改为10.244.0.0/16。
# 搜索并修改这一行 - name: CALICO_IPV4POOL_CIDR value: "10.244.0.0/16"修改完成后,应用这个清单文件:
kubectl apply -f calico.yaml这个命令会创建一系列的资源,包括DaemonSet、Deployment、ServiceAccount等。稍等片刻,使用kubectl get pods -n kube-system命令查看calico-node和calico-kube-controllers这两个Pod的状态,当它们都变成Running时,网络插件就部署成功了。此时再查看节点状态,k8s-master应该已经变为Ready。
5.2 将工作节点加入集群
现在,回到初始化成功时输出的那行kubeadm join命令。将这条命令分别在k8s-node1和k8s-node2上以root权限执行。
# 在 k8s-node1 和 k8s-node2 上分别执行 kubeadm join 192.168.110.130:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx如果令牌过期或丢失,可以在Master节点上使用kubeadm token create --print-join-command生成新的加入命令。
执行成功后,在Master节点上再次运行kubectl get nodes,你应该能看到三个节点都是Ready状态。
[root@k8s-master ~]# kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane,master 15m v1.20.15 k8s-node1 Ready <none> 2m v1.20.15 k8s-node2 Ready <none> 1m v1.20.156. 集群验证与常用配置
集群搭建完成后,我们还需要进行一些验证和优化配置,确保其稳定可用。
6.1 基础功能验证
部署一个最简单的Nginx应用来测试集群是否正常工作。
# 1. 部署一个Nginx Deployment kubectl create deployment nginx --image=nginx:1.21-alpine # 2. 将Nginx服务暴露为NodePort类型 kubectl expose deployment nginx --port=80 --type=NodePort # 3. 查看Pod和服务状态 kubectl get pods,svc -l app=nginx如果Pod状态为Running,并且SERVICE列显示了一个端口映射(如80:3xxxx/TCP),说明部署成功。你可以通过任意节点的IP地址和映射出的端口(如http://192.168.110.129:3xxxx)在浏览器中访问到Nginx的欢迎页面。
6.2 配置kubectl命令自动补全
为了提高使用kubectl的效率,强烈建议启用命令自动补全功能。
# 对于bash用户 echo 'source <(kubectl completion bash)' >> ~/.bashrc source ~/.bashrc # 对于zsh用户 echo 'source <(kubectl completion zsh)' >> ~/.zshrc source ~/.zshrc配置后,输入kubectl get p然后按Tab键,会自动补全为kubectl get pods。
6.3 移除Master节点的污点(Taint)
默认情况下,出于安全考虑,Master节点被设置了NoSchedule污点,普通Pod不会被调度到上面运行。对于资源有限的测试环境,你可能希望Master节点也能运行工作负载。
# 查看节点的污点 kubectl describe node k8s-master | grep Taints # 移除污点 kubectl taint nodes k8s-master node-role.kubernetes.io/master-移除污点后,Master节点就可以像Worker节点一样接受Pod调度了。
7. 常见问题排查与避坑指南
即便按照步骤操作,你也可能会遇到一些问题。这里我总结几个最常见的“坑”及其解决方法。
问题一:kubeadm init时卡在[kubelet-check]或镜像拉取失败。
- 原因:最常见的原因是网络问题,无法从指定仓库拉取镜像。
- 解决:
- 使用
kubeadm config images pull --image-repository=registry.aliyuncs.com/google_containers预拉取镜像。 - 手动拉取并重命名镜像,如前面步骤所示。
- 检查Docker的cgroup驱动是否为
systemd。 - 确保Swap已关闭,使用
free -h确认Swap行显示为0。
- 使用
问题二:Worker节点加入集群失败,提示connection refused或证书错误。
- 原因:Master节点的API Server(6443端口)无法访问,或者加入令牌过期。
- 解决:
- 检查Master节点的防火墙是否放行了6443端口:
firewall-cmd --list-ports。 - 在Master节点重新生成加入命令:
kubeadm token create --print-join-command。 - 检查
/etc/hosts文件,确保Worker节点能正确解析Master的主机名和IP。
- 检查Master节点的防火墙是否放行了6443端口:
问题三:节点状态长时间为NotReady。
- 原因:几乎可以肯定是网络插件(CNI)没有正确安装或配置错误。
- 解决:
- 使用
kubectl get pods -n kube-system检查calico-node或coredns的Pod是否处于Running状态。如果不是,使用kubectl describe pod <pod-name> -n kube-system和kubectl logs <pod-name> -n kube-system查看详细错误信息。 - 确认
calico.yaml中的CALICO_IPV4POOL_CIDR是否与kubeadm init时设置的--pod-network-cidr一致。 - 检查内核参数
net.ipv4.ip_forward和net.bridge.bridge-nf-call-iptables是否已设置为1。
- 使用
问题四:使用kubectl命令时提示The connection to the server xxxx was refused。
- 原因:
kubectl没有正确的配置文件或API Server未启动。 - 解决:
- 确认已正确执行
mkdir -p $HOME/.kube和cp /etc/kubernetes/admin.conf $HOME/.kube/config。 - 检查Master节点上
kube-apiserver的Pod是否运行:kubectl get pods -n kube-system | grep apiserver。
- 确认已正确执行
问题五:Pod一直处于Pending状态。
- 原因:通常是没有节点满足调度条件,比如资源不足或节点有污点。
- 解决:
- 使用
kubectl describe pod <pod-name>查看Events部分,通常会有明确的调度失败原因。 - 检查节点资源:
kubectl describe node。 - 检查节点污点:
kubectl describe node | grep Taint。
- 使用
搭建Kubernetes集群是一个系统工程,涉及操作系统、网络、存储、安全等多个层面。这份指南覆盖了在CentOS 7上部署Kubernetes 1.20的核心路径和常见陷阱。实际操作中,你可能还会遇到硬盘空间不足、内核版本过低、特定硬件驱动等问题。我的建议是,保持耐心,善用kubectl describe和journalctl -u kubelet等命令查看日志,大部分问题都能找到线索。把这个三节点的集群搭起来,只是Kubernetes之旅的第一步,后面还有存储、服务发现、负载均衡、监控日志等更广阔的天地等着你去探索。