Skip to content

K3s HA 终极筑基:eBPF 与 VIP 的暴力美学

🌱 创建: 2026/04/12 ⏱️ 更新: 2026/05/22

This content is not available in your language yet.

📖 认识赛博靶场:K3s + Cilium + kube-vip

Section titled “📖 认识赛博靶场:K3s + Cilium + kube-vip”

在正式给这 3 台 Ubuntu 虚拟机敲下回车之前,我们先理清这套终极架构的底牌和初衷。

“高可用的本质不是不犯错,而是机器内部炸了之后,你还能在朋友面前装作无事发生。”

必须坦白,如果你的 Homelab 只是为了跑几个家庭服务(比如看剧、下电影、做个网盘),那么 Docker 已经完全足够了。K3s 的维护成本并不低,我极其不推荐只追求家庭网络稳定性的朋友来蹚这趟浑水。

那我为什么还要折腾这套架构?

  1. 我想彻底理解 Kubernetes 的整体架构与流量走向。
  2. 我想在 Homelab 里搭建一个真正具备“集群化思维”的测试环境。

网上的 K3s 教程大多教你单节点起步,自带的 Flannel 网络和 Traefik 网关确实够用。但在咱们“赛博工地”,这套“全家桶”过于黑盒,缺乏折腾的乐趣。今天我们要搭建的,是一套完全解耦、职责分离的终极形态靶场:

  1. 大脑 (K3s 控制平面):这 3 台虚拟机(10.0.10.10/20/30)将共同组成控制平面。为什么必须是 3 台? 因为 K3s 依赖内嵌的 etcd 数据库同步状态,它采用“选举机制”,必须有超过半数的节点存活才能工作。1 台挂了全军覆没;2 台挂 1 台不够半数,依然瘫痪;只有 3 台挂 1 台还剩 2 台(>50%),集群才能继续存活。这就是实现高可用的最低成本模型。
  2. 护卫 (kube-vip):既然有 3 个大脑,我们访问谁?kube-vip 会在集群外围生成一个虚拟入口(VIP:10.0.10.2)。不管底层哪台机器挂了,这个 IP 会自动、无缝地漂移到健康的节点上。你只需访问这个 VIP,永远不会失联。
  3. 灵魂 (Cilium):利用 eBPF 黑科技完全接管数据面网络,不仅消除传统 iptables 转发的性能损耗,还兼职负责内部网络分配和规则通信。

在开干之前,请盘点你的赛博作案工具:

  • 受害者们: 3 台已经装好纯净版 Ubuntu 22.04+ 的虚拟机(建议至少 2C2G)。
  • 静态资产: - Master 01: 10.0.10.10
    • Master 02: 10.0.10.20
    • Master 03: 10.0.10.30
    • 集群 VIP: 10.0.10.2
    • 业务 IP 池: 10.0.10.50 - 10.0.10.200
  • 指挥中心: 一台装好高颜值 SSH 工具(如 Termius)的电脑,准备多开窗口。

🗺️ 赛博靶场底层拓扑图 (Architecture Blueprint)

Section titled “🗺️ 赛博靶场底层拓扑图 (Architecture Blueprint)”

在敲击键盘之前,请先将这张系统架构图刻在脑子里。搞懂了它,后面的排错将犹如探囊取物。

💡 图纸核心解析:

  1. 控制面护卫 (红圈)kube-vip 三兄弟随时在内网里抢地盘。只要还有一台机器活着,10.0.10.2 这个大门就不会塌。
  2. 数据面发牌 (蓝框)Cilium 不仅接管了内部路由,还像个全自动的 DHCP 服务器一样,从 10.0.10.50 开始给咱们的业务发内网 IP,并向物理交换机广播。
  3. etcd 数据库同步 (紫色):数据库之间通过粗连线进行毫秒级的强同步。

🔄 流量到底是怎么跑的?(Traffic Flow)

Section titled “🔄 流量到底是怎么跑的?(Traffic Flow)”

上面的架构图解决的是“组件怎么摆”。
但如果你还不清楚请求是怎么一步步走到底层容器里的,我们换个视角,剥离掉那些复杂的配置概念,只看最核心的数据流向(Data Path)

在咱们的集群里,有两条互不干扰的高速公路:

💡 极简流量剖析:

  • 走下路(管理线):当你在电脑上敲下 kubectl 命令时,请求会打向红色 VIP(10.0.10.2)。 kube-vip 通过 ARP 广播 + Leader Election 确保这个 IP 始终绑定在一台健康的 Master 上,请求最终进入 API Server,并写入 etcd
  • 走上路(业务线):当用户在浏览器访问服务时,请求会打向蓝色的 LoadBalancer IP(如 10.0.10.50)。 Cilium 会通过 L2 Announcement (ARP) 向局域网宣告“这个 IP 在某个节点上”,流量因此被引导到对应节点。 数据包进入节点后,Cilium eBPF 在内核态(早于 iptables)直接完成转发决策,通过 eBPF 程序在 TC / XDP 层对数据包进行重定向,将流量“瞬移”到目标 Pod,实现低延迟高性能访问。
  • kube-vip:保证你能“连上 Kubernetes”
  • Cilium:保证你的“服务能被访问”
  • etcd:保证“集群不会失忆”

遇到玄学问题,查官方文档永远比百度强。特别是下面这两份专门针对 K3s 适配的官方指引,是本次施工的核心参考图纸:


