CentOS 7.9上部署Kubernetes v1.15.1的实战避坑指南

那天下午三点二十七分,当我在CentOS 7.9服务器上第5次执行kubeadm init命令时,熟悉的红色报错再次无情地出现在终端上——"error execution phase preflight"。作为一个经历过多次k8s部署的老手,我没想到会在一个看似简单的旧版本部署上耗费整整两天时间。本文将还原这次充满戏剧性的排错历程,带你穿越那些官方文档没写的暗坑。

1. 环境准备:那些容易被忽视的细节

在开始讲述具体报错之前,有必要先交代下这次部署的特殊背景。客户的生产环境由于历史原因必须使用Kubernetes v1.15.1这个相对陈旧的版本,而服务器操作系统则是CentOS 7.9。这种"新系统+旧k8s"的组合本身就埋下了不少隐患。

1.1 系统基础配置检查

首先确认系统基本信息,这步很关键但很多人会跳过:

# 查看系统版本
cat /etc/redhat-release
# 内核版本
uname -r
# 内存和CPU
free -h
lscpu

我的环境输出如下:

  • CentOS Linux release 7.9.2009 (Core)
  • 内核版本:5.4.259-1.el7.elrepo.x86_64
  • 8核CPU,16GB内存

特别注意:虽然官方建议使用3.10内核,但实际测试发现5.x内核也能正常工作。不过如果你遇到奇怪的cgroup问题,可能需要降级内核。

1.2 必要的系统参数调整

在CentOS 7上部署k8s前,必须调整几个关键系统参数:

# 关闭SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

# 加载br_netfilter模块
modprobe br_netfilter
echo 'br_netfilter' > /etc/modules-load.d/br_netfilter.conf

# 设置网络参数
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system

这些配置看似基础,但任何一个遗漏都可能导致后续出现难以诊断的问题。特别是网络相关参数,如果不设置正确,后面安装网络插件时会出现各种连接问题。

2. 第一个拦路虎:Swap分区问题

当我第一次执行kubeadm init时,遇到了最经典的swap报错:

[ERROR Swap]: running with swap on is not supported. Please disable swap

2.1 为什么k8s要禁用swap?

这个设计决策源于k8s的资源管理机制。Swap会干扰kubelet对Pod内存使用的准确评估,可能导致:

  1. 内存压力判断失准,影响调度决策
  2. 性能下降(内存换入换出开销)
  3. QoS保障失效(重要Pod可能被换出)

2.2 彻底禁用swap的完整步骤

大多数教程只告诉你swapoff -a,但在生产环境中这远远不够:

# 临时关闭所有swap分区
swapoff -a

# 永久禁用 - 需要修改fstab
sed -i '/swap/d' /etc/fstab

# 检查是否还有swap设备
blkid | grep swap
# 如果有输出,需要进一步处理对应的设备

# 重启后验证
reboot
free -h

特别注意:某些云平台的CentOS镜像会使用特殊的swap配置方式,仅修改fstab可能不够。如果重启后发现swap又回来了,需要检查是否有systemd-swap服务在运行。

3. Docker版本的地雷阵

解决swap问题后,我遇到了第二个报错:

WARNING SystemVerification: this Docker version is not on the list of validated versions: 18.03.1-ce

3.1 k8s v1.15.1的Docker版本兼容性

查阅官方文档发现,v1.15.x验证过的Docker版本是18.09.x系列。而CentOS 7默认仓库中的Docker版本(18.03.1)确实不在支持列表中。

版本兼容性对照表:

Kubernetes版本 已验证Docker版本
v1.15.x 18.09.x
v1.16.x 18.09.x, 19.03.x
v1.17.x 19.03.x

3.2 彻底清理和安装指定Docker版本

在CentOS上处理Docker版本问题需要格外小心,以下是完整流程:

# 完全卸载现有Docker
sudo yum remove -y docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-selinux \
    docker-engine-selinux \
    docker-engine

# 清理残留文件和配置
sudo rm -rf /var/lib/docker
sudo rm -rf /etc/docker

# 安装必要依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# 添加Docker官方仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 列出可用版本
yum list docker-ce --showduplicates | sort -r

# 安装特定版本(这里选择18.09.9)
sudo yum install -y docker-ce-18.09.9 docker-ce-cli-18.09.9 containerd.io

# 配置Docker
mkdir /etc/docker
cat <<EOF > /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}
EOF

# 启动服务
systemctl enable --now docker

关键提示:cgroup驱动必须设置为systemd以与kubelet保持一致,否则后续会引发cgroup不一致问题。

4. kubeadm的版本陷阱

当Docker问题解决后,我以为胜利在望,却遇到了第三个坑:

Flag --experimental-upload-certs has been deprecated, use --upload-certs instead

4.1 kubeadm命令的版本差异

这个报错揭示了k8s版本管理中的一个重要事实:不同小版本间的CLI参数可能有变化。v1.15.x确实已经弃用了--experimental-前缀的参数。

正确的初始化命令应该是:

kubeadm init \
    --config=kubeadm-config.yaml \
    --upload-certs \
    | tee kubeadm-init.log

4.2 推荐的kubeadm配置文件

为了避免命令行参数的各种版本兼容问题,最佳实践是使用配置文件:

apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: "192.168.1.100"  # 替换为实际IP
  bindPort: 6443
nodeRegistration:
  criSocket: "/var/run/dockershim.sock"
  name: "k8s-master"
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/master
---
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: "v1.15.1"
controlPlaneEndpoint: "192.168.1.100:6443"
networking:
  podSubnet: "10.244.0.0/16"  # 根据选择的网络插件调整
apiServer:
  extraArgs:
    runtime-config: "storage.k8s.io/v1=true"
controllerManager:
  extraArgs:
    node-cidr-mask-size: "24"
scheduler:
  extraArgs:
    address: "0.0.0.0"

这个配置文件不仅解决了参数弃用问题,还能统一集群的初始配置,方便后续维护。

5. 最后的障碍:镜像拉取问题

当所有配置都调整正确后,kubeadm init还是卡在了镜像拉取阶段。这是因为旧版本k8s的镜像仓库已经发生了变化。

5.1 手动拉取镜像的解决方案

对于v1.15.1,需要手动拉取镜像并重新打标签:

# 列出需要的镜像
kubeadm config images list --kubernetes-version v1.15.1

# 从可用的镜像源拉取
images=(
    k8s.gcr.io/kube-apiserver:v1.15.1
    k8s.gcr.io/kube-controller-manager:v1.15.1
    k8s.gcr.io/kube-scheduler:v1.15.1
    k8s.gcr.io/kube-proxy:v1.15.1
    k8s.gcr.io/pause:3.1
    k8s.gcr.io/etcd:3.3.10
    k8s.gcr.io/coredns:1.3.1
)

for image in "${images[@]}"; do
    docker pull registry.aliyuncs.com/google_containers/${image#k8s.gcr.io/}
    docker tag registry.aliyuncs.com/google_containers/${image#k8s.gcr.io/} $image
done

5.2 验证集群状态

成功初始化后,执行以下命令验证集群状态:

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

# 检查节点状态
kubectl get nodes
# 检查各组件状态
kubectl get pods -n kube-system

如果一切正常,你应该能看到控制平面组件全部处于Running状态。至此,这个历经多重考验的k8s v1.15.1集群终于成功启动。回顾整个过程,最大的教训就是:部署旧版本k8s时,必须仔细研究该版本特定时期的文档和已知问题,不能简单套用最新版本的经验。

更多推荐