基于 kubeadm + Keepalived + HAProxy 构建企业级 HA 集群,涵盖多 Master 架构、etcd 集群、负载均衡、故障转移等核心场景的完整实战指南。
目录
- 为什么你的 K8s 集群不够“高可用”?
- HA 集群架构设计
- 环境规划与准备
- 部署负载均衡层
- 部署 etcd 集群
- 部署多 Master 节点
- 部署 Worker 节点
- 部署网络插件与存储
- 验证高可用能力
- 监控与告警配置
- 故障排查与恢复
- 集群升级与扩展
1. 为什么你的 K8s 集群不够“高可用”?
搭建一个单 Master 的 Kubernetes 集群或许不难,但把它投入生产环境,你可能会在各种意想不到的时刻遭遇“暴击”。高可用不是可选项,而是生产环境的生命线。我们先看几个真实的“翻车”现场。
1.1 真实故障场景回顾
场景 1:单 Master 节点宕机
某创业公司单 Master 集群,Master 节点所在物理机故障,
导致整个集群无法调度新 Pod,API 服务完全不可用。
恢复时间:4 小时
业务损失:订单系统停滞,损失约 50 万元
场景 2:etcd 数据丢失
某电商平台 etcd 单节点部署,磁盘故障导致数据丢失,
整个集群状态信息丢失,无法恢复。
恢复时间:重建集群 8 小时 + 数据恢复 12 小时
业务损失:全平台服务中断,损失约 200 万元
场景 3:网络分区导致脑裂
某金融系统跨机房部署,网络分区导致两个 Master 都认为自己是主节点,
出现脑裂,数据不一致。
恢复时间:6 小时
业务损失:交易数据不一致,需人工对账
这些并非危言耸听,而是每天都在发生的真实故事。单点故障、数据丢失、脑裂问题,每一个都足以让业务停摆。
1.2 高可用核心指标
我们来量化一下,高可用到底带来了哪些提升?
| 指标 |
单 Master |
多 Master HA |
提升 |
| API Server 可用性 |
99.5% |
99.95% |
⬆️ 10x |
| etcd 数据可靠性 |
单点风险 |
3 副本冗余 |
⬆️ 100x |
| 故障恢复时间 |
小时级 |
秒级 |
⬆️ 100x |
| 计划内维护 |
需停机 |
无感知 |
✅ |
| 脑裂风险 |
无 |
可防控 |
✅ |
1.3 高可用层级
高可用是一个立体化的概念,需要从多个层面来构建:
┌─────────────────────────────────────────────────────────┐
│ 应用层高可用 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Deployment │ │ StatefulSet│ │ DaemonSet │ │
│ │ 多副本 │ │ 持久化存储 │ │ 节点级冗余 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
▲
┌─────────────────────────────────────────────────────────┐
│ 控制平面高可用 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 多 Master │ │ etcd 集群 │ │ 负载均衡器 │ │
│ │ N+1 冗余 │ │ 3/5 副本 │ │ 双机热备 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
▲
┌─────────────────────────────────────────────────────────┐
│ 基础设施高可用 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 多机房部署 │ │ 网络冗余 │ │ 存储冗余 │ │
│ │ 跨 AZ │ │ 多 CNI │ │ 多 Storage │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
本文重点聚焦于控制平面高可用,这是构建稳定 Kubernetes 集群的基石。
2. HA 集群架构设计
2.1 推荐架构(3 Master + 3 etcd + N Worker)
我们采用业界广泛验证的经典架构:基于 kubeadm,配合 Keepalived + HAProxy 实现负载均衡和高可用。
┌─────────────────┐
│ 外部负载均衡 │
│ (F5/Nginx/SLB)│
└────────┬────────┘
│
┌────────▼────────┐
│ VIP 浮动 IP │
│ (Keepalived) │
└────────┬────────┘
│
┌───────────────────────────────────┼───────────────────────────────────┐
│ │ │
│ ┌────────▼────────┐ │
│ │ HAProxy 集群 │ │
│ │ (主备模式) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ │ │ │ │
│ ┌─────────▼─────────┐ ┌────────▼────────┐ ┌────────▼─────────┐ │
│ │ Master Node 1 │ │ Master Node 2 │ │ Master Node 3 │ │
│ │ │ │ │ │ │ │
│ │ - API Server │ │ - API Server │ │ - API Server │ │
│ │ - Controller │ │ - Controller │ │ - Controller │ │
│ │ - Scheduler │ │ - Scheduler │ │ - Scheduler │ │
│ │ - etcd (内嵌) │ │ - etcd (内嵌) │ │ - etcd (内嵌) │ │
│ └─────────┬─────────┘ └────────┬────────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ │ │
│ ┌────────────────────┼────────────────────┐ │
│ │ │ │ │
│ ┌─────────▼─────────┐ ┌────────▼────────┐ ┌────────▼─────────┐ │
│ │ Worker Node 1 │ │ Worker Node 2 │ │ Worker Node N │ │
│ │ │ │ │ │ │ │
│ │ - Kubelet │ │ - Kubelet │ │ - Kubelet │ │
│ │ - Kube-proxy │ │ - Kube-proxy │ │ - Kube-proxy │ │
│ │ - Container Runtime│- Container Runtime│- Container Runtime│ │
│ │ - Pod │ │ - Pod │ │ - Pod │ │
│ └───────────────────┘ └─────────────────┘ └──────────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────────┘
核心思想:所有组件均无单点。
- 负载均衡:通过 Keepalived 的 VIP 提供单一访问入口,由 HAProxy 将流量分发到后端的多个 API Server。
- 控制平面:至少三个 Master 节点,奇数个避免脑裂。
- 数据存储:etcd 以集群模式运行,同样为奇数个副本。
2.2 架构选型对比
没有最好的架构,只有最适合的。下面是几种常见方案的对比:
| 架构模式 |
优点 |
缺点 |
适用场景 |
| 堆叠 etcd |
部署简单,资源节省 |
etcd 与 API Server 相互影响 |
中小规模集群 (<100 节点) |
| 独立 etcd |
性能隔离,更稳定 |
需要额外节点 |
大规模集群 (>100 节点) |
| 外部 LB |
高可用,性能好 |
成本高,依赖外部设备 |
企业生产环境 |
| Keepalived+HAProxy |
开源免费,灵活 |
需要自行维护 |
通用场景 |
本文选择 Keepalived+HAProxy 作为负载均衡方案,堆叠式 etcd 以简化部署,这是绝大多数场景下的最佳平衡点。
2.3 网络规划
清晰的网络规划是成功的一半。以下是一个示例方案:
网络规划示例:
管理网络:192.168.1.0/24
- Master 节点:192.168.1.10-192.168.1.12
- Worker 节点:192.168.1.20-192.168.1.50
- HAProxy:192.168.1.5-192.168.1.6
- VIP:192.168.1.100
Pod 网络:10.244.0.0/16
- 每个节点分配 /24 子网
Service 网络:10.96.0.0/12
请根据你的实际环境进行调整。
3. 环境规划与准备
3.1 服务器规划
建议的最小化生产环境配置如下:
| 主机名 |
IP 地址 |
角色 |
配置 |
数量 |
| lb-01 |
192.168.1.5 |
负载均衡主 |
2C4G |
1 |
| lb-02 |
192.168.1.6 |
负载均衡备 |
2C4G |
1 |
| master-01 |
192.168.1.10 |
控制节点 |
4C8G |
1 |
| master-02 |
192.168.1.11 |
控制节点 |
4C8G |
1 |
| master-03 |
192.168.1.12 |
控制节点 |
4C8G |
1 |
| worker-01 |
192.168.1.20 |
工作节点 |
8C16G |
1 |
| worker-02 |
192.168.1.21 |
工作节点 |
8C16G |
1 |
| worker-03 |
192.168.1.22 |
工作节点 |
8C16G |
1 |
说明:Master 节点资源主要用于运行控制平面组件;Worker 节点资源根据实际业务负载决定。
3.2 系统初始化脚本
在所有节点上执行此脚本,完成基础环境配置。
#!/bin/bash
# init.sh - 系统初始化脚本
set -e
echo "========== 开始系统初始化 =========="
# 1. 关闭防火墙
echo "关闭防火墙..."
systemctl stop firewalld || true
systemctl disable firewalld || true
systemctl stop ufw || true
systemctl disable ufw || true
# 2. 关闭 SELinux
echo "关闭 SELinux..."
setenforce 0 || true
sed -i 's/^SELINUX=enforcing$/SELINUX=disabled/' /etc/selinux/config || true
# 3. 关闭 Swap
echo "关闭 Swap..."
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
# 4. 配置内核参数
echo "配置内核参数..."
cat > /etc/sysctl.d/k8s.conf << EOF
# 启用 IP 转发
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
# 桥接网络监听
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
# 内存管理
vm.swappiness = 0
vm.overcommit_memory = 1
vm.panic_on_oom = 0
# 文件句柄
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512
# 网络连接
net.core.somaxconn = 32768
net.ipv4.tcp_max_syn_backlog = 8096
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 1024 65535
EOF
sysctl --system
# 5. 配置系统限制
echo "配置系统限制..."
cat > /etc/security/limits.d/k8s.conf << EOF
* soft nofile 65536
* hard nofile 65536
* soft nproc 65536
* hard nproc 65536
* soft memlock unlimited
* hard memlock unlimited
EOF
# 6. 配置时间同步
echo "配置时间同步..."
timedatectl set-timezone Asia/Shanghai
systemctl enable chronyd || systemctl enable ntpd
systemctl start chronyd || systemctl start ntpd
# 7. 配置 hosts
echo "配置 hosts..."
cat >> /etc/hosts << EOF
192.168.1.5 lb-01
192.168.1.6 lb-02
192.168.1.10 master-01
192.168.1.11 master-02
192.168.1.12 master-03
192.168.1.20 worker-01
192.168.1.21 worker-02
192.168.1.22 worker-03
192.168.1.100 k8s-ha-vip
EOF
# 8. 加载内核模块
echo "加载内核模块..."
cat > /etc/modules-load.d/k8s.conf << EOF
overlay
br_netfilter
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack
EOF
modprobe overlay
modprobe br_netfilter
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe nf_conntrack
# 9. 配置 SSH 免密(可选)
echo "配置 SSH 免密..."
# ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
# ssh-copy-id root@master-01
# ssh-copy-id root@master-02
# ssh-copy-id root@master-03
# ssh-copy-id root@worker-01
# ssh-copy-id root@worker-02
# ssh-copy-id root@worker-03
echo "========== 系统初始化完成 =========="
3.3 安装容器运行时
我们选择 containerd 作为容器运行时,它是目前 Kubernetes 社区推荐的选择。
#!/bin/bash
# install-containerd.sh - 安装 containerd
set -e
echo "========== 安装 containerd =========="
# 1. 安装依赖
apt-get update || yum update -y
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release || \
yum install -y yum-utils device-mapper-persistent-data lvm2
# 2. 添加 Docker 官方源(containerd)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg || \
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 3. 安装 containerd
apt-get update || yum makecache fast
apt-get install -y containerd.io || yum install -y containerd.io
# 4. 生成默认配置
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
# 5. 修改配置(使用 systemd cgroup)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sed -i 's#k8s.gcr.io/pause:3.6#registry.aliyuncs.com/google_containers/pause:3.9#' /etc/containerd/config.toml
# 6. 启动 containerd
systemctl daemon-reload
systemctl enable containerd
systemctl start containerd
# 7. 验证安装
crictl --runtime-endpoint unix:///run/containerd/containerd.sock info
echo "========== containerd 安装完成 =========="
3.4 安装 Kubernetes 组件
在所有节点上安装 kubelet、kubeadm 和 kubectl。
#!/bin/bash
# install-k8s.sh - 安装 Kubernetes 组件
set -e
# 配置参数
K8S_VERSION="1.28.4"
CR_VERSION="0.12.0" # crictl 版本
echo "========== 安装 Kubernetes v${K8S_VERSION} =========="
# 1. 添加 Kubernetes 源
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.28/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
# 2. 安装 kubelet, kubeadm, kubectl
yum install -y kubelet-${K8S_VERSION} kubeadm-${K8S_VERSION} kubectl-${K8S_VERSION} --disableexcludes=kubernetes
# 3. 配置 kubelet
cat > /etc/sysconfig/kubelet << EOF
KUBELET_EXTRA_ARGS="--cgroup-driver=systemd --container-runtime-endpoint=unix:///run/containerd/containerd.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9"
EOF
# 4. 启动 kubelet
systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet
# 5. 安装 crictl
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v${CR_VERSION}/crictl-v${CR_VERSION}-linux-amd64.tar.gz
tar zxvf crictl-v${CR_VERSION}-linux-amd64.tar.gz -C /usr/local/bin
rm -f crictl-v${CR_VERSION}-linux-amd64.tar.gz
# 6. 配置 crictl
cat > /etc/crictl.yaml << EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 2
debug: false
pull-image-on-create: false
EOF
# 7. 配置 kubectl 自动补全
echo "source <(kubectl completion bash)" >> ~/.bashrc
echo "alias k=kubectl" >> ~/.bashrc
echo "complete -F __start_kubectl k" >> ~/.bashrc
echo "========== Kubernetes 安装完成 =========="
echo "验证安装:"
echo " kubelet --version"
echo " kubeadm version"
echo " kubectl version --client"
4. 部署负载均衡层
高可用的第一道关口。我们将使用 Keepalived 实现 VIP 飘移,HAProxy 实现四层负载均衡。
4.1 部署 Keepalived + HAProxy
在 lb-01 和 lb-02 节点上执行此脚本。
#!/bin/bash
# deploy-haproxy.sh - 在所有 LB 节点执行
set -e
echo "========== 部署 HAProxy + Keepalived =========="
# 1. 安装 HAProxy 和 Keepalived
yum install -y haproxy keepalived
# 2. 配置 HAProxy
cat > /etc/haproxy/haproxy.cfg << 'EOF'
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
option tcplog
option dontlognull
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
# API Server 负载均衡
listen k8s-api-server
bind 0.0.0.0:6443
mode tcp
balance roundrobin
option tcp-check
server master-01 192.168.1.10:6443 check fall 3 rise 2 inter 5s
server master-02 192.168.1.11:6443 check fall 3 rise 2 inter 5s
server master-03 192.168.1.12:6443 check fall 3 rise 2 inter 5s
# kubelet API 负载均衡
listen k8s-kubelet-api
bind 0.0.0.0:10250
mode tcp
balance roundrobin
option tcp-check
server worker-01 192.168.1.20:10250 check fall 3 rise 2 inter 5s
server worker-02 192.168.1.21:10250 check fall 3 rise 2 inter 5s
server worker-03 192.168.1.22:10250 check fall 3 rise 2 inter 5s
# etcd 负载均衡
listen k8s-etcd
bind 0.0.0.0:2379
mode tcp
balance roundrobin
option tcp-check
server master-01 192.168.1.10:2379 check fall 3 rise 2 inter 5s
server master-02 192.168.1.11:2379 check fall 3 rise 2 inter 5s
server master-03 192.168.1.12:2379 check fall 3 rise 2 inter 5s
# 健康检查页面
listen stats
bind 0.0.0.0:8888
mode http
stats enable
stats uri /haproxy?stats
stats refresh 5s
stats admin if LOCALHOST
EOF
# 3. 配置 Keepalived(主节点 lb-01)
cat > /etc/keepalived/keepalived.conf << 'EOF'
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script check_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip
}
track_script {
check_haproxy
}
notify_master "/etc/keepalived/master.sh"
notify_backup "/etc/keepalived/backup.sh"
notify_fault "/etc/keepalived/fault.sh"
}
EOF
# 4. 配置 Keepalived(备节点 lb-02)
# 在备节点上,修改 state 为 BACKUP,priority 为 90
cat > /etc/keepalived/keepalived.conf << 'EOF'
! Configuration File for keepalived
global_defs {
router_id LVS_DEVEL
script_user root
enable_script_security
}
vrrp_script check_haproxy {
script "/etc/keepalived/check_haproxy.sh"
interval 2
weight -20
fall 3
rise 2
}
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.1.100/24 dev eth0 label eth0:vip
}
track_script {
check_haproxy
}
}
EOF
# 5. 创建健康检查脚本
cat > /etc/keepalived/check_haproxy.sh << 'EOF'
#!/bin/bash
if ! pgrep -x "haproxy" > /dev/null; then
exit 1
fi
exit 0
EOF
chmod +x /etc/keepalived/check_haproxy.sh
# 6. 创建通知脚本
cat > /etc/keepalived/master.sh << 'EOF'
#!/bin/bash
echo "Become MASTER, start HAProxy"
systemctl start haproxy
EOF
chmod +x /etc/keepalived/master.sh
cat > /etc/keepalived/backup.sh << 'EOF'
#!/bin/bash
echo "Become BACKUP"
EOF
chmod +x /etc/keepalived/backup.sh
cat > /etc/keepalived/fault.sh << 'EOF'
#!/bin/bash
echo "Enter FAULT state"
EOF
chmod +x /etc/keepalived/fault.sh
# 7. 启动服务
systemctl daemon-reload
systemctl enable haproxy
systemctl start haproxy
systemctl enable keepalived
systemctl start keepalived
echo "========== HAProxy + Keepalived 部署完成 =========="
echo "VIP: 192.168.1.100"
echo "HAProxy Stats: http://<IP>:8888/haproxy?stats"
4.2 验证负载均衡
部署完成后,务必进行验证。
# 检查 VIP 状态
ip addr show eth0 | grep 192.168.1.100
# 检查 HAProxy 状态
systemctl status haproxy
curl http://localhost:8888/haproxy?stats
# 测试 API Server 连通性
curl -k https://192.168.1.100:6443/healthz
# 查看连接状态
echo "show stat" | socat stdio /var/lib/haproxy/stats | grep k8s-api-server
5. 部署 etcd 集群
etcd 是 Kubernetes 的“数据库”,其高可用性至关重要。我们采用堆叠式部署,即将 etcd 与 Master 组件部署在同一节点上。
5.1 独立 etcd 集群部署(推荐)
如果你追求更高的性能和稳定性,可以将 etcd 部署在独立的节点上。这里以堆叠式为例,在 Master 节点上部署。
#!/bin/bash
# deploy-etcd.sh - 在 Master 节点执行
set -e
ETCD_VERSION="3.5.9"
CLUSTER_TOKEN="k8s-etcd-cluster"
ETCD_DATA_DIR="/var/lib/etcd"
echo "========== 部署 etcd 集群 =========="
# 1. 下载 etcd
wget https://github.com/etcd-io/etcd/releases/download/v${ETCD_VERSION}/etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
tar zxvf etcd-v${ETCD_VERSION}-linux-amd64.tar.gz
mv etcd-v${ETCD_VERSION}-linux-amd64/{etcd,etcdctl} /usr/local/bin/
rm -rf etcd-v${ETCD_VERSION}-linux-amd64*
# 2. 创建 etcd 用户
useradd -r -s /sbin/nologin etcd
mkdir -p ${ETCD_DATA_DIR}
chown -R etcd:etcd ${ETCD_DATA_DIR}
# 3. 生成 etcd 证书(使用 kubeadm 生成的证书)
# 或手动生成
mkdir -p /etc/kubernetes/pki/etcd
# 4. 配置 etcd 服务(master-01)
cat > /etc/systemd/system/etcd.service << EOF
[Unit]
Description=etcd
Documentation=https://github.com/coreos/etcd
After=network.target
[Service]
Type=notify
User=etcd
Group=etcd
ExecStart=/usr/local/bin/etcd \
--name=etcd-1 \
--data-dir=${ETCD_DATA_DIR} \
--listen-client-urls=https://192.168.1.10:2379,https://127.0.0.1:2379 \
--advertise-client-urls=https://192.168.1.10:2379 \
--listen-peer-urls=https://192.168.1.10:2380 \
--initial-advertise-peer-urls=https://192.168.1.10:2380 \
--initial-cluster=etcd-1=https://192.168.1.10:2380,etcd-2=https://192.168.1.11:2380,etcd-3=https://192.168.1.12:2380 \
--initial-cluster-token=${CLUSTER_TOKEN} \
--initial-cluster-state=new \
--client-cert-auth=true \
--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
--cert-file=/etc/kubernetes/pki/etcd/server.crt \
--key-file=/etc/kubernetes/pki/etcd/server.key \
--peer-client-cert-auth=true \
--peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
--peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt \
--peer-key-file=/etc/kubernetes/pki/etcd/peer.key \
--auto-compaction-retention=1 \
--quota-backend-bytes=8589934592 \
--heartbeat-interval=250 \
--election-timeout=2000
Restart=always
RestartSec=5
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
EOF
# 5. 在 master-02 和 master-03 上修改相应配置
# master-02: name=etcd-2, IP=192.168.1.11
# master-03: name=etcd-3, IP=192.168.1.12
# 6. 启动 etcd
systemctl daemon-reload
systemctl enable etcd
systemctl start etcd
# 7. 验证集群状态
etcdctl --endpoints=https://192.168.1.10:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
member list
etcdctl --endpoints=https://192.168.1.10:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint status --write-out=table
echo "========== etcd 集群部署完成 =========="
5.2 etcd 性能优化
对于生产环境,建议调整以下参数以获得最佳性能。
# etcd 性能调优参数
# /etc/systemd/system/etcd.service
ExecStart=/usr/local/bin/etcd \
# 数据目录(使用 SSD)
--data-dir=/var/lib/etcd \
# 自动压缩(保留最近 1 小时数据)
--auto-compaction-retention=1 \
# 后端数据库大小限制(8GB)
--quota-backend-bytes=8589934592 \
# 心跳间隔(250ms)
--heartbeat-interval=250 \
# 选举超时(2000ms)
--election-timeout=2000 \
# 快照计数
--snapshot-count=10000 \
# 最大请求大小(1.5MB)
--max-request-bytes=1572864 \
# 预分配空间
--prealloc=16384 \
5.3 etcd 备份与恢复
定期备份是 etcd 运维的黄金法则。你必须有一个可靠的备份恢复方案。
备份脚本:
#!/bin/bash
# etcd-backup.sh - etcd 备份脚本
set -e
BACKUP_DIR="/backup/etcd"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/etcd-snapshot-${DATE}.db"
mkdir -p ${BACKUP_DIR}
# 备份
etcdctl --endpoints=https://192.168.1.10:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save ${BACKUP_FILE}
# 验证备份
etcdctl snapshot status ${BACKUP_FILE} --write-out=table
# 清理 7 天前的备份
find ${BACKUP_DIR} -name "etcd-snapshot-*.db" -mtime +7 -delete
echo "备份完成:${BACKUP_FILE}"
恢复脚本:
#!/bin/bash
# etcd-restore.sh - etcd 恢复脚本
set -e
BACKUP_FILE=$1
if [ -z "${BACKUP_FILE}" ]; then
echo "用法:$0 <备份文件>"
exit 1
fi
# 停止 etcd
systemctl stop etcd
# 清除旧数据
rm -rf /var/lib/etcd/*
# 恢复数据
etcdctl snapshot restore ${BACKUP_FILE} \
--data-dir=/var/lib/etcd \
--name=etcd-1 \
--initial-cluster=etcd-1=https://192.168.1.10:2380,etcd-2=https://192.168.1.11:2380,etcd-3=https://192.168.1.12:2380 \
--initial-cluster-token=k8s-etcd-cluster \
--initial-advertise-peer-urls=https://192.168.1.10:2380
# 设置权限
chown -R etcd:etcd /var/lib/etcd
# 启动 etcd
systemctl start etcd
echo "恢复完成"
6. 部署多 Master 节点
这是构建高可用集群的核心步骤。我们将使用 kubeadm 来初始化第一个 Master,然后将其它 Master 加入集群。
6.1 生成 kubeadm 配置
首先,创建一个详细的 kubeadm 配置文件,它能让你对集群有更精细的控制。
# kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.28.4
# 控制平面配置
controlPlaneEndpoint: "192.168.1.100:6443" # VIP 地址
# API Server 配置
apiServer:
certSANs:
- "192.168.1.100"
- "192.168.1.10"
- "192.168.1.11"
- "192.168.1.12"
- "kubernetes"
- "kubernetes.default"
- "kubernetes.default.svc"
- "kubernetes.default.svc.cluster.local"
- "localhost"
- "127.0.0.1"
extraArgs:
authorization-mode: Node,RBAC
enable-admission-plugins: NodeRestriction,PodSecurityPolicy
audit-log-path: /var/log/kubernetes/audit.log
audit-log-maxage: "30"
audit-log-maxbackup: "10"
audit-log-maxsize: "100"
extraVolumes:
- name: audit-log
hostPath: /var/log/kubernetes
mountPath: /var/log/kubernetes
readOnly: false
pathType: DirectoryOrCreate
# Controller Manager 配置
controllerManager:
extraArgs:
bind-address: 0.0.0.0
node-cidr-mask-size: "24"
node-monitor-grace-period: 40s
pod-eviction-timeout: 5m0s
# Scheduler 配置
scheduler:
extraArgs:
bind-address: 0.0.0.0
# etcd 配置(独立部署)
etcd:
external:
endpoints:
- https://192.168.1.10:2379
- https://192.168.1.11:2379
- https://192.168.1.12:2379
caFile: /etc/kubernetes/pki/etcd/ca.crt
certFile: /etc/kubernetes/pki/apiserver-etcd-client.crt
keyFile: /etc/kubernetes/pki/apiserver-etcd-client.key
# 网络配置
networking:
dnsDomain: cluster.local
serviceSubnet: 10.96.0.0/12
podSubnet: 10.244.0.0/16
# 镜像仓库
imageRepository: registry.aliyuncs.com/google_containers
---
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
containerRuntimeEndpoint: unix:///run/containerd/containerd.sock
maxPods: 110
podPidsLimit: 4096
resolvConf: /etc/resolv.conf
rotateCertificates: true
serverTLSBootstrap: true
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"
---
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: ipvs
ipvs:
strictARP: true
scheduler: "lc"
6.2 初始化第一个 Master
在 master-01 节点上执行初始化。
#!/bin/bash
# init-first-master.sh
set -e
echo "========== 初始化第一个 Master 节点 =========="
# 1. 拉取镜像
kubeadm config images pull --config kubeadm-config.yaml
# 2. 初始化集群
kubeadm init --config kubeadm-config.yaml \
--upload-certs \
--v=5
# 3. 配置 kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 4. 验证集群
kubectl cluster-info
kubectl get nodes
kubectl get pods -n kube-system
# 5. 保存 join 命令
kubeadm token create --print-join-command > /tmp/join-command.sh
chmod +x /tmp/join-command.sh
# 6. 获取证书密钥
kubeadm init phase upload-certs --upload-certs --config kubeadm-config.yaml
echo "========== 第一个 Master 初始化完成 =========="
echo "请保存以下信息:"
echo "1. Join 命令:cat /tmp/join-command.sh"
echo "2. 证书密钥:kubeadm init phase upload-certs --upload-certs"
6.3 加入其他 Master 节点
在 master-02 和 master-03 上执行加入操作。
#!/bin/bash
# join-master.sh
set -e
echo "========== 加入 Master 节点 =========="
# 从第一个 Master 复制证书
# 方式 1:使用 kubeadm join 命令(推荐)
kubeadm join 192.168.1.100:6443 \
--control-plane \
--certificate-key <certificate-key> \
--apiserver-advertise-address=<当前节点 IP>
# 方式 2:手动复制证书
# scp root@master-01:/etc/kubernetes/pki/* /etc/kubernetes/pki/
# scp -r root@master-01:/etc/kubernetes/admin.conf /etc/kubernetes/kubelet.conf
# 配置 kubectl
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
echo "========== Master 节点加入完成 =========="
6.4 验证 Master 高可用
所有 Master 加入后,进行高可用性验证。
# 查看所有节点状态
kubectl get nodes -o wide
# 查看控制平面组件
kubectl get pods -n kube-system -l component in (kube-apiserver,kube-controller-manager,kube-scheduler)
# 查看 etcd 状态
kubectl get pods -n kube-system -l component=etcd
# 测试 API Server 故障转移
# 1. 停止 master-01 的 API Server
ssh root@master-01 "systemctl stop kubelet"
# 2. 验证集群仍然可用
kubectl get nodes
# 3. 恢复 master-01
ssh root@master-01 "systemctl start kubelet"
# 查看集群信息
kubectl cluster-info
7. 部署 Worker 节点
控制平面就绪后,就可以将计算节点加入集群了。
7.1 加入 Worker 节点
在 Worker 节点上执行从第一个 Master 获取的 join 命令。
#!/bin/bash
# join-worker.sh
set -e
echo "========== 加入 Worker 节点 =========="
# 在 Master 节点生成 join 命令
# kubeadm token create --print-join-command
# 在 Worker 节点执行
kubeadm join 192.168.1.100:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash> \
--apiserver-advertise-address=<当前节点 IP> \
--node-name=<节点名称> \
--cri-socket unix:///run/containerd/containerd.sock
# 配置节点标签
kubectl label node worker-01 node-role.kubernetes.io/worker=worker
kubectl label node worker-01 node-type=general
# 配置节点污点(可选)
kubectl taint node worker-01 dedicated=special-workload:NoSchedule
echo "========== Worker 节点加入完成 =========="
7.2 节点配置优化
针对 Worker 节点进行一些优化,以提升稳定性和性能。
#!/bin/bash
# optimize-worker.sh
set -e
# 1. 配置系统参数
cat >> /etc/sysctl.d/k8s-worker.conf << EOF
# 优化网络连接
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
# 优化文件句柄
fs.file-max = 2097152
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 8192
# 内存优化
vm.swappiness = 0
vm.overcommit_memory = 1
vm.vfs_cache_pressure = 50
EOF
sysctl --system
# 2. 配置容器运行时
cat >> /etc/containerd/config.toml << EOF
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://registry-1.docker.io"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
endpoint = ["https://gcr.io"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
endpoint = ["https://registry.aliyuncs.com/google_containers"]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."quay.io"]
endpoint = ["https://quay.io"]
EOF
systemctl restart containerd
# 3. 配置 kubelet 参数
cat > /var/lib/kubelet/config.yaml << EOF
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
cgroupDriver: systemd
containerRuntimeEndpoint: unix:///run/containerd/containerd.sock
maxPods: 110
podPidsLimit: 4096
serializeImagePulls: false
imagePullProgressDeadline: 5m
evictionHard:
memory.available: "100Mi"
nodefs.available: "10%"
nodefs.inodesFree: "5%"
imagefs.available: "15%"
evictionSoft:
memory.available: "300Mi"
nodefs.available: "15%"
evictionSoftGracePeriod:
memory.available: 2m
nodefs.available: 2m
evictionMaxPodGracePeriod: 180
EOF
systemctl restart kubelet
echo "========== Worker 节点优化完成 =========="
8. 部署网络插件与存储
集群节点就绪后,需要安装网络插件使 Pod 之间能够通信,并配置存储以满足有状态应用的需求。
8.1 部署 Calico 网络
Calico 是性能出色的网络插件,也支持网络策略。
# calico.yaml
apiVersion: operator.tigera.io/v1
kind: Installation
metadata:
name: default
spec:
# 使用 Calico 网络
variant: Calico
# 镜像仓库
registry: registry.aliyuncs.com/calico
# 网络配置
calicoNetwork:
# IP 池配置
ipPools:
- blockSize: 26
cidr: 10.244.0.0/16
encapsulation: VXLANCrossSubnet
natOutgoing: Enabled
nodeSelector: all()
# BGP 配置(跨子网通信)
bgp: Enabled
# 多接口支持
nodeAddressAutodetectionV4:
firstFound: true
# 控制平面副本数
controlPlaneReplicas: 3
# 组件配置
componentResources:
- componentName: Node
resourceRequirements:
requests:
cpu: 250m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
- componentName: Typha
resourceRequirements:
requests:
cpu: 100m
memory: 32Mi
limits:
cpu: 200m
memory: 64Mi
---
apiVersion: operator.tigera.io/v1
kind: APIServer
metadata:
name: default
spec: {}
# 部署 Calico
kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
kubectl apply -f calico.yaml
# 验证网络
kubectl get pods -n calico-system
kubectl get nodes -o wide
# 测试网络连通性
kubectl run test1 --image=busybox --restart=Never -- sleep 3600
kubectl run test2 --image=busybox --restart=Never -- sleep 3600
kubectl exec test1 -- ping -c 4 test2
8.2 部署 CoreDNS
CoreDNS 是集群的服务发现核心,需要确保其高可用。
# coredns-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . 114.114.114.114 8.8.8.8 {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
# 部署 CoreDNS(高可用)
kubectl scale deployment coredns -n kube-system --replicas=3
# 配置 Pod 反亲和性
kubectl patch deployment coredns -n kube-system --type=json -p='
[
{"op": "add", "path": "/spec/template/spec/affinity",
"value": {
"podAntiAffinity": {
"preferredDuringSchedulingIgnoredDuringExecution": [{
"weight": 100,
"podAffinityTerm": {
"labelSelector": {
"matchExpressions": [{
"key": "k8s-app",
"operator": "In",
"values": ["kube-dns"]
}]
},
"topologyKey": "kubernetes.io/hostname"
}
}]
}
}
}
]'
8.3 部署存储(NFS 示例)
为有状态应用提供持久化存储。这里以 NFS 为例创建 StorageClass。
# nfs-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-storage
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: nfs.csi.k8s.io
parameters:
server: 192.168.1.50
share: /data/kubernetes
reclaimPolicy: Retain
volumeBindingMode: Immediate
mountOptions:
- hard
- timeo=130
- retrans=2
- noresvport
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 100Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs-storage
nfs:
server: 192.168.1.50
path: /data/kubernetes
mountOptions:
- hard
- timeo=130
9. 验证高可用能力
部署完成不代表万事大吉,必须通过测试来验证高可用能力是否真正生效。
9.1 故障转移测试
模拟各种故障场景,观察集群的自我恢复能力。
#!/bin/bash
# ha-test.sh - 高可用测试脚本
set -e
echo "========== 开始高可用测试 =========="
# 测试 1:Master 节点故障
echo "测试 1:Master 节点故障转移"
kubectl get nodes
echo "停止 master-01..."
ssh root@master-01 "systemctl stop kubelet"
sleep 10
kubectl get nodes
echo "恢复 master-01..."
ssh root@master-01 "systemctl start kubelet"
sleep 30
# 测试 2:API Server 故障
echo "测试 2:API Server 故障转移"
echo "停止 master-02 的 API Server..."
ssh root@master-02 "crictl pods --name kube-apiserver -q | xargs -r crictl stopp"
sleep 10
kubectl get nodes
echo "验证 API 可用性..."
kubectl get pods -n kube-system
# 测试 3:etcd 故障
echo "测试 3:etcd 故障转移"
echo "停止 master-03 的 etcd..."
ssh root@master-03 "systemctl stop etcd"
sleep 10
kubectl get nodes
echo "验证集群状态..."
kubectl get pods -n kube-system
echo "恢复 etcd..."
ssh root@master-03 "systemctl start etcd"
# 测试 4:负载均衡故障
echo "测试 4:负载均衡故障转移"
echo "停止 lb-01 的 Keepalived..."
ssh root@lb-01 "systemctl stop keepalived"
sleep 5
echo "检查 VIP 漂移..."
ssh root@lb-02 "ip addr show eth0 | grep 192.168.1.100"
kubectl get nodes
echo "恢复 Keepalived..."
ssh root@lb-01 "systemctl start keepalived"
# 测试 5:网络分区模拟
echo "测试 5:网络分区模拟"
echo "隔离 master-01 的网络..."
ssh root@master-01 "iptables -A INPUT -j DROP"
sleep 30
kubectl get nodes
echo "恢复网络..."
ssh root@master-01 "iptables -F"
echo "========== 高可用测试完成 =========="
9.2 性能基准测试
量化集群的性能表现,建立性能基线。
#!/bin/bash
# performance-test.sh
echo "========== 性能基准测试 =========="
# 1. API Server 响应时间
echo "API Server 响应时间测试..."
for i in {1..100}; do
start=$(date +%s%N)
kubectl get pods > /dev/null 2>&1
end=$(date +%s%N)
echo "请求 $i: $(( (end - start) / 1000000 )) ms"
done
# 2. Pod 启动时间
echo "Pod 启动时间测试..."
time kubectl run test-perf --image=nginx --restart=Never
kubectl wait --for=condition=ready pod/test-perf --timeout=120s
kubectl delete pod test-perf
# 3. 调度延迟
echo "调度延迟测试..."
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: perf-test
spec:
replicas: 10
selector:
matchLabels:
app: perf-test
template:
metadata:
labels:
app: perf-test
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
cpu: 100m
memory: 64Mi
EOF
time kubectl rollout status deployment/perf-test
kubectl delete deployment perf-test
echo "========== 性能测试完成 =========="
10. 监控与告警配置
没有监控的系统就像在黑暗中开车。我们需要一套完整的监控告警体系来保障集群健康。
10.1 部署 Prometheus Operator
使用 Helm 快速部署完整的监控栈。
# 添加 Helm 仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
# 部署 kube-prometheus-stack
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false \
--set prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false \
--set grafana.adminPassword=admin123 \
--set alertmanager.alertmanagerSpec.replicas=3
10.2 K8s 集群监控告警规则
定义关键的告警规则,以便在问题出现时及时获知。
# k8s-alerts.yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: k8s-cluster-alerts
namespace: monitoring
labels:
prometheus: prometheus
role: alert-rules
spec:
groups:
- name: k8s.rules
rules:
# Master 节点不可用
- alert: K8sMasterNodeNotReady
expr: kube_node_status_condition{node=~".*master.*", condition="Ready", status="true"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Master 节点 {{ $labels.node }} 未就绪"
description: "Master 节点 {{ $labels.node }} 已经 5 分钟未就绪"
# Worker 节点不可用
- alert: K8sWorkerNodeNotReady
expr: kube_node_status_condition{node=~".*worker.*", condition="Ready", status="true"} == 0
for: 5m
labels:
severity: warning
annotations:
summary: "Worker 节点 {{ $labels.node }} 未就绪"
description: "Worker 节点 {{ $labels.node }} 已经 5 分钟未就绪"
# etcd 成员缺失
- alert: K8sEtcdMemberMissing
expr: count(etcd_server_has_leader) < 3
for: 2m
labels:
severity: critical
annotations:
summary: "etcd 集群成员缺失"
description: "etcd 集群当前只有 {{ $value }} 个成员,少于 3 个"
# etcd 无 Leader
- alert: K8sEtcdNoLeader
expr: etcd_server_has_leader == 0
for: 1m
labels:
severity: critical
annotations:
summary: "etcd 集群无 Leader"
description: "etcd 集群当前没有 Leader,无法提供服务"
# API Server 不可用
- alert: K8sAPIServerDown
expr: up{job="kubernetes-apiservers"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "API Server 不可用"
description: "API Server {{ $labels.instance }} 已经 2 分钟不可用"
# 节点 CPU 使用率过高
- alert: K8sNodeCPUHigh
expr: 100 - (avg by(instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 85
for: 10m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} CPU 使用率过高"
description: "节点 {{ $labels.instance }} CPU 使用率 {{ $value }}%"
# 节点内存使用率过高
- alert: K8sNodeMemoryHigh
expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
for: 10m
labels:
severity: warning
annotations:
summary: "节点 {{ $labels.instance }} 内存使用率过高"
description: "节点 {{ $labels.instance }} 内存使用率 {{ $value }}%"
# Pod 频繁重启
- alert: K8sPodCrashLooping
expr: rate(kube_pod_container_status_restarts_total[15m]) * 60 * 5 > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} 频繁重启"
description: "Pod {{ $labels.pod }} 在 5 分钟内重启 {{ $value }} 次"
# 持久卷空间不足
- alert: K8sPVSpaceLow
expr: (kubelet_volume_stats_available_bytes / kubelet_volume_stats_capacity_bytes) * 100 < 15
for: 5m
labels:
severity: warning
annotations:
summary: "持久卷 {{ $labels.persistentvolumeclaim }} 空间不足"
description: "持久卷 {{ $labels.persistentvolumeclaim }} 剩余空间 {{ $value }}%"
10.3 Grafana 仪表板
导入预置的仪表板,快速可视化集群状态。
# 导入预置仪表板
# 315 - Kubernetes cluster monitoring (via Prometheus)
# 6417 - Kubernetes API server
# 594 - etcd dashboard
# 7 - Node Exporter Full
# 访问 Grafana
kubectl port-forward svc/prometheus-grafana -n monitoring 3000:80
# 用户名:admin, 密码:admin123
11. 故障排查与恢复
即使做了万全准备,故障仍有可能发生。掌握排查和恢复的方法至关重要。
11.1 常见故障排查
一个集成的排查脚本,可以帮助你快速定位问题。
#!/bin/bash
# troubleshoot.sh - 故障排查脚本
echo "========== K8s 集群故障排查 =========="
# 1. 检查节点状态
echo "=== 节点状态 ==="
kubectl get nodes -o wide
kubectl describe node $(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')
# 2. 检查系统 Pod
echo "=== 系统 Pod 状态 ==="
kubectl get pods -n kube-system -o wide
kubectl get pods -n kube-system --field-selector=status.phase!=Running
# 3. 检查 etcd 状态
echo "=== etcd 状态 ==="
etcdctl --endpoints=https://192.168.1.10:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint status --write-out=table
# 4. 检查证书有效期
echo "=== 证书有效期 ==="
for cert in /etc/kubernetes/pki/*.crt /etc/kubernetes/pki/etcd/*.crt; do
echo "$cert:"
openssl x509 -in $cert -noout -dates
done
# 5. 检查组件日志
echo "=== API Server 日志 ==="
kubectl logs -n kube-system -l component=kube-apiserver --tail=50
echo "=== Controller Manager 日志 ==="
kubectl logs -n kube-system -l component=kube-controller-manager --tail=50
echo "=== Scheduler 日志 ==="
kubectl logs -n kube-system -l component=kube-scheduler --tail=50
# 6. 检查网络
echo "=== 网络连通性 ==="
kubectl run test-network --image=busybox --rm -it --restart=Never -- ping -c 4 kubernetes.default
# 7. 检查资源使用
echo "=== 资源使用 ==="
kubectl top nodes
kubectl top pods -n kube-system
# 8. 检查事件
echo "=== 集群事件 ==="
kubectl get events -n kube-system --sort-by='.lastTimestamp'
11.2 Master 节点恢复
当某个 Master 节点彻底故障时,如何安全地将其移除并重新加入。
#!/bin/bash
# recover-master.sh - Master 节点恢复
set -e
MASTER_TO_RECOVER=$1
if [ -z "${MASTER_TO_RECOVER}" ]; then
echo "用法:$0 <要恢复的 Master 节点>"
exit 1
fi
echo "========== 恢复 Master 节点:${MASTER_TO_RECOVER} =========="
# 1. 从集群中移除故障节点
kubectl drain ${MASTER_TO_RECOVER} --ignore-daemonsets --delete-emptydir-data --force
kubectl delete node ${MASTER_TO_RECOVER}
# 2. 在故障节点上重置 kubeadm
ssh root@${MASTER_TO_RECOVER} "kubeadm reset -f"
# 3. 清理证书
ssh root@${MASTER_TO_RECOVER} "rm -rf /etc/kubernetes/pki/*"
# 4. 重新加入集群
ssh root@${MASTER_TO_RECOVER} << 'EOF'
kubeadm join 192.168.1.100:6443 \
--control-plane \
--certificate-key <certificate-key> \
--apiserver-advertise-address=<节点 IP>
EOF
# 5. 验证恢复
kubectl wait node/${MASTER_TO_RECOVER} --for=condition=Ready --timeout=300s
echo "========== Master 节点恢复完成 =========="
11.3 etcd 数据恢复
当 etcd 数据损坏或丢失时,从备份中恢复。
#!/bin/bash
# restore-etcd.sh - etcd 数据恢复
set -e
BACKUP_FILE=$1
if [ -z "${BACKUP_FILE}" ]; then
echo "用法:$0 <备份文件>"
exit 1
fi
echo "========== 恢复 etcd 数据 =========="
# 1. 停止所有 Master 节点的 etcd
for node in master-01 master-02 master-03; do
ssh root@${node} "systemctl stop etcd"
done
# 2. 在 master-01 上恢复数据
ssh root@master-01 << EOF
rm -rf /var/lib/etcd/*
etcdctl snapshot restore ${BACKUP_FILE} \
--data-dir=/var/lib/etcd \
--name=etcd-1 \
--initial-cluster=etcd-1=https://192.168.1.10:2380,etcd-2=https://192.168.1.11:2380,etcd-3=https://192.168.1.12:2380 \
--initial-cluster-token=k8s-etcd-cluster
chown -R etcd:etcd /var/lib/etcd
systemctl start etcd
EOF
# 3. 在其他节点上重建 etcd
for node in master-02 master-03; do
ssh root@${node} << EOF
rm -rf /var/lib/etcd/*
systemctl start etcd
EOF
done
# 4. 验证集群
sleep 30
etcdctl --endpoints=https://192.168.1.10:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
endpoint status --write-out=table
echo "========== etcd 数据恢复完成 =========="
12. 集群升级与扩展
集群的运维是持续的过程,包括版本升级和规模伸缩。
12.1 集群升级流程
安全、平滑地升级 Kubernetes 版本。
#!/bin/bash
# upgrade-cluster.sh - 集群升级脚本
set -e
OLD_VERSION="1.28.4"
NEW_VERSION="1.29.0"
echo "========== 升级 K8s 集群:${OLD_VERSION} -> ${NEW_VERSION} =========="
# 1. 升级 kubeadm
yum install -y kubeadm-${NEW_VERSION} --disableexcludes=kubernetes
# 2. 验证升级计划
kubeadm upgrade plan
# 3. 升级第一个 Master
kubeadm upgrade apply v${NEW_VERSION} -y
# 4. 升级其他 Master(逐个)
for node in master-02 master-03; do
echo "升级 ${node}..."
# 驱逐节点
kubectl drain ${node} --ignore-daemonsets --delete-emptydir-data --force
# 升级 kubelet 和 kubectl
ssh root@${node} "yum install -y kubelet-${NEW_VERSION} kubectl-${NEW_VERSION} --disableexcludes=kubernetes"
# 重启 kubelet
ssh root@${node} "systemctl daemon-reload && systemctl restart kubelet"
# 升级 kubeadm
ssh root@${node} "kubeadm upgrade node"
# 恢复节点
kubectl uncordon ${node}
# 验证
kubectl wait node/${node} --for=condition=Ready --timeout=300s
done
# 5. 升级 Worker 节点
for node in worker-01 worker-02 worker-03; do
echo "升级 Worker ${node}..."
kubectl drain ${node} --ignore-daemonsets --delete-emptydir-data --force
ssh root@${node} "yum install -y kubelet-${NEW_VERSION} kubeadm-${NEW_VERSION} kubectl-${NEW_VERSION} --disableexcludes=kubernetes"
ssh root@${node} "kubeadm upgrade node"
ssh root@${node} "systemctl daemon-reload && systemctl restart kubelet"
kubectl uncordon ${node}
kubectl wait node/${node} --for=condition=Ready --timeout=300s
done
# 6. 验证升级
kubectl version
kubectl get nodes
echo "========== 集群升级完成 =========="
12.2 添加新节点
水平扩展集群,增加计算能力。
#!/bin/bash
# add-node.sh - 添加新节点
set -e
NEW_NODE_IP=$1
NEW_NODE_NAME=$2
if [ -z "${NEW_NODE_IP}" ] || [ -z "${NEW_NODE_NAME}" ]; then
echo "用法:$0 <新节点 IP> <新节点名称>"
exit 1
fi
echo "========== 添加新节点:${NEW_NODE_NAME} (${NEW_NODE_IP}) =========="
# 1. 在新节点上执行系统初始化
ssh root@${NEW_NODE_IP} "bash -s" < init.sh
ssh root@${NEW_NODE_IP} "bash -s" < install-containerd.sh
ssh root@${NEW_NODE_IP} "bash -s" < install-k8s.sh
# 2. 生成 join 命令
JOIN_COMMAND=$(kubeadm token create --print-join-command)
# 3. 加入集群
ssh root@${NEW_NODE_IP} "${JOIN_COMMAND} --node-name=${NEW_NODE_NAME}"
# 4. 配置标签
kubectl label node ${NEW_NODE_NAME} node-role.kubernetes.io/worker=worker
# 5. 验证
kubectl wait node/${NEW_NODE_NAME} --for=condition=Ready --timeout=300s
echo "========== 新节点添加完成 =========="
12.3 移除节点
安全地缩容集群,移除指定节点。
#!/bin/bash
# remove-node.sh - 移除节点
set -e
NODE_NAME=$1
if [ -z "${NODE_NAME}" ]; then
echo "用法:$0 <节点名称>"
exit 1
fi
echo "========== 移除节点:${NODE_NAME} =========="
# 1. 驱逐 Pod
kubectl drain ${NODE_NAME} --ignore-daemonsets --delete-emptydir-data --force
# 2. 从集群删除
kubectl delete node ${NODE_NAME}
# 3. 在节点上重置 kubeadm
ssh root@${NODE_NAME} "kubeadm reset -f"
echo "========== 节点移除完成 =========="
总结
恭喜你!至此,一个生产级高可用的 Kubernetes 集群已经部署完成。但这只是开始,持续的运维和优化才是保证其长期稳定运行的关键。
高可用检查清单
部署完成后,对照此清单进行检查:
| 检查项 |
状态 |
说明 |
| Master 节点数量 |
✅ 3 个 |
奇数个,避免脑裂 |
| etcd 集群 |
✅ 3 副本 |
独立部署或堆叠 |
| 负载均衡 |
✅ Keepalived+HAProxy |
VIP 浮动 |
| 网络插件 |
✅ Calico |
高性能 |
| DNS 高可用 |
✅ CoreDNS×3 |
反亲和性部署 |
| 存储高可用 |
✅ NFS/ceph |
多副本 |
| 监控告警 |
✅ Prometheus |
完整覆盖 |
| 备份策略 |
✅ etcd 定时备份 |
可恢复 |
| 证书管理 |
✅ 自动轮换 |
避免过期 |
性能基准
为你的集群建立性能基线,并定期对比:
| 指标 |
目标值 |
| API Server P99 延迟 |
< 100ms |
| Pod 启动时间 |
< 30s |
| 故障恢复时间 |
< 30s |
| etcd 写入延迟 |
< 10ms |
| 网络延迟(同节点) |
< 0.1ms |
| 网络延迟(跨节点) |
< 1ms |
运维建议
- 定期演练:每季度进行故障转移演练,包括 Master 节点、etcd、负载均衡器的故障模拟。
- 监控告警:不要满足于默认告警,根据业务特点定制关键指标的告警规则,并确保告警能有效通知到人。
- 备份策略:etcd 备份是最后一道防线。除了每日全量备份,在重大变更前一定要手动备份。
- 证书管理:证书过期是常见的“低级”故障。利用监控提前告警,或使用
cert-manager 等工具自动管理。
- 容量规划:不要让集群“满负荷”运行。节点资源使用率(CPU/内存)建议长期保持在 70% 以下,为突发流量预留缓冲。
- 升级策略:关注 Kubernetes 社区发布的安全公告。制定保守的升级策略,小版本可以每季度评估升级一次,大版本升级需要更充分的测试。
写在最后
高可用不是一次性的配置,而是一种持续的状态和运维理念。本文提供的架构和脚本是经过验证的可靠起点,但请务必根据你的具体环境(硬件、网络、业务需求)进行调整和优化。
希望这份详尽的指南能帮助你构建出坚如磐石的 Kubernetes 生产环境。如果你在部署过程中遇到问题,或者有更好的实践经验,欢迎在 云栈社区 的云原生板块与广大开发者交流探讨。
本文基于生产环境实践经验总结,具体配置请根据实际场景调整。
高可用不是配置出来的,是演练出来的!