🎬 施工演示录像 (Video Walk-through)

Section titled “🎬 施工演示录像 (Video Walk-through)”
正在嗅探浏览器语言并加载赛博录像...

🔨 核心施工流程 (Installation Steps)

Section titled “🔨 核心施工流程 (Installation Steps)”
  1. 拨钟对表 (时钟同步防脑裂)

    所有 3 台虚拟机上执行。这是防止集群自杀的第一道防线。为了条理清晰,我们将它分为两步操作:

    步骤 1.1:安装 QEMU Guest Agent 这能让虚拟机与 PVE 宿主机保持底层互动,不仅能进行基础时钟对齐,还能让你在 PVE 后台优雅地重启/关闭虚拟机。

    Terminal window
    sudo apt update && sudo apt install qemu-guest-agent -y
    sudo systemctl enable --now qemu-guest-agent

    步骤 1.2:配置稳定的 NTP 时钟源

    Terminal window
    # 1. 设置内网主 NTP 服务器 (⚠️ 请将 10.0.10.1 替换为你的真实网关 IP)
    sudo sed -i '/^#NTP=/c\NTP=10.0.10.1' /etc/systemd/timesyncd.conf
    # 2. 设置公网备用 NTP 服务器 (阿里云与国家授时中心,作为兜底)
    sudo sed -i '/^#FallbackNTP=/c\FallbackNTP=ntp.aliyun.com cn.pool.ntp.org' /etc/systemd/timesyncd.conf
    # 3. 重启服务并检查状态
    sudo systemctl restart systemd-timesyncd
    timedatectl status

    稍等片刻,确认输出的信息中包含 System clock synchronized: yes,说明这三台机器的时间已经死死咬合在一起了。

  2. 安装helm 在所有节点安装helm

    Terminal window
    # 安装helm
    sudo curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-4 | bash
  3. 浇筑 1 号主控节点 (Master 01)

    登录 10.0.10.10。这串命令是整个赛博靶场地基的核心。我们不仅显式声明了集群网段(避免未来与你的家庭 VPN 发生惨烈的路由冲突),更重要的是,我们通过一连串的 --disable 参数,无情地卸载了 K3s 原生的“傻瓜式”网络组件。

    Terminal window
    curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server \
    --cluster-init \
    --tls-san 10.0.10.2 \
    --node-ip 10.0.10.10 \
    --cluster-cidr=10.60.0.0/16 \
    --service-cidr=10.61.0.0/16 \
    --flannel-backend=none \
    --disable-network-policy \
    --disable servicelb \
    --disable traefik \
    --disable-kube-proxy" sh -

    💡 包工头硬核解析:我们到底禁用了什么,为什么要禁?

    为了给终极形态的网络底座腾出空间,这串命令执行了一场彻底的“大清洗”:

    • 🔪 禁用 flannelnetwork-policy
      • 它是什么:K3s 默认的 CNI(容器网络接口)和网络策略控制器。
      • 为什么禁:Flannel 默认走 VXLAN 隧道,封包解包有性能损耗,且网络策略比较基础。
      • 用什么代替Cilium。我们将用它实现基于 eBPF 的局域网原生直通路由(Native Routing),性能直接拉满。
    • 🔪 禁用 servicelb
      • 它是什么:K3s 自带的轻量级负载均衡器(Klipper),靠占用宿主机端口来暴露服务。
      • 为什么禁:功能简陋,且会与我们后续高级的 IP 分配机制产生端口和 IP 冲突。
      • 用什么代替Cilium 的 L2 Announcement。让 eBPF 直接从我们的 IP 池(10.0.10.50-100)里抽调真实的内网 IP 发给业务服务。
    • 🔪 禁用 traefik
      • 它是什么:K3s 捆绑安装的 Traefik Ingress 网关。
      • 为什么禁:捆绑版通过晦涩的 HelmChart CRD 部署,你想改个配置极其痛苦,且版本更新完全受制于 K3s 发版。
      • 用什么代替手动 Helm 部署纯净版 Traefik v3。底层剥离干净后,我们将用标准企业级方法独立部署,拥有 100% 的掌控权。
    • 🔪 禁用 kube-proxy
      • 它是什么:Kubernetes 经典的“交通警察”,通过在 Linux 里写海量的 iptables 规则来转发集群流量。
      • 为什么禁iptables 是用来做防火墙的,不是用来做微服务路由的!当集群变大,规则匹配极其缓慢。
      • 用什么代替Cilium eBPF (kubeProxyReplacement=true)。把交通警察干掉,直接在网卡底层修立交桥,彻底消除 iptables 的历史包袱。

    具体见官方文档

    为了避免后续每次执行 kubectl 命令都必须加 sudo,或者频频遭遇 permission denied 权限拒绝,我们需要将 K3s 自动生成的认证文件复制到当前用户的 .kube 目录下,并赋予对应权限:

    Terminal window
    mkdir -p ~/.kube
    sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
    sudo chown $(id -u):$(id -g) ~/.kube/config
    echo "export KUBECONFIG=$HOME/.kube/config" >> ~/.bashrc
    source ~/.bashrc

    🔍 初步验收:不要慌,它现在是个“植物人”

    K3s 启动后,你可以敲下这行命令来看看你的 1 号节点状态:

    Terminal window
    sudo kubectl get nodes
  4. 召唤金牌护卫 (配置 kube-vip)

    依然在 10.0.10.10 上。这一步我们将引入 kube-vip,让 6443 入口实现真正的“大门不倒”。

    🔍 寻宝环节:版本号与网卡确认 为了保证教程的透明度,你可以通过以下官方指令查询 kube-vip 的最新稳定版本:

    Terminal window
    # 查看当前官方发布的最新版本号 (需安装 jq)
    curl -sL https://api.github.com/repos/kube-vip/kube-vip/releases | jq -r ".[0].name"
    # 确认你的真实网卡名称 (在输出中找到带 10.0.10.10 的那个名字)
    ip a
    # 或者执行这个命令
    ip -o -4 addr show | awk '{print $2,$4}'

    🧱 分步施工:配置与镜像准备

    Terminal window
    # 1. 准备目录并获取权限模板
    sudo mkdir -p /var/lib/rancher/k3s/server/manifests/
    sudo curl https://kube-vip.io/manifests/rbac.yaml -o /var/lib/rancher/k3s/server/manifests/kube-vip-rbac.yaml
    Terminal window
    # 2. 设置施工变量 (请修改为你自己的 IP 和网卡名)
    export VIP=10.0.10.2
    export INTERFACE=<你的网卡名>
    export KVVERSION="v1.1.2"
    Terminal window
    # 3. 预拉取护卫镜像
    sudo ctr image pull ghcr.io/kube-vip/kube-vip:$KVVERSION

    ⚡ 核心生成:生成 kube-vip 部署清单 执行下面这个长命令。它的作用是让 kube-vip 容器跑一次“自我介绍”,并将生成的部署配置保存到 K3s 的自动部署目录。

    Terminal window
    sudo ctr run --rm --net-host ghcr.io/kube-vip/kube-vip:$KVVERSION vip \
    /kube-vip manifest daemonset \
    --interface $INTERFACE \
    --address $VIP \
    --inCluster \
    --taint \
    --controlplane \
    --arp \
    --leaderElection | sudo tee /var/lib/rancher/k3s/server/manifests/kube-vip.yaml > /dev/null

    📖 命令参数“白话说明书” 为了让你死个明白,这几个参数分别代表了:

    • --interface: 告诉 kube-vip 在哪块物理网卡上广播 VIP 地址。
    • --address: 指定我们要生成的那个虚拟 IP(10.0.10.2)。
    • --inCluster: 声明这是在集群内运行,生成 DaemonSet 格式的配置。
    • --taint: 关键!允许 kube-vip 运行在带有“污点”的主节点上(默认主节点是不跑业务的,不加这个它跑不起来)。
    • --controlplane: 明确这是为控制平面 API Server 提供高可用。
    • --arp: 开启二层 ARP 广播模式。不需要交换机支持 BGP,非常适合咱们 Homelab。
    • --leaderElection: 开启“带头大哥”选举。多台机器会商量谁来接管 IP,防止冲突。

    具体见官方文档

    🔧 补强施工:注入免死金牌 这一步非常重要,由于 K3s 自动部署可能需要一点时间,请确认文件生成后再执行:

    Terminal window
    # 补丁 1:注入最高调度优先级 (免死金牌,防止节点压力大时 kube-vip 被 K8s 驱逐)
    sudo sed -i '/hostNetwork: true/a \ priorityClassName: system-cluster-critical' /var/lib/rancher/k3s/server/manifests/kube-vip.yaml
    # 补丁 2:注入本地 API 指路牌 (强行指引它访问本机的 6443 端口,解决 Timeout 报错)
    sudo sed -i 's/env:/env:\n - name: KUBERNETES_SERVICE_HOST\n value: "127.0.0.1"\n - name: KUBERNETES_SERVICE_PORT\n value: "6443"/' /var/lib/rancher/k3s/server/manifests/kube-vip.yaml

    最后,提取你的集群钥匙 (Token)

    Terminal window
    sudo cat /var/lib/rancher/k3s/server/node-token

    🔍 阶段验收:验证金牌护卫是否到岗

    代码敲完了,现在我们要看看这个 VIP 到底有没有在你的局域网里“显灵”。

    步骤 A:检查 Pod 是否正常启动 kube-vip 是以 DaemonSet 形式运行的。首先确认它的进程有没有在你的 1 号节点上跑起来:

    Terminal window
    sudo kubectl get pods -n kube-system

    期望结果:看到 STATUSRunning。如果显示 Pending,多半是 --taint 参数没加对,导致它不敢在主节点上落脚。

    步骤 B:检查 VIP 是否成功绑定网卡 这是最关键的一步。kube-vip 会在你的物理网卡上“挂载”一个额外的 IP。再次执行 ip a

    Terminal window
    # ⚠️ 请将 <你的网卡名> 换成你自己的网卡名
    ip addr show <你的网卡名>

    期望结果:在输出中,除了你原本的 10.0.10.10,你应该能看到一行 inet 10.0.10.2/32 ...。如果看到了这个 /32 的 IP,说明 kube-vip 已经强行接管了这条街道。

    步骤 C:连通性生死测试 在你的**个人电脑(Mac/Win)**打开终端,直接尝试去 ping 这个虚拟 IP:

    Terminal window
    ping 10.0.10.2

    期望结果:如果能 ping 通,说明你的二层网络广播(ARP)工作正常。

    步骤 D:API Server 业务验收 最后,我们看看这个 VIP 能不能带我们找到 K8s 的大门:

    Terminal window
    curl -k https://10.0.10.2:6443/livez

    期望结果:你会收到一段包含 "message": "Unauthorized", "code": 401 的 JSON 报错。

    步骤 E:查看vip在哪个节点下

    Terminal window
    kubectl get lease plndr-cp-lock -n kube-system

    HOLDER 这一列,写着谁的名字,10.0.10.2 这个 VIP 就在谁的网卡上。

    步骤 F:审查图纸(验证注入是否成功) 确认我们刚才的 sed 命令确实把“免死金牌”塞进去了:

    Terminal window
    sudo grep -C 2 "priorityClassName" /var/lib/rancher/k3s/server/manifests/kube-vip.yaml

    期望结果:你能看到 priorityClassName: system-cluster-critical 和上面的 hostNetwork: true 对齐得严严实实。如果没输出,说明你没注入成功。

  5. 备用主控入列 (Master 02 & 03 加入)

    登录 10.0.10.2010.0.10.30。 注意看这里的 --server 参数,我们直接打向了 VIP 10.0.10.2。既然大门已经建好,就绝对不能再走 1 号节点的“后门”,这才是纯正的高可用。

    🔍 寻宝环节:再次确认本机 IP 在执行加入命令前,请分别在 Master 02 和 03 上确认自己的物理 IP:

    Terminal window
    ip a

    记住那个用于集群内部通信的网卡 IP(例如 10.0.10.20)。

    🧱 施工:执行集群加入命令

    Terminal window
    # 在 Master 02 上执行时,--node-ip 填 10.0.10.20(你实际规划的ip)
    # 在 Master 03 上执行时,--node-ip 填 10.0.10.30(你实际规划的ip)
    curl -sfL https://get.k3s.io | K3S_TOKEN="<替换为你的真实_TOKEN>" INSTALL_K3S_EXEC="server \
    --server https://10.0.10.2:6443 \
    --tls-san 10.0.10.2 \
    --node-ip <替换为当前机器的真实_IP> \
    --cluster-cidr=10.60.0.0/16 \
    --service-cidr=10.61.0.0/16 \
    --flannel-backend=none \
    --disable-network-policy \
    --disable servicelb \
    --disable traefik \
    --disable-kube-proxy" sh -

    我们这里也将 K3s 自动生成的认证文件复制到当前用户的 .kube 目录下,并赋予对应权限:

    Terminal window
    mkdir -p ~/.kube
    sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config
    sudo chown $(id -u):$(id -g) ~/.kube/config
    echo "export KUBECONFIG=$HOME/.kube/config" >> ~/.bashrc
    source ~/.bashrc

    🔍 阶段验收:多节点 etcd 状态查房

    现在你有了三台 Master,集群应该已经开始进行内部的“民主选举”了。

    步骤 A:检查节点名录 在任意一台机器上执行:

    Terminal window
    sudo kubectl get nodes

    期望结果:你应该能看到三台机器全员到齐。虽然状态全是 NotReady(因为我们还没装 Cilium),但只要名字都在列表里,说明加入成功。

    步骤 B:查看 API 端点负载 (Endpoints)** 这是最硬核的验证。我们要看看 Kubernetes 内部大门背后,到底有几个保安在执勤:

    Terminal window
    sudo kubectl get endpoints kubernetes -n default

    期望结果:你会看到 ENDPOINTS 这一列,输出了类似 10.0.10.10:6443,10.0.10.20:6443,10.0.10.30:6443 的 IP 列表。这意味着你的 3 台物理机都已经成为 API Server 的合法入口,etcd 的高可用底座已完美浇筑!

    步骤 C:查看集群核心组件状态 (埋下伏笔)

    控制平面大阵已经结成,我们最后来看看 K3s 内部的基础业务组件现在的生存状态:

    Terminal window
    sudo kubectl get pods -n kube-system -o wide

    期望结果:你会看到咱们的金牌护卫 kube-vip 正在坚挺地 Running,但是 corednsmetrics-server 等原生组件,全部处于 Pending(挂起排队)状态。

  6. 注入 eBPF 灵魂 (安装 Cilium 完全体)

    回到 10.0.10.10 的终端。此时你的集群处于没有网络的 NotReady 植物人状态。 我们将下载官方的 Cilium CLI,并用极其激进的参数来唤醒它:开启原生路由、全面接管伪装、切断不必要的七层代理(为了省内存),并开启二层广播。

    🧱 施工:安装并注入 Cilium

    Terminal window
    # 1. 安装 Cilium CLI (自动拉取最新稳定版)
    CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
    CLI_ARCH=amd64
    if [ "$(uname -m)" = "aarch64" ]; then CLI_ARCH=arm64; fi
    curl -L --fail --remote-name-all https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
    sha256sum --check cilium-linux-${CLI_ARCH}.tar.gz.sha256sum
    sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
    rm cilium-linux-${CLI_ARCH}.tar.gz{,.sha256sum}
    Terminal window
    # 2. 执行终极底层替换
    # ⚠️ 如果你的节点不在同一二层网络(例如 VLAN / 跨网段),请关闭:--set autoDirectNodeRoutes=false
    cilium install \
    --set kubeProxyReplacement=true \
    --set k8sServiceHost=10.0.10.2 \
    --set k8sServicePort=6443 \
    --set ipam.operator.clusterPoolIPv4PodCIDRList="10.60.0.0/16" \
    --set ipv4NativeRoutingCIDR="10.60.0.0/16" \
    --set routingMode=native \
    --set autoDirectNodeRoutes=true \
    --set bpf.masquerade=true \
    --set endpointRoutes.enabled=true \
    --set l7Proxy=true \
    --set bandwidthManager.enabled=true \
    --set l2announcements.enabled=true \
    --set externalIPs.announcements.enabled=true \
    --set externalIPs.enabled=true \
    --set devices='{en+,eth+}' \
    --set operator.replicas=1 \
    --set resources.requests.cpu=100m \
    --set resources.requests.memory=128Mi

    📖 这堆长串参数到底干了啥? 对于 Homelab 玩家,我不建议无脑复制,这几个核心参数你必须懂:

    • 🔪 干掉历史包袱
      • kubeProxyReplacement=true:这就是我们前面不装 kube-proxy 的原因。Cilium 利用 eBPF 深入 Linux 内核态,彻底接管了所有的网络流量转发。再也不用面对几万条低效的 iptables 规则了。
    • 🛣️ 原生直通路由 (Native Routing)
      • routingMode=native & autoDirectNodeRoutes=true:抛弃了传统的 VXLAN 隧道封装。局域网内的 Pod 互访直接走 Linux 原生路由表,网络延迟降到最低,跑满万兆网卡不是梦。
      • bpf.masquerade=true:利用 eBPF 处理 SNAT(源地址转换),比传统的 iptables MASQUERADE 快得多。
    • 🔌 精准锁定“主干道” (物理网卡接管)
      • devices='{en+,eth+}':Homelab 的底层环境五花八门,你的节点网卡可能叫传统的 eth0,也可能是 PVE 虚拟机里的 ens18,或者是迷你主机直通的 enp2s0
      • 防翻车原理解析:这个带有正则通配符的参数明确指令 Cilium:“去把你的 eBPF 探针挂载到这些真正负责节点对外通信的主网卡上”。如果不加这个限制,Cilium 可能会“抓瞎”,错把探针挂载到内部的虚拟网桥(比如 Docker 遗留网卡或虚拟机内部桥接)上,直接导致数据包进了黑洞,集群网络瞬间瘫痪。精准认路,是跑满万兆的前提。
    • ⚖️ 性能与功能的平衡术
      • l7Proxy=true:虽然会额外消耗约 150MiB 内存,但它解锁了 Cilium 的七层协议解析能力。没有它,Hubble 只能看个寂寞;有了它,你才能在监控中洞察每一次 HTTP 请求的细节。
      • operator.replicas=1:在 3 节点的 Homelab 规模下,单副本 Operator 配合 K8s 的自愈能力已经足够稳健,多开一个副本除了浪费几十兆内存,并不能带来质的飞跃。
    • 📢 化身二层发牌员
      • l2announcements.enabled=true:由于我们前面禁用了 Klipper (servicelb),开启这个参数后,Cilium 就能自己通过 ARP 协议,把后面生成的业务 IP 广播到局域网里,充当咱们的 LoadBalancer 发牌员。
      • externalIPs.announcements.enabled=true:极其隐蔽的附属开关!除了 LoadBalancer,它确保 K8s 原生的 ExternalIPs 也能被二层网络正确广播,实现服务暴露的全场景覆盖。
    • 🛡️ 抢占“低保”生存权 (Resource Requests)
      • resources.requests.cpu=100mmemory=128Mi:这是 K8s 的“资源请求”机制。Cilium 作为企业级网络插件,默认的预留资源预期是比较高的。如果我们不显式声明,它可能会向 K8s 申请过高的配额,导致我们只有 2G 内存的节点直接拒绝分配,Pod 永远卡在 Pending 状态。
      • 包工头原理解析:注意,这是 requests(低保),不是 limits(封顶)。我们强行设定 100m(0.1个CPU核)和 128Mi 内存,主要是向 K8s 调度器宣告:“这是我这具网络躯壳的最低生存口粮,只要节点还剩这点资源,就必须让我开机;而且系统再卡,也绝不能克扣我的这部分粮草!”。

    具体见官方文档cilium-cli


    🔍 阶段验收:植物人满血复活

    命令敲完后,大概需要等待 1-2 分钟让镜像拉取并启动。现在,见证奇迹的时刻到了。

    步骤 A:Cilium 自检10.0.10.10 上执行官方的健康检查命令:

    Terminal window
    # 如果已经配置了环境变量,直接执行
    cilium status

    期望结果:你会看到极其舒适的全绿 OK 状态。最重要的是,请盯紧这两行:

    • KubeProxyReplacement: True (说明 eBPF 成功接管,老交警下班)
    • Routing: Native (说明直通路由开启,没有隧道损耗)
    • Cluster Pods: 3/3 managed by Cilium (说明所有 Pod 已被收编)

    步骤 B:检查节点是否苏醒 还记得第 2 步里那个令人不安的 NotReady 吗?现在再敲一次:

    Terminal window
    sudo kubectl get nodes

    期望结果:由于网络大动脉已经被 Cilium 彻底打通,三台 Master 节点的状态将全部华丽地转变为 Ready!这代表你的大脑与肢体已经完美连接。

    步骤 C:查看 Cilium 集群

    Terminal window
    # 查看底层工兵状态
    sudo kubectl get pods -n kube-system -l k8s-app=cilium -o wide

    期望结果:你应该能看到 3 个 cilium-xxxxx 的 Pod 分别稳定运行在 master-01/02/03 上。

    步骤 D:查看全家福 (Pod 状态深度检查)

    Terminal window
    sudo kubectl get pods -n kube-system -o wide

    期望结果:你会发现 corednsmetrics-server 已经拿到了以 10.60.x.x 开头的 Pod IP,并且状态全部变为了 Running

    🔭 安装Cilium Hubble UI 既然用了 Cilium,怎么能不装 Hubble?这是一个能让你拥有上帝视角的网络流量动态拓扑图。谁在连谁,哪个包被丢弃了,一目了然。


    🛠️ 终极排错照妖镜 如果你想知道 Cilium 到底吃进去了哪些底层配置,千万别去翻最初的安装脚本,直接在终端敲这个命令:

    Terminal window
    helm get values -n kube-system cilium

    它会把当前真正在底层运行的所有 Helm 参数(包括默认生成的和我们手动设置的)原形毕露,这是后期排错的绝佳利器!

  7. 发放数据面牌照 (配置 Cilium L2 IP 池)

    底层大动脉已经通了,但谁来给未来的业务服务(比如你的博客、网盘、面板)发 IP?在传统的云厂商那里是外置的 ELB,但在咱们的 Homelab 里,我们直接甩给 Cilium 一本“支票簿”,让它兼职当发牌员。

    🧱 施工:编写并发放牌照10.0.10.10(Master 01)上创建配置文件。

    创建并编辑文件 cilium-lb-pool.yaml

    ---
    apiVersion: "cilium.io/v2"
    kind: CiliumLoadBalancerIPPool
    metadata:
    name: "homelab-pool"
    spec:
    blocks:
    - start: "10.0.10.50" # ⚠️ 替换为你的真实起始 IP
    stop: "10.0.10.200" # ⚠️ 替换为你的真实结束 IP
    ---
    apiVersion: "cilium.io/v2alpha1"
    kind: CiliumL2AnnouncementPolicy
    metadata:
    name: "default-l2-policy"
    spec:
    interfaces:
    - ^<你的网卡名> # ⚠️ 极度危险:千万换成你自己的物理网卡名!
    - ^<你的网卡名2> # 其他网卡
    loadBalancerIPs: true # ⚠️ 致命开关:允许向局域网广播 LB IP
    externalIPs: true # ⚠️ 致命开关:允许广播 External IP

    执行应用:

    Terminal window
    kubectl apply -f cilium-lb-pool.yaml

    📖 包工头原理解析

    • CiliumLoadBalancerIPPool:这就是那本支票簿。以后你在 K8s 里只要声明一个 type: LoadBalancer 的服务,Cilium 就会自动从这段 IP 里抽一个没用过的发给它。
    • CiliumL2AnnouncementPolicy:这是广播许可证。它告诉 Cilium:“拿到 IP 后,请通过指定的网卡,用二层 ARP 协议向局域网里大喊,告诉家里所有的设备这个新 IP 在我这里!”

    关于更多CiliumL2AnnouncementPolicy见官网文档

    🔍 阶段验收:支票簿入库核验

    步骤 A:检查配置是否被 Cilium 吞下 执行以下命令查看我们刚建的自定义资源(CRD):

    Terminal window
    kubectl get CiliumLoadBalancerIPPool
    kubectl get CiliumL2AnnouncementPolicy

    期望结果:终端应返回 homelab-pooldefault-l2-policy,说明配置已生效且没有冲突。

    步骤 B:终极实战演习(封顶大吉!) 光看配置没用,发牌员到底能不能干活,我们得拉一个真实的项目出来“遛遛”。

    我们将部署一款极具赛博朋克风格的轻量级 Kubernetes 可视化面板 —— Headlamp,并向 Cilium 申请我们的第一张 LoadBalancer 业务 IP(10.0.10.50)!如果能通过这个 IP 在浏览器里直接打开面板,那么恭喜你,这套终极高可用架构的底层网络正式宣告“封顶”!


