K8s简介以及使用 kubeadm 安装 K8s 集群实践

K8s 介绍

K8s 官网


什么是 K8s

我们结合微服务项目演进,来谈谈对它的理解。在传统的 Docker / Docker Compose 时代,我们虽然解决了容器集装箱式的敏捷打包问题,但当系统步入中大型微服务集群后,单机编排的运维痛点就会集中爆发:

  • 当某台 CentOS 宿主机突然物理断电或发生硬件故障时,由于缺乏集群级别的健康感知,上面挂掉的业务容器无法实现跨机器的自动漂移;
  • 在面对大促或突发流量时,我们无法做到秒级的弹性横向伸缩(HPA);
  • 容器高频重建导致的 IP 频繁变更,也让底层的负载均衡和网络路由维护变得极其繁琐。

而 Kubernetes 的本质,就是一个大师级的分布式微服务操作系统,或者说是超级全自动化运维管家。 它让我们从传统的关注单台服务器、手动拉起容器的思维,彻底并网跃迁到了基于声明式 API、面向集群最终状态的现代化云原生运维阶段。

在底层架构设计上(请参考 官网Kubernetes 架构介绍 ),K8s 采用了极其稳健的 Control Plane(控制平面)与 Worker Node(工作节点) 异步分离的架构。如果把集群比作一个高度自动化的现代化工厂:

  • Master 节点就是工厂的总调度指挥部。其中,API Server 是唯一的网关入口,所有指令和鉴权都通过它中转;Etcd 作为分布式强一致性的键值数据库,保存了整个集群的生死簿和所有状态快照;Scheduler 负责动态计算算力,将任务完美分派到最空闲的机器;而 Controller Manager 则是核心灵魂,它通过死循环的监听-对比-协调机制,一旦发现实际运行状态偏离了预期声明,就会立刻自愈修复。
  • Worker 节点则是底层的车间苦力。Kubelet 作为驻地工头,雷厉风行地监督底层容器运行时(如 Containerd)把 Pod 拉起来;Kube-proxy 则是网络交警,通过优化后的 IPVS / Iptables 规则,死死卡住集群内部的虚拟网络拓扑和流量转发。

在日常的业务研发中,我们几乎天天在和 K8s 封装好的几大核心工业规范打交道:

  • Pod:它是 K8s 的最小原子调度单位。一般我们会将紧密耦合的微服务或辅助组件(如 Logback 日志收集的 Sidecar)打包在同一个 Pod 里,让他们共享同一个网络和存储卷,实现零延迟通信。
  • Deployment:用来控制无状态的 Java/Python 微服务。我通过它来实现蓝绿部署或滚动升级。在生产环境下,它能保证新旧版本平滑交替,前端用户完全做到零中断、零感知。
  • Service 与 Ingress:由于 Pod 会经常销毁重建导致内网 IP 漂移,一般我们会用 Service 作为一个死死固定的内部高可用负载均衡网关;再并网 Ingress 作为面向公网的七层反向代理,实现优雅的域名路由和 SSL 证书卸载。

K8s 真正能在大厂降本增效的核心竞争力,在于它的高级动态调配机制。在实际的项目里:

  • 我们会为每个 Pod 精准配置 Resources.Requests(资源索求上限)和 Resources.Limits(资源物理封顶),防止某一个 Java 节点由于内存泄漏(OOM)或者 Netty 线程风暴跑满 CPU,从而把整台 CentOS 宿主机拖死,实现完美的系统级故障隔离。
  • 同时,结合容器的 Liveness(存活探针) 和 Readiness(就绪探针),K8s 可以在 Java 应用发生假死、或者 Spring Boot 还没完全初始化完成时,自动将其剔除流量,甚至直接重启 Pod。这种强悍的故障自愈与链路保护能力,是传统 Docker 矩阵完全无法匹敌的。

总的来说,Kubernetes 绝不仅仅是一个部署工具,它是整个现代互联网企业构建高并发、高可用、可弹性伸缩的分布式分布式微服务架构时,必不可少的底座级并网基础设施。


使用 kubeadm 搭建 K8s 集群

集群介绍

Master 节点阵营:这 3 台虚拟机对应图中的 Master01、Master02、Master03。

  • 为什么非要 3 台 Master?因为 K8s 采用的是分布式强一致性选举算法(Raft),为了防止其中一台机器突然死机导致集群 “群龙无首”,Master 节点的数量必须是奇数个(3, 5, 7…)。3 台机器可以容忍任意 1 台断电瘫痪,集群大脑依然能轰鸣运转。
  • 它们内部跑什么?这 3 台机器上会同时起航图中的三大核心核心组件:Kube-APIServer(集群网关)、ControllerManager(总管家)、Scheduler(调度员)。

工作节点阵营(体力担当):Node01、Node02。图中的 NodeNN 代表未来你可以无限横向并网扩展)。

  • 它们负责干嘛?它们才是真正用来跑 Java 微服务、Redis 哨兵或者 MySQL 的苦力。它们身上只跑两个核心网络/调度组件:Kubelet(当地工头)和 Kube-Proxy(网络交警)。

三大数据流向

  • 底层生死簿 Etcd Cluster:图中最下方,3 台 Master 的指针全部整齐地向下扎入了 Etcd Cluster。在我们的 5 台机器并网时,我们会在 3 台 Master 上同时安装 Etcd,让它们在底层手拉手组成一个高可用的分布式数据库集群。Master 内的 Kube-APIServer 产生的任何数据(比如你部署了一个新微服务),都会实时写入这个底层数据库。3 台 Master 共享这本 “生死簿”。
  • 稳压中枢 Load Balancer:图的中部,Node01、Node02 以及 Master 自己,并没有直接去连具体的某一台 Master,而是统一把线插在了 Load Balancer 上。为什么这么设计?因为:
    • 如果让 2 台 Node 固定连 Master01 的 APIServer,一旦 Master01 挂了,这两台 Node 就会瞬间失联。
    • 实际中我们通常会在集群外或者直接在 Master 节点上利用 Nginx + Keepalived 或者 HAProxy 虚拟出一个高可用的 VIP(虚拟 IP)。
    • 所有的 Worker Node(Kubelet/Kube-Proxy)在向大脑汇报工作时,流量先轰到这个 Load Balancer 上,由它把请求均匀地分发给健康的 Kube-APIServer。哪怕死了一台 Master,流量也会被自动切走。
  • Master虚线框里的 Kubelet 和 Kube-Proxy:在 K8s 的工业规范里,Master 节点默认不参与业务 Pod 的调度(身上带有污点 Taint,不允许普通应用居住)。但在管理集群时,Master 节点自身的一些管理组件(比如集群内部的网络插件 Calico/Flannel、DNS 服务 CoreDNS)也是以 Pod 的形式运行的。为了管理 Master 自己身上的这些管理级容器,Master 内部也会选择性地安装精简版的 Kubelet 和 Kube-Proxy。


集群安装

我们准备了 5 台 centos10 主机。分别是:

