生产环境 Kubernetes 高可用集群部署实战:外部 etcd + Keepalived + HAProxy + 多 Master 架构
摘要:单 Master 集群一旦控制面故障,整个集群就不可用。生产环境必须消除单点故障。本文先讲清 etcd、kube-apiserver、controller-manager/scheduler、kube-dns 四类组件的高可用思路,再以 Ubuntu 24.04 为基座,从虚拟机模板、containerd、外部 etcd 集群、Keepalived 虚拟 IP、HAProxy 负载均衡,到 kubeadm 初始化三 Master 集群、Calico 网络与多 Worker 加入,一步步搭建一套"外部 etcd + 双 HA 节点 + 三 Master + 三 Worker"的生产级高可用集群,最后通过停机演练验证高可用效果。
一、为什么需要高可用集群?
Kubernetes 中的业务系统通过控制器实现负载均衡和高可用,但集群自身的控制面如果采用单节点 Master 架构,会存在两个致命问题:
- 单点故障:Master 一旦故障,整个集群不可用;
- 性能瓶颈:单节点 Master 的工作负载能力有限。
生产环境要做到"没有单点故障",必须为以下组件分别建立高可用方案:
- etcd(集群数据存储);
- kube-apiserver(所有请求的入口);
- kube-controller-manager 与 kube-scheduler(控制回路与调度器);
- kube-dns / CoreDNS(集群内 DNS 服务)。
二、各组件高可用方案分析
2.1 Etcd 集群高可用的三种思路
| 方案 | 说明 | 适用场景 |
|---|---|---|
| 独立的 etcd 集群 | 用 3 台或 5 台服务器独立运行 etcd 集群。节点增减需显式通知集群,便于程序化滚动升级;若在 etcd 前再建 HA + 负载均衡,节点增减无需通知 K8s | 预算充足但保守的项目 |
| Kubernetes 中运行 etcd | 在多个 Master 上用 static pod 方式运行 etcd 并组成集群。etcd 实例被注册进 K8s,监控与日志组件可正常工作,可管理性更强 | 折中选择 |
| self-hosted etcd | 将底层为 K8s 提供服务的 etcd 运行在 K8s 之上,可用 etcd-operator 自动化运维,最符合 K8s 使用习惯 | 想一步到位且愿意承担一定风险的项目 |
本文实验采用方案一:独立的 3 节点外部 etcd 集群。
2.2 kube-apiserver 高可用的两种思路
apiserver 本身是无状态服务 ,高可用的关键是把运行在多台服务器上的 apiserver 用统一的外部入口暴露给所有 Node 和客户端:
- 外部负载均衡器:公有云 LB,或私有云自建 LVS / HAProxy;此时还需保证负载均衡器自身的高可用;
- 网络层负载均衡:Master 上用 BGP 做 ECMP,或在 Node 上用 iptables 做 NAT;无需额外外部服务,但对网络配置有要求。
选型建议:公有云集群多用方案一;私有云集群建议方案二(避免额外维护 LB)。
本文实验采用方案一变形:两台 HA 节点分别运行 HAProxy(四层负载 6443),再用 Keepalived 把虚拟 IP 绑定到健康节点,实现"负载均衡器自身也高可用"。
2.3 controller-manager 与 scheduler
这两个组件是 Master 的一部分,高可用相对容易------运行多份实例即可。目前常见做法是在多个 Master 节点上用 static pod 模式部署两份以上实例,配合 apiserver 的租约(Lease)机制实现选主,同一时刻只有一个实例真正工作。
2.4 kube-dns / CoreDNS
严格说 CoreDNS 不是 Master 组件,它可以运行在 Node 上并通过 Service 向集群内提供服务。为避免单点故障:
- 将副本数(replicas)设为 2 或更多;
- 使用**反亲和性(anti-affinity)**把副本调度到不同 Node 上。
2.5 高可用架构总览
本文最终架构:
text
┌────────────────────────────────────────────┐
│ 虚拟 IP:10.1.8.100(Keepalived) │
└──────────────┬─────────────────────────────┘
┌──────────┴──────────┐
▼ ▼
ha01(10.1.8.101) ha02(10.1.8.102)
HAProxy:6443 HAProxy:6443
└──────────┬──────────┘
▼ 负载均衡转发
┌─────────────────────┼─────────────────────┐
▼ ▼ ▼
master21(10.1.8.121) master22(10.1.8.122) master23(10.1.8.123)
apiserver/controller apiserver/controller apiserver/controller
manager/scheduler manager/scheduler manager/scheduler
│ │ │
└───────── 外部 etcd 集群(独立)────────────┘
etcd11(10.1.8.111) etcd12(10.1.8.112) etcd13(10.1.8.113)
worker31(10.1.8.131) worker32(10.1.8.132) worker33(10.1.8.133)
三、实验拓扑与主机清单
3.1 主机清单
| 集群 | 节点 | 地址 | 角色 |
|---|---|---|---|
| 高可用 | ha01.shaka.cn | 10.1.8.101 | 高可用和负载均衡 |
| 高可用 | ha02.shaka.cn | 10.1.8.102 | 高可用和负载均衡 |
| etcd | etcd11.shaka.cn | 10.1.8.111 | etcd 节点 |
| etcd | etcd12.shaka.cn | 10.1.8.112 | etcd 节点 |
| etcd | etcd13.shaka.cn | 10.1.8.113 | etcd 节点 |
| Kubernetes | master21.shaka.cn | 10.1.8.121 | master 节点 |
| Kubernetes | master22.shaka.cn | 10.1.8.122 | master 节点 |
| Kubernetes | master23.shaka.cn | 10.1.8.123 | master 节点 |
| Kubernetes | worker31.shaka.cn | 10.1.8.131 | worker 节点 |
| Kubernetes | worker32.shaka.cn | 10.1.8.132 | worker 节点 |
| Kubernetes | worker33.shaka.cn | 10.1.8.133 | worker 节点 |
3.2 硬件配置
- Kubernetes 节点:2 CPU、4G 内存;
- 其他节点(HA/etcd):1 CPU、1G 内存。
所有节点使用 Ubuntu 24.04,Kubernetes 版本 1.30.2 ,容器运行时 containerd 1.7.20 ,网络插件 Calico v3.30.7。
四、准备虚拟机 base 模板
10 台节点配置几乎一样,正确做法是先把一台虚拟机调教成黄金模板,打快照,再克隆。先准备 base 模板(不含容器运行时与 K8s 组件)。
4.1 安装系统
Ubuntu 24.04 最小化安装,不需要 swap 分区,建议分区:
/boot:2G/:90G
4.2 安装基础软件包
bash
[root@ubuntu2404 ~]# apt update && apt install -y vim bash-completion open-vm-tools apt-transport-https lrzsz unzip
4.3 配置软件仓库源
① 操作系统仓库换成华为云(速度更快):
编辑 /etc/apt/sources.list.d/ubuntu.sources:
text
Types: deb
URIs: http://mirrors.huaweicloud.com/ubuntu/
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
#Types: deb
#URIs: http://security.ubuntu.com/ubuntu/
#Suites: noble-security
#Components: main restricted universe multiverse
#Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
② 添加 containerd 仓库:
bash
# 导入仓库 key
[root@ubuntu2404 ~]# curl -fsSL https://mirrors.huaweicloud.com/docker-ce/linux/ubuntu/gpg | gpg --dearmour -o /etc/apt/trusted.gpg.d/containerd.gpg
# 添加仓库
[root@ubuntu2404 ~]# cat << 'EOF' > /etc/apt/sources.list.d/docker-ce.list
deb [arch=amd64] https://mirrors.huaweicloud.com/docker-ce/linux/ubuntu noble stable
EOF
③ 添加 Kubernetes 仓库(1.28+ 新版配置方法):
注意:Kubernetes 官方变更了仓库存储路径与使用方式,1.28 及以上版本必须用下面的新版方法。示例为 1.30 版本,其他版本把
v1.30替换成对应版本号即可(当前源支持 v1.24 - v1.30)。
bash
# 添加仓库 key
[root@ubuntu2404 ~]# curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
# 添加仓库
[root@ubuntu2404 ~]# echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/deb/ /" > /etc/apt/sources.list.d/kubernetes.list
# 更新
[root@ubuntu2404 ~]# apt update
4.4 设置 IP(netplan)
模板默认 IP 设为 10.1.8.10,后续用脚本批量改成各节点地址:
bash
[root@ubuntu2404 ~]# mkdir /etc/netplan/origin
[root@ubuntu2404 ~]# mv /etc/netplan/*yaml /etc/netplan/origin
[root@ubuntu2404 ~]# cat > /etc/netplan/00-installer-config.yaml <<EOF
network:
ethernets:
ens32:
dhcp4: no
addresses:
- 10.1.8.10/24
routes:
- to: default
via: 10.1.8.2
nameservers:
addresses:
- 10.1.8.2
- 223.5.5.5
version: 2
EOF
[root@ubuntu2404 ~]# chmod 600 /etc/netplan/00-installer-config.yaml
[root@ubuntu2404 ~]# netplan apply
4.5 设置 /etc/hosts
bash
[root@ubuntu2404 ~]# cat << 'EOF' >> /etc/hosts
####### kubernetes #####
10.1.8.100 k8s.shaka.cn k8s
10.1.8.101 ha01.shaka.cn ha01
10.1.8.102 ha02.shaka.cn ha02
10.1.8.111 etcd11.shaka.cn etcd11
10.1.8.112 etcd12.shaka.cn etcd12
10.1.8.113 etcd13.shaka.cn etcd13
10.1.8.121 master21.shaka.cn master21
10.1.8.122 master22.shaka.cn master22
10.1.8.123 master23.shaka.cn master23
10.1.8.131 worker31.shaka.cn worker31
10.1.8.132 worker32.shaka.cn worker32
10.1.8.133 worker33.shaka.cn worker33
EOF
4.6 配置时间同步(chrony)
bash
[root@ubuntu2404 ~]# apt-get install -y chrony
[root@ubuntu2404 ~]# systemctl enable chrony --now
4.7 优化 SSH
bash
# 避免 sshd 对客户端 IP 反向解析为域名,加快连接
[root@ubuntu2404 ~]# echo 'UseDNS no' >> /etc/ssh/sshd_config
# 避免客户端校验服务器公钥,免去首次连接交互输入 yes
[root@ubuntu2404 ~]# echo 'StrictHostKeyChecking no' >> /etc/ssh/ssh_config
# 生成密钥并配置免密登录自己
[root@ubuntu2404 ~]# ssh-keygen -N '' -f ~/.ssh/id_rsa -t rsa
[root@ubuntu2404 ~]# ssh-copy-id root@localhost
4.8 配置 IPVS 内核模块
kube-proxy 使用 ipvs 模式,需要先准备 IPVS 相关内核模块。
bash
# 1. 安装依赖包
[root@ubuntu2404 ~]# apt install -y iptables ipvsadm ipset conntrack
# 2-1. 临时加载基础网络模块
[root@ubuntu2404 ~]# modprobe overlay
[root@ubuntu2404 ~]# modprobe br_netfilter
# 2-2. 临时加载 ipvs 模块
[root@ubuntu2404 ~]# modprobe ip_vs
[root@ubuntu2404 ~]# modprobe ip_vs_rr
[root@ubuntu2404 ~]# modprobe ip_vs_wrr
[root@ubuntu2404 ~]# modprobe ip_vs_lc
[root@ubuntu2404 ~]# modprobe ip_vs_sh
[root@ubuntu2404 ~]# modprobe nf_conntrack
# 3. 永久加载(重启生效,systemd-modules-load 会自动加载)
[root@ubuntu2404 ~]# cat > /etc/modules-load.d/k8s-net.conf << EOF
# K8s 基础网络
br_netfilter
overlay
# IPVS 必需
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_lc
ip_vs_sh
nf_conntrack
EOF
模块作用速查:
| 模块 | 作用 |
|---|---|
| br_netfilter | 允许桥接设备通过 iptables 过滤,K8s 网络通信必需 |
| overlay | Overlay 文件系统,容器分层镜像必备 |
| ip_vs | IPVS 核心模块,四层负载均衡,kube-proxy ipvs 模式基础 |
| ip_vs_rr / wrr | 轮询 / 加权轮询调度算法 |
| ip_vs_lc | 最少连接调度 |
| ip_vs_sh | 源地址哈希,同一客户端始终访问同一后端 |
| nf_conntrack | 连接跟踪,保证数据包正确转发 |
4.9 配置内核参数
bash
[root@ubuntu2404 ~]# 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
vm.swappiness=0
EOF
# 立即生效
[root@ubuntu2404 ~]# sysctl -p /etc/sysctl.d/k8s.conf
参数含义:
net.bridge.bridge-nf-call-iptables=1:启用 iptables 对桥接 IPv4 流量的过滤;net.bridge.bridge-nf-call-ip6tables=1:同上,IPv6;net.ipv4.ip_forward=1:开启 IPv4 路由转发;vm.swappiness=0:禁止使用交换分区。
4.10 编写 sethost 批量设置脚本
克隆出来的每台机器都要改 IP 和主机名,写一个脚本一劳永逸:
bash
[root@ubuntu2404 ~]# cat > /usr/local/bin/sethost <<'EOF'
#!/bin/bash
# 以 root 身份运行
[ $UID -ne 0 ] && echo 'Please run as root.' && exit 1
# 指定接口名称
net_config=/etc/netplan/00-installer-config.yaml
# 指定域名称
domain=shaka.cn
# 脚本使用说明
usage (){
echo "Usage: $0 101-102 | 111-113 | 121-123 | 131-133"
exit 1
}
# 设置 IP 地址
function set_ip () {
# 修改网卡配置文件IP
IP=10.1.8.$1
sed -ri "s#10.1.8.[0-9]{,3}/#$IP/#g" ${net_config}
# 设置网卡文件权限
chmod 600 ${net_config}
# 激活IP
netplan apply
}
# 设置主机名
function set_hostname () {
HOSTNAME_END=$[ $1 - 100 ].$domain
# 获取主机名
case $1 in
10[1-2])
HOSTNAME=ha0${HOSTNAME_END}
;;
11[1-3])
HOSTNAME=etcd${HOSTNAME_END}
;;
12[1-3])
HOSTNAME=master${HOSTNAME_END}
;;
13[1-3])
HOSTNAME=worker${HOSTNAME_END}
;;
*)
usage
;;
esac
# 设置主机名
hostnamectl hostname $HOSTNAME
}
# 定义 main 函数调用功能函数
function main() {
# 设置主机名
set_hostname $1
# 设置 IP
set_ip $1
# 显示修改结果
bash -c 'clear;hostname;echo;ip -br a;echo'
# 关机打快照
while true
do
echo -ne "Press the \033[1;31mEnter\033[0;39m key, and the system will shut down in 5 seconds.";read
echo -e "Press \033[1;35mCTRL+C\033[0;39m to cancel the shutdown."
for i in {5..1}
do
echo "The system will shut down in $i seconds."
sleep 1
done
echo "Shutdown system Now." && init 0
done
}
# 执行 main 函数
main $*
EOF
[root@ubuntu2404 ~]# chmod +x /usr/local/bin/sethost
脚本把 IP 末段映射到主机名:101-102 → ha01/ha02,111-113 → etcd11-13,121-123 → master21-23,131-133 → worker31-33。
4.11 打快照 base
bash
[root@ubuntu2404 ~]# init 0
关机后在虚拟化平台打快照,名称 base。
五、准备虚拟机 k8s 模板
基于 base 快照继续配置容器运行时与 Kubernetes 组件 ,完成后打快照 k8s,用于克隆全部 K8s 节点。
5.1 关闭 swap
Kubernetes 不需要 swap 分区:
bash
[root@ubuntu2404 ~]# swapoff -a && sed -i '/^.*swap/d' /etc/fstab
[root@ubuntu2404 ~]# rm -f /swap.img
5.2 配置 containerd
bash
# 安装指定版本 containerd 与 cri-tools
[root@ubuntu2404 ~]# apt-get install -y containerd.io=1.7.20-1 cri-tools
# 设置 crictl 的 runtime-endpoint
[root@ubuntu2404 ~]# crictl config runtime-endpoint unix:///var/run/containerd/containerd.sock
# 生成默认配置
[root@ubuntu2404 ~]# containerd config default > /etc/containerd/config.toml
# 修改 SystemdCgroup 与 sandbox_image(K8s 1.28+ 用 pause:3.9)
[root@ubuntu2404 ~]# sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
[root@ubuntu2404 ~]# sed -i 's|sandbox_image = ".*"|sandbox_image = "registry.k8s.io/pause:3.9"|' /etc/containerd/config.toml
然后编辑 /etc/containerd/config.toml 配置镜像加速与私有仓库信任:
toml
# 查找 configs 行,添加私有仓库 TLS 信任(注意缩进)
[plugins."io.containerd.grpc.v1.cri".registry.configs."hub.shaka.cn".tls]
insecure_skip_verify = true
# 查找 mirrors 行,添加镜像加速
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://docker.m.daocloud.io","https://09def58152000fc00ff0c00057bad7e0.mirror.swr.myhuaweicloud.com"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.k8s.io"]
endpoint = ["https://k8s.m.daocloud.io","https://09def58152000fc00ff0c00057bad7e0.mirror.swr.myhuaweicloud.com"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."hub.shaka.cn"]
endpoint = ["http://hub.shaka.cn"]
重启并测试:
bash
[root@ubuntu2404 ~]# systemctl restart containerd.service
# crictl 走 containerd CRI 接口,读取 config.toml 中的 registry.mirrors 配置
[root@ubuntu2404 ~]# crictl pull busybox
Image is up to date for sha256:925ff61909aebae4bcc9bc04bb96a8bd15cd2271f13159fe95ce4338824531dd
5.3 安装 nerdctl 与 CNI 插件
bash
# 下载安装 nerdctl(项目:github.com/containerd/nerdctl)
[root@ubuntu2404 ~]# wget http://192.168.46.100/01.softwares/03.stage-3/nerdctl-1.7.7-linux-amd64.tar.gz
[root@ubuntu2404 ~]# tar -xf nerdctl-1.7.7-linux-amd64.tar.gz -C /usr/bin/
# 下载 nerdctl 需要的 CNI 插件
[root@ubuntu2404 ~]# wget http://192.168.46.100/01.softwares/03.stage-3/cni-plugins-linux-amd64-v1.6.0.tgz
[root@ubuntu2404 ~]# mkdir -p /opt/cni/bin
[root@ubuntu2404 ~]# tar -xf cni-plugins-linux-amd64-v1.6.0.tgz -C /opt/cni/bin
关键区别 :
crictl走 containerd CRI 接口,读取/etc/containerd/config.toml的 registry 配置;而nerdctl走 containerd 原生 API,不读取 CRI 专属配置 ,需要单独维护/etc/containerd/certs.d/下的 hosts.toml。
配置 nerdctl 的镜像加速:
bash
# docker.io 加速
mkdir -p /etc/containerd/certs.d/docker.io
cat > /etc/containerd/certs.d/docker.io/hosts.toml << EOF
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
[host."https://09def58152000fc00ff0c00057bad7e0.mirror.swr.myhuaweicloud.com"]
capabilities = ["pull", "resolve"]
EOF
# registry.k8s.io 加速
mkdir -p /etc/containerd/certs.d/registry.k8s.io
cat > /etc/containerd/certs.d/registry.k8s.io/hosts.toml << EOF
server = "https://registry.k8s.io"
[host."https://k8s.m.daocloud.io"]
capabilities = ["pull", "resolve"]
[host."https://registry.cn-hangzhou.aliyuncs.com/google_containers"]
capabilities = ["pull", "resolve"]
override_path = true
EOF
# 私有仓库 hub.shaka.cn
mkdir -p /etc/containerd/certs.d/hub.shaka.cn
cat > /etc/containerd/certs.d/hub.shaka.cn/hosts.toml << EOF
server = "http://hub.shaka.cn"
[host."http://hub.shaka.cn"]
capabilities = ["pull", "resolve"]
EOF
测试:
bash
[root@ubuntu2404 ~]# nerdctl pull nginx
docker.io/library/nginx:latest: resolved ...
5.4 安装 Kubernetes 软件包
bash
# 查看可用版本
[root@ubuntu2404 ~]# apt list kubeadm -a|head
kubeadm/unknown 1.30.2-1.1 amd64
kubeadm/unknown 1.30.1-1.1 amd64
...
# 安装固定版本(生产环境务必锁定小版本)
[root@ubuntu2404 ~]# apt install -y kubeadm=1.30.2-1.1 kubelet=1.30.2-1.1 kubectl=1.30.2-1.1
# 设置 kubelet 开机自启(此时处于 activating,初始化集群后变 active)
[root@ubuntu2404 ~]# systemctl enable kubelet --now
5.5 配置命令补全
bash
[root@ubuntu2404 ~]# mkdir /etc/bash_completion.d
# crictl
[root@ubuntu2404 ~]# crictl completion bash > /etc/bash_completion.d/crictl
[root@ubuntu2404 ~]# source /etc/bash_completion.d/crictl
# nerdctl
[root@ubuntu2404 ~]# nerdctl completion bash > /etc/bash_completion.d/nerdctl
[root@ubuntu2404 ~]# echo 'export CONTAINERD_NAMESPACE=k8s.io' >> /etc/bash_completion.d/nerdctl
[root@ubuntu2404 ~]# source /etc/bash_completion.d/nerdctl
# kubectl / kubeadm
[root@ubuntu2404 ~]# kubectl completion bash > /etc/bash_completion.d/kubectl
[root@ubuntu2404 ~]# kubeadm completion bash > /etc/bash_completion.d/kubeadm
注意 :必须设置环境变量
CONTAINERD_NAMESPACE=k8s.io!否则 nerdctl 默认把镜像导入default命名空间,而 Kubernetes 使用k8s.io命名空间的镜像,导致拉取镜像"看不见"。
5.6 打快照 k8s
bash
[root@ubuntu2404 ~]# init 0
关机打快照,名称 k8s。
六、克隆集群节点并设置主机名/IP
克隆规划:
- 用
base快照克隆 ha01、ha02、etcd11、etcd12、etcd13; - 用
k8s快照克隆 master21/22/23、worker31/32/33。
开机后分别执行 sethost 脚本:
bash
[root@ubuntu2404 ~]# sethost 101 # ha01
[root@ubuntu2404 ~]# sethost 102 # ha02
[root@ubuntu2404 ~]# sethost 111 # etcd11
...
[root@ubuntu2404 ~]# sethost 131 # worker31
脚本会修改 IP 与主机名,随后关机,可以再为每台机器建立独立快照,方便回滚。
七、部署外部 Etcd 集群
7.1 配置三个 etcd 节点
所有 etcd 节点先安装并清理:
bash
[root@etcdN ~]# apt update && apt install -y etcd-server etcd-client
[root@etcdN ~]# systemctl stop etcd.service
[root@etcdN ~]# rm -fr /var/lib/etcd/default
etcd11(10.1.8.111):
bash
[root@etcd11 ~]# cp /etc/default/etcd /etc/default/etcd.ori
[root@etcd11 ~]# cat > /etc/default/etcd <<'EOF'
ETCD_NAME="etcd11"
ETCD_DATA_DIR="/var/lib/etcd/default"
ETCD_LISTEN_PEER_URLS="http://localhost:2380,http://10.1.8.111:2380"
ETCD_LISTEN_CLIENT_URLS="http://localhost:2379,http://10.1.8.111:2379"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://10.1.8.111:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379,http://10.1.8.111:2379"
ETCD_INITIAL_CLUSTER="etcd11=http://10.1.8.111:2380,etcd12=http://10.1.8.112:2380,etcd13=http://10.1.8.113:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
EOF
etcd12 / etcd13 配置同理,把节点名、IP 换成自己的(etcd12:10.1.8.112;etcd13:10.1.8.113),ETCD_INITIAL_CLUSTER 三台机器保持一致。
7.2 启动并验证
bash
# 所有 etcd 节点执行
[root@etcdN ~]# systemctl enable etcd
[root@etcdN ~]# systemctl start etcd
# 验证成员
[root@etcd11 ~]# etcdctl member list
56ee9a8b1143f1f6, started, etcd12, http://10.1.8.112:2380, ...
b09f448fe279511f, started, etcd11, http://10.1.8.111:2380, ...
b83c09e0fc6318a6, started, etcd13, http://10.1.8.113:2380, ...
三个 etcd 节点全部 started,外部 etcd 集群就绪。
八、部署 Keepalived:为负载均衡器提供高可用
HAProxy 本身也可能故障,所以两台 HA 节点都部署 Keepalived + HAProxy,由 Keepalived 提供虚拟 IP 10.1.8.100,平时由 ha01 持有,ha01 故障时 VIP 漂移到 ha02。
8.1 安装 Keepalived
bash
[root@haN ~]# apt install -y keepalived
8.2 ha01 配置(主,priority 200)
bash
[root@ha01 ~]# cp /etc/keepalived/keepalived.conf{.sample,}
[root@ha01 ~]# cat > /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id ha01
}
vrrp_instance k8s {
state BACKUP
interface ens32
virtual_router_id 51
priority 200
nopreempt
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.1.8.100/24
}
}
EOF
[root@ha01 ~]# systemctl enable keepalived.service
[root@ha01 ~]# systemctl restart keepalived.service
# 验证:VIP 10.1.8.100 出现在 ha01 上
[root@ha01 ~]# ip -br a
ens32 UP 10.1.8.101/24 10.1.8.100/24 ...
8.3 ha02 配置(备,priority 100)
bash
[root@ha02 ~]# cp /etc/keepalived/keepalived.conf{.sample,}
[root@ha02 ~]# cat > /etc/keepalived/keepalived.conf <<'EOF'
! Configuration File for keepalived
global_defs {
router_id ha02
}
vrrp_instance k8s {
state BACKUP
interface ens32
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.1.8.100/24
}
}
EOF
[root@ha02 ~]# systemctl enable keepalived.service
[root@ha02 ~]# systemctl restart keepalived.service
配置要点:
- 两台都用
state BACKUP+nopreempt(ha01),避免主节点恢复时 VIP 频繁抖动; virtual_router_id、auth_pass两台必须一致;- ha01 的
priority 200高于 ha02 的priority 100,正常情况下 VIP 在 ha01。
九、部署 HAProxy:kube-apiserver 的统一入口
9.1 安装并追加配置
bash
# 两台 HA 节点都安装
[root@haN ~]# apt install -y haproxy
[root@haN ~]# cat >> /etc/haproxy/haproxy.cfg <<'EOF'
# 新增统计面板配置
listen stats
bind *:1080
mode http
stats enable
stats uri /haproxy-stats
stats refresh 3s
stats auth admin:123456
stats hide-version
# 新增 k8s 面板配置:四层负载均衡到三个 master
listen k8s
mode tcp
bind *:6443
balance roundrobin
server master21 10.1.8.121:6443 check inter 3000ms fall 3 rise 2
server master22 10.1.8.122:6443 check inter 3000ms fall 3 rise 2
server master23 10.1.8.123:6443 check inter 3000ms fall 3 rise 2
EOF
[root@haN ~]# systemctl enable haproxy
[root@haN ~]# systemctl restart haproxy.service
9.2 验证统计面板
浏览器访问(账号 admin,密码 123456):
text
http://10.1.8.101:1080/haproxy-stats
http://10.1.8.102:1080/haproxy-stats
此时三个 master 还未加入,后端会显示 DOWN,等集群初始化完成后变 UP。
配置说明:
listen stats提供 HAProxy 监控面板;listen k8s用 TCP 模式把 6443 端口轮询转发给三台 Master 的 kube-apiserver,并每 3 秒做一次健康检查(3 次失败摘除、2 次成功恢复)。
十、初始化 Kubernetes 集群
10.1 生成并修改初始化配置
在 master21 上执行:
bash
[root@master21 ~]# kubeadm config print init-defaults | tee kubeadm-config.yaml
[root@master21 ~]# vim kubeadm-config.yaml
按生产需求修改为:
yaml
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
apiServer:
timeoutForControlPlane: 4m0s
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
# 添加控制平面统一入口(Keepalived VIP + HAProxy 端口)
controlPlaneEndpoint: "10.1.8.100:6443"
# 修改 etcd:使用外部 etcd 集群
etcd:
external:
endpoints:
- "http://10.1.8.111:2379"
- "http://10.1.8.112:2379"
- "http://10.1.8.113:2379"
dns: {}
controllerManager: {}
scheduler: {}
# 按需修改镜像仓库,实验环境使用提前导入的镜像
imageRepository: registry.k8s.io
# 设置版本
kubernetesVersion: 1.30.2
networking:
# 设置集群内部域名
dnsDomain: kubernetes.shaka.cn
# 设置 Pod 子网(要与 Calico 保持一致!)
podSubnet: 10.224.0.0/16
serviceSubnet: 10.96.0.0/12
---
# 添加 kube-proxy 配置:使用 IPVS 模式
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs
ipvs:
scheduler: rr
clientConnection:
kubeconfig: /var/lib/kube-proxy/kubeconfig.conf
关键字段解读:
| 字段 | 值 | 含义 |
|---|---|---|
controlPlaneEndpoint |
10.1.8.100:6443 | 所有 kubeadm join 与客户端都走 VIP + HAProxy 入口 |
etcd.external.endpoints |
三个 etcd 地址 | 声明使用外部 etcd,kubeadm 不会在 Master 上自建 etcd |
podSubnet |
10.224.0.0/16 | Pod 网段,必须与 Calico 的 CALICO_IPV4POOL_CIDR 一致 |
serviceSubnet |
10.96.0.0/12 | Service 网段 |
dnsDomain |
kubernetes.shaka.cn | 集群内 DNS 域名后缀 |
KubeProxyConfiguration.mode |
ipvs | 让 kube-proxy 使用 ipvs 模式,调度算法 rr |
10.2 执行初始化
bash
[root@master21 ~]# kubeadm init --config kubeadm-config.yaml
关键日志解读:
External etcd mode: Skipping etcd/... certificate generation:确认走的是外部 etcd,无需生成 etcd 证书;apiserver serving cert is signed for DNS names [... master21.shaka.cn] and IPs [10.96.0.1 10.1.8.121 10.1.8.100]:apiserver 证书同时签发了 VIP 10.1.8.100;Marking the node master21.shaka.cn as control-plane:给节点打控制面标签和NoSchedule污点;- 末尾输出两条 join 命令(含 token 与 CA 哈希),分别用于添加 control-plane 节点与 worker 节点,务必保存。
初始化成功提示:
text
Your Kubernetes control-plane has initialized successfully!
10.3 配置 kubectl 凭据
bash
[root@master21 ~]# mkdir -p $HOME/.kube
[root@master21 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master21 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
十一、部署 Calico 网络插件
11.1 下载并修改 Pod 网段
bash
[root@master21 ~]# wget --no-check-certificate https://raw.githubusercontent.com/projectcalico/calico/v3.30.7/manifests/calico.yaml
# 查看集群 Pod 子网配置
[root@master21 ~]# kubectl get cm -n kube-system kubeadm-config -o yaml|grep podSubnet
podSubnet: 10.224.0.0/16
# 修改 Calico 的 IP 池网段与初始化配置一致
[root@master21 ~]# sed -i "s|# - name: CALICO_IPV4POOL_CIDR|- name: CALICO_IPV4POOL_CIDR|g" calico.yaml
[root@master21 ~]# sed -i "s|# value: \"192.*| value: \"10.224.0.0/16\"|g" calico.yaml
11.2 所有节点提前下载镜像
bash
[root@master21 ~]# grep image: calico.yaml | uniq
image: docker.io/calico/cni:v3.30.7
image: docker.io/calico/node:v3.30.7
image: docker.io/calico/kube-controllers:v3.30.7
# 所有节点执行(提前导入可避免集群部署时逐个拉取)
[root@all-node ~]# nerdctl pull docker.io/calico/cni:v3.30.7
[root@all-node ~]# nerdctl pull docker.io/calico/node:v3.30.7
[root@all-node ~]# nerdctl pull docker.io/calico/kube-controllers:v3.30.7
11.3 部署 Calico
bash
[root@master21 ~]# kubectl apply -f calico.yaml
十二、添加更多控制平面节点(master22/23)
高可用要求至少三个 Master。master22 和 master23 加入方式相同。
12.1 拷贝 PKI 证书
新 Master 需要与第一个 Master 共享 CA 与 SA 密钥,必须从 master21 拷贝以下文件:
bash
[root@master22-23 ~]# mkdir -p /etc/kubernetes/pki
[root@master22-23 ~]# scp master21:/etc/kubernetes/pki/{ca.crt,ca.key,front-proxy-ca.crt,front-proxy-ca.key,sa.key,sa.pub} /etc/kubernetes/pki/
12.2 以控制平面身份加入
bash
[root@master22-23 ~]# kubeadm join 10.1.8.100:6443 --token b0pnwt.1o94h2j82okh7gzt \
--discovery-token-ca-cert-hash sha256:0bd96f3415a5913f0fe28e6ad29c6bdfd487f4f7f46aa2dcfdf13f3bca00464f \
--control-plane
关键日志:
text
[certs] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[certs] Using the existing "sa" key
[check-etcd] Skipping etcd check in external mode
The Kubernetes control plane instances scaled up.
加入成功后同样配置凭据:
bash
[root@master22-23 ~]# mkdir -p $HOME/.kube
[root@master22-23 ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master22-23 ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
十三、Worker 节点加入集群
所有 worker 节点执行(不带 --control-plane):
bash
[root@workerN ~]# kubeadm join 10.1.8.100:6443 --token b0pnwt.1o94h2j82okh7gzt \
--discovery-token-ca-cert-hash sha256:0bd96f3415a5913f0fe28e6ad29c6bdfd487f4f7f46aa2dcfdf13f3bca00464f
提示:token 默认 24 小时过期。过期后可在 master21 用
kubeadm token create --print-join-command重新生成。
十四、验证安装结果
14.1 节点状态
bash
[root@master21 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master21.shaka.cn Ready control-plane 14m v1.30.2
master22.shaka.cn Ready control-plane 9m47s v1.30.2
master23.shaka.cn Ready control-plane 9m15s v1.30.2
worker31.shaka.cn Ready <none> 8m18s v1.30.2
worker32.shaka.cn Ready <none> 8m17s v1.30.2
worker33.shaka.cn Ready <none> 8m15s v1.30.2
14.2 系统 Pod 状态
bash
[root@master21 ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-node-bdzbb 1/1 Running 0 8m48s
calico-node-h6rrw 1/1 Running 0 10m
...
kube-apiserver-master21.shaka.cn 1/1 Running 0 14m
kube-apiserver-master22.shaka.cn 1/1 Running 0 10m
kube-apiserver-master23.shaka.cn 1/1 Running 0 9m44s
kube-controller-manager-master21.shaka.cn 1/1 Running 0 14m
kube-controller-manager-master22.shaka.cn 1/1 Running 0 10m
kube-controller-manager-master23.shaka.cn 1/1 Running 0 9m43s
kube-scheduler-master21.shaka.cn 1/1 Running 0 14m
...
可以看到:3 个 kube-apiserver、3 个 kube-controller-manager、3 个 kube-scheduler 分别运行在三台 Master 上,calico-node 覆盖全部 6 个节点。
14.3 部署测试应用验证负载均衡
bash
# 创建三副本应用
[root@master21 ~]# kubectl create deployment webapp --image=hub.shaka.cn/library/nginx --replicas 3
[root@master21 ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
webapp-7fb6dc88f9-48kcx 1/1 Running 0 57s 10.224.113.129 worker32.shaka.cn
webapp-7fb6dc88f9-9z87c 1/1 Running 0 57s 10.224.19.1 worker31.shaka.cn
webapp-7fb6dc88f9-wtn6t 1/1 Running 0 57s 10.224.162.129 worker33.shaka.cn
# 3 个 Pod 分布在 3 个不同节点(Calico 网络与调度器协同生效)
# 在每个 Pod 写入自己的主机名页面
[root@master21 ~]# for pod in $(kubectl get pods -o name | awk -F/ '{print $2}')
do
kubectl exec -it $pod -- bash -c "echo $pod > /usr/share/nginx/html/index.html"
done
# 暴露 Service
[root@master21 ~]# kubectl expose deployment webapp --port 80 --target-port 80
[root@master21 ~]# kubectl get svc webapp
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
webapp ClusterIP 10.102.27.219 <none> 80/TCP 13s
# 连续访问 30 次,观察负载均衡效果
[root@master21 ~]# for i in {1..30};do curl -s 10.102.27.219:80;done|sort |uniq -c
9 webapp-7fb6dc88f9-48kcx
10 webapp-7fb6dc88f9-9z87c
11 webapp-7fb6dc88f9-wtn6t
三次请求分别落到三个 Pod,说明 kube-proxy(ipvs 模式,rr 轮询)工作正常。
十五、高可用演练:拔掉节点验证
集群搭建完成,最后做破坏性演练,验证每个层面都没有单点故障:
- 关闭任意 HA 节点(如 ha01):Keepalived 检测到故障后,VIP 10.1.8.100 应漂移到 ha02,集群继续可用;
- 关闭任意 Master(如 master21):HAProxy 健康检查把 master21 摘除,请求由 master22/23 继续服务;
- 关闭任意 Worker(如 worker31):上面的 webapp 副本在 worker31 被驱逐后,由控制器在其他节点重建,服务不中断;
- 关闭任意 etcd 节点(如 etcd11):etcd 三节点集群中剩余两节点仍构成多数派(quorum),集群数据读写不受影响。
每步演练后,通过 kubectl get nodes、curl 服务地址以及 HAProxy 统计面板确认集群状态。
十六、总结与最佳实践
本文从零搭建了一套生产级高可用集群,回顾整条链路:
text
Ubuntu 24.04 模板(base/k8s 两级快照)
→ 外部 etcd 三节点集群
→ Keepalived(VIP 10.1.8.100)+ HAProxy(6443 负载均衡)
→ kubeadm 初始化 master21(external etcd + controlPlaneEndpoint)
→ Calico 网络(podSubnet 10.224.0.0/16)
→ master22/23 以 --control-plane 加入
→ worker31/32/33 加入
→ 验证负载均衡与高可用
几个容易被忽略的关键点,务必记牢:
- 证书一致性:新 Master 加入前必须拷贝 ca/sa/front-proxy-ca 等密钥文件,否则集群无法互信;
- 网段一致性 :kubeadm 的
podSubnet与 Calico 的CALICO_IPV4POOL_CIDR必须一致; - VIP 进证书 :
controlPlaneEndpoint的 IP 会被签进 apiserver 证书,所以初始化时就要规划好 VIP; - nerdctl 命名空间 :提前导入镜像时务必设置
CONTAINERD_NAMESPACE=k8s.io; - 外部 etcd 无本地 etcd :日志中出现
External etcd mode说明配置生效,Master 节点不会自建 etcd; - 生产加固建议:etcd 启用 TLS 认证、HAProxy 统计面板修改默认密码、节点启用防火墙白名单、kubeadm token 及时轮换。
集群已经 Ready,下一篇我们将为它装上"可视化驾驶舱"------Kuboard 与 Kubernetes Dashboard,敬请期待!