🏗️ 8. 扩充工兵连:加入 Agent 子节点 (Adding Worker Nodes)

Section titled “🏗️ 8. 扩充工兵连:加入 Agent 子节点 (Adding Worker Nodes)”

虽然 3 台 Master 构成了坚不可摧的“大脑”,但在咱们这种内存只有 2G 的“受害者”节点上,大脑已经快被 Cilium 和 etcd 榨干了。为了让业务(比如下一篇的容器化应用)有更宽敞的住处,我们需要引入只干活、不思考的 Agent 节点。

  1. 工兵前置整备 (Requirements)

    在新节点(如 192.168.100.182)上,执行基础初始化。必须安装存储依赖,否则后续 Longhorn 无法挂载硬盘。

    Terminal window
    # 安装基础工具、NFS 客户端和 iSCSI 组件 (Longhorn 必需)
    sudo apt update && sudo apt install -y nfs-common open-iscsi
    sudo systemctl enable --now iscsid
    # 永久关闭 Swap (K8s 运行稳定性要求)
    sudo swapoff -a
    sudo sed -i '/swap/s/^/#/' /etc/fstab
  2. 提取集群入场券 (Get Token)

    回到你的 Master 01 节点,取出那串密钥:

    Terminal window
    sudo cat /var/lib/rancher/k3s/server/node-token
  3. 奔赴工地:执行加入命令

    登录到你的子节点。注意,这里的 K3S_URL 必须指向你的 VIP

    Terminal window
    # ⚠️ 请将 <TOKEN> 替换为你刚才提取的密钥
    # ⚠️ 请将 --node-ip 替换为当前子节点的真实 IP
    curl -sfL https://get.k3s.io | KUBERNETES_SERVICE_HOST=10.0.10.2 \
    K3S_URL=https://10.0.10.2:6443 \
    K3S_TOKEN="<替换为你的真实_TOKEN>" \
    INSTALL_K3S_EXEC="agent --node-ip <当前子节点_IP>" sh -

    🔍 包工头硬核解析:为什么加了 KUBERNETES_SERVICE_HOST? 由于我们禁用了 kube-proxy,新节点在加入的一瞬间可能会因为找不到本地路由而无法联系上 API Server。通过显式声明环境变量,我们强行给新兵指明了 VIP 这座“灯塔”的方向。

  4. 点名验收 (Verify Nodes)

    回到你的 指挥中心 (Mac/Win),查看全家福:

    Terminal window
    kubectl get nodes -o wide

    期望结果:你会看到一个角色为 <none> 的新节点。稍等 1 分钟,它的状态会从 NotReady 变为 Ready。

  5. 🚨 紧急避坑:打通跨网段大动脉 (解决 Longhorn 插件无限重启)

    如果你加入的新节点与 Master 节点不在同一个物理网段(例如 Master 在 10.0.10.x,而子节点在 192.168.100.x),你会发现新节点虽然 Ready,但上面的 longhorn-csi-plugin 容器会陷入无限重启,日志疯狂报错 DNS 超时(i/o timeout)。

    🔍 包工头硬核解析: 这是因为咱们之前配置 Cilium 时开启了 Native Routing(原生直通路由)。跨网段通信时,物理路由器根本不认识咱们 K8s 内部的 10.60.x.x Pod 网段,直接把通信包当成非法流量丢弃了。

    🧱 抢修施工:热切换至 VXLAN 隧道模式 回到你的 Master 01 节点,执行以下命令。这能让 Cilium 在物理机之间建立 UDP 加密暗道,强行“骗过”底层路由器:

    Terminal window
    # 1. 确保已添加 Cilium 官方 Helm 仓库
    helm repo add cilium https://helm.cilium.io/
    helm repo update
    # 2. 强制热更新:切断 Native 路由,开启 VXLAN 隧道
    # (这里用 10.60.0.0/16 骗过 Helm 的死板校验语法)
    helm upgrade cilium cilium/cilium \
    --namespace kube-system \
    --reuse-values \
    --set routingMode=tunnel \
    --set tunnelProtocol=vxlan \
    --set autoDirectNodeRoutes=false \
    --set ipv4NativeRoutingCIDR="10.60.0.0/16" \
    --force-conflicts

    配置刷入后,必须强制底层的网络保安重新交接班:

    Terminal window
    kubectl rollout restart ds/cilium -n kube-system

    稍等片刻,等所有 cilium-agent 重新恢复 Running 后,跨网段的暗道就彻底打通了。再去查看那个报错的 Longhorn 插件,它已经奇迹般地变绿干活了!