1
2
3
4
5
6
$ cat /etc/hosts
192.168.1.9 centos10-01 # 充当 Master 节点,以及负载均衡器 Keepalived + Nginx
192.168.1.10 centos10-02 # 充当 Master 节点,以及负载均衡器 Keepalived + Nginx
192.168.1.11 centos10-03 # 充当 Master 节点,以及负载均衡器 Keepalived + Nginx
192.168.1.12 centos10-04 # 充当 Node 节点
192.168.1.13 centos10-05 # 充当 Node 节点


全节点安装准备工作

在所有 5 台机器上,用 root 账户无情地砸入以下准备工作,这是奠定集群稳定性的底层基石。

1、彻底关闭 Swap 交换分区(K8s 铁律)。K8s 调度为了极致的性能,默认绝不允许内存数据去挤占慢速的磁盘 Swap。

1
2
3
4
# 临时关闭
swapoff -a && sysctl -w vm.swappiness=0
# 永久关闭:直接用 sed 注释掉 /etc/fstab 中的 swap 挂载行
sed -i '/swap/s/^/#/' /etc/fstab

2、彻底关闭 SELinux 与防火墙。为了防止 CentOS 10 严苛的安全策略误杀 K8s 内部复杂的隧道网络:

1
2
3
4
5
6
7
# 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

# 禁用 SELinux
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config

3、安装 Chrony 时间同步服务

注意:Chrony 守护进程本身就是“渐进式微调”的高手,很多老运维习惯在 crontab 里写一句 “/5 * /usr/sbin/ntpdate ntp.aliyun.com”,但建议千万别这么干,因为致命的 “时间跳变” 会瞬间搞崩 Etcd。如果时间突然往前跳或者往后大跨步,Etcd 会误以为心跳超时,从而疯狂触发 Leader 重新选举,直接导致你的 K8s 脑裂、集群瞬间瘫痪。

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
# 五台机器上全部执行
dnf install -y chrony

# 擦除国外脏源,换上国内大厂黄金时间隧道
# 你会看到有几行默认的国外 pool 服务器,用 # 号全部无情注释掉,然后把阿里云和腾讯云的顶级物理时钟源砸进去
vim /etc/chrony.conf
server ntp.aliyun.com iburst
server time1.cloud.tencent.com iburst
server time.windows.com iburst

# 启动并设置为开机自启
systemctl enable --now chronyd

# 强行让系统物理硬件时间(RTC)与系统同步,防止重启后时间回滚
chronyc hwtimestamp *
# 让 Linux 系统的内核时间(软时间)直接去同步阿里云的时钟源
chronyc tracking
# 强行把当前已经对齐的正确系统时间,同步写入虚拟机的模拟 CMOS 硬件时钟(彻底固化)
hwclock --systohc

# 在 5 台机器上敲下这行命令,看看时间是否已经彻底并网通车:
chronyc sources -v

# 验证:只要你看到输出的列表里,ntp.aliyun.com 那一行的 Offset(时间偏差) 处于个位数、甚至后面
# 带着 ns(纳秒)或 us(微秒)级别,就说明 5 台机器的微弱时间差早就被 Chrony 用极尽温柔的方式“揉”平了。
chronyc sourcestats -v

4、更改文件描述符上线(一般改成 65535 以上)

  • nofile (Number of Open Files):单进程允许打开的最大文件句柄数。直接从 1024 暴力拔高到 65536,Java 微服务和高并发网络从此彻底解套。
  • nproc (Number of Processes):单个用户允许创建的最大进程/线程数。防止高并发下 Netty 或 Tomcat 线程池发生“线程风暴”时被 Linux 强行锁喉。
  • limits.conf 的修改不需要你重启整台 CentOS 10 虚拟机,它对新建立的会话(SSH 连接)会当场生效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vim /etc/security/limits.conf

# 突破系统进程与文件打开限制(为 K8s 集群全线松绑)
* soft nofile 65536
* hard nofile 65536
* soft nproc 65535
* hard nproc 65535
# root 用户单独加固(防止某些极端管理组件爆表)
root soft nofile 65536
root hard nofile 65536
root soft nproc 65535
root hard nproc 65535


# 重启会话查看是否生效
# 查看最大文件打开数限制
ulimit -n
# 查看最大进程数限制
ulimit -u

5、并网桥接流量(启用内核转发)

让 Linux 的 IPv4 流量能够平滑穿透网桥,这是 Pod 之间能隔空通信的硬核前提:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cat <<EOF | tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

# 激活内核模块
modprobe overlay
modprobe br_netfilter

# 配置内核参数
cat <<EOF | 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 已经全盘告别了臃肿的传统 Docker,全面拥抱轻量极致的 Containerd。所有节点执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 引入阿里云提供的纯净 Docker-CE 软件源(内部包含 Containerd)
dnf config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 2. 独立下载安装 container.io 包
dnf install -y containerd.io

# 3. 生成并配置标准纯净配置文件
mkdir -p /etc/containerd
# 让 Containerd 引擎当场吐出一份官方配置说明书,把这份说明书强行覆盖写入到系统指定的配置文件中
containerd config default > /etc/containerd/config.toml

# 极其关键:将底层 Cgroup 驱动修改为 systemd,并换上沙箱镜像的国内加速通道
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
sed -i "s|registry.k8s.io/pause:3.10.1|registry.aliyuncs.com/google_containers/pause:3.10.1|g" /etc/containerd/config.toml

# 4. 拉闸启动
systemctl daemon-reload
systemctl restart containerd
systemctl enable containerd


安装工具三剑客

给所有节点安装工具三剑客:kubelet kubeadm kubectl

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
# 引入专为现代系统(CentOS 9/10)重构的官方黄金 DNF 软件源
# 新源的 baseurl 路径里依然有 kubernetes-el7-x86_64 这个残留字眼,这是阿里云和 Google 官方为了兼容
# 旧生态,特意将现代版本的 RPM 包(包含兼容现代 RHEL 9/10 的最新构建版本)统一封装在这个扁平化的路径下。
# 但它内部的 RPM 索引和签名(gpgkey)早就支持了 CentOS 10。
cat <<EOF | tee /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
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

# 在 CentOS 10 上安装三剑客的正确姿势
# 因为我们在上面的源里加上了安全系数极高的 exclude=kubelet kubeadm kubectl(防止平时系统自动更新大版本把 K8s 集群冲垮),所以在真正安装时,必须在 DNF 命令后面显式加上参数释放它。
# 强行释放 exclude 锁,一键安装极稳的 K8s 搬砖三剑客
dnf install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
# 顺手合闸刷新,并设置为开机自启
systemctl daemon-reload
systemctl enable --now kubelet

# 验证三剑客之首是否安全着陆
kubeadm version


安装高可用负载均衡

负载均衡在 centos10-01/02/03 这三台master 节点安装即可。有两种实现方案:Keepalived+Nginx 和 Keepalived+HAProxy。

