ithuang
ithuang
发布于 2026-06-05 / 2 阅读
0

Kubernetes 三 Master 控制

以下是 Kubernetes 三 Master 控制平面高可用(HA)集群的完整实现指南,并以 Nginx 项目为例演示应用部署。


一、架构设计原理

1. 为什么需要三 Master?

Kubernetes 控制平面包含四个核心组件:

  • kube-apiserver:集群入口,所有操作都通过它
  • etcd:分布式键值存储,保存集群所有状态数据
  • kube-scheduler:负责 Pod 调度
  • kube-controller-manager:运行各种控制器

单 Master 架构下,一旦该节点故障,整个集群将不可用。三 Master 架构通过以下机制实现高可用:

机制说明
etcd Quorumetcd 使用 Raft 共识算法,3 节点可容忍 1 节点故障((3-1)/2 = 1),保证数据一致性
API Server 多活多个 kube-apiserver 同时对外提供服务,通过负载均衡器统一入口
Controller/Scheduler 选主通过 --leader-elect 机制,只有一个实例工作,故障时自动切换

2. 两种拓扑结构

Stacked etcd(堆叠式)——推荐用于大多数场景:

  • etcd 运行在 Master 节点上,与控制平面组件共存
  • 部署简单,由 kubeadm 自动管理
  • 适合中小型集群

External etcd(外部 etcd):

  • etcd 运行在独立节点上,与 Master 分离
  • 更灵活,但部署复杂
  • 适合大规模或对 etcd 有独立运维需求的场景

本指南采用 Stacked etcd 拓扑。

3. 负载均衡器设计

所有节点(包括 Master 自身)都通过负载均衡器访问 API Server,确保:

  • 单一稳定的入口地址(controlPlaneEndpoint)
  • 自动故障转移,屏蔽后端 Master 节点故障
  • 健康检查,自动剔除不可用的 API Server

二、环境准备

节点规划

角色主机名IP 地址配置建议
Master-1k8s-master-1192.168.1.1014C8G+
Master-2k8s-master-2192.168.1.1024C8G+
Master-3k8s-master-3192.168.1.1034C8G+
Worker-1k8s-worker-1192.168.1.2014C8G+
Worker-2k8s-worker-2192.168.1.2024C8G+
LB (Nginx)k8s-lb192.168.1.1002C4G

注意:LB 节点可以是独立服务器,也可以复用 Worker 节点。生产环境建议使用硬件负载均衡或云厂商 LB。

所有节点基础配置

# 1. 关闭 Swap
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab

# 2. 配置主机名(分别在对应节点执行)
sudo hostnamectl set-hostname k8s-master-1  # 或 master-2, master-3, worker-1...

# 3. 配置 hosts
cat <<EOF | sudo tee /etc/hosts
192.168.1.100 k8s-lb
192.168.1.101 k8s-master-1
192.168.1.102 k8s-master-2
192.168.1.103 k8s-master-3
192.168.1.201 k8s-worker-1
192.168.1.202 k8s-worker-2
EOF

# 4. 配置内核参数
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

sudo sysctl --system

# 5. 安装容器运行时(containerd)
sudo apt-get update
sudo apt-get install -y containerd
sudo mkdir -p /etc/containerd
sudo containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
sudo systemctl enable containerd

# 6. 安装 kubeadm, kubelet, kubectl
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

三、负载均衡器配置(Nginx Stream 模块)

在 LB 节点(192.168.1.100)上配置 Nginx 作为四层负载均衡器,转发 TCP 6443 端口到三个 Master 节点。

# 安装 Nginx
sudo apt-get update
sudo apt-get install -y nginx

# 配置 Nginx Stream(四层负载均衡)
sudo tee /etc/nginx/nginx.conf <<'EOF'
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 4096;
}

stream {
    upstream k8s_apiserver {
        least_conn;
        server 192.168.1.101:6443 weight=5 max_fails=3 fail_timeout=30s;
        server 192.168.1.102:6443 weight=5 max_fails=3 fail_timeout=30s;
        server 192.168.1.103:6443 weight=5 max_fails=3 fail_timeout=30s;
    }

    server {
        listen 6443;
        proxy_pass k8s_apiserver;
        proxy_timeout 300s;
        proxy_connect_timeout 5s;
    }
}
EOF

# 检查配置并重启
sudo nginx -t
sudo systemctl restart nginx
sudo systemctl enable nginx

验证:从任意节点执行 curl -k https://192.168.1.100:6443/healthz,应返回 ok(此时后端 Master 尚未启动,可能会失败,待 Master 启动后再验证)。