🚪 终极交付:提取指挥权与防线极限跑分

Section titled “🚪 终极交付:提取指挥权与防线极限跑分”

地基彻底打完了!在引入具体的业务(比如我们下一篇要搞的 Headlamp 可视化面板)之前,我们需要做最后的交付验收。

🔑 步骤 A:提取集群最高指挥权 (Kubeconfig)

Section titled “🔑 步骤 A:提取集群最高指挥权 (Kubeconfig)”

你总不能以后每次折腾集群,都要用 SSH 连到服务器的黑框框里。我们要把集群的“遥控器”拿回你的个人电脑(Mac/Win)。

10.0.10.10 服务器上执行:

Terminal window
sudo cat /etc/rancher/k3s/k3s.yaml

配置指南:

  1. 复制终端里输出的全部 YAML 文本。
  2. 回到你自己的个人电脑(Mac/Windows),打开或新建 ~/.kube/config 文件,将内容粘贴进去。
  3. 关键一步:找到 server: https://127.0.0.1:6443 这一行,无情地把 IP 修改为你那扇永不宕机的大门 —— VIP https://10.0.10.2:6443
  4. 在你的个人电脑终端里敲下 kubectl get nodes。如果能看到节点列表,说明遥控器配对成功!以后你就可以优雅地在本地掌控整个赛博工地了。
  5. 如果你有多个集群,可以设置不同名称,例如~/.kube/N100config 命令行执行export KUBECONFIG=~/.kube/N100config就可以切换到对应集群 官方文档:

