适用场景:手头有 2+ 闲置 SBC(树莓派 ⅘、NanoPi R4S/R6S、N100 盒子、零刻 EQ12/SEi12、护云/联想 M920q 等),想组建 低功耗、高可用、边缘计算集群
典型功耗:全套 3 节点 15-30W(含 PoE 交换机),年电费 100-200 元
核心栈:k3s(轻量 K8s)+ 长安链/监控/存储/服务编排,离线部署、GitOps、自动恢复
—
0. 硬件选型速查表(2024/2025 主流二手/新款)
| 设备 | CPU | 内存 | 网口 | 扩展 | 典型功耗 | 二手价(¥) | 适配角色 |
| 树莓派 4B 8GB | BCM2711 4×A72 1.5GHz | 8GB LPDDR4 | 1×GbE | 2×USB3、PCIe(需转接) | 4-6W | 350-450 | 控制平面 / 存储节点 |
| 树莓派 5 8GB | BCM2712 4×A76 2.4GHz | 8GB LPDDR4X | 1×GbE | PCIe 2.0 ×1、2×USB3 | 5-8W | 550-650 | 高性能控制平面 / AI 推理 |
| NanoPi R4S | RK3399 2×A72+4×A53 | 4GB LPDDR4 | 2×GbE | 1×USB3、PCIe(底板) | 3-5W | 300-380 | 网关/旁路/集群节点 |
| NanoPi R6S | RK3588 4×A76+4×A55 | 8GB LPDDR4X | 2×2.5GbE | 1×USB3、M.2 NVMe | 5-8W | 600-750 | 高性能网关/存储节点 |
| N100 盒子 (零刻/联想/护云/GMKtec) | N100 4×E-core 3.4GHz | 8/16GB DDR5 | 2×2.5GbE | M.2 2280 NVMe、SATA | 6-12W | 500-900 | 性价比之王,全能节点 |
| 零刻 EQ12 / SEi12 (N100/N200/N305) | 同上 | 16/32GB | 2×2.5GbE | 双 M.2、2.5″ SATA | 8-15W | 800-1300 | 单节点承载全套服务 |
| PoE 交换机 (4/8 口) | — | — | 4/8×GbE/2.5G | PoE+/++ | 5-15W | 100-300 | 供电+数据一根线 |
组网建议:
- 3 节点起步(1 控制 + 2 工作),生产建议 3 控制 + N 工作(奇数保证 etcd 仲裁)
- 统一用 PoE 供电,省去 3-5 个电源适配器,机柜整洁
- 存储节点必配 NVMe/SATA,跑 Longhorn / Ceph / MinIO / PostgreSQL
- 网关节点(R4S/R6S)跑 OpenWrt/iStoreOS + k3s agent,双角色复用
—
1. 硬件组装清单(3 节点示例:1×N100 控制 + 2×Pi 4 工作)
| 物品 | 数量 | 备注 |
| N100 盒子 (8G/128G) | 1 | 控制平面 + 存储 (挂 1T NVMe) |
| 树莓派 4B 8GB | 2 | 工作节点 (各挂 512G SSD) |
| 5 口 2.5G PoE 交换机 | 1 | 供电 + 互联 |
| 12V/24V PoE 分离器 | 3 | Pi 4 无原生 PoE,需分离器 |
| M.2 NVMe 1TB / 512GB | 3 | 系统盘 + 数据盘 |
| 短网线 (Cat6a 0.3m) | 3 | 机箱内整齐 |
| 机架/导轨/散热贴 | 1套 | 可选,3D 打印支架更省钱 |
接线图:
[上游光猫/路由] ──(WAN)──> [N100 网口1] (OpenWrt/iStoreOS 旁路网关)
[N100 网口2] ──> [PoE 交换机] ──> [Pi4-1] [Pi4-2] [N100 网口3(可选)]
所有设备同一 VLAN / 子网:192.168.10.0/24 (管理) + 10.10.10.0/24 (集群 Pod 网段)
—
2. 系统底座:统一刷入 Ubuntu 24.04 LTS (ARM64/AMD64)
理由:内核新、包新、长期支持、k3s/containerd/helm 官方首发测试平台。
2.1 树莓派:Raspberry Pi Imager → Ubuntu Server 24.04.1 LTS (64-bit)
# 启用 SSH、设置用户名/密码/hostname、配置 WiFi(可选)
# 烧录后首启自动完成 cloud-init
2.2 N100 盒子:Ventoy + Ubuntu Server 24.04.1 ISO → 安装到 NVMe
# BIOS 关闭 Secure Boot、开启 UEFI、关闭 CSM
# 分区:/boot/efi 1G + / 剩余 (ext4/btrfs/zfs)
# 安装 openssh-server、配置静态 IP
2.3 统一后初始化(所有节点跑一次)
#!/usr/bin/env bash
# init-node.sh —— 以 root 运行
set -euo pipefail
HOSTNAME=$(hostname)
IP=$(ip -4 addr show scope global | awk '/inet/{print $2}' | cut -d/ -f1 | head -1)
echo ">>> 初始化 $HOSTNAME ($IP)"
# 1. 换源(中科大/阿里云)
sed -i 's|http://ports.ubuntu.com|https://mirrors.ustc.edu.cn|g' /etc/apt/sources.list.d/*.list /etc/apt/sources.list 2>/dev/null || true
apt update && apt upgrade -y
# 2. 基础工具
apt install -y curl wget vim htop iotop iftop net-tools ipvsadm conntrack \
socat jq yq git ca-certificates gnupg lsb-release \
nfs-common open-iscsi multipath-tools \
linux-modules-extra-$(uname -r) # 含 overlay、br_netfilter 等
# 3. 内核参数(K8s 必需)
cat >/etc/sysctl.d/99-k8s.conf <<'EOF'
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
fs.inotify.max_user_watches = 524288
fs.inotify.max_user_instances = 512
vm.max_map_count = 262144
kernel.panic = 10
kernel.panic_on_oops = 1
EOF
sysctl --system
# 4. 关闭 swap(K8s 要求)
swapoff -a
sed -i '/ swap / s/^/#/' /etc/fstab
# 5. containerd(k3s 自带,这里只配置镜像加速)
mkdir -p /etc/containerd
containerd config default | sed 's|sandbox_image = "registry.k8s.io/pause:3.6"|sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"|' > /etc/containerd/config.toml
sed -i 's|config_path = ""|config_path = "/etc/containerd/certs.d"|' /etc/containerd/config.toml
mkdir -p /etc/containerd/certs.d/docker.io
cat >/etc/containerd/certs.d/docker.io/hosts.toml <<'EOF'
server = "https://docker.io"
[host."https://docker.mirrors.ustc.edu.cn"]
capabilities = ["pull", "resolve"]
EOF
systemctl restart containerd
# 6. 时间同步
timedatectl set-timezone Asia/Shanghai
systemctl enable --now systemd-timesyncd
# 7. 磁盘调度器(NVMe/SSD 用 none)
for d in /sys/block/*/queue/scheduler; do echo none > $d 2>/dev/null || true; done
echo ">>> $HOSTNAME 初始化完成,IP: $IP"
—
3. 部署 k3s 高可用集群(3 控制平面 + N 工作)
3.1 准备:固定 VIP(虚拟 IP)用 Keepalived / kube-vip
# 在所有控制平面节点跑(示例 VIP 192.168.10.100)
# 这里用 kube-vip(更云原生,无需额外 VRRP)
3.2 第一控制节点( bootstrap )
# N100 控制节点 (假设 IP 192.168.10.11)
export K3S_TOKEN="$(openssl rand -hex 32)" # 记下来,其它控制节点要用
export K3S_CLUSTER_INIT="true"
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION="v1.28.5+k3s1" \
INSTALL_K3S_EXEC="server \
--cluster-init \
--tls-san 192.168.10.100 \
--node-ip 192.168.10.11 \
--flannel-backend=wireguard-native \
--disable=traefik \
--disable=servicelb \
--disable=local-storage \
--write-kubeconfig-mode 644" \
sh -s -
# 验证
kubectl get nodes -o wide
镜像加速:INSTALL_K3S_MIRROR=cn 自动走 Rancher 中国镜像站。
3.3 其余控制节点(加入集群)
# Pi4-1 (192.168.10.12)、Pi4-2 (192.168.10.13)
export K3S_TOKEN="<第一节点生成的 TOKEN>"
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_VERSION="v1.28.5+k3s1" \
INSTALL_K3S_EXEC="server \
--server https://192.168.10.11:6443 \
--node-ip 192.168.10.12 \
--flannel-backend=wireguard-native \
--disable=traefik \
--disable=servicelb \
--disable=local-storage \
--write-kubeconfig-mode 644" \
sh -s -
3.4 工作节点(仅 agent)
# 同理,去掉 --cluster-init 和 --tls-san
export K3S_TOKEN="<TOKEN>"
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_EXEC="agent \
--server https://192.168.10.100:6443 \
--node-ip 192.168.10.xx \
--flannel-backend=wireguard-native" \
sh -s -
3.5 部署 kube-vip(提供 VIP 192.168.10.100)
# 在任意控制节点(需 kubectl 已配置)
kubectl apply -f https://raw.githubusercontent.com/kube-vip/kube-vip/main/manifests/rbac.yaml
# 创建 kube-vip 镜像别名(避免拉取超时)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-vip
namespace: kube-system
data:
vip: "192.168.10.100"
interface: "eth0"
controlplane: "true"
services: "true"
leaderElection: "true"
leaseDuration: "5"
renewDeadline: "3"
retryPeriod: "1"
EOF
kubectl apply -f https://raw.githubusercontent.com/kube-vip/kube-vip/main/manifests/daemonset.yaml
现在 https://192.168.10.100:6443 就是集群统一入口,任意控制节点挂了 VIP 自动漂移。
—
4. 核心插件:存储、网络、DNS、监控、GitOps
4.1 存储:Longhorn(云原生块存储,自动副本、快照、备份)
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.6.2/deploy/longhorn.yaml
# 等待所有 longhorn-* pod Running
# UI: kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80
# 设置:默认副本数 3、过度配比 200%、备份目标 S3/NFS/MinIO
节点打标签:kubectl label node pi4-1 longhorn=true,只让有盘节点跑副本。
4.2 网络:Cilium(eBPF、NetworkPolicy、Hubble 可视化)
helm repo add cilium https://helm.cilium.io --force-update
helm install cilium cilium/cilium --version 1.15.5 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set ipam.mode=kubernetes \
--set ipv4NativeRoutingCIDR=10.10.10.0/24 \
--set hubble.enabled=true,hubble.ui.enabled=true \
--set prometheus.enabled=true,operator.prometheus.enabled=true
4.3 DNS:CoreDNS(k3s 自带)+ 外部域名同步
# external-dns 同步 Service/Ingress 到 Cloudflare
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm install external-dns external-dns/external-dns --version 1.14.0 \
--namespace kube-system \
--set provider=cloudflare \
--set env[0].name=CF_API_TOKEN,env[0].value=xxx \
--set policy=sync \
--set txtOwnerId=my-cluster
4.4 入口:Traefik(k3s 自带)或 Nginx Ingress
# k3s 自带 Traefik,已禁用;改用 Nginx Ingress Controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx --version 4.10.1 \
--namespace ingress-nginx --create-namespace \
--set controller.service.type=LoadBalancer \
--set controller.service.externalIPs={192.168.10.100} \
--set controller.publishService.enabled=true \
--set controller.metrics.enabled=true
4.5 监控:kube-prometheus-stack(Prometheus + Grafana + Alertmanager)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install monitoring prometheus-community/kube-prometheus-stack --version 56.0.0 \
--namespace monitoring --create-namespace \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.storageClassName=longhorn \
--set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.resources.requests.storage=50Gi \
--set grafana.persistence.enabled=true,grafana.persistence.storageClassName=longhorn,grafana.persistence.size=10Gi \
--set alertmanager.alertmanagerSpec.storage.volumeClaimTemplate.spec.storageClassName=longhorn \
--set alertmanager.alertmanagerSpec.storage.volumeClaimTemplate.spec.resources.requests.storage=10Gi
导入 Dashboard:Grafana → Dashboards → Import → ID: 315 (K8s Cluster), 1860 (Node Exporter), 14282 (Cilium), 12034 (Longhorn)。
4.6 GitOps:ArgoCD(声明式同步、多环境、自动修复)
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.9.3/manifests/install.yaml
# 修改 argocd-server 为 LoadBalancer 或 Ingress
kubectl patch svc argocd-server -n argocd -p '{"spec":{"type":"LoadBalancer","externalIPs":["192.168.10.100"]}}'
# 初始密码
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
应用示例:创建 Application CRD 指向 Git 仓库,ArgoCD 自动同步所有服务。
—
5. 典型服务编排(Helm / Kustomize / ArgoCD App of Apps)
| 服务 | 存储类 | 关键配置 |
| Jellyfin | Longhorn (配置) + NFS/本地盘 (媒体库) | 硬解需 devicePlugin: vfio + /dev/dri 挂载 |
| Home Assistant | Longhorn (配置+DB) | network_mode: host 需 hostNetwork: true + privileged: true |
| PostgreSQL (CloudNativePG) | Longhorn | cluster: instances: 3, replicas: 2, backup: schedule: "0 2 * * *" |
| MinIO (Operator) | Longhorn | pools: - name: data, servers: 3, volumesPerServer: 4 |
| Gitea + Drone/Woodpecker | Longhorn (Gitea) + 空目录 | CI/CD 全自托管 |
| Vaultwarden | Longhorn | signups_allowed: false, admin_token: xxx |
| Uptime Kuma | Longhorn | 心跳监控、状态页 |
| AdGuard Home | Longhorn (配置) | hostNetwork: true 做 DNS 入口 |
所有服务 只写 YAML,扔给 ArgoCD,集群挂了重装 k3s → 装 ArgoCD → 全自动恢复。
—
6. 离线/气隙部署包(生产必备)
# 1. 镜像打包
ctr -n k8s.io images export /tmp/k3s-images.tar \
$(ctr -n k8s.io images ls -q | grep -E 'rancher|cilium|longhorn|prometheus|grafana|argocd|traefik|nginx|external-dns|kube-vip|pause|coredns|metrics-server|local-path-provisioner')
# 2. Helm Chart 打包
mkdir -p /tmp/charts
for chart in cilium longhorn prometheus-community/kube-prometheus-stack ingress-nginx/ingress-nginx argoproj/argo-cd external-dns/external-dns; do
helm pull $chart --untar -d /tmp/charts --version latest
done
# 3. 打包发运
tar -czf k3s-airgap-$(date +%Y%m%d).tar.gz /tmp/k3s-images.tar /tmp/charts /usr/local/bin/init-node.sh
# 目标机器:解压 → ctr images import → helm install -f values.yaml ./charts/xxx
—
7. 运维自动化(Ansible + 生命周期)
# ansible/inventory.yml
all:
vars:
ansible_user: ubuntu
ansible_become: yes
k3s_version: "v1.28.5+k3s1"
cluster_vip: "192.168.10.100"
children:
control:
hosts:
n100: { ansible_host: 192.168.10.11, node_ip: 192.168.10.11 }
pi4-1: { ansible_host: 192.168.10.12, node_ip: 192.168.10.12 }
pi4-2: { ansible_host: 192.168.10.13, node_ip: 192.168.10.13 }
worker:
hosts: {}
# 一键全生命周期
ansible-playbook -i inventory.yml site.yml --tags bootstrap # 首次部署
ansible-playbook -i inventory.yml site.yml --tags upgrade # 版本升级
ansible-playbook -i inventory.yml site.yml --tags rotate-cert # 证书轮换
ansible-playbook -i inventory.yml site.yml --tags backup # etcd/Longhorn 备份
ansible-playbook -i inventory.yml site.yml --tags scale # 扩缩容节点
—
8. 常见坑 & 避坑指南
| 现象 | 原因 | 修正 |
Pi 4 启动卡 Waiting for network | 无系统盘、从网络启动超时 | 刷 Ubuntu Server 到 NVMe/SSD、关闭 PXE Boot |
| k3s agent 连不上 server | VIP 不通 / 端口 6443 拦截 | 检查 kube-vip pod、防火墙放行 6443、Keepalived 状态 |
Longhorn 副本卡 Pending | 节点无 longhorn=true 标签 / 磁盘未挂载 | kubectl label node <node> longhorn=true、确认 /var/lib/longhorn 挂载点 |
| Pod 无法解析外部域名 | CoreDNS 转发配置缺失 | kubectl -n kube-system edit cm coredns → forward . 1.1.1.1 8.8.8.8 |
| 证书过期导致集群不可用 | k3s 默认 1 年自签证书 | 升级 k3s 自动轮换、或手动 k3s certificate rotate |
| N100 盒子睡眠/断电后集群脑裂 | BIOS 电源管理、Watchdog 未启用 | BIOS 关闭 C-state、开启 Watchdog、systemd RuntimeWatchdogSec=30 |
| 磁盘 IO 瓶颈导致 etcd 延迟报警 | NVMe 共享/无 QoS | Longhorn 设置 replicaSoftAntiAffinity: true、etcd 专盘、限制 io.qos |
| 升级 k3s 后 CNI 冲突 | flannel ↔ wireguard-native ↔ cilium 混用 | 统一迁移到 Cilium、卸载 flannel、kubectl delete ds -n kube-system kube-flannel-ds |
—
9. 维护清单(贴机柜)
| 频率 | 动作 |
| 每天 | Grafana 巡检:节点 CPU/内存/磁盘/网络、Pod 重启率、Longhorn 副本健康、证书剩余天数 |
| 每周 | kubectl get nodes -o wide 确认版本一致、kubectl top nodes/pods 看资源水位、检查 ArgoCD 同步状态 |
| 每月 | k3s 小版本升级(k3s upgrade)、容器镜像扫描(Trivy)、Longhorn 备份验证恢复 |
| 每季度 | etcd 备份验证、CA 证书轮换、硬件巡检(风扇/温度/电源/PoE 供电)、固件/BIOS 更新 |
| 每年 | 硬件更换评估(电池/风扇/SSD 健康度)、架构演进(ARM64→RISC-V、引入 GPU/NPU 节点) |
—
10. 一键部署脚本(全自动,含上述所有组件)
# cluster-bootstrap.sh —— 在第一控制节点以 root 运行
#!/usr/bin/env bash
set -euo pipefail
source /etc/os-release
echo ">>> Ubuntu $VERSION_ID 集群引导开始"
# 1. 所有节点跑 init-node.sh(通过 Ansible 或 pdsh 并行)
# ansible all -m script -a ./init-node.sh
# 2. 部署 k3s HA(按上文 3.2-3.5)
# ansible-playbook -i inventory.yml k3s-ha.yml
# 3. 部署核心插件(Helm 统一管理)
helm repo add longhorn https://charts.longhorn.io
helm repo add cilium https://helm.cilium.io
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
# Longhorn
helm upgrade --install longhorn longhorn/longhorn -n longhorn-system --create-namespace \
--version 1.6.2 --set defaultSettings.defaultReplicaCount=3
# Cilium
helm upgrade --install cilium cilium/cilium -n kube-system --version 1.15.5 \
--set kubeProxyReplacement=true --set ipam.mode=kubernetes \
--set hubble.enabled=true,hubble.ui.enabled=true
# Monitoring
helm upgrade --install monitoring prometheus-community/kube-prometheus-stack -n monitoring --create-namespace \
--version 56.0.0 --set prometheus.prometheusSpec.storageSpec.volumeClaimTemplate.spec.storageClassName=longhorn
# ArgoCD
helm upgrade --install argocd argo/argo-cd -n argocd --create-namespace \
--version 7.6.0 --set server.service.type=LoadBalancer,server.service.externalIPs={192.168.10.100}
echo ">>> 集群引导完成!访问:"
echo " Longhorn UI: https://longhorn.${DOMAIN}"
echo " Grafana: https://grafana.${DOMAIN}"
echo " ArgoCD: https://argocd.${DOMAIN}"
echo " kube-vip VIP: https://192.168.10.100:6443"
—
11. 资源 & 参考
—