CentOS 7 单机部署 Kubernetes v1.28.2 完整避坑指南

CentOS 7 单机部署 Kubernetes v1.28.2 完整避坑指南

前言

本文详细记录了在 CentOS 7.9 系统上,使用 Kubeadm 部署 Kubernetes v1.28.2 单机集群的全过程。

部署环境:

  • 操作系统:CentOS 7.9
  • Kubernetes 版本:v1.28.2
  • 容器运行时:Containerd
  • 网络插件:Flannel v0.22.0
  • 服务器配置:建议 2核4G 以上

第一阶段:系统环境准备

在安装 Kubernetes 之前,必须对 Linux 系统进行一系列内核和网络配置,否则后续初始化会频繁报错。

1. 关闭 Swap 分区

原理:Kubernetes 为了保证性能和稳定性,要求关闭 Swap。如果开启了 Swap,Kubelet 将无法启动。

1
2
3
4
5
# 临时关闭 Swap,立即生效
swapoff -a

# 永久关闭 Swap,注释掉 fstab 中的 swap 行,防止重启后自动挂载
sed -i '/swap/d' /etc/fstab

2. 关闭 SELinux 和防火墙

原理:SELinux 和防火墙的安全策略可能会拦截 K8s 组件之间的网络通信(如 API Server 和 Kubelet 之间),导致集群异常。初学者建议先关闭,熟练后再配置策略。

1
2
3
4
5
6
7
8
9
# 临时关闭 SELinux
setenforce 0

# 永久关闭 SELinux,修改配置文件
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config

# 停止并禁用防火墙
systemctl stop firewalld
systemctl disable firewalld

3. 配置内核参数

原理:K8s 的网络插件(如 Flannel)需要网桥过滤器支持,且需要开启 IPv4 转发,否则容器无法跨节点通信。

1
2
3
4
5
6
7
8
9
10
11
12
# 加载 br_netfilter 内核模块
modprobe br_netfilter

# 创建内核参数配置文件
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

# 应用内核参数配置
sysctl --system

第二阶段:安装 Containerd(容器运行时)

K8s 1.24 版本之后不再直接支持 Docker,而是使用 Containerd 作为容器运行时。

1. 安装 Containerd

避坑点:CentOS 默认 yum 源的 Containerd 版本过旧,不支持 K8s 1.28 所需的 CRI v1 接口,必须安装较新版本。

1
2
3
4
5
6
7
8
# 安装 yum-utils 工具
yum install -y yum-utils

# 添加 Docker 官方源(阿里云镜像),以便获取新版 Containerd
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 安装最新版 Containerd
yum install -y containerd.io

2. 配置 Containerd

避坑点:Containerd 默认配置不支持 K8s 的 SystemdCgroup 驱动,必须修改配置,否则 Kubelet 会报 Cgroup 不匹配错误。同时配置国内镜像源加速镜像下载。

1
2
3
4
5
6
7
8
9
10
11
# 生成默认配置文件
containerd config default > /etc/containerd/config.toml

# 修改配置:启用 SystemdCgroup (K8s 官方推荐)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml

# 修改配置:替换 sandbox 镜像为阿里云源 (解决国内无法拉取 pause 镜像的问题)
sed -i 's#sandbox_image = "registry.k8s.io/pause.*"#sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"#g' /etc/containerd/config.toml

# 启动 Containerd 并设置开机自启
systemctl enable --now containerd

3. 配置代理(关键步骤)

遇到的问题:国内网络环境无法直接访问 Docker Hub 和 GitHub,导致 Flannel 等镜像下载失败。
解决方案:为主机配置代理,并让 Containerd 使用该代理。

前提:假设你的代理客户端(如 V2RayN)已开启“允许来自局域网的连接”,且监听端口为 10808。请将 172.20.10.4 替换为你实际的主机 IP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建 Containerd 服务的 systemd 配置目录
mkdir -p /etc/systemd/system/containerd.service.d

# 写入代理配置
# 解释:HTTP_PROXY 和 HTTPS_PROXY 指定代理地址
# NO_PROXY 指定不走代理的地址(本地回环和内网 IP)
cat > /etc/systemd/system/containerd.service.d/http_proxy.conf << EOF
[Service]
Environment="HTTP_PROXY=http://172.20.10.4:10808"
Environment="HTTPS_PROXY=http://172.20.10.4:10808"
Environment="NO_PROXY=127.0.0.1,localhost,192.168.88.130"
EOF

# 重新加载 systemd 配置并重启 Containerd 使代理生效
systemctl daemon-reload
systemctl restart containerd

第三阶段:安装 Kubeadm, Kubelet, Kubectl

1. 配置 K8s 阿里云源

原理:K8s 官方源在国外,速度极慢甚至无法访问,使用阿里云镜像源加速下载。

1
2
3
4
5
6
7
8
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
repo_gpgcheck=0
EOF

2. 安装指定版本组件

1
2
3
4
5
6
# 安装 K8s 三剑客:kubeadm(集群初始化), kubelet(节点代理), kubectl(命令行工具)
# 指定版本 v1.28.2 防止版本自动升级导致的不兼容
yum install -y kubeadm-1.28.2 kubelet-1.28.2 kubectl-1.28.2

# 设置 Kubelet 开机自启
systemctl enable --now kubelet

第四阶段:初始化 Kubernetes 集群

1. 执行初始化命令

避坑点

  1. --apiserver-advertise-address 必须填写当前虚拟机的真实内网 IP,不能填错,否则 etcd 无法启动。
  2. --image-repository 指定阿里云镜像源,解决拉取核心组件超时问题。
1
2
3
4
5
6
7
8
# 初始化集群
kubeadm init \
--apiserver-advertise-address=192.168.88.130 \ # 填写你的虚拟机实际 IP
--image-repository registry.aliyuncs.com/google_containers \ # 使用阿里云镜像源拉取核心镜像
--kubernetes-version v1.28.2 \
--service-cidr=10.96.0.0/12 \ # Service 网段,集群内部虚拟 IP
--pod-network-cidr=10.244.0.0/16 \ # Pod 网段,Flannel 默认网段
--ignore-preflight-errors=NumCPU # 忽略 CPU 核数不足 2 核的警告

2. 配置 Kubectl 权限

原理:Kubectl 需要读取 admin.conf 证书才能操作集群。

1
2
3
4
5
6
7
8
# 创建配置目录
mkdir -p $HOME/.kube

# 将集群管理员配置文件复制到用户目录
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

# 修改文件所有权,赋予当前用户读写权限
chown $(id -u):$(id -g) $HOME/.kube/config

3. 允许 Master 节点运行 Pod(单机必做)

原理:默认 Master 节点带有“污点”,禁止调度业务 Pod。单机部署需要移除污点。

1
2
# 移除 Master 节点的污点,允许调度普通 Pod
kubectl taint nodes --all node-role.kubernetes.io/control-plane-

第五阶段:安装网络插件(Flannel)

这一步是网络问题的重灾区。Flannel 镜像仓库地址已迁移,且国内直接拉取困难。

1. 准备 Flannel 部署文件