在 macOS 系统上安装和设置 kubectl

在 Windows 上安装 kubectl

命令行工具 (kubectl)具体命令


🏎️ 步骤 B:赛博防线极限跑分 (Cilium Connectivity Test)

Section titled “🏎️ 步骤 B:赛博防线极限跑分 (Cilium Connectivity Test)”

是骡子是马,拉出来跑跑。Cilium 官方提供了一个企业级的连通性测试工具,它会自动拉起大量的 Pod,进行跨节点、跨策略的疯狂互殴测试。

Terminal window
cilium connectivity test

此时你可以去泡杯咖啡。当你回来,如果能看到满屏绿色的 ✅ [cilium-test-1] All XX tests (XX actions) successful, XX tests skipped, 0 scenarios skipped.,那么恭喜你!这套 K3s + kube-vip + Cilium 的终极硬核底座,已经完美通过了企业级的极限检验!

测试后删除测试数据

Terminal window
kubectl delete ns cilium-test-1 cilium-test-ccnp1 cilium-test-ccnp2

🎯 下一步施工计划: 纯命令行的世界太枯燥了。现在,带上你的安全帽,跟我去申请第一个发牌员 IP,部署极具赛博朋克风格的轻量级 Kubernetes 面板! 👉 实战演练:部署 Headlamp 仪表盘检验集群战力


老规矩,把核心参数记录到知识库里,防止半年后遗忘。

## 🚀 K3s-HA 核心资产备忘 (替换为你的参数)
- **网段规划**: Pod CIDR: `10.60.0.0/16` | Service CIDR: `10.61.0.0/16`
- **控制平面 VIP**: `10.0.10.2` (由 kube-vip 提供 ARP 宣告)
- **业务负载均衡池**: `10.0.10.50 - 200` (由 Cilium eBPF L2 提供)
- **底层特性**: 完全抛弃 kube-proxy,开启 Native Routing,关闭自带 Traefik。
Section titled “🔗 赛博包工头的秘密书签 (Useful Links)”
  • 💣 K3s 官方卸载脚本
    • 包工头点评:在 Homelab 折腾,推倒重来是家常便饭。如果集群被你玩炸了,千万别直接删虚拟机,在节点上执行 k3s-uninstall.sh,这能把网络规则和文件残留清理得干干净净。

[Q] 为什么要给 kube-vip 打最高优先级? [A]: 在资源吃紧的 Homelab 环境里(比如你跑了个转码吃满 CPU 的服务),Kubernetes 的驱逐机制六亲不认。如果不给 kube-vip 免死金牌,它一旦被干掉,整个集群的控制平面大门就直接消失了。

