>

适用场景:手头有 2+ 闲置 SBC(树莓派 ⅘、NanoPi R4S/R6S、N100 盒子、零刻 EQ12/SEi12、护云/联想 M920q 等),想组建 低功耗、高可用、边缘计算集群
> 典型功耗:全套 3 节点 15-30W(含 PoE 交换机),年电费 100-200 元
> 核心栈:k3s(轻量 K8s)+ 长安链/监控/存储/服务编排,离线部署、GitOps、自动恢复


0. 硬件选型速查表(2024/2025 主流二手/新款)

设备CPU内存网口扩展典型功耗二手价(¥)适配角色
树莓派 4B 8GBBCM2711 4×A72 1.5GHz8GB LPDDR41×GbE2×USB3、PCIe(需转接)4-6W350-450控制平面 / 存储节点
树莓派 5 8GBBCM2712 4×A76 2.4GHz8GB LPDDR4X1×GbEPCIe 2.0 ×1、2×USB35-8W550-650高性能控制平面 / AI 推理
NanoPi R4SRK3399 2×A72+4×A534GB LPDDR42×GbE1×USB3、PCIe(底板)3-5W300-380网关/旁路/集群节点
NanoPi R6SRK3588 4×A76+4×A558GB LPDDR4X2×2.5GbE1×USB3、M.2 NVMe5-8W600-750高性能网关/存储节点
N100 盒子 (零刻/联想/护云/GMKtec)N100 4×E-core 3.4GHz8/16GB DDR52×2.5GbEM.2 2280 NVMe、SATA6-12W500-900性价比之王,全能节点
零刻 EQ12 / SEi12 (N100/N200/N305)同上16/32GB2×2.5GbE双 M.2、2.5″ SATA8-15W800-1300单节点承载全套服务
PoE 交换机 (4/8 口)——4/8×GbE/2.5GPoE+/++5-15W100-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 8GB2工作节点 (各挂 512G SSD)
5 口 2.5G PoE 交换机1供电 + 互联
12V/24V PoE 分离器3Pi 4 无原生 PoE,需分离器
M.2 NVMe 1TB / 512GB3系统盘 + 数据盘
短网线 (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)

服务存储类关键配置
JellyfinLonghorn (配置) + NFS/本地盘 (媒体库)硬解需 devicePlugin: vfio + /dev/dri 挂载
Home AssistantLonghorn (配置+DB)network_mode: host 需 hostNetwork: true + privileged: true
PostgreSQL (CloudNativePG)Longhorncluster: instances: 3, replicas: 2, backup: schedule: "0 2 * * *"
MinIO (Operator)Longhornpools: - name: data, servers: 3, volumesPerServer: 4
Gitea + Drone/WoodpeckerLonghorn (Gitea) + 空目录CI/CD 全自托管
VaultwardenLonghornsignups_allowed: false, admin_token: xxx
Uptime KumaLonghorn心跳监控、状态页
AdGuard HomeLonghorn (配置)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 连不上 serverVIP 不通 / 端口 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 共享/无 QoSLonghorn 设置 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. 资源 & 参考


— 感谢阅读 —

一起交流

分享你的思考,让讨论更进一步。