在搭建 Kubernetes(K8s)多 Master 高可用集群时,为了给 3 台 Master 的 kube-apiserver 提供统一的并网入口,Keepalived 几乎是必选的底层组件(负责虚拟 IP / VIP 的平滑漂移和故障自愈)。但在四层负载均衡器的选择上,业界通常有两种主流的黄金搭档:Nginx 和 HAProxy。

这里我们选择 Keepalived + HAProxy 的方案,HAProxy 不仅能获得天花板级别的四层转发性能,更重要的是它那强悍的主动健康检查机制,能在某台 Master 假死时,做到毫秒级的无缝流量切离,完美适用于高可用架构。

第一步:在 3 台 Master 节点上,直接用 DNF 拉闸安装这两个核心组件:

1
dnf install -y haproxy keepalived

第二步:配置 HAProxy,其中控配置文件在 /etc/haproxy/haproxy.cfg。我们直接用 cat 命令在这3个节点彻底洗白并覆盖旧文件,注入纯净的 生产级四层代理 + 监控面板配置

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
cat <<EOF | tee /etc/haproxy/haproxy.cfg
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats

defaults
mode tcp
log global
retries 3
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout check 10s
maxconn 3000

# 1. 开启 HAProxy 状态监控面板(按需查阅集群健康度)
listen stats
mode http
bind *:1080
stats enable
stats refresh 5s
stats uri /haproxy_stats
stats auth admin:123456 # 网页登录的用户名和密码

# 2. 控制平面四层代理网关(前端接流入口)
frontend k8s-apiserver-in
bind *:16443 # 监听 16443 端口
mode tcp
default_backend k8s-apiserver-out

# 3. 流量分发后端(精准咬合 3 台 Master 的 6443 真实端口)
backend k8s-apiserver-out
mode tcp
balance roundrobin # 轮询算法
# check inter 2000: 每2秒主动探测一次; rise 2: 成功2次算健康; fall 3: 失败3次彻底踢出
server centos10-01 192.168.1.9:6443 check inter 2000 rise 2 fall 3
server centos10-02 192.168.1.10:6443 check inter 2000 rise 2 fall 3
server centos10-03 192.168.1.11:6443 check inter 2000 rise 2 fall 3
EOF

第三步:精细配置 Keepalived(01、02、03 略有不同)。Keepalived 的核心作用是顶出一个虚拟 IP。为了防范 HAProxy 进程意外暴毙,我们需要在 Keepalived 内部注入一个心跳自愈脚本:如果发现本机的 HAProxy 死了,Keepalived 会主动把 VIP 吐出来,让给另外两台健康的 Master。先在 3 台 Master 上同时写入健康检查脚本:

1
2
3
4
5
6
7
8
9
10
cat <<EOF | tee /etc/keepalived/check_haproxy.sh
#!/bin/bash
# 检查本地是否有 haproxy 进程存在(只检查进程是否存在,不发送实际信号),如果不存在,则停掉 keepalived!
if ! killall -0 haproxy &>/dev/null; then
systemctl stop keepalived
fi
EOF

# 必须赋予该脚本可执行权限,否则 Keepalived 无法调用!
chmod +x /etc/keepalived/check_haproxy.sh

定制 3 台 Master 的 keepalived.conf。打开 /etc/keepalived/keepalived.conf,根据节点分别覆盖以下内容:

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
# centos10-01 (元老主节点) 配置:
cat <<EOF | tee /etc/keepalived/keepalived.conf
global_defs {
router_id lb-master01
script_user root
enable_script_security
}

# 引入自愈脚本
vrrp_script check_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -20
}

vrrp_instance VI_1 {
state MASTER # 01机为 MASTER,02/03机改为 BACKUP
interface enp0s3 # 已经对齐你通过 ip a 确认的本地真实网卡名
virtual_router_id 51
priority 100 # 01机给 100,02机给 90,03机给 80
advert_int 1
authentication {
auth_type PASS
auth_pass k8sha123
}
virtual_ipaddress {
192.168.1.200/24 # 全集群高可用 VIP,注意IP占用和必须处在同一网段
}
track_script {
check_haproxy
}
}
EOF



# centos10-02 (备用节点) 配置:
cat <<EOF | tee /etc/keepalived/keepalived.conf
global_defs {
router_id lb-master02
script_user root
enable_script_security
}

vrrp_script check_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -20
}

vrrp_instance VI_1 {
state BACKUP # 改为 BACKUP
interface enp0s3
virtual_router_id 51
priority 90 # 权重降为 90
advert_int 1
authentication {
auth_type PASS
auth_pass k8sha123
}
virtual_ipaddress {
192.168.1.200/24
}
track_script {
check_haproxy
}
}
EOF



# centos10-03 (备用节点) 配置:
cat <<EOF | tee /etc/keepalived/keepalived.conf
global_defs {
router_id lb-master03
script_user root
enable_script_security
}

vrrp_script check_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -20
}

vrrp_instance VI_1 {
state BACKUP # 改为 BACKUP
interface enp0s3
virtual_router_id 51
priority 80 # 权重降为 80
advert_int 1
authentication {
auth_type PASS
auth_pass k8sha123
}
virtual_ipaddress {
192.168.1.200/24
}
track_script {
check_haproxy
}
}
EOF

第四步:拉闸合闸,让高可用大盘并网通车。在 3 台 Master 上同时执行以下命令,唤醒高可用负载均衡。

1
2
3
4
# 启动 HAProxy 与 Keepalived 并锁死开机自启
systemctl daemon-reload
systemctl restart haproxy keepalived
systemctl enable --now haproxy keepalived

第五步:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 在 centos10-01 上
# 你应该能看到网卡上除了原本的 192.168.1.9/24,还凭空多出了一个 inet 192.168.1.200/24
# 而在 02/03 上敲这个命令则没有,说明 VIP 完美挂载。
$ ip addr show enp0s3
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 08:00:27:b8:53:25 brd ff:ff:ff:ff:ff:ff
altname enx080027b85325
inet 192.168.1.9/24 brd 192.168.1.255 scope global noprefixroute enp0s3
valid_lft forever preferred_lft forever
inet 192.168.1.200/24 scope global secondary enp0s3
valid_lft forever preferred_lft forever
inet6 2409:8a00:2453:0:a00:27ff:feb8:5325/64 scope global dynamic noprefixroute
valid_lft 258944sec preferred_lft 172544sec
inet6 fe80::a00:27ff:feb8:5325/64 scope link noprefixroute
valid_lft forever preferred_lft forever

你在宿主机或任何一台 Node 上输入 ping 192.168.1.200,只要能看到稳定的 ICMP 报文回显,说明高可用物理管道已经全部铺设完毕!

打开你 Mac/Windows 宿主机的浏览器,直接访问:http://192.168.1.11:1080/haproxy_stats, 输入用户名 admin 和密码 123456。你会看到一个非常专业的 HAProxy Stats 报表。由于此时你还没执行 kubeadm init,底层的 k8s-apiserver-out 这一栏里的 3 台 Master 会全部显示为红色(DOWN)。别慌!这正说明 HAProxy 的主动健康检查完全生效了。等稍后执行 kubeadm init 点火之后,APIServer 进程在 6443 端口一响,大盘对应的节点就会瞬间秒变绿色(UP)!控制平面高可用钢筋底座,到现在算彻底铸造完成了!下一步,直接去 01 上点火初始化集群!