[Q] 为什么分配了 LoadBalancer IP,但在宿主机执行 ip a 看不到它? [A]: 这是 Cilium eBPF 的“降维打击”。IP 并没有挂载在 Linux 内核的网络栈上,而是直接存在于 eBPF 的内存映射表中。流量在进入网卡的瞬间就被劫持并转发给容器了,Linux 内核全程甚至都不知道这个 IP 的存在。

[Q] 业务分配了 IP,局域网能访问,但 Ping 却丢包或超时? [A]: 正常现象。Cilium 的 L2 LB 默认只放行 Service 定义的端口(如 TCP 80)。由于这个 VIP 并不真实存在于宿主机内核,宿主机不会自动回复 ICMP 请求。这反而是一种天然的安全屏障。

[Q] 未来加入不同网卡名(如 eth0)的机器,会导致网络瘫痪吗? [A]: 不会。由于我们在 Helm 配置中使用了 devices="en+,eth+" 正则,并在 L2 策略中预留了匹配规则,新节点会自动识别自己的物理大门并上岗,实现了“异构节点”的自适应兼容。

[Q] 部署完成后,想修改或追加 Cilium 的底层参数怎么办? [A]: 绝对不要卸载重装!作为架构师,我们要习惯使用 Helm 进行“热更新”。操作时有两步铁律:

  1. 必须携带护身符:使用 upgrade 命令时,必须带上 --reuse-values 参数。它会继承你之前所有的老配置,只覆盖或追加你新写的 --set,否则之前的网络地基会被瞬间清空!
  2. 强制交接班:Helm 更新的只是 K8s 里的“图纸”(ConfigMap)。必须要重启底层的 cilium-agent,保安们才会按照新图纸上岗。

标准施工命令:

Terminal window
# 1. 刷新配置图纸
helm upgrade cilium ./cilium \
--namespace kube-system \
--reuse-values \
--set <你的新参数>=<值>
# 2. 强制底层保安重新加载配置
kubectl rollout restart ds/cilium -n kube-system

常用命令(自用)


赛博地基虽然打好了,但现在的集群还只是一个没有记忆、无法与外界通信的“孤岛”。接下来的施工顺序极其关键,请严格按步骤推进:

  • 🚦 流量网关:部署 Traefik v3 与 Gateway API
    • 任务:替换掉被我们卸载的 K3s 默认网关,让外部流量优雅地根据域名流入咱们的赛博靶场。
  • 🛡️ 内网穿透:集成 Cloudflare Tunnel
    • 任务:没有公网 IP 也能建站。安全打穿内网,把咱们的服务发布到互联网上。
  • 💾 数据堡垒:部署 Longhorn 分布式存储
    • 任务(核心前置节点) 没有持久化存储的 Kubernetes 只是没有记忆的鱼。我们将把 3 台虚拟机的剩余硬盘捏合成一个高可用存储池,为数据库和核心业务提供保障。
  • 🐙 GitOps 革命:引入 Argo CD
    • 任务(强烈建议在 Longhorn 完成后再搞) 抛弃手敲 kubectl apply 的低级趣味。让 GitHub 仓库成为你集群的唯一真理,实现“代码即基建”的自动化部署。