Kubernetes集群部署实战:使用kubeadm在4台ECS服务器上搭建K8s v1.31集群

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 关键经验总结

  1. 镜像源配置是关键:国内环境下,必须配置containerd镜像源加速。特别是ghcr.io(Flannel镜像)需要单独配置南京大学镜像源。

  2. 使用--image-repository参数 :kubeadm init时使用--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers可以避免registry.k8s.io镜像拉取失败。

  3. 私有IP作为API Server地址 :使用私有IP(192.168.0.214)作为--apiserver-advertise-address,Worker节点通过内网访问Master,更安全且稳定。

  4. 清理残留很重要 :如果之前有失败的部署,必须先执行kubeadm reset -f并清理CNI配置、iptables规则,否则会遇到各种冲突问题。

  5. 端口冲突排查:Flannel默认使用8081端口作为healthz端口,如果节点上已有服务占用该端口(如GitLab),需要先释放。

  6. 网络接口残留:重新部署集群时,旧的cni0和flannel.1接口可能残留,需要手动删除后重启kubelet。

12.3 架构特点

特性 说明
高可用Master 单Master(生产环境建议3Master HA)
网络模型 Flannel VXLAN
服务发现 CoreDNS
代理模式 kube-proxy iptables模式
安全认证 RBAC + x509证书
镜像仓库 阿里云ACR + 南京大学ghcr镜像

12.4 后续规划

本集群已就绪,后续可以继续:

  1. 部署Helm包管理器:简化应用部署和管理
  2. 配置存储类:对接NFS、Ceph等持久化存储
  3. 部署Ingress Controller:实现七层负载均衡和域名路由
  4. 配置监控告警:Prometheus + Grafana监控集群状态
  5. 设置多Master HA:生产环境高可用架构
  6. 配置网络策略:NetworkPolicy实现Pod间访问控制

作者注:本文所有命令均在真实环境中执行并验证通过。K8s集群版本v1.31.14,部署于华为云ECS(Ubuntu 24.04.4 LTS)。如遇问题,参考第十节"常见问题排查"。

相关文章:

相关推荐
闲云自留地1 小时前
K8s Ingress 全解:ingress-nginx 部署、域名路径路由、HTTPS 限流灰度一篇通
nginx·https·kubernetes
SelectDB技术团队2 小时前
ClickHouse 支持存算分离吗?Doris 社区版原生存算分离怎么落地
数据库·clickhouse·云原生·开源·apache doris·技术选型·存算分层
正经教主2 小时前
【FDE系列】阶段2:Day 43(UP详细版):Docker Compose — 多容器一键编排
运维·docker·容器
csdn_aspnet2 小时前
构建、运行与管理容器的 18 个常用 Docker 命令
docker·容器·eureka·command
木雷坞2 小时前
AnythingLLM Docker部署:中文资料上传后无引用的检查路径
运维·docker·容器
暖核2 小时前
K8s 深度拆解:健康检查、认证授权与可视化面板全梳理
云原生·容器·kubernetes
分布式存储与RustFS2 小时前
十分钟在自己机器上跑通一个 S3 兼容对象存储(RustFS + Docker)
云原生·开源·对象存储·分布式存储·s3·rustfs·性能基准
知已难寻3 小时前
00 · OpenStack 总纲——从云计算到 OpenStack 全景
运维·服务器·云原生·云计算·负载均衡·openstack·perl
小白的码BUG之路3 小时前
K8S -- 集群搭建环境基础
java·服务器·kubernetes