kubeadm 点火安装 K8s集群

在 kubeadm 正式全盘点火之前,先进性如下准备,在 5 台机器节点执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 强行清空原有的乱麻 DNS 配置,直接注入阿里云与腾讯云的公共 DNS
cat <<EOF | tee /etc/resolv.conf
nameserver 223.5.5.5
nameserver 119.29.29.29
EOF
# 测试
ping -c 3 registry.aliyuncs.com

# 2. 消灭 Kubelet 警告,将 kubelet 服务锁死为开机自启
systemctl enable kubelet

# 3. 提前把这 7 个核心控制平面镜像全盘合闸拉到本地
kubeadm config images pull --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.28.2

核心舞台搭好,开始通过 kubeadm 正式全盘点火。

在 centos10-01(元老 Master)上执行集群初始化。在 01 控制台输入以下命令。注意我们通过 –control-plane-endpoint 把灵魂直接锁在刚搭好的高可用 VIP 上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 清理战场
$ kubeadm reset -f

# --kubernetes-version v1.28.2,这个参数极有可能需要微调。
# K8s 官方有一个硬性铁律,kubeadm init 指定的版本,必须和你用 DNF 真实下载安装的 kubeadm、kubelet 软件版本严格一致(至少大版本号和次版本号必须咬合)。
# 使用 kubeadm version 进行查看,如果 Major:"1", Minor:"28", GitVersion:"v1.28.2" 则不需要改!
## Flannel 网络插件只认 10.244.0.0/16,Calico 网络插件(推荐)默认支持 192.168.0.0/16,但同时也完美兼容你指定的 10.244.0.0/16。
## 为了保险和通用性,保持 10.244.0.0/16 别动,这是最稳妥的黄金选择。
$ kubeadm init \
--control-plane-endpoint "192.168.1.200:16443" \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.28.2 \
--pod-network-cidr=10.244.0.0/16 \
--upload-certs

大概两分钟后,终端会吐出两大段极其耀眼的 join 命令(这是通往新世界的两把钥匙,赶紧拿小本本记下来):

钥匙 A(给另外两台 Master 联姻用的,在 centos10-02 和 centos10-03 上执行)

1
2
3
4
5
6
7
# 它们专门用来告诉 K8s 引擎:“这两台新来的机器不是去干体力活的,它们要进入核心中枢,担任 Master 节点。
# 并网动作:当你在 02 和 03 上砸入这段命令后,它们会顺着高可用 VIP(192.168.1.200)找到 01,
# 自动把 01 上生成的集群根证书(TLS 密钥)安全同步过来,并当场克隆出另外两套一套一样的 kube-apiserver
# 和 Etcd 强一致性数据库,完成 3 Master 大脑的三位一体高可用咬合。
kubeadm join 192.168.1.200:16443 --token xxxxx \
--discovery-token-ca-cert-hash sha256:xxxxx \
--control-plane --upload-certs

钥匙 B(给两台 Worker Node 搬砖苦力用的,在 centos10-04 和 centos10-05 上执行):

1
2
3
4
# 当你在 04 和 05 上敲下这行命令时,它们不会去触碰任何控制平面和数据库证书。它们只会在本地拉起 kubelet 
# 和 kube-proxy,然后向高可用网关高喊:“大脑,四肢已经并网,请开始向我们分发 Pod 容器业务吧!“
kubeadm join 192.168.1.200:16443 --token xxxxx \
--discovery-token-ca-cert-hash sha256:xxxxx

配置 centos10-01 的本地管理员权限

1
2
3
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config


部署 Calico 网络插件

Kubeadm 负责把集群的 “大脑”(控制平面)和 “四肢”(节点)物理连接起来,Calico 负责把集群的 “血液”(网络流量)彻底打通。如果把 Kubernetes 集群比作一个刚建好的高科技产业园区,那么物理机(你的 5 台 CentOS 10 虚拟机)就是园区的土地和一栋栋办公楼,而新创建的 Pod(容器应用)就是入驻到楼里面的各个公司和员工。如果没有安装 Calico,这个园区就会处于一种“瘫痪”状态:每栋楼内部都是孤立的,楼与楼之间没有通路,甚至楼里的员工连隔壁房间都去不了。Calico 在这里充当的角色,就是这个园区的 “交通局 + 电话局 + 安保大队”。具体来说,它的核心作用只有三个:

  • 给每个 Pod 发放全网唯一的 “身份证号”(IP 分配),在 K8s 园区里,每天都会有大量的 Pod 被创建、销毁和漂移。只要有一个新 Pod 诞生,Calico 就会立刻从你初始化时指定的 –pod-network-cidr=10.244.0.0/16 大网段里,切出一个绝对不重复的 IP 地址(比如 10.244.1.5)精准地贴在它的头上。它就像园区的人口普查办,确保集群里的每一个容器都有一个独一无二的“电话号码”,全网可查。
  • 修桥铺路,让 Pod 之间可以 “全网无障碍通车”(网络路由)。这是它最核心的硬核黑科技。我们的 5 台虚拟机处于 192.168.1.x 物理网段,而 Pod 处于 10.244.x.x 虚拟网段。如果没有人引路,centos10-04 上的 Pod 根本没办法给 centos10-05 上的 Pod 发送任何数据。Calico 在每台 Linux 机器的内核里偷偷塞了一个叫作 BGP 路由协议的超级导航员(就是你刚才看到的 calico-node 容器)。当 04 上的 Pod 想要发消息给 05 上的 Pod 时,Calico 会在底层把数据包包装好,通过这里的物理网卡 enp0s3 走最直、最快的通路送过去,不需要经过任何复杂的中间层转译(就像走纯天然的高速公路)。这就像是 Calico 在 5 栋办公楼之间修了一条地下秘密高速公路,04 楼里的小张想找 05 楼里的小李,直接顺着高速公路就能直接串门,甚至感觉不到自己跨越了不同的物理机器。
  • 设置 “办公室门禁系统”(网络策略 Network Policy)。随着你的业务越来越多,你可能不希望所有的公司之间都能互相串门。比如:你部署了一个数据库 Pod(存放机密数据),和一个前端网页 Pod(面向全网用户)。它允许你写几行简单的规则,强制规定:只有带了 app=backend 标签的后端 Pod 才能访问数据库,其他任何人(比如网页前端)如果敢嗅探数据库,一律在内核层直接乱棍打死(丢弃数据包)。Calico 就是园区的专业安保大队。不仅修了路,还能在每条路上设立关卡和门禁。谁能访问谁,谁必须离谁远点,它执行得滴水不漏。

回到 centos10-01 的主控制台上,此时输入 kubectl get nodes,你会发现 5 台机器都排好队了,但状态全是 NotReady。这是因为集群内部的虚拟网络隧道还没有打通。我们一键引入大厂工业级、完美兼容 Cgroups v2 的 Calico 网络矩阵:

1
2
3
4
5
6
7
# 1. 顺着网络抓取 Calico 的官方纯净部署模板
wget https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml
# 把官方镜像全部重定向到国内加速通道
sed -i 's|docker.io/calico/|docker.m.daocloud.io/calico/|g' calico.yaml

# 2. 轰鸣部署进集群
kubectl apply -f calico.yaml

部署完成后,Calico 的 Sidecar 容器会自动分发到 5 台机器上开始疯狂构建网络栈。在 centos10-01 上静静等待两分钟,输入你的终极肉测验证指令,如果预期展示:

1
2
3
4
5
6
7
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
centos10-01 Ready control-plane 33m v1.28.2 192.168.1.9 <none> CentOS Stream 10 (Coughlan) 6.12.0-233.el10.x86_64 containerd://2.2.4
centos10-02 Ready control-plane 21m v1.28.2 192.168.1.10 <none> CentOS Stream 10 (Coughlan) 6.12.0-233.el10.x86_64 containerd://2.2.4
centos10-03 Ready control-plane 20m v1.28.2 192.168.1.11 <none> CentOS Stream 10 (Coughlan) 6.12.0-233.el10.x86_64 containerd://2.2.4
centos10-04 Ready <none> 20m v1.28.2 192.168.1.12 <none> CentOS Stream 10 (Coughlan) 6.12.0-233.el10.x86_64 containerd://2.2.4
centos10-05 Ready <none> 19m v1.28.2 192.168.1.13 <none> CentOS Stream 10 (Coughlan) 6.12.0-233.el10.x86_64 containerd://2.2.4

当看到 5 个节点齐刷刷地吐出 Ready 绿灯时,说明你亲手打造的、完全对标生产级架构的 3 Master + 2 Node 高可用高自愈 K8s 集群已经全线合闸、并网通车成功了!

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
# 查看核心控制组件是否全线健康
$ kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-769669454f-4ldvj 1/1 Running 0 12m
calico-node-dhbpp 1/1 Running 3 (4m59s ago) 12m
calico-node-l6m7t 1/1 Running 1 (2m17s ago) 12m
calico-node-mxx59 1/1 Running 0 12m
calico-node-v7zls 1/1 Running 1 (4m26s ago) 12m
calico-node-whltt 1/1 Running 5 (4m41s ago) 12m
coredns-66f779496c-cfkrq 1/1 Running 0 36m
coredns-66f779496c-mr6pt 1/1 Running 0 36m
etcd-centos10-01 1/1 Running 0 36m
etcd-centos10-02 1/1 Running 0 24m
etcd-centos10-03 1/1 Running 0 23m
kube-apiserver-centos10-01 1/1 Running 1 (5m31s ago) 36m
kube-apiserver-centos10-02 1/1 Running 4 (3m12s ago) 24m
kube-apiserver-centos10-03 1/1 Running 1 (6m28s ago) 23m
kube-controller-manager-centos10-01 1/1 Running 3 (7m29s ago) 36m
kube-controller-manager-centos10-02 1/1 Running 2 (4m54s ago) 24m
kube-controller-manager-centos10-03 1/1 Running 1 (9m15s ago) 23m
kube-proxy-5hvtb 1/1 Running 0 36m
kube-proxy-nzrtg 1/1 Running 0 23m
kube-proxy-q776s 1/1 Running 0 24m
kube-proxy-v4nnw 1/1 Running 0 22m
kube-proxy-zl995 1/1 Running 0 22m
kube-scheduler-centos10-01 1/1 Running 3 (7m34s ago) 36m
kube-scheduler-centos10-02 1/1 Running 2 (4m11s ago) 24m
kube-scheduler-centos10-03 1/1 Running 2 23m

# 查看 3 个 Master 数据库的高可用
$ kubectl exec -it etcd-centos10-01 -n kube-system -- etcdctl --cacert=/etc/kubernetes/pki/etcd/ca.crt --cert=/etc/kubernetes/pki/etcd/server.crt --key=/etc/kubernetes/pki/etcd/server.key --endpoints=https://192.168.1.9:2379,https://192.168.1.10:2379,https://192.168.1.11:2379 endpoint health
https://192.168.1.10:2379 is healthy: successfully committed proposal: took = 54.807979ms
https://192.168.1.9:2379 is healthy: successfully committed proposal: took = 67.635102ms
https://192.168.1.11:2379 is healthy: successfully committed proposal: took = 56.553232ms

打开你 Mac/Windows 宿主机的浏览器,再次访问:http://192.168.1.11:1080/haproxy_stats, 你会看到大盘对应的节点就会瞬间秒变绿色(UP)!


安装 token 过期问题

以上 master 和 node 加入,都需要执行 kubeadm join 并携带 token,但这两条命令里的关键凭证都有极其严格的“保鲜期”。在 Kubernetes 的安全白皮书中,为了防止集群并网接口被恶意扫描和非法潜入,这些加入集群的令牌和证书都被设计成了“阅后即焚” 或 “定时报废”的机制。如果集群搭建好之后,过了一个星期或者几个月,突然想要扩容一台新的 Master(04)或者新的 Worker(06),直接复制当时保存的这两条命令绝对会直接报错、弹回入场券。我们来看看它们的生命周期以及过期后的处理方案。


场景 A:扩容普通的 Worker 节点(对应第二条命令过期)

如果只是要加一台 centos10-06 纯牛马搬砖节点,直接在 centos10-01 上执行:

1
2
3
# 终端会直接为你打印出一条全新的、立即可用的、有效期重新刷新为 24 小时的完整 kubeadm join 命令。你直接复制它,去新节点上一贴,瞬间并网!
$ kubeadm token create --print-join-command
kubeadm join 192.168.1.200:16443 --token xxx.xxxxxx --discovery-token-ca-cert-hash sha256:xxxxxx


场景 B:扩容高可用的 Master 节点(对应第一条命令过期)

如果要加一台新的 Master 节点,因为涉及证书解密 Key 的阅后即焚,我们需要分两步走:

第一步:重新把证书加密上传,抓出全新的 2 小时限制 Key。在 centos10-01 上跑这行命令,让 K8s 重新把证书打包加密:

1
2
3
$ kubeadm init phase upload-certs --upload-certs
[upload-certs] Using certificate key:
f4748e0f3e2be90b76498e...

此时控制台会吐出类似下面这行长长的 64 位十六进制字符串(这就是你的新密钥),物理复制并记下这行密钥!同样在 centos10-01 上,利用刚才学到的命令重新生成基础加入命令:

1
2
$ kubeadm token create --print-join-command
kubeadm join 192.168.1.200:16443 --token aaa --discovery-token-ca-cert-hash sha256:bbb...

第二步:把第 2 步拿到的基础命令,屁股后面死死焊上 –control-plane –certificate-key [第1步抓出的新密钥],拼装出来的全新高可用 Master 续命命令长这样:

1
2
3
4
$ kubeadm join 192.168.1.200:16443 --token aaa \
--discovery-token-ca-cert-hash sha256:bbb... \
--control-plane \
--certificate-key f4748e0f3e2be90b76498e... # 把新密钥贴在这里

把这串合体命令粘贴到你新准备的 Master 机器上,它就会再次顺着我们的 HAProxy + Keepalived 高可用 VIP 顺利归队,完成大脑的再度扩容!


安装 Metrics-server

其实,到现在我们的集群已经搭好。安装 Metrics-Server 和 Dashboard 就是给这辆车装上 “数显仪表盘” 和 “中控大屏幕”。在刚建好的原生 K8s 集群里,你现在去敲 kubectl top node 或者 kubectl top pod,系统会直接无情抛出错误。因为默认情况下,K8s 根本没有收集各节点 CPU 和内存消耗量的能力,它是个 “瞎子”。把这两个组件拉闸并网,能直接给你的集群带来质的飞跃。

  • Metrics-Server(集群的感知神经):它是 K8s 核心监控数据的官方供给站。如果没有安装,我们的集群失去了动态自愈和弹性伸缩的能力。装上后开启 kubectl top 命令,让你随时能肉眼定损哪台机器、哪个容器在疯狂吃内存。它还可以支撑 HPA(Pod 自动扩容),这是 K8s 最灵魂的功能。比如当我们的应用由于突发流量导致 CPU 飙到 80% 时,Metrics-Server 会立刻感知到并通知系统:“快,把当前的 2 个 Pod 暴力自动扩容到 10 个!”流量过去后,再自动缩容。
  • Dashboard(可视化的中控大屏):它是官方出品的 Web 网页版集群管理面板。装上他就可以告别枯燥的命令行,不需要天天苦哈哈地敲 kubectl get…,直接在浏览器里就能看全集群 5 台机器的 CPU/内存波形图。点一下鼠标就能直接看容器日志、甚至一键 “肉身钻进” 容器内部弹出的 Terminal 终端执行命令。

既然要装,我们就直接上国内无痛加速版(解决国外镜像拉取卡死问题)。

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
# 1.1. 利用阿里云官方源直接拉取
crictl pull registry.aliyuncs.com/google_containers/metrics-server:v0.7.2


# 2.1. 物理清空刚才折腾的旧文件
rm -f metrics-server.yaml

# 2.2. 重新叼回官方 v0.7.2 干净模板
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.2/components.yaml -O metrics-server.yaml

# 2.3. 编辑覆盖使用下面的 metrics-server.yaml 的内容
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: metrics-server
name: metrics-server
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: metrics-server
strategy:
type: Recreate
template:
metadata:
labels:
k8s-app: metrics-server
spec:
containers:
- args:
- --cert-dir=/tmp
- --secure-port=10443
- --kubelet-preferred-address-types=Hostname,InternalIP,ExternalIP
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls
image: registry.aliyuncs.com/google_containers/metrics-server:v0.7.1 # 确保是你之前拉成功的镜像版本
imagePullPolicy: IfNotPresent
name: metrics-server
ports:
- containerPort: 10443
name: https
protocol: TCP
# 核心改动:把原先致命的 20 秒死锁线,放宽到足足 2 分钟!不给 K8s 任何误杀的机会
livenessProbe:
failureThreshold: 10
httpGet:
path: /livez
port: https
scheme: HTTPS
initialDelaySeconds: 90
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
failureThreshold: 10
httpGet:
path: /readyz
port: https
scheme: HTTPS
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
volumeMounts:
- mountPath: /tmp
name: tmp-dir
nodeSelector:
kubernetes.io/os: linux
priorityClassName: system-cluster-critical
serviceAccountName: metrics-server
volumes:
- emptyDir: {}
name: tmp-dir


# 3.1. 物理毁灭卡在后台的旧版本 Pod 遗迹
kubectl delete -f metrics-server.yaml --ignore-not-found=true

# 3.2. 重新合闸注入大盘
kubectl apply -f metrics-server.yaml

验证和修复1:

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
$ kubectl top nodes
error: Metrics API not available

$ kubectl logs -n kube-system deployment/metrics-server --tail=50

$ kubectl edit configmap coredns -n kube-system
# 在弹出的 Vim 界面里,向下滚动找到 Corefile: 下面的 ready 或者 kubernetes cluster.local 附近。
# 我们在中间人肉打字塞入一个全新的 hosts 策略块。修改后的配置应该完美长这样:
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
# 从这里开始,死死焊入下面这几行物理 hosts 映射!
hosts {
192.168.1.9 centos10-01
192.168.1.10 centos10-02
192.168.1.11 centos10-03
192.168.1.12 centos10-04
192.168.1.13 centos10-05
fallthrough
}
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
kind: ConfigMap

# 重启 DNS 电话局让它当场生效
$ kubectl rollout restart deployment coredns -n kube-system

$ kubectl delete -f metrics-server.yaml --ignore-not-found=true
$ kubectl apply -f metrics-server.yaml
$ kubectl top nodes

验证和修复2:

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
$ kubectl edit deployment metrics-server -n kube-system
# 在弹出的 Vim 界面中,向下滚动找到 livenessProbe 和 readinessProbe(大概在文件后半段,containers 的下面),把这两个探针的参数加大,改成下面这个非常有弹性的黄金比例:
livenessProbe:
failureThreshold: 6 # 从 3 改成 6,允许错报 6 次
httpGet:
path: /livez
port: https
scheme: HTTPS
initialDelaySeconds: 60 # 从 0 或 10 改成 60!启动后给它足足 1 分钟缓冲
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5 # 超时放宽到 5 秒
readinessProbe:
failureThreshold: 6 # 同样允许错报 6 次
httpGet:
path: /readyz
port: https
scheme: HTTPS
initialDelaySeconds: 30 # 缓冲改到 30 秒
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5 # 超时放宽到 5 秒


$ kubectl get pods -n kube-system -l k8s-app=metrics-server -w
$ kubectl top nodes

验证和修复3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ kubectl edit svc metrics-server -n kube-system
# 向下滚动找到 ports: 数组,把它人肉改成下面这样(重点是 name: https 和 targetPort: 10443):
ports:
- name: https # 必须叫这个名字,多一个少一个字母都不行!
port: 443
protocol: TCP
targetPort: 10443 # 对应你 Deployment 里配的 --secure-port=10443


# 1. 检查 Endpoint 是不是终于有物理 IP 挂上去了
$ kubectl get endpoints metrics-server -n kube-system
# 2. 检查前台账本是不是转绿了
$ kubectl get apiservice v1beta1.metrics.k8s.io

$ kubectl delete -f metrics-server.yaml --ignore-not-found=true
$ kubectl apply -f metrics-server.yaml
$ kubectl top nodes

最终成功的样本大致是:

1
2
3
4
5
6
7
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
centos10-01 525m 26% 1440Mi 40%
centos10-02 473m 23% 1315Mi 37%
centos10-03 505m 25% 1476Mi 41%
centos10-04 136m 6% 727Mi 20%
centos10-05 156m 7% 718Mi 20%