创建 kube-flannel.yml 文件,内容如下(已包含国内镜像源配置):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
apiVersion: v1
kind: Namespace
metadata:
labels:
k8s-app: flannel
pod-security.kubernetes.io/enforce: privileged
name: kube-flannel
---
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: flannel
name: flannel
namespace: kube-flannel
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
k8s-app: flannel
name: flannel
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
labels:
k8s-app: flannel
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-flannel
---
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-flannel-cfg
namespace: kube-flannel
labels:
tier: node
k8s-app: flannel
app: flannel
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "1.0.0",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds
namespace: kube-flannel
labels:
tier: node
app: flannel
k8s-app: flannel
spec:
selector:
matchLabels:
app: flannel
k8s-app: flannel
template:
metadata:
labels:
tier: node
app: flannel
k8s-app: flannel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
hostNetwork: true
priorityClassName: system-node-critical
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: flannel
initContainers:
- name: install-cni-plugin
image: docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.2.0
command:
- cp
args:
- -f
- /flannel
- /opt/cni/bin/flannel
volumeMounts:
- name: cni-plugin
mountPath: /opt/cni/bin
- name: install-cni
image: docker.io/rancher/mirrored-flannelcni-flannel:v0.22.0
command:
- cp
args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel
image: docker.io/rancher/mirrored-flannelcni-flannel:v0.22.0
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
resources:
requests:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: false
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EVENT_QUEUE_DEPTH
value: "5000"
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
- name: xtables-lock
mountPath: /run/xtables.lock
volumes:
- name: run
hostPath:
path: /run/flannel
- name: cni-plugin
hostPath:
path: /opt/cni/bin
- name: cni
hostPath:
path: /etc/cni/net.d
- name: flannel-cfg
configMap:
name: kube-flannel-cfg
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate

2. 解决 Flannel 镜像拉取问题(核心难点)

遇到的问题:配置文件中请求的镜像名 rancher/mirrored-flannelcni-flannel 在 Docker Hub 上已更名,直接拉取会报 Not Found
解决方案

  1. 拉取新名字的镜像。
  2. 使用 ctr 命令给镜像打上旧名字的标签。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 通过代理拉取 Flannel 主镜像 (新名字)
crictl pull docker.io/flannel/flannel:v0.22.0

# 2. 拉取 Flannel CNI 插件镜像
crictl pull docker.io/flannel/flannel-cni-plugin:v1.2.0

# 3. 给主镜像打标签:将新镜像伪装成 yaml 文件中要求的旧名字
ctr -n k8s.io images tag \
docker.io/flannel/flannel:v0.22.0 \
docker.io/rancher/mirrored-flannelcni-flannel:v0.22.0

# 4. 给插件镜像打标签
ctr -n k8s.io images tag \
docker.io/flannel/flannel-cni-plugin:v1.2.0 \
docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.2.0

注:crictl 是 K8s 专用镜像工具,ctr -n k8s.io 是 Containerd 的原生工具,K8s 的镜像存储在 k8s.io 命名空间下,所以打标签时必须指定 -n k8s.io

3. 部署 Flannel

1
2
# 应用配置文件
kubectl apply -f kube-flannel.yml

第六阶段:最终验证

等待约 1-2 分钟,让 Pod 启动。

1
2
3
4
5
6
7
# 查看节点状态
kubectl get nodes
# 期望输出:STATUS 为 Ready

# 查看所有 Pod 状态
kubectl get pods -A
# 期望输出:所有 Pod 状态为 Running

常见问题与解决方案总结

现象/报错 原因 解决方案
Port 6443 is in use 上次初始化残留进程占用端口 执行 kubeadm reset -f 并重启 kubelet/containerd。
connection refused API Server 未启动或配置文件 IP 错误 检查 admin.conf 中的 IP 是否为本机 IP;检查 containerd 状态。
ImagePullBackOff 镜像拉取失败(网络或镜像名错误) 配置代理或使用阿里云源;注意 Flannel 镜像是否需要打标签改名。
CrashLoopBackOff (etcd) IP 地址配置错误 kubeadm init 时必须确保 --apiserver-advertise-address 是本机真实 IP。
CRI v1 runtime API is not implemented Containerd 版本过旧或 CRI 插件未开启 安装新版 Containerd,确保 SystemdCgroup = true
NotReady 网络插件未安装 安装 Flannel 或 Calico。

结语

通过本文,你不仅成功部署了一个单机 K8s 集群,还深入了解了 Linux 内核参数、容器运行时、网络代理以及 K8s 镜像管理的细节。希望这篇避坑指南能帮助你在 K8s 的学习之路上少走弯路!