标准生产架构:3 Master + 3 Worker + 2 LB + Harbor
基于 VMware Workstation 克隆机
Kubernetes v1.36 · containerd · Calico · Keepalived + HAProxy · Harbor
Tommypeng
2026-09-12
目 录
第一章 架构设计与规划
1.1 总体架构
1.2 节点清单与 IP 规划
1.3 版本选型
1.4 资源分配建议
1.5 端口清单(运维放行参考)
第二章 基于标准机克隆出 9 台虚拟机
2.0 执行环境说明(重要)
2.1 标准机基础配置
2.2 VMware Workstation 克隆步骤
2.3 逐台设置主机名
2.4 逐台修改静态 IP
第三章 部署负载均衡层(Keepalived + HAProxy)
3.1 安装 Keepalived 与 HAProxy
3.2 LB 节点内核参数(生产增强)
3.3 配置 HAProxy(lb01 与 lb02 相同)
3.4 配置 Keepalived(lb01 主、lb02 备)
3.5 验证负载均衡层
第四章 部署容器运行时与 Kubernetes 组件
4.1 加载内核模块(6 节点)
4.2 配置内核参数(6 节点)
4.3 关闭 swap(Rocky 9 默认系统无 swap,如有则关闭)
4.4 安装 containerd(6 节点)
4.5 配置 K8s 软件源(6 节点)
4.6 安装 kubeadm / kubelet / kubectl(6 节点)
4.7 验证基础环境
第五章 初始化高可用控制面(Master)
5.1 生成并修改初始化配置(k8s-m1 执行)
5.2 校验配置语法(强烈建议)
5.3 预拉取镜像(可选,加速初始化)
5.4 初始化第一个控制面节点(k8s-m1)
5.5 配置 kubectl(k8s-m1)
5.6 加入其余 Master(k8s-m2 / k8s-m3)
5.7 有效期提醒与补救
第六章 部署 Calico 网络插件
6.1 获取并修改 Calico 清单
6.2 应用 Calico
6.3 确认节点就绪
第七章 加入 Worker 节点
7.1 重新生成 token(若已过期)
7.2 验证集群节点
第八章 验证高可用与故障演练
8.1 通过 VIP 访问 API Server
8.2 测试 API Server 负载均衡
8.3 演练:Master 故障转移
8.4 演练:LB 故障转移
8.5 部署测试应用
第九章 集群部署后巡检清单
9.1 集群整体状态
9.2 etcd 健康检查(生产关键)
9.3 etcd 定期备份(生产必备)
9.4 控制面组件状态
9.5 网络插件与 CoreDNS
9.6 资源与节点健康
第十章 部署 Harbor 私有镜像仓库
10.1 架构澄清:Harbor 与集群认证的关系
10.2 安装 Docker(harbor 节点)
10.3 准备自签 HTTPS 证书
10.4 下载并配置 Harbor
10.5 执行安装
10.6 时间同步校验(证书依赖)
10.7 集群节点信任 Harbor CA(6 台节点全部配置)
10.8 创建 regcred 拉取凭证
10.9 验证从 Harbor 拉取镜像
结语
第一章 架构设计与规划
1.1 总体架构
本方案采用标准生产高可用架构:3 台控制面节点(Master)内置 etcd 组成三副本,前端由 2 台 Keepalived + HAProxy 负载均衡节点提供虚拟 IP(VIP),3 台工作节点(Worker)承载业务 Pod。任何一台 Master 或 LB 宕机均不影响集群可用性。
- 3 台 Master:部署 API Server、Scheduler、Controller Manager 及内置 etcd,形成 3 副本多数选举,可容忍 1 台故障
- 2 台 LB:Keepalived 实现 VIP 漂移,HAProxy 将 6443 端口负载均衡到 3 个 Master
- 3 台 Worker:运行业务工作负载
- 1 台 Harbor:私有镜像仓库(独立节点),提供 HTTPS 与镜像拉取认证
- VIP:外部统一通过虚拟 IP 访问 Kubernetes API Server
1.2 节点清单与 IP 规划
沿用现有 10.10.10.0/24 网段。为避开已有主机(内网源 .10、MGR .41-.43、旧 K8s .80-.84),规划如下:
|
主机名 |
角色 |
IP 地址 |
部署组件 |
|
lb01 |
负载均衡 |
10.10.10.50 |
Keepalived + HAProxy |
|
lb02 |
负载均衡 |
10.10.10.51 |
Keepalived + HAProxy |
|
k8s-m1 |
Master1 |
10.10.10.52 |
API Server / Scheduler / Controller Manager / etcd |
|
k8s-m2 |
Master2 |
10.10.10.53 |
同上 |
|
k8s-m3 |
Master3 |
10.10.10.54 |
同上 |
|
k8s-w1 |
Worker1 |
10.10.10.55 |
kubelet / kube-proxy / 业务 Pod |
|
k8s-w2 |
Worker2 |
10.10.10.56 |
同上 |
|
k8s-w3 |
Worker3 |
10.10.10.57 |
同上 |
|
harbor |
镜像仓库 |
10.10.10.58 |
Harbor 私有仓库(HTTPS) |
虚拟 IP(VIP):10.10.10.60(由 lb01/lb02 上的 Keepalived 管理,正常挂载在 lb01,故障漂移到 lb02)。
1.3 版本选型
Rocky Linux 9.x 使用 systemd + NetworkManager,内核 cgroup v2,与 CentOS 7 差异较大。推荐版本如下:
|
软件 |
版本 |
说明 |
|
操作系统 |
Rocky Linux 9.5 |
Minimal 安装即可 |
|
Kubernetes |
v1.36.x |
稳定版,支持 Rocky 9 |
|
容器运行时 |
containerd 2.x(Rocky 9 自带) |
配置结构不同于 1.x,见 4.4 |
|
网络插件 |
Calico v3.32.2 |
Pod 网段 10.244.0.0/16 |
|
负载均衡 |
Keepalived + HAProxy |
系统源自带 |
网络段规划:Pod 网段 10.244.0.0/16,Service 网段 10.96.0.0/12(均为 Calico/Flannel 默认值,可保持默认)。
1.4 资源分配建议
由于 Master 内置 etcd,建议 Master 节点内存不低于 4GB、CPU 不低于 2 核;Worker 节点按业务负载分配,建议至少 2 核 4GB。VMware Workstation 单台宿主建议内存 32GB 以上。
1.5 端口清单(运维放行参考)
若启用了防火墙(Rocky 9 默认 firewalld 已关闭,此处供需要开防火墙的场景参考),需放行以下端口:
|
端口 |
协议 |
用途 |
节点 |
|
6443 |
TCP |
Kubernetes API Server |
Master + LB |
|
2379/2380 |
TCP |
etcd 客户端/peer |
Master |
|
10250 |
TCP |
kubelet API(健康检查) |
全部节点 |
|
10259 |
TCP |
kube-scheduler |
Master |
|
10257 |
TCP |
kube-controller-manager |
Master |
|
10256 |
TCP |
kube-proxy |
全部节点 |
|
30000-32767 |
TCP |
NodePort 服务端口段 |
全部节点 |
|
443 |
TCP |
Harbor HTTPS |
Harbor |
|
5000 |
TCP |
Harbor HTTP(可选) |
Harbor |
注:本方案已关闭 firewalld;若需开启防火墙,请按上表放行并相应调整。
第二章 基于标准机克隆出 9 台虚拟机
2.0 执行环境说明(重要)
本手册大量命令涉及系统配置与包管理,需区分执行身份:
- 带 sudo 的命令:以普通用户执行,sudo 需要该用户已加入 wheel 组(Rocky 默认首建用户即 wheel 成员)
- 不带 sudo 的命令:大多可在普通用户下执行(如 kubectl、docker、openssl 生成等),但写 /etc 下文件仍需 sudo
建议全程使用一个普通用户(如登录用户)配合 sudo,避免直接以 root 操作带来的权限习惯不一致;涉及 systemctl 服务管理、写 /etc 均用 sudo。后续命令统一按此约定,不再逐一说明。
在 VMware Workstation 中,先准备一台"标准机"(Rocky 9.5 最小化安装,配置好基础环境),再用它克隆出 9 台节点。推荐使用"链接克隆"以节省磁盘,或"完整克隆"保证独立。
2.1 标准机基础配置
标准机需完成:最小化安装、配置静态 IP 模板、开启 SSH、配置 hosts、安装基础工具。以下命令在标准机上执行,随后会被克隆继承。
(1)配置静态网络(Rocky 9 用 nmcli / NetworkManager):
# 标准机先取任意占位 IP,克隆后再逐台改
sudo nmcli con mod ens160 ipv4.addresses 10.10.10.99/24
sudo nmcli con mod ens160 ipv4.gateway 10.10.10.2
sudo nmcli con mod ens160 ipv4.dns 10.10.10.2,8.8.8.8
sudo nmcli con mod ens160 ipv4.method manual
sudo nmcli con up ens160
(2)关闭防火墙、SELinux(生产如需防火墙可后续按端口放行):
sudo systemctl disable –now firewalld
sudo sed -i "s/^SELINUX=.*/SELINUX=disabled/" /etc/selinux/config
sudo setenforce 0
(3)安装基础工具与时间同步:
sudo dnf install -y vim net-tools wget curl bash-completion chrony ipvsadm ipset
sudo systemctl enable –now chronyd
(4)配置统一 hosts(9 台最终一致):
# 追加到 /etc/hosts
sudo bash -c 'cat >> /etc/hosts <<EOF
10.10.10.50 lb01
10.10.10.51 lb02
10.10.10.52 k8s-m1
10.10.10.53 k8s-m2
10.10.10.54 k8s-m3
10.10.10.55 k8s-w1
10.10.10.56 k8s-w2
10.10.10.57 k8s-w3
10.10.10.58 harbor
EOF'
2.2 VMware Workstation 克隆步骤
(1)标准机需先关机,右键虚拟机 → 管理 → 克隆。
(2)克隆类型选"创建链接克隆"(省磁盘,但源机不能删);生产更推荐"创建完整克隆"。
(3)逐台克隆 9 次,分别命名 lb01、lb02、k8s-m1、k8s-m2、k8s-m3、k8s-w1、k8s-w2、k8s-w3、harbor。
(4)克隆完成后逐台启动,依次修改主机名与 IP。
2.3 逐台设置主机名
# lb01 / lb02 / k8s-m1 / k8s-m2 / k8s-m3 / k8s-w1 / k8s-w2 / k8s-w3 / harbor 分别执行
sudo hostnamectl set-hostname lb01 # 其余节点换成各自主机名
sudo bash
2.4 逐台修改静态 IP
# 以 k8s-m1 为例(改 ipv4.addresses 为对应 IP)
sudo nmcli con mod ens160 ipv4.addresses 10.10.10.52/24
sudo nmcli con mod ens160 ipv4.gateway 10.10.10.2
sudo nmcli con mod ens160 ipv4.dns 10.10.10.2,8.8.8.8
sudo nmcli con up ens160
hostnamectl set-hostname k8s-m1
bash
每台克隆机重复上述操作,直至 9 台主机名、IP、hosts 全部就位。用 ping 验证各节点互通。
最后确认各节点是 Rocky 9、内核与时间正常:
cat /etc/rocky-release
uname -r
date
第三章 部署负载均衡层(Keepalived + HAProxy)
负载均衡层跑在 lb01、lb02 两台节点上,作用有二:一是用 HAProxy 将客户端对 6443 端口的访问分发到 3 个 Master 的 API Server;二是用 Keepalived 维护虚拟 IP 10.10.10.60,实现主备漂移。
3.1 安装 Keepalived 与 HAProxy
sudo dnf install -y keepalived haproxy
3.2 LB 节点内核参数(生产增强)
建议在 lb01、lb02 上同样开启 IP 转发,避免特殊网络场景下 VIP 转发异常:
sudo tee /etc/sysctl.d/99-lb.conf <<EOF
net.ipv4.ip_forward = 1
EOF
sudo sysctl –system
sysctl net.ipv4.ip_forward # 期望输出 1
3.3 配置 HAProxy(lb01 与 lb02 相同)
HAProxy 需同时配置 global 日志与后端负载均衡。为使故障排查能看到后端节点健康检查日志,启用 log-health-checks 并交给 rsyslog 收集。完整替换 /etc/haproxy/haproxy.cfg:
sudo tee /etc/haproxy/haproxy.cfg <<'EOF'
global
log /dev/log local0 info
maxconn 4096
user haproxy
group haproxy
daemon
defaults
log global
mode tcp
option tcplog
option log-health-checks
timeout connect 5s
timeout client 50s
timeout server 50s
listen kubernetes-apiserver
bind 0.0.0.0:6443
balance roundrobin
server k8s-m1 10.10.10.52:6443 check inter 3s fall 3 rise 3
server k8s-m2 10.10.10.53:6443 check inter 3s fall 3 rise 3
server k8s-m3 10.10.10.54:6443 check inter 3s fall 3 rise 3
EOF
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl enable –now haproxy
sudo systemctl status haproxy
让 rsyslog 单独收集 haproxy 日志,便于查看健康检查与访问记录:
sudo tee /etc/rsyslog.d/haproxy.conf <<'EOF'
:programname, isequal, "haproxy" /var/log/haproxy.log
& stop
EOF
sudo systemctl restart rsyslog
查看健康检查与访问日志:
sudo tail -f /var/log/haproxy.log
3.4 配置 Keepalived(lb01 主、lb02 备)
健康检查建议采用脚本方式:既检测 haproxy 进程存活,又检测 6443 端口是否实际监听,避免「进程假死、VIP 不漂移」的问题。先创建健康检查脚本(两台 LB 一致):
# /etc/keepalived/check_haproxy.sh
sudo tee /etc/keepalived/check_haproxy.sh <<'EOF'
#!/bin/bash
# haproxy 进程存活检查
if ! pgrep -x haproxy >/dev/null; then
exit 1
fi
# 6443 端口监听检查
if ! (echo > /dev/tcp/127.0.0.1/6443) 2>/dev/null; then
exit 1
fi
exit 0
EOF
sudo chmod +x /etc/keepalived/check_haproxy.sh
主节点 lb01 配置 /etc/keepalived/keepalived.conf,vrrp_script 改为调用该脚本:
# lb01 主节点
global_defs {
router_id K8S_LB_1
}
vrrp_script chk_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight 2
}
vrrp_instance K8S_LB {
state MASTER
interface ens160
virtual_router_id 51
priority 150
advert_int 1
authentication {
auth_type PASS
auth_pass K8S_LB_PASS
}
virtual_ipaddress {
10.10.10.60/24
}
track_script {
chk_haproxy
}
}
备节点 lb02 配置基本相同,仅需改三处:router_id、state 改为 BACKUP、priority 降低(如 100)。
# lb02 备节点:三处不同
router_id K8S_LB_2
state BACKUP
priority 100
keepalived.conf 中含 VRRP 认证密码,需收紧文件权限防止泄露(仅 root 可读写):
sudo chmod 600 /etc/keepalived/keepalived.conf
sudo chown root:root /etc/keepalived/keepalived.conf
sudo systemctl enable –now keepalived
sudo systemctl status keepalived
说明:VRRP 认证 (auth_type PASS) 仅用于组内节点间简单鉴权,密码以明文存储在配置文件中,因此 600 权限是必要加固;更安全的方式是使用 AH 认证或限制接口访问。
3.5 验证负载均衡层
在任一节点查看 VIP 是否漂移正常:
ip addr show ens160 # 期望看到 10.10.10.60 在 lb01 上
ping -c 2 10.10.10.60
手动验证健康检查脚本逻辑是否正确(此时 Master 尚未部署,6443 端口未监听,脚本应返回 1,Keepalived 会判定主节点不健康):
sudo /etc/keepalived/check_haproxy.sh; echo $? # 期望输出 1
# 待 Master 部署后再次执行,期望输出 0
测试 6443 端口转发(此时 Master 尚未部署,TCP 连接会失败属正常;端口可达即可):
nc -vz 10.10.10.60 6443
第四章 部署容器运行时与 Kubernetes 组件
本章在 3 台 Master 和 3 台 Worker 共 6 台节点上执行(LB 节点不需要)。包括:加载内核模块、配置 sysctl、安装 containerd、配置 K8s 软件源并安装 kubeadm / kubelet / kubectl。
4.1 加载内核模块(6 节点)
# 加载 overlay 与 br_netfilter
sudo modprobe overlay
sudo modprobe br_netfilter
sudo tee /etc/modules-load.d/containerd.conf <<EOF
overlay
br_netfilter
EOF
4.2 配置内核参数(6 节点)
K8s 官方要求的内核参数仅需开启 iptables 桥接转发与 IP 转发;vm.swappiness 不属于 K8s 强制项,故不在此设置(详见 4.3 说明)。
sudo tee /etc/sysctl.d/99-kubernetes.conf <<EOF
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl –system
4.3 关闭 swap(Rocky 9 默认系统无 swap,如有则关闭)
关闭 swap 是 K8s 硬性要求:kubelet 检测到节点有 swap 时默认拒绝启动(failSwapOn)。Rocky 9 默认无 swap,若开启了则执行:
sudo swapoff -a
sudo sed -i "/ swap / s/^/#/" /etc/fstab
4.4 安装 containerd(6 节点)
sudo dnf install -y yum-utils
# Rocky 9 自带 containerd 包;也可用 docker-ce 源装 containerd.io
sudo dnf install -y containerd.io
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
Rocky 9 默认自带 containerd 2.x(配置结构不同于 1.x):sandbox 镜像位于 pinned_images.sandbox(而非 sandbox_image),插件段为 plugins.io.containerd.cri.v1.images。4.6 的 pause 替换与下方 cgroup 配置均已按 2.x 适配。
Rocky 9 使用 cgroup v2,必须将 containerd 的 cgroup 驱动改为 systemd(与 kubelet 一致,避免 cgroup v2 下驱动冲突导致节点 NotReady):
sudo sed -i "s/SystemdCgroup = false/SystemdCgroup = true/" /etc/containerd/config.toml
sudo systemctl enable –now containerd
# 配置已修改,必须 restart 重载,否则改动不生效
sudo systemctl restart containerd
sudo systemctl status containerd
说明:enable –now 仅在服务首次启动时读取配置;若 containerd 已在运行(安装包时通常已启动),必须先 restart 才能重载修改后的 config.toml。
修改后务必确认 cgroup 驱动确实生效为 systemd(而非默认的 cgroupfs),这一步遗漏会导致 kubelet 无法启动。注意:Rocky 9 安装 containerd 后 crictl 默认可能未安装,需先装 cri-tools:
# 若提示 crictl: command not found,先安装 cri-tools
sudo dnf install -y cri-tools
sudo crictl info | grep -A5 -i cgroup
# 期望输出: "cgroupDriver": "systemd"
若显示 cgroupfs 或 cgroupDriver 非 systemd,说明配置未生效,需检查 config.toml 的 SystemdCgroup 是否为 true 并重新 restart。
统一 crictl 的 runtime-endpoint(生产增强,可选):crictl 默认 socket 路径多数环境正确,但部分环境会异常,建议显式配置 /etc/crictl.yaml 保证 crictl 能正常连接 containerd:
sudo tee /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
debug: false
EOF
sudo crictl info # 验证 crictl 可正常连接
pause 镜像的国内源替换放到 4.6 安装 kubeadm 之后执行,这样能用 kubeadm 自动探测当前版本的准确 tag,避免写死 tag 导致版本不匹配(详见 4.6)。
4.5 配置 K8s 软件源(6 节点)
K8s 从 v1.28 起 yum 源改为按大版本分目录,旧路径(mirrors.aliyun.com/kubernetes/yum/repos/…)对新版本会返回 404,必须使用官方 pkgs.k8s.io 源(按大版本目录):
sudo tee /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.36/rpm/repodata/repomd.xml.key
repo_gpgcheck=1
EOF
说明:官方源带 gpg 校验。若内网访问 pkgs.k8s.io 受限,可用 dnf makecache 测试连通性,或改用内网 yum 源(10.10.10.10 彭大帅源)。
4.6 安装 kubeadm / kubelet / kubectl(6 节点)
指定 v1.36 稳定版本(如安装时已有更新版本,可用 yum 搜索确定):
sudo dnf install -y kubelet-1.36.0 kubeadm-1.36.0 kubectl-1.36.0
# 若版本号有出入,先查询可用版本
sudo dnf list –available kubelet kubeadm kubectl
sudo systemctl enable –now kubelet
注:kubelet 现在启动会失败属正常(缺少集群配置),等初始化后即正常。
动态修正 pause 镜像为国内源:先用 kubeadm 探测当前版本真实的 pause 镜像名(含 tag,避免写死版本不匹配),再替换 containerd 配置中的 registry 前缀。这样 tag 始终与 kubeadm 版本对齐:
# 1. 查看当前版本 pause 镜像(registry.k8s.io/pause:<tag>)
kubeadm config images list | grep pause
# 2. 自动提取当前 pause 镜像并换成阿里云(sed 直接接管道,避免嵌套)
PAUSE_IMG=$(kubeadm config images list | grep pause | head -1 | sed "s#registry.k8s.io#registry.aliyuncs.com/google_containers#")
# 3. 替换 containerd 的 sandbox 镜像(containerd 2.x 用 pinned_images.sandbox,非 sandbox_image)
sudo sed -i "s#registry.k8s.io/pause:#$PAUSE_IMG#" /etc/containerd/config.toml
# 4. 确认替换结果并重启 containerd
grep -n "pause:" /etc/containerd/config.toml
sudo systemctl restart containerd
说明:此方式用 kubeadm 自动探测 tag,不依赖手工写死的版本号,从根源上避免 pause 版本与 K8s 版本不匹配导致的节点 NotReady。
4.7 验证基础环境
containerd –version
kubeadm version
kubectl version –client
第五章 初始化高可用控制面(Master)
在 k8s-m1 上创建 kubeadm 初始化配置文件。关键点:–control-plane-endpoint 指向负载均衡 VIP 10.10.10.60:6443,使所有 Master 通过 VIP 通信;advertiseAddress 填本机真实 IP。
5.1 生成并修改初始化配置(k8s-m1 执行)
print init-defaults 会输出大量默认字段。为避免误改,务必遵循「只改以下必要字段,其余默认值一律不动」的原则:
sudo kubeadm config print init-defaults > kubeadm-init.yaml
sudo vim kubeadm-init.yaml
注意:kubeadm config print 在 root 下执行无需 sudo,但普通用户需 sudo;生成的是 root 属主文件,vim 编辑也需 sudo。
vim 中只需改 4 处,其余保持默认(controlPlaneEndpoint 为高可用核心):
# ① 本机真实 IP(默认 1.2.3.4)
advertiseAddress: 10.10.10.52
# ② 高可用核心:必须手动新增该字段,指向 VIP
controlPlaneEndpoint: 10.10.10.60:6443
# ③ 与安装版本一致
kubernetesVersion: v1.36.0
# ④ 国内镜像源(替换 registry.k8s.io)
imageRepository: registry.aliyuncs.com/google_containers
Pod 网段默认 10.244.0.0/16、Service 网段默认 10.96.0.0/12,已与 Calico 兼容,无需修改。若你确需改网段,再调整 networking 段且两处需一致。
5.2 校验配置语法(强烈建议)
用 kubeadm migrate 校验配置,避免字段写错导致 init 失败:
sudo kubeadm config migrate –old-config kubeadm-init.yaml –new-config kubeadm-init-new.yaml
sudo vim kubeadm-init.yaml # 人工复核上述 4 处
5.3 预拉取镜像(可选,加速初始化)
sudo kubeadm config images pull –config kubeadm-init.yaml
5.4 初始化第一个控制面节点(k8s-m1)
预检提醒:kubeadm 要求控制面节点 CPU ≥ 2 核。若虚拟机只有 1 核,init 会报 [ERROR NumCPU],需先在 VMware 中关闭虚拟机将 CPU 调至 2 核,再重新 init。生产 Master 建议 2 核 4GB 以上。
主机名提醒:kubeadm init 会把节点注册为当前主机名。务必先确认 k8s-m1 主机名正确(hostname 显示 k8s-m1),否则集群中第一台 Master 的节点名会是旧主机名(如 node),后续需额外处理。
hostname # 确认显示 k8s-m1,再执行 init
sudo kubeadm init –config kubeadm-init.yaml –upload-certs
初始化成功后,末尾会输出两条关键 join 命令,务必立即保存(有效期不同,详见 5.6):
- kubeadm join 命令(加入 Master 的 –control-plane 命令,含 –certificate-key)
- kubeadm join 命令(加入 Worker 的普通命令,含 token)
5.5 配置 kubectl(k8s-m1)
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown
(id -g) $HOME/.kube/config
# 启用 kubectl 自动补全(可选)
echo "source <(kubectl completion bash)" >> ~/.bashrc
source ~/.bashrc
5.6 加入其余 Master(k8s-m2 / k8s-m3)
在 k8s-m2、k8s-m3 上执行 5.4 输出中带 –control-plane 的 join 命令,例如:
sudo kubeadm join 10.10.10.60:6443 –token <token> \\
–discovery-token-ca-cert-hash sha256:<hash> \\
–control-plane –certificate-key <cert-key>
加入完成后,在 k8s-m1 上确认 3 个 Master 均已就绪:
kubectl get nodes
kubectl get pods -n kube-system -o wide
5.7 有效期提醒与补救
join 命令中的两个凭据有效期不同,务必注意:
- token:默认 24 小时有效
- certificate-key:仅 2 小时有效(用于其他 Master 以 –control-plane 方式加入,需解密/传输控制面证书)
若超过 2 小时才加入剩余 Master,–certificate-key 已失效,报错如 "certificate key does not exist or is expired"。此时需在 k8s-m1 重新生成 certificate-key 并继续 join:
sudo kubeadm init phase upload-certs –upload-certs
# 重新生成 token(如需新 join 命令)
sudo kubeadm token create –print-join-command
upload-certs 会输出一个新的 –certificate-key,用它替换 5.6 命令中的旧 key 即可继续加入 Master。
第六章 部署 Calico 网络插件
控制面初始化完成后,集群处于 NotReady 状态,需部署 CNI 网络插件让节点就绪。本方案使用 Calico。
6.1 获取并修改 Calico 清单
必须拉取指定稳定版本标签,切勿使用 master 开发分支(master 随时是未发布测试版本,不适用于生产)。本方案用当前稳定版 v3.32.2:
# 在 k8s-m1 下载指定稳定版本的 Calico 清单
wget https://raw.githubusercontent.com/projectcalico/calico/v3.32.2/manifests/calico.yaml
grep -n "10.244.0.0" calico.yaml # 确认 Pod 网段
grep -n "CALICO_IPV4POOL_CIDR" calico.yaml
若清单默认网段不是 10.244.0.0/16,编辑该处替换为 10.244.0.0/16,与 kubeadm 初始化时 podSubnet 保持一致。
MTU 提示:若虚拟机网络为 NAT 模式,默认 MTU 可能导致 Pod 间访问异常(部分环境丢包/超时)。此时需调整 Calico 的 MTU 参数,将其设置为主机物理网卡 MTU 减去封装开销(VXLAN 模式减 50),例如主机 MTU 1500 则 Calico MTU 设为 1450。若 Pod 通信异常,优先检查此参数。
6.2 应用 Calico
kubectl apply -f calico.yaml
6.3 确认节点就绪
kubectl get nodes
kubectl get pods -n kube-system
等待片刻后,所有节点 STATUS 变为 Ready,kube-system 下 calico-node、coredns 等 Pod 处于 Running 状态即部署成功。
常见问题:若 coredns 一直 Pending,通常是 Calico 网段与初始化不一致,需回退重新初始化;若 calico-node 起不来,检查内核模块与 IP 转发是否已开启。
第七章 加入 Worker 节点
在 3 台 Worker 节点(k8s-w1、k8s-w2、k8s-w3)上执行 5.4 输出中普通的 kubeadm join 命令(不带 –control-plane),例如:
sudo kubeadm join 10.10.10.60:6443 –token <token> \\
–discovery-token-ca-cert-hash sha256:<hash>
7.1 重新生成 token(若已过期)
token 默认 24 小时有效。若已过期,在 k8s-m1 上重新生成:
sudo kubeadm token create –print-join-command
7.2 验证集群节点
回到 k8s-m1 查看所有节点,期望 6 个节点(3 Master + 3 Worker)均为 Ready:
kubectl get nodes -o wide
查看节点角色与标签,确认 Master 与 Worker 分工正确:
kubectl get nodes –show-labels
第八章 验证高可用与故障演练
8.1 通过 VIP 访问 API Server
curl -k https://10.10.10.60:6443/version
# 或使用 kubectl 指定 VIP(生产环境配好证书后可直接用)
kubectl –server=https://10.10.10.60:6443 get nodes
8.2 测试 API Server 负载均衡
多次执行 get nodes,观察请求被分发到不同 Master(可结合 haproxy 日志验证)。
8.3 演练:Master 故障转移
**安全提醒:etcd 三副本仅能容忍 1 台故障,演练时禁止同时关闭两台 Master 节点**,否则 etcd 无法形成多数选举、集群将不可用。
在 k8s-m1 上执行关机,观察集群是否仍可用(其余 Master 接管):
sudo shutdown now # 或 sudo reboot
等待 1-2 分钟后,在任意存活 Master 上执行:
kubectl get nodes
期望:集群管理功能仍正常,k8s-m1 显示 NotReady;重启后自动恢复为 Ready。
8.4 演练:LB 故障转移
在 lb01 上停止 keepalived 或直接关机,观察 VIP 是否漂移到 lb02:
sudo systemctl stop keepalived # lb01 上执行
在 lb02 上执行确认 VIP 已漂移:
ip addr show ens160 # 期望看到 10.10.10.60
ping -c 2 10.10.10.60
8.5 部署测试应用
kubectl create deployment nginx –image=nginx –replicas=3
kubectl expose deployment nginx –port=80 –type=NodePort
kubectl get svc -o wide
通过任意 Worker 节点的 NodePort 访问测试应用,验证集群业务能力正常。
第九章 集群部署后巡检清单
集群搭建完成并验证高可用后,建议按以下清单逐项巡检,确保生产就绪:
9.1 集群整体状态
注意:kubectl get componentstatuses 在 Kubernetes 1.24 之后已废弃,不再返回数据,不要使用它来检查控制面组件。
kubectl get nodes -o wide # 所有节点 Ready
kubectl get pods -A # 全部 Running/Completed,无 CrashLoop
kubectl top nodes # 需安装 metrics-server 后可用
9.2 etcd 健康检查(生产关键)
etcd 是集群可用性的核心,需确认健康、对齐状态正常。在任一 Master 上执行(kubeadm 部署的 etcd 为 static pod):
# 查看 etcd 成员与健康
sudo crictl ps | grep etcd
sudo ETCDCTL_API=3 etcdctl –cacert=/etc/kubernetes/pki/etcd/ca.crt \\
–cert=/etc/kubernetes/pki/etcd/server.crt \\
–key=/etc/kubernetes/pki/etcd/server.key \\
endpoint health –cluster
sudo ETCDCTL_API=3 etcdctl –cacert=/etc/kubernetes/pki/etcd/ca.crt \\
–cert=/etc/kubernetes/pki/etcd/server.crt \\
–key=/etc/kubernetes/pki/etcd/server.key \\
endpoint status -w table
期望所有 etcd 成员 Raft 状态为 leader/follower 且 healthy=true;若 leader 数不为 1 或出现 alarm,需及时排查。
9.3 etcd 定期备份(生产必备)
生产务必配置 etcd 定期快照备份,以防数据丢失。手动备份命令如下,建议通过 crontab 定时执行并异地保存快照文件:
# 手动快照备份
sudo ETCDCTL_API=3 etcdctl –cacert=/etc/kubernetes/pki/etcd/ca.crt \\
–cert=/etc/kubernetes/pki/etcd/server.crt \\
–key=/etc/kubernetes/pki/etcd/server.key \\
snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db
# 验证快照
sudo ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot-*.db
恢复时使用 etcdctl snapshot restore 恢复到临时目录后替换 etcd 数据目录,生产建议先演练恢复流程。
9.4 控制面组件状态
kubectl -n kube-system get pod -l tier=control-plane -o wide
# 确认 kube-apiserver / kube-controller-manager / kube-scheduler 均 Running
9.5 网络插件与 CoreDNS
kubectl -n kube-system get pods -l k8s-app=calico-node -o wide
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl get svc -n kube-system kube-dns # 确认 DNS 服务存在
9.6 资源与节点健康
kubectl describe node <节点名> # 查看资源压力、污点、条件
df -h && free -m # 磁盘与内存水位
systemctl status containerd kubelet # 关键服务状态
以上巡检项如全部通过,集群即可视为生产就绪。建议纳入日常运维 SOP 定期执行。
第十章 部署 Harbor 私有镜像仓库
生产集群通常需要一个私有镜像仓库存放业务镜像,Harbor 是业界标准选择。本方案在独立节点 harbor(10.10.10.58)上部署,配置自签 HTTPS 证书,并让集群各节点信任该 CA,通过 regcred 实现镜像拉取认证。
10.1 架构澄清:Harbor 与集群认证的关系
注意区分三类认证:集群内部组件间(API Server / kubelet / etcd)的 TLS 双向认证由 kubeadm 自带的 PKI 体系管理,与 Harbor 无关;Harbor 只负责「节点访问仓库时校验其 HTTPS 证书」以及「kubelet 拉私有镜像时的账号认证」。
10.2 安装 Docker(harbor 节点)
Harbor 依赖 Docker 与 Docker Compose。Rocky 9 使用 docker-ce 官方源安装:
sudo dnf install -y yum-utils
sudo dnf config-manager –add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable –now docker
sudo docker version
10.3 准备自签 HTTPS 证书
生产环境 Harbor 必须走 HTTPS。为简化,使用自签 CA 并为 harbor 域名/IP 签发证书(含主机名与 IP SAN):
mkdir -p /opt/harbor/{ssl,data}
cd /opt/harbor/ssl
# 生成 CA 私钥与自签根证书
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 \\
-subj "/CN=Harbor CA" -out ca.crt
# 生成 harbor 私钥
openssl genrsa -out harbor.key 2048
生成带 SAN(含域名 + IP)的证书签名请求。注意 SAN 必须放在 req_extensions(req_ext 段)而非 [SAN] 段,否则 openssl req 阶段不会读取,生成的证书将不含 IP SAN,浏览器 / containerd 访问会报证书 IP 不匹配,导致镜像拉取失败。
# 正确写法:subjectAltName 定义在 req_ext 段,签发时用 -extensions req_ext
cat > harbor.cnf <<EOF
[req]
distinguished_name=dn
prompt=no
[req_ext]
subjectAltName=@alt_names
[alt_names]
DNS.1 = harbor
IP.1 = 10.10.10.58
[dn]
CN = harbor
EOF
openssl req -new -key harbor.key -out harbor.csr -config harbor.cnf
openssl x509 -req -in harbor.csr -CA ca.crt -CAkey ca.key \\
-CAcreateserial -out harbor.crt -days 3650 \\
-sha256 -extensions req_ext -extfile harbor.cnf
签发后务必用 openssl 校验证书确实包含 IP SAN,否则后续访问必报错:
openssl x509 -in harbor.crt -noout -text | grep -A1 "Subject Alternative Name"
# 期望输出: DNS:harbor, IP Address:10.10.10.58
10.4 下载并配置 Harbor
cd /opt/harbor
wget https://github.com/goharbor/harbor/releases/download/v2.11.1/harbor-offline-installer-v2.11.1.tgz
tar -xzf harbor-offline-installer-*.tgz
cd harbor
cp harbor.yml.tmpl harbor.yml
编辑 harbor.yml。最佳实践建议使用域名作为 hostname(而非 IP),证书 SAN 中需包含对应域名;集群各节点 hosts 已配置 harbor 域名解析(见第二章)。
hostname: harbor # 建议用域名;hosts 已映射 harbor → 10.10.10.58
http: port: 80
https: port: 443
certificate: /opt/harbor/ssl/harbor.crt
private_key: /opt/harbor/ssl/harbor.key
harbor_admin_password: HarborAdmin@123
注意:harbor_admin_password 以明文写入 harbor.yml,安装后务必到 Web 界面尽快修改默认管理员密码,避免弱口令风险。
10.5 执行安装
sudo ./prepare
sudo ./install.sh
sudo docker compose ps
安装完成后,浏览器访问 https://harbor(或 https://10.10.10.58)登录(默认账号 admin / 上述密码,请尽快修改)。建议先创建一个用于拉取镜像的普通账号,例如 k8s 用户。
10.6 时间同步校验(证书依赖)
HTTPS 证书校验强依赖节点时间,Harbor 节点与 K8s 节点时间必须一致,否则会出现证书验证失败。执行前先校验 Harbor 节点时间同步是否正常:
chronyc tracking # 查看系统时钟同步状态
chronyc sources -v # 确认已连接时间源
timedatectl # 确认 NTP synchronized: yes
若 timedatectl 显示 NTP synchronized: no,说明 chrony 未正常同步,需修复后再继续,否则证书校验会因时间偏移失败。
10.7 集群节点信任 Harbor CA(6 台节点全部配置)
K8s 节点使用 containerd 作为运行时,只会读取 /etc/containerd/certs.d 目录,不会读取 Docker 的 /etc/docker/certs.d。因此**只需配置 containerd 目录**,切勿混淆。
此操作必须在**全部 6 台 K8s 节点**(3 Master + 3 Worker)上执行,仅在 m1 配置会导致其他节点拉取镜像报错。
前提:以下用 scp 分发的 for 循环依赖**已配置 SSH 免密登录**(harbor 节点到各节点)。若未配置免密,scp 会逐台卡在密码输入处(scp 无法交互式批量输密码)。未配免密时,请改用逐台手动 scp + 输密码,或先完成 ssh-copy-id。
# (可选)先在 harbor 节点配置到各节点的免密登录
for n in k8s-m1 k8s-m2 k8s-m3 k8s-w1 k8s-w2 k8s-w3; do
ssh-copy-id $n # 首次需手动输一次密码
done
# 免密就绪后,在 harbor 节点把 CA 拷到各节点
for n in k8s-m1 k8s-m2 k8s-m3 k8s-w1 k8s-w2 k8s-w3; do
scp /opt/harbor/ssl/ca.crt $n:/tmp/harbor-ca.crt
done
# 在各节点执行:仅配 containerd 的 certs.d
sudo mkdir -p /etc/containerd/certs.d/harbor
sudo mv /tmp/harbor-ca.crt /etc/containerd/certs.d/harbor/ca.crt
sudo tee /etc/containerd/certs.d/harbor/hosts.toml <<EOF
server = "https://harbor"
[host."https://harbor"]
ca = "/etc/containerd/certs.d/harbor/ca.crt"
skip_verify = false
EOF
sudo systemctl restart containerd
说明:目录名与 hosts.toml 的 server 需与镜像仓库地址一致(此处用域名 harbor)。skip_verify = false 显式要求校验证书,防止误配导致跳过校验带来安全风险。
10.8 创建 regcred 拉取凭证
在 Harbor 创建好项目(如 project01)并授权 k8s 账号后,在 K8s 集群创建镜像拉取 Secret 并绑定默认 ServiceAccount:
kubectl create secret docker-registry regcred \\
–docker-server=harbor \\
–docker-username=k8s –docker-password=<密码> \\
–docker-email=admin@example.com
kubectl patch serviceaccount default -p '{"imagePullSecrets":[{"name":"regcred"}]}'
风险提示:patch 默认 ServiceAccount 会让集群内所有 Pod 默认携带该拉取凭证。若后续接入多个私有仓库,此全局注入会有副作用(不同仓库需不同凭证时无法区分),建议仅在需要私有镜像的命名空间或工作负载上显式指定 imagePullSecrets。
10.9 验证从 Harbor 拉取镜像
用 Harbor 推送一个测试镜像并验证集群可拉取。以下在 Harbor 节点本机用 IP 操作(SAN 已含 IP,最省事);集群内则统一用域名 harbor 拉取,与 10.7 配置一致:
# Harbor 节点本机:登录并推送
sudo docker login 10.10.10.58 -u k8s -p <密码>
sudo docker tag nginx:latest 10.10.10.58/project01/nginx:v1
sudo docker push 10.10.10.58/project01/nginx:v1
# 集群内创建 Pod 验证(用域名 harbor)
kubectl create deployment myapp –image=harbor/project01/nginx:v1
kubectl get pods
若 Pod 能成功 Running,说明 Harbor 仓库、HTTPS 信任与 regcred 认证全部打通。
结语
至此,一套基于 Rocky Linux 9 的标准生产高可用 Kubernetes 集群部署完成:3 台 Master(内置 etcd)提供控制面高可用,2 台 LB(Keepalived + HAProxy)保证 API 入口高可用,3 台 Worker 承载业务,独立 harbor 节点提供私有镜像仓库。任一台控制面、负载均衡或仓库节点故障都不影响集群整体可用性。
后续可在此基础上扩展:部署 Rancher/K8s Dashboard 可视化、配置 Metrics Server 与 HPA、启用 NetworkPolicy 与审计日志等安全加固,或为 Harbor 配置 Trivy 镜像漏洞扫描。
网硕互联帮助中心





评论前必须登录!
注册