四、初始化第一个 Master 节点

在 k8s-master-1 上执行:

1. 生成证书密钥

# 生成用于共享证书的密钥,务必保存好
CERT_KEY=$(sudo kubeadm certs certificate-key)
echo $CERT_KEY
# 示例输出:f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07

2. 创建 kubeadm 配置文件

cat <<EOF | sudo tee /root/kubeadm-config.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
certificateKey: ${CERT_KEY}
nodeRegistration:
  criSocket: unix:///run/containerd/containerd.sock
  imagePullPolicy: IfNotPresent
  taints:
  - effect: NoSchedule
    key: node-role.kubernetes.io/control-plane
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: stable-1
controlPlaneEndpoint: "192.168.1.100:6443"
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
  podSubnet: 10.244.0.0/16
apiServer:
  certSANs:
  - "192.168.1.100"
  - "127.0.0.1"
  - "k8s-lb"
  timeoutForControlPlane: 4m0s
etcd:
  local:
    dataDir: /var/lib/etcd
EOF

3. 初始化集群

sudo kubeadm init --config=/root/kubeadm-config.yaml --upload-certs

关键参数说明:

  • --control-plane-endpoint:指向负载均衡器地址,这是 HA 的核心
  • --upload-certs:将证书加密上传到集群,供其他 Master 节点加入时使用(2 小时内有效)
  • certificateKey:解密证书所需的密钥

4. 配置 kubectl

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

5. 保存 join 命令

初始化完成后,终端会输出两个重要的 join 命令,务必保存:

# 控制平面节点加入命令(用于 Master-2, Master-3)
kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxx \
  --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07

# Worker 节点加入命令(用于 Worker-1, Worker-2)
kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxx

五、安装 CNI 网络插件

在 Master-1 上执行,必须在加入其他节点之前完成:

# 使用 Calico 作为 CNI
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/tigera-operator.yaml
kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/custom-resources.yaml

# 等待 Pod 启动
kubectl wait --for=condition=ready pod -l k8s-app=calico-node -n calico-system --timeout=300s

如果不先安装 CNI,后续加入的节点将一直处于 NotReady 状态。


六、加入其他控制平面节点

在 Master-2 和 Master-3 上执行控制平面加入命令:

sudo kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 \
  --control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07

执行完成后,在每个新 Master 上配置 kubectl:

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

验证控制平面

kubectl get nodes
# 应看到 3 个 master 节点,状态为 Ready

kubectl get pods -n kube-system -l component=kube-apiserver
# 应看到 3 个 apiserver Pod

kubectl get pods -n kube-system -l component=etcd
# 应看到 3 个 etcd Pod

验证 etcd 集群健康

sudo kubectl exec -n kube-system etcd-k8s-master-1 -- \
  etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  endpoint health

七、加入 Worker 节点

在 Worker-1 和 Worker-2 上执行:

sudo kubeadm join 192.168.1.100:6443 --token abcdef.0123456789abcdef \
  --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866

验证集群状态

kubectl get nodes

预期输出:

NAME           STATUS   ROLES           AGE   VERSION
k8s-master-1   Ready    control-plane   10m   v1.30.x
k8s-master-2   Ready    control-plane   8m    v1.30.x
k8s-master-3   Ready    control-plane   6m    v1.30.x
k8s-worker-1   Ready    <none>          3m    v1.30.x
k8s-worker-2   Ready    <none>          2m    v1.30.x

八、以 Nginx 项目为例部署应用

现在集群已就绪,我们来部署一个完整的 Nginx 应用,演示在 HA 集群上的工作负载分布。

1. 创建 Namespace

kubectl create namespace nginx-demo

2. 创建 Deployment

# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
  namespace: nginx-demo
  labels:
    app: nginx
spec:
  replicas: 6
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - nginx
              topologyKey: kubernetes.io/hostname
      containers:
      - name: nginx
        image: nginx:1.25-alpine
        ports:
        - containerPort: 80
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
kubectl apply -f nginx-deployment.yaml

podAntiAffinity 配置尽量将 Nginx Pod 分散到不同节点,提升应用层高可用。

3. 创建 Service

# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: nginx-demo
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: ClusterIP
kubectl apply -f nginx-service.yaml

4. 创建 Ingress(使用 Gateway API)

注意:Kubernetes 社区已于 2026 年 3 月 24 日正式停止维护 Ingress NGINX 项目,新集群建议使用 Gateway API。

