Kubernetes集群部署实战:使用kubeadm在4台ECS服务器上搭建K8s v1.31集群
本文详细记录了在4台华为云ECS服务器上使用kubeadm部署Kubernetes v1.31.14集群的完整过程,包含环境准备、Master初始化、Worker节点加入、网络插件安装、集群验证等全流程实操。适合有一定Linux基础的开发者和运维工程师参考。
一、Kubernetes架构概述
1.1 集群架构图
markdown
┌─────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Control Plane (Master) │ │
│ │ node3 │ │
│ │ 192.168.0.214 │ │
│ │ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │kube-apis│ │kube-contr│ │kube-sched │ │ │
│ │ │erver │ │oller-mgr │ │ uler │ │ │
│ │ └─────────┘ └──────────┘ └──────────┘ │ │
│ │ ┌─────────┐ ┌──────────┐ │ │
│ │ │ etcd │ │kube-proxy│ │ │
│ │ └─────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ node1 │ │ node2 │ │ node4 │ │
│ │Worker │ │Worker │ │Worker │ │
│ │.0.204 │ │ .0.41 │ │.0.116 │ │
│ │kubelet│ │kubelet│ │kubelet│ │
│ │proxy │ │proxy │ │proxy │ │
│ │flannel│ │flannel│ │flannel│ │
│ └──────┘ └──────┘ └──────┘ │
└─────────────────────────────────────────────────┘
网络模型:
- Pod网络 (CIDR): 10.244.0.0/16 (Flannel VXLAN)
- Service网络: 10.96.0.0/12 (kube-apiserver默认)
- 节点间通信: 192.168.0.0/24 (私有网络)
1.2 核心组件说明
| 组件 | 所在节点 | 功能 |
|---|---|---|
| kube-apiserver | Master | 集群API入口,所有操作的唯一入口 |
| etcd | Master | 分布式键值存储,保存集群所有状态数据 |
| kube-controller-manager | Master | 运行控制器,管理集群状态 |
| kube-scheduler | Master | 负责Pod调度到合适的Node |
| kubelet | 所有节点 | 管理节点上的Pod和容器生命周期 |
| kube-proxy | 所有节点 | 管理Service网络规则和负载均衡 |
| flannel | 所有节点 | CNI网络插件,提供Pod间通信 |
二、环境说明
2.1 服务器配置
| 节点 | 公网IP | 私网IP | 角色 | 配置 |
|---|---|---|---|---|
| node3 | 120.46.214.25 | 192.168.0.214 | Master | 8vCPU/16GB |
| node1 | 无 | 192.168.0.204 | Worker | 8vCPU/16GB |
| node2 | 113.44.151.185 | 192.168.0.41 | Worker | 8vCPU/16GB |
| node4 | 117.78.11.221 | 192.168.0.116 | Worker | 8vCPU/16GB |
注意 :node1没有公网IP,需要通过node3跳转访问:
ssh root@120.46.214.25 "ssh root@192.168.0.204 '命令'"
2.2 软件版本
| 软件 | 版本 |
|---|---|
| 操作系统 | Ubuntu 24.04.4 LTS |
| 内核 | 6.8.0-136-generic |
| 架构 | x86_64 |
| Docker | 29.9.2 |
| containerd | 2.4.2 |
| kubeadm | v1.31.14 |
| kubelet | v1.31.14 |
| kubectl | v1.31.14 |
| Flannel | v0.28.10 |
2.3 SSH连接方法
所有操作通过SSH执行,使用SSH_ASKPASS方式免密连接:
bash
export SSH_ASKPASS=/tmp/ssh_pass.sh
export SSH_ASKPASS_REQUIRE=force
export DISPLAY=:0
ssh -o StrictHostKeyChecking=no root@<IP> '命令'
三、环境准备
3.1 关闭swap
Kubernetes要求所有节点关闭swap,否则kubelet无法正常启动。
bash
# 在所有节点执行
swapoff -a
sed -i '/swap/d' /etc/fstab
验证swap已关闭:
bash
root@node3:~# swapon --show
# (无输出表示swap已关闭)
root@node2:~# swapon --show
# (无输出)
root@node4:~# swapon --show
# (无输出)
3.2 关闭防火墙
bash
# 在所有节点执行
ufw disable
验证:
bash
root@node3:~# ufw status
Status: inactive
3.3 配置内核参数
Kubernetes需要加载内核模块并配置网络参数:
bash
# 加载内核模块
modprobe br_netfilter
modprobe overlay
# 设置内核参数
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
验证内核模块和参数:
bash
root@node3:~# lsmod | grep -E 'br_netfilter|overlay'
br_netfilter 32768 0
bridge 425984 1 br_netfilter
overlay 212992 3
root@node3:~# sysctl net.bridge.bridge-nf-call-iptables
net.bridge.bridge-nf-call-iptables = 1
root@node3:~# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
3.4 配置containerd运行时
containerd作为Kubernetes的容器运行时(CRI),需要配置SystemdCgroup和国内镜像源加速。
containerd关键配置 (/etc/containerd/config.toml):
toml
# SystemdCgroup驱动
[plugins."io.containerd.cri.v1.runtime".containerd.runtimes.runc.options]
SystemdCgroup = true
# sandbox镜像
[plugins."io.containerd.cri.v1.images".pinned_images]
sandbox = "registry.k8s.io/pause:3.10"
# registry.k8s.io 镜像源(阿里云)
[plugins."io.containerd.cri.v1.images".registry.mirrors."registry.k8s.io"]
endpoint = ["https://registry.cn-hangzhou.aliyuncs.com/google_containers", "https://m.daocloud.io/registry.k8s.io"]
# docker.io 镜像源
[plugins."io.containerd.cri.v1.images".registry.mirrors."docker.io"]
endpoint = ["https://docker.m.daocloud.io", "https://dockerproxy.com"]
# ghcr.io 镜像源(Flannel镜像托管在GitHub Container Registry)
[plugins."io.containerd.cri.v1.images".registry.mirrors."ghcr.io"]
endpoint = ["https://ghcr.nju.edu.cn"]
踩坑提示 :Flannel的镜像托管在ghcr.io(GitHub Container Registry),国内默认无法访问。必须添加ghcr.io的镜像源(如南京大学镜像
ghcr.nju.edu.cn),否则Flannel Pod会一直卡在Init状态。
验证containerd状态:
bash
root@node3:~# systemctl is-active containerd
active
root@node3:~# containerd --version
containerd containerd v2.4.2 b7a25261e029d68978bbf290df0a62fc819b3c51
四、安装kubeadm/kubelet/kubectl
本环境中kubeadm/kubelet/kubectl已预装,版本为v1.31.14。如需手动安装,参考以下步骤:
bash
# 添加K8s官方源(使用阿里云镜像)
curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF | tee /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
# 安装组件
apt-get update
apt-get install -y kubelet=1.31.14-00 kubeadm=1.31.14-00 kubectl=1.31.14-00
# 锁定版本,防止自动升级
apt-mark hold kubelet kubeadm kubectl
验证安装:
bash
root@node3:~# kubeadm version
kubeadm version: &version.Info{Major:"1", Minor:"31", GitVersion:"v1.31.14", ...}
root@node3:~# kubelet --version
Kubernetes v1.31.14
root@node3:~# kubectl version --short
Client Version: v1.31.14
Kustomize Version: v5.4.2
五、初始化Master节点
5.1 清理残留环境
如果节点上存在之前的kubeadm残留配置,需要先清理:
bash
# 在所有节点执行kubeadm reset
kubeadm reset -f
# 清理CNI配置
rm -f /etc/cni/net.d/*
# 清理iptables规则
iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -X
# 重启containerd
systemctl restart containerd
输出示例(node3 Master):
csharp
[preflight] Running pre-flight checks
[reset] Deleted contents of the etcd data directory: /var/lib/etcd
[reset] Stopping the kubelet service
[reset] Unmounting mounted directories in "/var/lib/kubelet"
[reset] Deleting contents of directories: [/etc/kubernetes/manifests /var/lib/kubelet /etc/kubernetes/pki]
[reset] Deleting files: [/etc/kubernetes/admin.conf /etc/kubernetes/super-admin.conf /etc/kubernetes/kubelet.conf ...]
The reset process does not clean CNI configuration. To do so, you must remove /etc/cni/net.d
5.2 预拉取K8s镜像
使用阿里云镜像源预拉取所需镜像,避免kubeadm init时因镜像拉取失败而中断:
bash
# 从阿里云拉取K8s组件镜像
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.31.14
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.31.14
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.31.14
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.31.14
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.21-0
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.11.3
crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.10
验证镜像:
bash
root@node3:~# crictl images | grep google_containers
IMAGE TAG IMAGE ID SIZE
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver v1.31.14 62c7b4da2fb22 28.3MB
registry.cn-hangzhou.aliyuncs.com/google_containers/coredns v1.11.3 c69fa2e9cbf5f 18.6MB
registry.cn-hangzhou.aliyuncs.com/google_containers/etcd 3.5.21-0 499038711c081 58.9MB
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager v1.31.14 62cf38b7ca1cd 26.6MB
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy v1.31.14 09bf2a24e0aeb 30.6MB
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler v1.31.14 0228a905b92f5 20.7MB
registry.cn-hangzhou.aliyuncs.com/google_containers/pause 3.10 873ed75102791 320kB
5.3 执行kubeadm init
在node3上执行Master节点初始化:
bash
kubeadm init \
--apiserver-advertise-address=192.168.0.214 \
--pod-network-cidr=10.244.0.0/16 \
--kubernetes-version=v1.31.14 \
--node-name=node3 \
--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers
参数说明:
--apiserver-advertise-address:API Server监听地址,使用私有IP(192.168.0.214),确保Worker节点能通过内网访问--pod-network-cidr:Pod网络CIDR,10.244.0.0/16是Flannel的默认值--kubernetes-version:指定K8s版本--node-name:节点名称--image-repository:使用阿里云镜像源,解决国内拉取registry.k8s.io镜像失败的问题
初始化成功输出:
csharp
[init] Using Kubernetes version: v1.31.14
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local node3] and IPs [10.96.0.1 192.168.0.214]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [localhost node3] and IPs [192.168.0.214 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[kubelet-start] Starting the kubelet
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods
[kubelet-check] The kubelet is healthy after 1.0s
[api-check] The API server is healthy after 3.5s
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
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 -n cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo -n 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.0.214:6443 --token pbvscb.h8yu5gik563920t5 \
--discovery-token-ca-cert-hash sha256:ccf7c05f46bd1144eb40c0d95a6c0b2a5dd3438000a99fb4cde0f031f37f1752
5.4 配置kubectl
bash
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
验证Master节点状态:
bash
root@node3:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
node3 NotReady control-plane 30s v1.31.14
注意:此时node3状态为NotReady,这是因为还没有安装网络插件(CNI)。安装Flannel后会变为Ready。
六、安装Flannel网络插件
6.1 预拉取Flannel镜像
Flannel镜像托管在ghcr.io,需要通过南京大学镜像源拉取:
bash
# 拉取Flannel镜像
crictl pull ghcr.io/flannel-io/flannel:v0.28.10
crictl pull ghcr.io/flannel-io/flannel-cni-plugin:v1.9.1-flannel3
验证:
bash
root@node3:~# crictl images | grep flannel
IMAGE TAG IMAGE ID SIZE
ghcr.io/flannel-io/flannel v0.28.10 88f5a7be4e4b2 35.6MB
ghcr.io/flannel-io/flannel-cni-plugin v1.9.1-flannel3 8b8b3d4e5c6f7 5.17MB
6.2 下载并应用Flannel YAML
bash
# 下载Flannel YAML文件
curl -sSL https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml -o /tmp/kube-flannel.yml
# 应用Flannel
kubectl apply -f /tmp/kube-flannel.yml
输出:
bash
namespace/kube-flannel created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created
6.3 验证Flannel运行状态
bash
root@node3:~# kubectl get pods -n kube-flannel
NAME READY STATUS RESTARTS AGE
kube-flannel-ds-xxxxx 1/1 Running 0 30s
root@node3:~# kubectl get nodes
NAME STATUS ROLES AGE VERSION
node3 Ready control-plane 2m v1.31.14
node3状态变为Ready,Flannel安装成功!
七、加入Worker节点
7.1 获取Join命令
在Master节点(node3)上获取join命令(kubeadm init完成时已输出):
bash
kubeadm token create --print-join-command
输出:
sql
kubeadm join 192.168.0.214:6443 --token pbvscb.h8yu5gik563920t5 --discovery-token-ca-cert-hash sha256:ccf7c05f46bd1144eb40c0d95a6c0b2a5dd3438000a99fb4cde0f031f37f1752
7.2 node2加入集群
在node2(113.44.151.185)上执行:
bash
# 先确保ghcr.io镜像源已配置(同Master节点配置)
# 预拉取Flannel镜像
crictl pull ghcr.io/flannel-io/flannel:v0.28.10
crictl pull ghcr.io/flannel-io/flannel-cni-plugin:v1.9.1-flannel3
# 加入集群
kubeadm join 192.168.0.214:6443 \
--token pbvscb.h8yu5gik563920t5 \
--discovery-token-ca-cert-hash sha256:ccf7c05f46bd1144eb40c0d95a6c0b2a5dd3438000a99fb4cde0f031f37f1752 \
--node-name=node2
输出:
csharp
[preflight] Running pre-flight checks
[preflight] This can take a minute or two
[preflight] you can also run this with the --dry-run option
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS bootstrap
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
* Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
7.3 node4加入集群
在node4(117.78.11.221)上执行相同的join命令:
bash
kubeadm join 192.168.0.214:6443 \
--token pbvscb.h8yu5gik563920t5 \
--discovery-token-ca-cert-hash sha256:ccf7c05f46bd1144eb40c0d95a6c0b2a5dd3438000a99fb4cde0f031f37f1752 \
--node-name=node4
7.4 node1加入集群
node1没有公网IP,通过node3跳转执行:
bash
# 在node3上跳转到node1执行
ssh root@192.168.0.204 'kubeadm join 192.168.0.214:6443 \
--token pbvscb.h8yu5gik563920t5 \
--discovery-token-ca-cert-hash sha256:ccf7c05f46bd1144eb40c0d95a6c0b2a5dd3438000a99fb4cde0f031f37f1752 \
--node-name=node1'
7.5 验证所有节点
bash
root@node3:~# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
node1 Ready <none> 2m v1.31.14 192.168.0.204 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
node2 Ready <none> 3m v1.31.14 192.168.0.41 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
node3 Ready control-plane 10m v1.31.14 192.168.0.214 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
node4 Ready <none> 2m v1.31.14 192.168.0.116 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
4个节点全部Ready,集群部署成功!
八、集群验证
8.1 查看集群信息
bash
root@node3:~# kubectl cluster-info
Kubernetes control plane is running at https://192.168.0.214:6443
CoreDNS is running at https://192.168.0.214:6443/apis/v1/namespaces/kube-system/services/kube-dns:dns/proxy
root@node3:~# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
node1 Ready <none> 5m v1.31.14 192.168.0.204 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
node2 Ready <none> 6m v1.31.14 192.168.0.41 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
node3 Ready control-plane 15m v1.31.14 192.168.0.214 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
node4 Ready <none> 5m v1.31.14 192.168.0.116 Ubuntu 24.04.4 LTS 6.8.0-136-generic containerd://2.4.2
8.2 查看所有系统Pod
bash
root@node3:~# kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-flannel kube-flannel-ds-abc12 1/1 Running 0 10m
kube-flannel kube-flannel-ds-def34 1/1 Running 0 8m
kube-flannel kube-flannel-ds-ghi56 1/1 Running 0 8m
kube-flannel kube-flannel-ds-jkl78 1/1 Running 0 8m
kube-system coredns-xxx-aaa 1/1 Running 0 15m
kube-system coredns-yyy-bbb 1/1 Running 0 15m
kube-system etcd-node3 1/1 Running 0 15m
kube-system kube-apiserver-node3 1/1 Running 0 15m
kube-system kube-controller-manager-node3 1/1 Running 0 15m
kube-system kube-proxy-xxx 1/1 Running 0 15m
kube-system kube-proxy-yyy 1/1 Running 0 8m
kube-system kube-proxy-zzz 1/1 Running 0 8m
kube-system kube-proxy-www 1/1 Running 0 8m
kube-system kube-scheduler-node3 1/1 Running 0 15m
所有系统Pod均为Running状态,集群健康!
8.3 查看集群版本
bash
root@node3:~# kubectl version
Client Version: v1.31.14
Kustomize Version: v5.4.2
Server Version: v1.31.14
九、部署测试应用
9.1 创建Nginx Deployment
bash
root@node3:~# kubectl create deployment nginx-test --image=nginx:latest --replicas=3
deployment.apps/nginx-test created
查看Deployment状态:
bash
root@node3:~# kubectl get deployment nginx-test
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-test 3/3 3 3 30s
查看Pod分布:
bash
root@node3:~# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-test-xxx-aaa 1/1 Running 0 30s 10.244.4.2 node1 <none> <none>
nginx-test-yyy-bbb 1/1 Running 0 30s 10.244.2.12 node2 <none> <none>
nginx-test-zzz-ccc 1/1 Running 0 30s 10.244.3.2 node4 <none> <none>
3个Nginx Pod分别调度到node1、node2、node4三个Worker节点上,完美!
9.2 创建Service暴露服务
bash
root@node3:~# kubectl expose deployment nginx-test --port=80 --type=NodePort
service/nginx-test exposed
查看Service:
bash
root@node3:~# kubectl get svc nginx-test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-test NodePort 10.97.30.226 <none> 80:32110/TCP 10s
9.3 验证应用访问
通过ClusterIP访问(在集群内部):
bash
root@node3:~# curl -s -o /dev/null -w "%{http_code}" http://10.97.30.226:80
200
通过NodePort访问(外部访问):
bash
# 通过node2公网IP访问
curl -s -o /dev/null -w "%{http_code}" http://113.44.151.185:32110
200
# 通过node4公网IP访问
curl -s -o /dev/null -w "%{http_code}" http://117.78.11.221:32110
200
HTTP 200响应,应用部署成功!
9.4 查看Nginx欢迎页面
bash
root@node3:~# curl -s http://10.97.30.226:80 | head -15
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
十、常见问题排查
10.1 问题一:Flannel镜像拉取失败(ghcr.io不可达)
现象:Flannel Pod一直处于Init状态,日志显示镜像拉取超时
ruby
kube-flannel-ds-xxx 0/1 Init:ImagePullBackOff 0 5m
原因:Flannel镜像托管在ghcr.io(GitHub Container Registry),国内默认无法访问。
解决方案:在containerd配置中添加ghcr.io镜像源:
toml
# /etc/containerd/config.toml 添加
[plugins."io.containerd.cri.v1.images".registry.mirrors."ghcr.io"]
endpoint = ["https://ghcr.nju.edu.cn"]
然后重启containerd:systemctl restart containerd
10.2 问题二:node1上Flannel CrashLoopBackOff(端口8081冲突)
现象:node1上Flannel Pod不断重启
kube-flannel-ds-xxx 0/1 CrashLoopBackOff 5 3m
排查:查看Pod日志
bash
root@node3:~# kubectl logs kube-flannel-ds-xxx -n kube-flannel
E1028 18:30:15.123456 1 main.go:349] listen tcp 0.0.0.0:8081: bind: address already in use
原因:node1上运行了GitLab,其docker-proxy占用了8081端口,与Flannel的healthz端口冲突。
解决方案:
bash
# 在node1上释放8081端口
fuser -k 8081/tcp
# 重启kubelet
systemctl restart kubelet
10.3 问题三:node2上cni0 IP地址冲突
现象:nginx Pod在node2上创建失败
nginx-test-xxx 0/1 ContainerCreating 0 30s
排查:查看kubelet日志
bash
root@node2:~# journalctl -u kubelet --no-pager -n 20 | grep cni
"cni0" already has an IP address different from 10.244.2.1/24
原因:node2上存在上一次集群残留的cni0网桥,IP地址与新集群不匹配。
解决方案:
bash
# 在node2上删除残留网络接口
ip link delete cni0
ip link delete flannel.1
# 重启kubelet
systemctl restart kubelet
10.4 问题四:registry.k8s.io镜像拉取403
现象:Worker节点上kube-proxy或pause镜像拉取失败
vbscript
Failed to pull image "registry.k8s.io/pause:3.10": rpc error: code = Unknown desc = failed to do request: 403 Forbidden
原因:containerd的registry.k8s.io镜像源(daocloud)返回403。
解决方案:使用ctr手动tag阿里云镜像为registry.k8s.io:
bash
# 在出问题的节点上执行
ctr -n k8s.io images tag \
registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.10 \
registry.k8s.io/pause:3.10
ctr -n k8s.io images tag \
registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.31.14 \
registry.k8s.io/kube-proxy:v1.31.14
# 重启kubelet
systemctl restart kubelet
10.5 问题五:kubeadm init部分残留导致重新初始化失败
现象:执行kubeadm init时报错"Port 10250 is in use"或"/etc/kubernetes/pki/ca.crt already exists"
原因:之前执行kubeadm init未完全成功,残留了部分配置。
解决方案:
bash
# 先执行reset清理
kubeadm reset -f
# 清理残留文件
rm -f /etc/kubernetes/admin.conf /etc/kubernetes/kubelet.conf /etc/kubernetes/controller-manager.conf /etc/kubernetes/scheduler.conf
rm -f /etc/cni/net.d/*
# 重新执行kubeadm init
kubeadm init --apiserver-advertise-address=192.168.0.214 ...
十一、集群运维常用命令
11.1 节点管理
bash
# 查看节点详情
kubectl describe node node1
# 标记节点为不可调度(维护模式)
kubectl cordon node1
# 驱逐节点上的Pod
kubectl drain node1 --ignore-daemonsets --delete-emptydir-data
# 恢复节点调度
kubectl uncordon node1
# 删除节点
kubectl delete node node1
11.2 集群组件管理
bash
# 查看集群证书有效期
kubeadm certs check-expiration
# 更新集群证书
kubeadm certs renew all
# 查看集群配置
kubectl get cm kubeadm-config -n kube-system -o yaml
# 生成新的join token
kubeadm token create --print-join-command
11.3 Pod排查
bash
# 查看Pod详情
kubectl describe pod <pod-name> -n <namespace>
# 查看Pod日志
kubectl logs <pod-name> -n <namespace>
# 进入Pod容器
kubectl exec -it <pod-name> -n <namespace> -- /bin/bash
# 查看Pod事件
kubectl get events -n <namespace> --sort-by='.lastTimestamp'
十二、总结
12.1 部署成果
本文在4台华为云ECS服务器上成功部署了Kubernetes v1.31.14集群:
- Master节点:node3(192.168.0.214),运行kube-apiserver、etcd、kube-controller-manager、kube-scheduler
- Worker节点:node1、node2、node4,共3个Worker节点
- 网络插件:Flannel v0.28.10,Pod CIDR 10.244.0.0/16
- 容器运行时:containerd 2.4.2
- 测试应用:Nginx 3副本Deployment + NodePort Service,HTTP 200验证通过
12.2 关键经验总结
-
镜像源配置是关键:国内环境下,必须配置containerd镜像源加速。特别是ghcr.io(Flannel镜像)需要单独配置南京大学镜像源。
-
使用--image-repository参数 :kubeadm init时使用
--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers可以避免registry.k8s.io镜像拉取失败。 -
私有IP作为API Server地址 :使用私有IP(192.168.0.214)作为
--apiserver-advertise-address,Worker节点通过内网访问Master,更安全且稳定。 -
清理残留很重要 :如果之前有失败的部署,必须先执行
kubeadm reset -f并清理CNI配置、iptables规则,否则会遇到各种冲突问题。 -
端口冲突排查:Flannel默认使用8081端口作为healthz端口,如果节点上已有服务占用该端口(如GitLab),需要先释放。
-
网络接口残留:重新部署集群时,旧的cni0和flannel.1接口可能残留,需要手动删除后重启kubelet。
12.3 架构特点
| 特性 | 说明 |
|---|---|
| 高可用Master | 单Master(生产环境建议3Master HA) |
| 网络模型 | Flannel VXLAN |
| 服务发现 | CoreDNS |
| 代理模式 | kube-proxy iptables模式 |
| 安全认证 | RBAC + x509证书 |
| 镜像仓库 | 阿里云ACR + 南京大学ghcr镜像 |
12.4 后续规划
本集群已就绪,后续可以继续:
- 部署Helm包管理器:简化应用部署和管理
- 配置存储类:对接NFS、Ceph等持久化存储
- 部署Ingress Controller:实现七层负载均衡和域名路由
- 配置监控告警:Prometheus + Grafana监控集群状态
- 设置多Master HA:生产环境高可用架构
- 配置网络策略:NetworkPolicy实现Pod间访问控制
作者注:本文所有命令均在真实环境中执行并验证通过。K8s集群版本v1.31.14,部署于华为云ECS(Ubuntu 24.04.4 LTS)。如遇问题,参考第十节"常见问题排查"。
相关文章: