Kubernetes 集群高可用与故障自动愈合实践
· 更新于 2026-07-26
⏱️ 阅读约 2 分钟 (683 字)
Kubernetes
SRE
高可用
深入探讨控制面多活部署、etcd 集群调优、Pod 反亲和策略与自定义 Controller 实现故障自愈的最佳实践。
一、控制面多活部署
Kubernetes 控制面的高可用是所有上层服务稳定的基石。我们采用三节点多活部署模式,通过负载均衡器分发 API Server 请求,etcd 集群以 Raft 协议保证数据一致性。
二、etcd 集群调优
etcd 是集群状态存储的核心,其性能直接关系到集群恢复速度。实践中我们将 etcd 的数据目录部署在 NVMe SSD 上,调整 --quota-backend-bytes 至 8GB,并开启客户端侧压缩。
三、Pod 反亲和与拓扑分布
为了规避单点故障,跨可用区部署时通过 Pod 反亲和策略保证同一服务的多个副本分布在不同的节点和可用区。
Deployment 反亲和配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
spec:
replicas: 3
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- api-gateway
topologyKey: "kubernetes.io/hostname"
topologySpreadConstraints:
- maxSkew: 1
topologyKey: "topology.kubernetes.io/zone"
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-gateway
containers:
- name: api-gateway
image: api-gateway:latest
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"
readinessProbe:
httpGet:
path: /health
port: 8080
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
spec:
replicas: 3
selector:
matchLabels:
app: api-gateway
template:
metadata:
labels:
app: api-gateway
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- api-gateway
topologyKey: "kubernetes.io/hostname"
topologySpreadConstraints:
- maxSkew: 1
topologyKey: "topology.kubernetes.io/zone"
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: api-gateway
containers:
- name: api-gateway
image: api-gateway:latest
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "2"
memory: "2Gi"
readinessProbe:
httpGet:
path: /health
port: 8080自定义自愈 Controller
package controller
import (
"context"
"log"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
type PodReconciler struct {
client.Client
}
func (r *PodReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
var pod corev1.Pod
if err := r.Get(ctx, req.NamespacedName, &pod); err != nil {
return reconcile.Result{}, client.IgnoreNotFound(err)
}
if pod.Status.Phase == corev1.PodPending {
for _, cond := range pod.Status.Conditions {
if cond.Reason == "Unschedulable" {
log.Printf("检测到不可调度 Pod: %s/%s, 尝试驱逐", pod.Namespace, pod.Name)
// 执行驱逐或通知调度器
}
}
}
return reconcile.Result{}, nil
}
go
package controller
import (
"context"
"log"
corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
type PodReconciler struct {
client.Client
}
func (r *PodReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
var pod corev1.Pod
if err := r.Get(ctx, req.NamespacedName, &pod); err != nil {
return reconcile.Result{}, client.IgnoreNotFound(err)
}
if pod.Status.Phase == corev1.PodPending {
for _, cond := range pod.Status.Conditions {
if cond.Reason == "Unschedulable" {
log.Printf("检测到不可调度 Pod: %s/%s, 尝试驱逐", pod.Namespace, pod.Name)
// 执行驱逐或通知调度器
}
}
}
return reconcile.Result{}, nil
}四、总结
高可用不是一次性配置,而是持续演进的过程。通过控制面多活、etcd 调优、反亲和策略与自愈 Controller 的组合,我们将集群的年化故障时间从 12 小时降低到 30 分钟以内。
本文作者:技术领航员 | 许可协议:CC BY-NC-SA 4.0
本文链接:https://sre-ai-blog.pages.dev/zh/posts/kubernetes-ha/(转载请注明出处)