安装 Gateway API CRD:

kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml

部署 Envoy Gateway:

helm repo add eg https://charts.envoyproxy.io/
helm repo update
helm install eg eg/gateway-helm -n envoy-gateway-system --create-namespace

创建 Gateway 和 HTTPRoute:

# nginx-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: nginx-gateway
  namespace: nginx-demo
spec:
  gatewayClassName: envoy-gateway
  listeners:
  - name: http
    protocol: HTTP
    port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: nginx-route
  namespace: nginx-demo
spec:
  parentRefs:
  - name: nginx-gateway
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: nginx-service
      port: 80
kubectl apply -f nginx-gateway.yaml

5. 验证应用部署

# 查看 Pod 分布
kubectl get pods -n nginx-demo -o wide

# 预期输出:6 个 Pod 分散在 5 个节点上
NAME                         READY   STATUS    NODE
nginx-app-7d8f9b2c5a-1x2y3   1/1     Running   k8s-worker-1
nginx-app-7d8f9b2c5a-2a4b6   1/1     Running   k8s-worker-2
nginx-app-7d8f9b2c5a-3c5d7   1/1     Running   k8s-master-1
nginx-app-7d8f9b2c5a-4e6f8   1/1     Running   k8s-master-2
nginx-app-7d8f9b2c5a-5g7h9   1/1     Running   k8s-master-3
nginx-app-7d8f9b2c5a-6i8j0   1/1     Running   k8s-worker-1

九、高可用验证测试

测试 1:控制平面故障容忍

# 在 Master-1 上停止 kubelet,模拟节点故障
sudo systemctl stop kubelet

# 在另一个终端继续操作集群
kubectl get nodes
# 应看到 k8s-master-1 变为 NotReady,但命令仍可执行

# 创建新的 Nginx Pod 测试
kubectl run nginx-test --image=nginx -n nginx-demo
# 应能成功创建,证明 API Server 仍通过 LB 由其他 Master 提供服务

测试 2:etcd Quorum 验证

# 查看 etcd 成员列表
kubectl exec -n kube-system etcd-k8s-master-2 -- \
  etcdctl member list \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 停止一个 etcd 实例(模拟故障)
kubectl exec -n kube-system etcd-k8s-master-1 -- \
  etcdctl endpoint health \
  --endpoints=https://192.168.1.101:2379,https://192.168.1.102:2379,https://192.168.1.103:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 即使一个 etcd 挂掉,只要 2/3 存活,集群仍可用

测试 3:Worker 故障与 Pod 迁移

# 模拟 Worker-1 故障
kubectl cordon k8s-worker-1
kubectl drain k8s-worker-1 --ignore-daemonsets --delete-emptydir-data

# 观察 Pod 自动迁移
kubectl get pods -n nginx-demo -o wide -w
# 原 Worker-1 上的 Nginx Pod 会被重新调度到其他节点

十、关键最佳实践

  1. 证书备份:定期备份 /etc/kubernetes/pki 目录,证书丢失将导致集群无法恢复

  2. etcd 备份:每 6 小时执行一次快照:

    sudo etcdctl snapshot save /backup/etcd-$(date +%Y%m%d%H%M).db \
      --endpoints=https://127.0.0.1:2379 \
      --cacert=/etc/kubernetes/pki/etcd/ca.crt \
      --cert=/etc/kubernetes/pki/etcd/server.crt \
      --key=/etc/kubernetes/pki/etcd/server.key
    
  3. 跨可用区部署:生产环境应将 3 个 Master 分布在不同 AZ/Rack

  4. 监控告警:使用 Prometheus 监控 etcd 集群健康、API Server 延迟、Controller Manager 选主状态

  5. 定期演练:每月执行一次故障注入测试(Chaos Engineering),验证自动恢复能力


总结

通过上述步骤,已成功构建了一个基于 kubeadm 的三 Master 高可用 Kubernetes 集群,并使用 Nginx 项目验证了工作负载的部署和调度。核心要点在于:

  • 负载均衡器 提供稳定的 controlPlaneEndpoint,是所有节点访问 API Server 的统一入口
  • Stacked etcd 自动形成三节点集群,通过 Raft 算法保证 (N-1)/2 的故障容忍
  • 证书共享机制 通过 --upload-certs 简化了多 Master 节点的加入流程
  • 应用层反亲和性 配置确保工作负载分散,避免单点故障

这套架构可支撑生产级高可用需求,后续可根据业务规模扩展 Worker 节点或迁移到 External etcd 架构。