安装 Dashboard

我们在安装的过程中,难免反复遇到 ErrImagePull,根源在于官方原版的 YAML 里面,镜像拉取策略默认是 Always,且镜像名是短路径。这导致 K8s 每次孵化 Pod 时,都会倔强地绕过我们导入的本地缓存,硬要去敲 Docker Hub 的大门,结果直接被网络大坝物理拦截,你懂得。为了彻底干净安装 dashborad,我们先在主节点 centos10-01 上依次砸入以下命令,把现有的所有组件、错位绑定的权限以及可能冲突的旧账本全部连根拔起:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 同时强制删除 命名空间、空间下的所有资源,以及曾经用过的全局权限绑定
kubectl delete ns kubernetes-dashboard --force --grace-period=0
kubectl delete ns kubernetes-dashboard-new --force --grace-period=0
kubectl delete all --all -n kubernetes-dashboard --force --grace-period=0
kubectl delete all --all -n kubernetes-dashboard-new --force --grace-period=0
kubectl delete clusterrolebinding admin-user admin-user-binding admin-user-perfect-binding dashboard-new-admin-fix dashboard-new-user-fix
kubectl delete clusterrolebinding admin-user-perfect-binding

# 或者
kubectl delete -f dashboard-final.yaml

# 彻底清理本地历史杂乱的 YAML 账本
rm -f recommended.yaml dashboard-perfect.yaml k8s-dashboard.yaml

# 检查确认列表中已经完全没有 kubernetes-dashboard 的影子。此时集群已恢复至最纯净的状态。
kubectl get ns

准备镜像文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 你可以找任意一台能正常访问公网、且安装了 Docker 的电脑。强行拉取官方正版镜像
docker pull kubernetesui/dashboard:v2.7.0
docker pull kubernetesui/metrics-scraper:v1.0.8

# 2. 将镜像就地物理打包成离线 .tar 档案
docker save -o dashboard.tar kubernetesui/dashboard:v2.7.0
docker save -o scraper.tar kubernetesui/metrics-scraper:v1.0.8

# 3. 运行完后,你的当前目录下就会凭空出生两个沉甸甸的物理文件:dashboard.tar 和 scraper.tar。
# 然后将他们复制到 work 节点
scp dashboard.tar root@centos10-04:/root
scp dashboard.tar root@centos10-05:/root
scp scraper.tar root@centos10-04:/root
scp scraper.tar root@centos10-05:/root

由于 K8s 会把 Pod 随机调度到不同机器,必须在所有可能运行大盘的 Worker 节点(如 centos10-04、centos10-05 等)全量灌入镜像。请在每台 Worker 节点的终端里执行:

1
2
3
4
5
6
# 确保将离线的 dashboard.tar 和 scraper.tar 上传到节点的 /root/ 目录下,然后强行灌入 Containerd:
ctr -n k8s.io images import /root/dashboard.tar
ctr -n k8s.io images import /root/scraper.tar

# 盘查对账,确保输出中带有 docker.io 长路径前缀,这代表本地缓存完全就位:
crictl --runtime-endpoint unix:///run/containerd/containerd.sock images | grep -E "dashboard|scraper"

在主节点 centos10-01 上,编辑:

1
vim dashboard-final.yaml

把下面这段我深度重构的 YAML 完整复制进去:注意主大盘和采集器的 image 全面锁定了我们本地 Containerd 里的 docker.io/kubernetesui/… 完整长路径,且 imagePullPolicy 死死焊死在 IfNotPresent 上,空间、离线镜像、特权 RoleBinding、物理 Service 也都进行处理。粘贴时使用 :set paste 和 :set nopaste,避免粘错。

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
apiVersion: v1
kind: Namespace
metadata:
name: kubernetes-dashboard-new

---

apiVersion: v1
kind: ServiceAccount
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard-new
namespace: kubernetes-dashboard-new

---

# 核心秘密武器 1:补齐大盘疯狂索要的看门狗 CSRF 物理资产
apiVersion: v1
kind: Secret
metadata:
name: kubernetes-dashboard-csrf
namespace: kubernetes-dashboard-new
type: Opaque

---

# 核心秘密武器 2:补齐官方默认的本地密钥抽屉
apiVersion: v1
kind: Secret
metadata:
name: kubernetes-dashboard-key-holder
namespace: kubernetes-dashboard-new
type: Opaque

---

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard-new
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: kubernetes-dashboard
template:
metadata:
labels:
k8s-app: kubernetes-dashboard
spec:
# 焊死正牌服务账号身份
serviceAccountName: kubernetes-dashboard-new
containers:
- args:
- --auto-generate-certificates
- --namespace=kubernetes-dashboard-new
- --metrics-provider=sidecar
# 焊死本地 Containerd 里的镜像长路径名
image: docker.io/kubernetesui/dashboard:v2.7.0
# 焊死拉取策略,有本地缓存绝不出墙
imagePullPolicy: IfNotPresent
name: kubernetes-dashboard
ports:
- containerPort: 8443
protocol: TCP
volumeMounts:
# 彻底剥离对外部证书 Secret 的挂载包袱,只留纯内存临时目录,完美触发内部原生自签
- mountPath: /tmp
name: tmp-volume
livenessProbe:
httpGet:
path: /
port: 8443
scheme: HTTPS
initialDelaySeconds: 30
timeoutSeconds: 30
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
nodeSelector:
kubernetes.io/os: linux
volumes:
- emptyDir: {}
name: tmp-volume

---

apiVersion: apps/v1
kind: Deployment
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard-new
spec:
replicas: 1
revisionHistoryLimit: 10
selector:
matchLabels:
k8s-app: dashboard-metrics-scraper
template:
metadata:
labels:
k8s-app: dashboard-metrics-scraper
spec:
containers:
- args: []
image: docker.io/kubernetesui/metrics-scraper:v1.0.8
imagePullPolicy: IfNotPresent
name: dashboard-metrics-scraper
ports:
- containerPort: 8000
protocol: TCP
livenessProbe:
httpGet:
path: /
port: 8000
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 30
volumeMounts:
- mountPath: /tmp
name: tmp-volume
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1001
nodeSelector:
kubernetes.io/os: linux
volumes:
- emptyDir: {}
name: tmp-volume

---

apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard-new
spec:
# 焊死外部物理映射端口,直接在 30443 大门放行
type: NodePort
ports:
- port: 443
targetPort: 8443
nodePort: 30443
selector:
k8s-app: kubernetes-dashboard

---

apiVersion: v1
kind: Service
metadata:
labels:
k8s-app: dashboard-metrics-scraper
name: dashboard-metrics-scraper
namespace: kubernetes-dashboard-new
spec:
ports:
- port: 8000
targetPort: 8000
selector:
k8s-app: dashboard-metrics-scraper

---

# 核心秘密武器 3:原生批发超级管理员账号,避开所有权限错位
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard-new

---

# 核心秘密武器 4:给大盘身份和管理员账号双重焊死最高集群特权(彻底解决 CSRF forbidden)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: dashboard-new-admin-perfect-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: kubernetes-dashboard-new
namespace: kubernetes-dashboard-new
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard-new

