1. 项目概述与核心目标
最近在帮团队搭建一套用于内部开发测试的Kubernetes环境,选定的基础操作系统是CentOS 7.9,Kubernetes版本则定位在相对稳定且兼容性经过验证的1.20系列。之所以选择这个组合,一方面是考虑到公司内部仍有大量存量应用运行在CentOS 7上,迁移成本较高;另一方面,K8s 1.20是一个承上启下的版本,它已经包含了相当多稳定的特性,同时又避开了早期版本的一些已知坑点,对于学习和中小规模部署来说非常合适。这个安装过程看似是标准操作,但在实际的机房或云主机环境中,从系统初始化到集群就绪,每一步都藏着细节,网络配置、镜像拉取、组件版本匹配,任何一个环节出问题都可能导致部署失败。接下来,我就把这次从零开始,在CentOS 7上成功部署K8s 1.20单控制平面集群的完整操作过程、踩过的坑以及总结的避坑指南,毫无保留地分享出来。无论你是刚开始接触容器编排的运维新人,还是需要快速搭建一套测试环境的老手,这份详尽的记录都能让你少走弯路。
2. 环境准备与系统初始化
在开始安装Kubernetes任何组件之前,我们必须为它准备一个“干净”且“合规”的操作系统环境。这里的“干净”指的是关闭可能干扰集群网络和服务的系统服务;“合规”则是指系统参数、内核模块等需要满足K8s运行的最低要求。很多安装失败的问题,根源都出在这一步。
2.1 基础系统要求与规划
首先,你需要准备至少两台CentOS 7.9的虚拟机或物理机。一台作为控制平面节点(Master),另一台作为工作节点(Worker)。对于实验环境,Master节点建议配置2核CPU、4GB内存、40GB磁盘;Worker节点建议2核CPU、2GB内存、40GB磁盘。生产环境请根据实际负载大幅提高配置。
所有节点需要确保:
- 网络互通,且每个节点有唯一的主机名。
- 防火墙和SELinux需要被正确配置或关闭,这是初期避免网络问题最直接的方式。
- 需要配置一个稳定的软件源,用于安装Docker、Kubernetes组件等。
我个人的习惯是,在开始任何操作前,先在所有节点上执行一遍系统更新和主机名设置。
# 在所有节点上执行 # 1. 更新系统并安装基础工具 yum update -y yum install -y vim wget net-tools bash-completion # 2. 设置永久主机名(以master节点为例) hostnamectl set-hostname k8s-master # 在worker节点上执行:hostnamectl set-hostname k8s-worker1 # 3. 修改 /etc/hosts 文件,添加所有节点的IP和主机名映射 # 假设 master IP 为 192.168.1.100, worker1 IP 为 192.168.1.101 cat >> /etc/hosts << EOF 192.168.1.100 k8s-master 192.168.1.101 k8s-worker1 EOF注意:
/etc/hosts的配置至关重要,尤其是在没有内部DNS解析的环境下。Kubernetes组件之间、节点之间需要通过主机名相互通信。务必确保每个节点上的这个文件内容一致且准确。
2.2 关键系统服务与安全配置
接下来是关闭防火墙和SELinux。在安全要求严格的生成环境,你可能需要配置精细的防火墙规则和SELinux策略,但对于学习和测试环境,关闭它们可以极大简化问题排查的复杂度。
# 在所有节点上执行 # 1. 关闭并禁用防火墙(firewalld) systemctl stop firewalld systemctl disable firewalld # 2. 关闭SELinux(临时生效+永久生效) setenforce 0 # 临时设置为宽松模式 sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config # 永久修改为permissive为什么是permissive而不是disabled?将SELinux设置为permissive模式,意味着系统会记录违反策略的操作但不阻止它。这既能让我们顺利安装,又能在日志中看到哪些操作可能被SELinux拦截,为日后开启安全策略提供参考。直接disabled可能会在后续某些操作中遇到更隐晦的问题。
2.3 内核参数与模块加载
Kubernetes对Linux内核有一些要求,特别是网络和存储方面。我们需要加载必要的内核模块并调整一些系统参数。
# 在所有节点上执行 # 1. 加载内核模块 cat > /etc/modules-load.d/k8s.conf << EOF br_netfilter ip_vs ip_vs_rr ip_vs_sh ip_vs_wrr nf_conntrack EOF # 手动加载模块(重启后也会自动加载) modprobe br_netfilter modprobe ip_vs modprobe ip_vs_rr modprobe ip_vs_sh modprobe ip_vs_wrr modprobe nf_conntrack # 2. 配置系统参数 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 vm.swappiness = 0 EOF # 使配置生效 sysctl --system参数解析:
net.bridge.bridge-nf-call-iptables = 1:这是让iptables规则能够对桥接网络流量生效的关键参数。Kubernetes Service和网络策略(NetworkPolicy)都依赖于此。net.ipv4.ip_forward = 1:开启IP转发,这是容器跨节点通信和Service网络模式(如NodePort)的基础。vm.swappiness = 0:尽可能避免使用交换分区(Swap)。Kubernetes从1.8版本开始就要求关闭Swap,但有些环境无法彻底关闭,设置为0可以最大程度减少Swap使用,因为Swap会导致kubelet性能不可预测。
2.4 配置国内Yum源与时间同步
由于网络原因,直接使用默认的国外源安装Docker和K8s可能会非常慢甚至失败。配置国内镜像源是必选项。同时,集群所有节点的时间必须同步,否则证书、日志等都会出问题。
# 在所有节点上执行 # 1. 备份原有yum源 mkdir -p /etc/yum.repos.d/backup mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/backup/ # 2. 下载阿里云CentOS 7镜像源(这里以Base源为例,EPEL源同样需要配置) wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo # 清理缓存并重建 yum clean all yum makecache # 3. 安装并配置NTP时间同步 yum install -y ntpdate # 使用阿里云的NTP服务器 ntpdate ntp1.aliyun.com # 设置定时任务,每30分钟同步一次 echo "*/30 * * * * /usr/sbin/ntpdate ntp1.aliyun.com > /dev/null 2>&1" >> /etc/crontab systemctl restart crond做完以上所有步骤,请务必重启所有节点。重启可以确保所有内核模块、系统参数和主机名更改完全生效。这是避免后续出现各种灵异问题的重要一步。
3. 容器运行时与Kubernetes组件安装
环境准备好后,我们开始安装核心软件:容器运行时和Kubernetes组件。这里我们选择Docker作为容器运行时,并使用阿里云镜像站来加速Kubernetes组件的下载。
3.1 安装与配置Docker
Kubernetes 1.20版本对Docker的支持是“最后的美好时光”,从1.24版本开始K8s就移除了对Docker的直接支持。所以1.20版本是我们仍能使用Docker的稳定选择。我们安装指定版本的Docker-ce。
# 在所有节点上执行 # 1. 安装yum工具包 yum install -y yum-utils device-mapper-persistent-data lvm2 # 2. 添加阿里云Docker CE镜像源 yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 3. 安装指定版本的Docker(这里选择19.03.15,与k8s 1.20兼容性好) yum install -y docker-ce-19.03.15 docker-ce-cli-19.03.15 containerd.io # 4. 配置Docker镜像加速和Cgroup驱动 mkdir -p /etc/docker cat > /etc/docker/daemon.json << EOF { "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2", "registry-mirrors": [ "https://registry.docker-cn.com", "https://hub-mirror.c.163.com", "https://mirror.baidubce.com" ] } EOF关键配置解析:
"native.cgroupdriver=systemd":这是至关重要的一步。必须将Docker的Cgroup驱动设置为systemd,使其与Kubernetes kubelet的默认驱动保持一致。如果不一致,kubelet将无法正确管理Docker创建的容器,导致节点状态异常。"storage-driver": "overlay2":Overlay2是CentOS 7上推荐且性能较好的存储驱动。registry-mirrors:配置国内镜像加速器,可以极大提升拉取Docker官方镜像的速度。
# 5. 启动并设置Docker开机自启 systemctl daemon-reload systemctl enable docker --now # 6. 验证Docker安装和配置 docker info | grep -i cgroup # 输出中应包含 `Cgroup Driver: systemd`3.2 安装Kubernetes组件(kubeadm, kubelet, kubectl)
我们将使用kubeadm这个官方推荐的集群部署工具。同样,为了速度,我们配置阿里云的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. 安装指定版本的kubelet, kubeadm, kubectl (1.20.15是一个稳定的补丁版本) 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这个参数是为了防止在安装时,因为某些仓库的排除规则而导致kubelet等包被跳过。
3.3 配置kubelet的Cgroup驱动
确保kubelet的Cgroup驱动与Docker保持一致。编辑kubelet的默认配置文件。
# 在所有节点上执行 cat > /etc/sysconfig/kubelet << EOF KUBELET_EXTRA_ARGS="--cgroup-driver=systemd" EOF至此,所有节点上的软件都已安装完毕。接下来,我们将在Master节点上初始化控制平面。
4. 使用Kubeadm初始化Master节点
kubeadm init是创建Kubernetes控制平面的核心命令。这个过程中会生成集群证书、部署核心组件(如API Server, Controller Manager, Scheduler, etcd),并生成加入集群的令牌。
4.1 初始化命令与参数详解
在Master节点(k8s-master)上执行初始化。这里有几个关键参数需要根据你的环境调整。
# 仅在Master节点执行 # 1. 预先拉取所需的镜像(避免init时因网络问题超时) kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers # 2. 执行初始化命令 kubeadm init \ --apiserver-advertise-address=192.168.1.100 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.20.15 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.244.0.0/16 \ --ignore-preflight-errors=Swap参数拆解与避坑指南:
--apiserver-advertise-address:指定API Server对外公告的IP地址。必须填写Master节点的实际IP,不能是127.0.0.1。Worker节点将通过这个地址连接到Master。--image-repository:指定拉取K8s核心组件镜像的仓库。使用阿里云镜像站registry.aliyuncs.com/google_containers,速度比谷歌原厂仓库快几个数量级。--kubernetes-version:必须与我们安装的kubeadm版本严格一致,这里是v1.20.15。--service-cidr:Kubernetes Service的虚拟IP地址段。使用默认的10.96.0.0/12即可,除非和你现有的网络冲突。--pod-network-cidr:这是为后续安装Pod网络插件(CNI)准备的。这里设置为10.244.0.0/16,是因为我们将使用flannel网络插件,这是flannel默认的网段。如果你计划使用Calico、Weave Net等其他插件,这个网段可能需要修改。--ignore-preflight-errors=Swap:因为我们之前只将swappiness设为0,并未彻底关闭Swap分区,所以初始化时会有一个警告。这个参数用于忽略Swap检查的警告。如果Swap已彻底关闭(swapoff -a并注释掉/etc/fstab中的swap行),则无需此参数。
4.2 初始化成功后的关键操作
如果一切顺利,命令执行最后会输出类似以下成功信息:
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 Alternatively, if you are the root user, you can run: export KUBECONFIG=/etc/kubernetes/admin.conf 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.1.100:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx请务必立即执行以下操作:
配置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记录
kubeadm join命令:将最后输出的kubeadm join ...命令完整地保存下来。这个命令包含了令牌和CA证书哈希,用于Worker节点加入集群。令牌默认24小时有效。(可选)启用Master节点调度Pod:默认情况下,出于安全考虑,Master节点不允许调度普通的Pod。在测试环境中,如果你只有一台Master节点,可以解除这个限制。
kubectl taint nodes --all node-role.kubernetes.io/master-
4.3 安装Pod网络插件(CNI)
没有网络插件,集群内的Pod无法跨节点通信。我们选择最经典且简单的flannel。
# 在Master节点执行 # 注意:这里使用的镜像仓库是docker.io,如果拉取慢,可以尝试先手动拉取或配置Docker镜像加速器。 kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml应用这个YAML文件后,Kubernetes会在kube-system命名空间下创建一系列DaemonSet等资源。使用以下命令查看安装状态:
kubectl get pods -n kube-system -l app=flannel # 等待所有Pod状态变为 Running kubectl get nodes # 此时Master节点的状态应该从 NotReady 变为 Ready当Master节点状态显示为Ready,说明控制平面和网络插件都已正常运行。现在,Master节点已经就绪,可以接受Worker节点加入了。
5. Worker节点加入集群与集群验证
现在,我们将Worker节点加入到刚刚创建好的集群中。
5.1 执行加入集群命令
在每一台Worker节点上,执行之前在Master节点初始化成功后输出的kubeadm join命令。命令格式如下(请替换为你自己的令牌和哈希):
# 在Worker节点(k8s-worker1)上执行 kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx如果令牌过期,可以在Master节点上重新生成:
# 在Master节点执行 kubeadm token create --print-join-command5.2 验证节点加入状态
回到Master节点,使用kubectl查看节点状态。
kubectl get nodes你应该能看到类似下面的输出,STATUS栏都显示为Ready:
NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane,master 10m v1.20.15 k8s-worker1 Ready <none> 2m v1.20.155.3 部署测试应用验证集群功能
集群状态就绪后,我们部署一个最简单的Nginx应用来验证整个集群是否工作正常。
部署一个Nginx Deployment:
kubectl create deployment nginx-test --image=nginx:1.21-alpine将Nginx服务暴露为NodePort类型:
kubectl expose deployment nginx-test --port=80 --type=NodePort查看服务详情:
kubectl get svc nginx-test输出中会有一个
PORT(S)列,例如80:30678/TCP。30678就是集群随机分配的NodePort。访问测试: 打开浏览器,访问
http://<任意节点的IP>:30678。如果看到Nginx的欢迎页面,恭喜你,一个功能完整的Kubernetes集群已经部署成功!查看Pod调度情况:
kubectl get pods -o wide这个命令可以看到Pod运行在哪个节点上,以及它的IP地址,这有助于理解K8s的调度和网络。
6. 安装后的优化与常见问题排查
集群跑起来只是第一步,要让其稳定、好用,还需要一些优化配置。同时,我把安装过程中最容易遇到的几个问题及其解决方案记录下来。
6.1 核心优化配置
配置kubectl命令自动补全:
# bash用户 echo 'source <(kubectl completion bash)' >> ~/.bashrc source ~/.bashrc # zsh用户 echo 'source <(kubectl completion zsh)' >> ~/.zshrc source ~/.zshrc配置
kube-proxy的IPVS模式(可选但推荐):kube-proxy默认使用iptables模式,当Service数量非常多时(超过几千),iptables规则会变得庞大,影响性能。可以改为ipvs模式,效率更高。# 编辑kube-proxy的ConfigMap kubectl edit configmap kube-proxy -n kube-system在
data.config.conf部分,找到mode: "",将其修改为mode: "ipvs",保存退出。然后删除现有的kube-proxy Pod让其重建。kubectl get pod -n kube-system -l k8s-app=kube-proxy -o name | xargs kubectl delete -n kube-system等待新的Pod启动后,验证模式:
kubectl logs -n kube-system <kube-proxy-pod-name> | grep -i "Using ipvs"配置Docker和kubelet日志轮转:防止日志占满磁盘。
- Docker:我们之前在
daemon.json中配置的max-size参数已生效。 - Kubelet:编辑
/etc/systemd/system/kubelet.service.d/10-kubeadm.conf,在[Service]部分添加:
然后执行Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml --log-file=/var/log/kubelet.log --logtostderr=false --rotate-log-file --rotate-log-max-size=100 --rotate-log-max-backups=5"systemctl daemon-reload && systemctl restart kubelet。
- Docker:我们之前在
6.2 常见问题排查实录
问题1:kubeadm init时卡在[preflight]或拉取镜像失败。
- 原因:网络问题,无法连接到镜像仓库。
- 解决:
- 确保已配置国内镜像源(阿里云)。
- 可以手动预拉取镜像:
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers。 - 检查节点是否能正常访问外网(
ping mirrors.aliyun.com)。
问题2:Worker节点kubeadm join失败,提示connection refused或证书错误。
- 原因:
- Master节点的6443端口(API Server)未开放或被防火墙拦截。
- 节点间主机名解析失败。
- 初始化时
--apiserver-advertise-address填错。
- 解决:
- 在Master节点检查端口:
netstat -tlnp | grep 6443。 - 检查Master和Worker节点的
/etc/hosts文件,确保能互相解析主机名。 - 检查Master节点防火墙和SELinux是否已按步骤关闭。
- 在Master节点检查端口:
问题3:节点状态为NotReady。
- 原因:通常是网络插件(flannel)没有成功运行。
- 解决:
kubectl get pods -n kube-system查看kube-flannel-ds-*Pod的状态。如果是ImagePullBackOff,可能是网络问题拉取不到quay.io的镜像。可以尝试修改flannel的YAML文件,将镜像地址替换为国内源(如registry.cn-hangzhou.aliyuncs.com/google_containers/flannel),但要注意版本匹配,操作较复杂。更简单的方法是确保节点能访问外网或配置Docker代理。journalctl -u kubelet -f查看kubelet日志,寻找具体错误信息。
问题4:Pod一直处于Pending状态。
- 原因:通常是资源不足(CPU/内存)或没有满足条件的节点(如Master节点不可调度)。
- 解决:
kubectl describe pod <pod-name>查看事件(Events),里面会有详细的调度失败原因。- 如果是
node(s) didn‘t have enough resource,考虑增加节点资源或减少Pod请求资源。 - 如果是
node(s) had taint {node-role.kubernetes.io/master: },说明Pod被调度到了Master节点但被污点排斥。可以给Pod添加容忍度(Toleration),或者在测试环境下直接去除Master节点的污点(如前文所述)。
问题5:Service的NodePort无法访问。
- 原因:
- 节点防火墙未开放NodePort端口范围(默认是30000-32767)。
kube-proxy服务异常。
- 解决:
- 在测试环境,可以临时在节点上开放端口范围:
firewall-cmd --permanent --add-port=30000-32767/tcp && firewall-cmd --reload(如果防火墙开启的话)。 - 检查
kube-proxyPod是否运行正常:kubectl get pods -n kube-system -l k8s-app=kube-proxy。
- 在测试环境,可以临时在节点上开放端口范围:
整个安装过程,最考验耐心的是网络和环境问题。严格按照步骤操作,并理解每一步的作用,遇到问题时查看组件日志(journalctl -u kubelet,docker logs <container-id>,kubectl logs <pod-name>),大部分都能找到线索。这个在CentOS 7上搭建的K8s 1.20集群,已经可以作为你学习容器编排、部署测试应用甚至搭建CI/CD环境的坚实基础。