生产环境 Kubernetes 高可用集群部署实战:外部 etcd + Keepalived + HAProxy + 多 Master 架构

生产环境 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 和客户端:

  1. 外部负载均衡器:公有云 LB,或私有云自建 LVS / HAProxy;此时还需保证负载均衡器自身的高可用;
  2. 网络层负载均衡: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_idauth_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 轮询)工作正常。

十五、高可用演练:拔掉节点验证

集群搭建完成,最后做破坏性演练,验证每个层面都没有单点故障:

  1. 关闭任意 HA 节点(如 ha01):Keepalived 检测到故障后,VIP 10.1.8.100 应漂移到 ha02,集群继续可用;
  2. 关闭任意 Master(如 master21):HAProxy 健康检查把 master21 摘除,请求由 master22/23 继续服务;
  3. 关闭任意 Worker(如 worker31):上面的 webapp 副本在 worker31 被驱逐后,由控制器在其他节点重建,服务不中断;
  4. 关闭任意 etcd 节点(如 etcd11):etcd 三节点集群中剩余两节点仍构成多数派(quorum),集群数据读写不受影响。

每步演练后,通过 kubectl get nodescurl 服务地址以及 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 加入
  → 验证负载均衡与高可用

几个容易被忽略的关键点,务必记牢:

  1. 证书一致性:新 Master 加入前必须拷贝 ca/sa/front-proxy-ca 等密钥文件,否则集群无法互信;
  2. 网段一致性 :kubeadm 的 podSubnet 与 Calico 的 CALICO_IPV4POOL_CIDR 必须一致;
  3. VIP 进证书controlPlaneEndpoint 的 IP 会被签进 apiserver 证书,所以初始化时就要规划好 VIP;
  4. nerdctl 命名空间 :提前导入镜像时务必设置 CONTAINERD_NAMESPACE=k8s.io
  5. 外部 etcd 无本地 etcd :日志中出现 External etcd mode 说明配置生效,Master 节点不会自建 etcd;
  6. 生产加固建议:etcd 启用 TLS 认证、HAProxy 统计面板修改默认密码、节点启用防火墙白名单、kubeadm token 及时轮换。

集群已经 Ready,下一篇我们将为它装上"可视化驾驶舱"------Kuboard 与 Kubernetes Dashboard,敬请期待!

相关推荐
jianqiang.xue1 小时前
ESP-IDF保姆级入门19|低功耗模式全解与工程化设计:分级休眠/唤醒源配置/功耗调优/外设适配,实现工业级功耗性能平衡
stm32·单片机·物联网·架构·esp32
源代码•宸2 小时前
前置准备:定时微服务有什么价值
经验分享·后端·微服务·云原生·架构
晏宁科技YaningAI2 小时前
企业通信系统技术规划方法:从业务需求到可演进架构
网络·性能优化·架构·gateway·paas
lhldsg3 小时前
相册印刷实战指南:从设计规范到系统落地全流程解析
java·小程序·架构·设计规范
IT大白鼠3 小时前
Kubernetes 核心资源ReplicaSet 控制器详解
云原生·容器·kubernetes
MindUp3 小时前
企业AI办公工具的多智能体调度与跨端自动化架构对比分析
人工智能·架构·自动化
flyxl3 小时前
DataZen 架构设计(二):Tauri 桌面应用的前后端边界
架构
μθημα3 小时前
Kubernetes 微服务网络实践:Service 与 Ingress 从入门到灰度发布
网络·微服务·kubernetes
众人皆醒我独醉3 小时前
Kubernetes GPU 调度与管理的完整机制——从节点上架到 Pod 拿到 GPU
面试·kubernetes·gpu