在主节点 centos10-01 上,一键合闸与并网通电:

1
2
3
4
5
# 1. 迎回新账本,全量满血合闸!
kubectl apply -f dashboard-final.yaml

# 2. 死死盯着屏幕,见证瞬间双星闪现飙绿:
kubectl get pods -n kubernetes-dashboard-new -w

当看到两个 Pod 稳稳并网亮起绿灯:

1
2
3
# 一键批发获取超级管理员 Token:
# 用鼠标右键完整复制屏幕上喷出来的那长串 eyJ... 加密令牌。
kubectl -n kubernetes-dashboard-new create token admin-user

打开你的物理机浏览器,直奔:https://192.168.1.9:30443 贴入你复制的 Token,点击登录。

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
$ kubectl get po --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-769669454f-4ldvj 1/1 Running 0 11h
kube-system calico-node-dhbpp 1/1 Running 3 (11h ago) 11h
kube-system calico-node-l6m7t 1/1 Running 1 (11h ago) 11h
kube-system calico-node-mxx59 1/1 Running 0 11h
kube-system calico-node-v7zls 1/1 Running 1 (11h ago) 11h
kube-system calico-node-whltt 1/1 Running 5 (11h ago) 11h
kube-system coredns-7bc9d85949-68msl 1/1 Running 0 10h
kube-system coredns-7bc9d85949-qt4hf 1/1 Running 0 10h
kube-system etcd-centos10-01 1/1 Running 0 12h
kube-system etcd-centos10-02 1/1 Running 0 12h
kube-system etcd-centos10-03 1/1 Running 0 12h
kube-system kube-apiserver-centos10-01 1/1 Running 1 (11h ago) 12h
kube-system kube-apiserver-centos10-02 1/1 Running 4 (11h ago) 12h
kube-system kube-apiserver-centos10-03 1/1 Running 1 (11h ago) 12h
kube-system kube-controller-manager-centos10-01 1/1 Running 3 (11h ago) 12h
kube-system kube-controller-manager-centos10-02 1/1 Running 2 (11h ago) 12h
kube-system kube-controller-manager-centos10-03 1/1 Running 1 (11h ago) 12h
kube-system kube-proxy-27ldc 1/1 Running 0 3h50m
kube-system kube-proxy-5j7fx 1/1 Running 0 3h50m
kube-system kube-proxy-6xq52 1/1 Running 0 3h50m
kube-system kube-proxy-tdjwz 1/1 Running 0 3h50m
kube-system kube-proxy-wxkkn 1/1 Running 0 3h50m
kube-system kube-scheduler-centos10-01 1/1 Running 3 (11h ago) 12h
kube-system kube-scheduler-centos10-02 1/1 Running 2 (11h ago) 12h
kube-system kube-scheduler-centos10-03 1/1 Running 2 12h
kube-system metrics-server-55cc4b87f8-vqdcq 1/1 Running 0 9h
kubernetes-dashboard-new dashboard-metrics-scraper-7df858c6c6-t5tsd 1/1 Running 0 30m
kubernetes-dashboard-new kubernetes-dashboard-574f995bc7-8p6sr 1/1 Running 0 30m

$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12h

$ kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 12h
metrics-server ClusterIP 10.103.78.113 <none> 443/TCP 10h


Dashboard的另一种选择

Kuboard - Kubernetes 多集群管理界面


彻底卸载 k8s 集群

既然要对 5 台 CentOS 钢铁巨兽上的 Kubernetes 进行全盘清洗,咱们就必须做到物理毁灭、斩草除根。如果卸载不干净,底层的虚拟网卡(Flannel/Calico 留下的 cni0、flannel.1)、防火墙规则(iptables/ipvs)以及 Containerd 里的残留容器会像幽灵一样潜伏。下次你再重新安装集群时,就会遭遇各种莫名其妙的 IP 冲突和端口占用。为了进行全面的卸载,请在集群每个节点上执行如下步骤。

第一步:让 K8s 脑死亡(驱逐与停止服务)

1
2
3
4
5
6
# 1. 让当前节点的 kubelet 彻底清空本地管理状态(如果报错可直接跳过)
kubeadm reset -f 2>/dev/null

# 2. 物理掐断 K8s 核心三大守护进程的电源
systemctl stop kubelet kube-apiserver kube-controller-manager kube-scheduler etcd 2>/dev/null
systemctl disable kubelet 2>/dev/null

第二步:强行卸载 K8s 物理软件(拔除根基)

1
2
3
4
5
# 1. 强行卸载工具包(kubeadm、kubectl、kubelet 以及底层网络插件)
yum remove -y kubelet kubeadm kubectl kubernetes-cni cri-tools --allow-erasure

# 2. 自动清理所有不再需要的依赖包
yum autoremove -y

第三步:物理粉碎历史残渣(毁灭所有目录与密钥)。这一步是防重装报错的核心。我们要手工毁灭 K8s 曾经驻留过的所有物理文件夹、证书、配置、以及组件运行时。

1
2
3
4
5
6
7
8
9
10
11
# 1. 粉碎 K8s 配置、证书、临时数据及日志目录
rm -rf /etc/kubernetes/
rm -rf /var/lib/kubelet/
rm -rf /var/lib/etcd/
rm -rf /var/lib/cni/
rm -rf /etc/cni/
rm -rf /var/log/pods/
rm -rf /var/log/containers/

# 2. 粉碎管理员本地的隐藏配置(防止 kubectl 残留干扰)
rm -rf ~/.kube/

第四步:核平网络大坝(清理虚拟网卡与路由)。K8s 的 CNI 网络插件(如 Flannel 或 Calico)会在你的 CentOS 宿主机上建立大量的虚拟网卡和路由规则,必须用物理手段把它们统统拔掉。

1
2
3
4
5
6
7
8
# 1. 强行下线并物理删除 CNI 遗留的虚拟网卡
ip link delete cni0 2>/dev/null
ip link delete flannel.1 2>/dev/null
ip link delete cali0 2>/dev/null
ip link delete tunl0 2>/dev/null

# 2. 刷新物理网卡状态
systemctl restart network

第五步:彻底冲刷 iptables 防火墙黑账。K8s 之前为了做 Service 转发,在内核里塞了成百上千条 iptables 规则。执行以下命令,让防火墙规则一秒钟回归出厂设置:

1
2
3
4
5
# 强行清空所有 iptables 链、规则和 NAT 转发
iptables -F && iptables -X && iptables -F -t nat && iptables -X -t nat
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT

第六步:清理 Containerd 里的镜像。

1
2
3
systemctl stop containerd
yum remove -y containerd.io
rm -rf /var/lib/containerd/ /etc/containerd/

当上面所有命令在你的 5 台机器上全量轰击完毕后,建议将 5 台服务器全部原地重启一次(reboot)。重启后,这几台 CentOS 将恢复到最原始、最纯净、没有一丝一毫 K8s 痕迹的钢铁底座状态!