☰
家庭实验室实战:18台服务器、60TB存储与双K8s集群的架构与运维
2026/9/25 12:49:12 网站建设 项目流程

1. 家庭实验室的缘起与整体架构设计

1.1 为什么要在家里搞这么一套“重装备”

很多人第一次听到“家里跑18台服务器、60TB存储、两个K8s集群”,第一反应是“这得烧多少钱、费多少电”。但如果你真的在运维、后端、存储或者AI方向干过几年,就会明白:家庭实验室的价值不在于省钱,而在于给你一个可以随便折腾、随便搞坏、随便重建的真实环境。公司里的生产集群你不敢乱动,云上的资源你按小时付费心里发慌,只有自己家里这套东西,才能让你真正把K8s的调度策略、分布式存储的故障恢复、GPU算子的执行流程这些硬骨头啃透。

我最初入坑也是从一台退役的迷你主机开始的,后来慢慢攒成了现在的规模。整个实验室的核心诉求其实就三条:第一,能跑真实的K8s工作负载,包括有状态服务、GPU任务、Ingress流量;第二,存储要够大够稳,60TB的可用容量要能扛住硬盘故障,还要能对接对象存储;第三,网络和远程访问要顺手,在外面也能连回来调试。这三条听起来简单,但每一条往下挖都是一堆坑。

1.2 整体拓扑:双集群分工与硬件分层

先把这个实验室的骨架说清楚。18台服务器不是堆在一起跑一个集群,而是分成了两个独立的K8s集群,各自有明确的分工。

第一个集群我称之为“生产模拟集群”,由6台机器组成,全部是x86架构,配了万兆网卡。这个集群跑的是长期在线的服务:比如自建的RustDesk中继、MinIO对象存储网关、PostgreSQL和MySQL的有状态副本集、以及一套完整的监控告警栈(Prometheus + Grafana + Loki)。它的定位是“不能随便挂”,所以节点选的是低功耗但稳定的平台,硬盘全部走RAID卡或者ZFS镜像。

第二个集群叫“实验集群”,12台机器,这里面就杂了:有ARM架构的迷你主机,有带NVIDIA RTX 4060 Laptop GPU的笔记本改的节点,还有几台老旧的瘦客户机刷了Linux当边缘节点。这个集群专门用来做“破坏性实验”:比如测试K8s的ExternalIPs行为、验证GPU算子的Cooperative Thread Array调度、跑Foldseek这种需要GPU加速的生物信息学工具、以及各种Operator的升级回滚。两个集群之间通过一个独立的万兆交换机互联,但K8s层面完全隔离,避免实验集群的幺蛾子影响生产模拟集群。

存储方面,60TB的裸容量分布在三个地方:一台群晖NAS做冷备和媒体库,一台自组的飞牛NAS做热数据和Docker卷,还有一台PVE宿主机上跑了一块直通的HBA卡接硬盘笼做Ceph的OSD。为什么不用一套存储全搞定?因为实际用下来,群晖的Btrfs在快照和共享上最省心,飞牛NAS的Docker集成和iStock相册管理对小白最友好,而Ceph则是K8s集群里动态供给PVC的唯一正解。三者各司其职,通过NFS和S3协议互通。

1.3 选型背后的逻辑:为什么是K8s而不是Docker Compose

有人会问:家里就自己用,Docker Compose不就够了?我一开始也是这么想的,直到我需要做这几件事:滚动更新一个有状态服务而不中断连接、把GPU任务调度到特定节点、用NetworkPolicy隔离不同命名空间的流量、通过HPA根据CPU温度自动扩容。这些需求Compose要么做不了,要么做得很别扭。

K8s带来的最大好处是声明式API和自愈能力。比如我那个跑MinIO的Pod,有一次因为节点内存泄漏被OOM Killer干掉了,K8s在30秒内就在另一个节点上重新拉起来了,PVC自动挂载,服务几乎无感知。这种体验在单机Docker上是很难做到的。当然代价就是复杂度飙升,所以我才把集群拆成两个:生产模拟集群用kubeadm装的标准版,实验集群用k3s省资源,不同场景用不同发行版,这也是K8s生态成熟的好处。

2. 60TB存储的层次化设计与实操要点

2.1 存储分层:热、温、冷三档怎么分

60TB听起来很大,但如果你把4K原盘、虚拟机镜像、数据库备份、容器镜像全混在一起放,很快就会乱成一锅粥。我的做法是按访问频率分三档,每档用不同的硬件和文件系统。

热数据层大约8TB,放在飞牛NAS的NVMe缓存加速的HDD阵列上,跑的是ZFS。这一层放的是正在运行的虚拟机磁盘、K8s的PV、以及经常读写的代码仓库。ZFS的ARC缓存和L2ARC让我在随机读的时候几乎感觉不到机械硬盘的延迟。温数据层大约20TB,放在群晖NAS的SHR阵列上,主要存媒体库、照片备份和文档。这一层用Btrfs,快照和压缩是刚需。冷数据层大约32TB,放在PVE宿主机直通的硬盘笼里,用Ceph做对象存储,专门存备份归档和不再修改的原始素材。

注意:不要把所有硬盘都塞进一个阵列。我曾经把12块盘全组了一个RAIDZ2,结果一次重建花了整整三天,期间性能掉到没法用。后来改成多个小阵列,重建时间缩短到几小时,风险也分散了。

2.2 ZFS参数调优:recordsize和ashift怎么选

ZFS的默认recordsize是128K,这对大文件很友好,但对虚拟机镜像和数据库这种小IO场景就不太合适。我的做法是按数据集分别设置:

# 虚拟机镜像数据集,recordsize设为16K zfs create tank/vm zfs set recordsize=16K tank/vm # 媒体库数据集,保持128K默认值 zfs create tank/media # 数据库数据集,recordsize设为8K,并开启logbias=throughput zfs create tank/db zfs set recordsize=8K tank/db zfs set logbias=throughput tank/db

ashift参数更关键。现代硬盘的物理扇区基本都是4K,所以ashift必须设为12(2的12次方等于4096)。如果你设成9(512字节),ZFS会做读-改-写,性能直接腰斩。检查方法:

zpool status -v # 看ashift值,如果是12就对了

如果已经建池且ashift错了,只能备份数据后重建池,没有在线修改的办法。这个坑我踩过两次,现在每次建池前都会用lsblk -o NAME,PHY-SEC确认物理扇区大小。

2.3 Ceph在家庭环境中的最小化部署

Ceph在K8s里做动态存储供给确实香,但官方文档动辄要求十几台节点,家庭环境根本跑不起。我的方案是三节点超融合:三台PVE宿主机,每台跑一个Ceph MON和MGR,每台直通一块HBA卡接四块硬盘做OSD。这样最小规模是3 MON + 3 MGR + 12 OSD,刚好满足Ceph的奇数仲裁要求。

关键配置在ceph.conf里:

[global] osd pool default size = 2 osd pool default min size = 1 osd pool default pg num = 128 osd pool default pgp num = 128

size=2而不是3,是因为家庭环境只有三节点,如果设成3,坏一台机器整个池就不可写了。设成2的话,坏一台还能降级运行,但要注意min size=1只在紧急恢复时用,平时保持2,否则数据一致性没保障。

在K8s里对接Ceph,我用的是Rook Operator。装完之后创建StorageClass:

apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: ceph-rbd provisioner: rook-ceph.rbd.csi.ceph.com parameters: clusterID: rook-ceph pool: replicapool imageFormat: "2" imageFeatures: layering reclaimPolicy: Delete allowVolumeExpansion: true

这样Pod申请PVC的时候就会自动从Ceph切一块RBD出来,删Pod的时候自动回收。实测下来,单块RBD的随机写IOPS在SSD缓存加持下能到8000左右,跑PostgreSQL完全够用。

2.4 对象存储:MinIO与它的替代者们

MinIO是我用得最久的对象存储方案,S3兼容性好,单二进制部署简单。但在2024年之后,MinIO的许可证变更和Web控制台功能削减让我开始找替代品。目前实验集群里跑的是Garage,一个轻量级的S3兼容存储,Rust写的,资源占用只有MinIO的三分之一。

Garage的部署很简单,每个节点跑一个garage二进制,配置文件里指定rpc_bind_addr和data_dir,然后通过garage layout assign把节点加入集群。它的优势是支持多站点复制,我在两个集群各放了一个Garage节点,通过garage bucket allow做跨集群同步,这样实验集群的备份可以直接推到生产模拟集群的存储里。

提示:如果你还在用MinIO,记得把MINIO_BROWSER=off关掉Web控制台,减少攻击面。家庭环境暴露在公网的服务越少越好。

3. 双K8s集群的搭建与差异化配置

3.1 生产模拟集群:kubeadm标准安装与高可用

生产模拟集群的6台机器里,3台做控制平面,3台做工作节点。控制平面用kubeadm装,关键是etcd的奇数仲裁和API Server的负载均衡。我在三台控制平面前面放了一个HAProxy做VIP,配置如下:

frontend k8s-api bind *:6443 mode tcp default_backend k8s-api-servers backend k8s-api-servers mode tcp balance roundrobin server cp1 10.0.0.11:6443 check server cp2 10.0.0.12:6443 check server cp3 10.0.0.13:6443 check

kubeadm初始化的时候用--control-plane-endpoint指向这个VIP:

kubeadm init --control-plane-endpoint "10.0.0.10:6443" \ --upload-certs \ --pod-network-cidr "10.244.0.0/16"

为什么用Flannel而不是Calico?因为家庭环境不需要复杂的NetworkPolicy,Flannel的VXLAN模式配置简单,跨节点通信稳定。Calico的BGP模式在只有三层交换机的家庭网络里反而容易出问题。

工作节点加入后,我给每个节点打了标签:

kubectl label node worker1 node-role.kubernetes.io/storage=true kubectl label node worker2 node-role.kubernetes.io/gpu=true kubectl label node worker3 node-role.kubernetes.io/ingress=true

这样在部署有状态服务的时候可以用nodeSelector精确调度。比如MinIO的Pod就只调度到storage节点,Ingress Controller只跑在ingress节点。

3.2 实验集群:k3s的轻量化与ARM混部

实验集群的12台机器里,有4台是ARM架构的迷你主机(RK3588),8台是x86的老旧设备。这种混合架构用kubeadm装会很痛苦,因为镜像要分架构。k3s原生支持多架构镜像,而且自带SQLite作为默认存储,省掉了etcd的运维负担。

安装k3s server:

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable traefik --disable servicelb" sh -

关掉traefik和servicelb,是因为我要用自己部署的Nginx Ingress和MetalLB。MetalLB在家庭环境里特别有用,它可以让LoadBalancer类型的Service直接拿到局域网IP,不用NodePort那么别扭。配置:

apiVersion: v1 kind: ConfigMap metadata: namespace: metallb-system name: config data: config: | address-pools: - name: default protocol: layer2 addresses: - 10.0.0.200-10.0.0.250

这样我创建一个LoadBalancer Service,MetalLB就会从池子里分配一个IP,局域网内直接访问。

ARM节点加入的时候要注意k3s的agent参数:

curl -sfL https://get.k3s.io | K3S_URL=https://10.0.1.10:6443 K3S_TOKEN=xxx sh -

ARM节点上跑的Pod必须是多架构镜像,否则会报exec format error。我一般用docker buildx构建多架构镜像,推送到私有Registry。私有Registry也是必须的,因为实验集群经常断网测试,不能依赖Docker Hub。

3.3 两个集群的互联与隔离

两个集群虽然K8s层面隔离,但底层网络是通的。我在两个集群各放了一个RustDesk中继节点,这样在外面可以随时连回家里调试。RustDesk自建服务器的好处是不依赖第三方中继,延迟低,而且可以自己控制密钥。

部署RustDesk Server:

docker run -d --name hbbs \ -p 21115:21115 -p 21116:21116 -p 21116:21116/udp \ -p 21118:21118 \ rustdesk/rustdesk-server:latest hbbs -r relay.example.com:21117 docker run -d --name hbbr \ -p 21117:21117 -p 21119:21119 \ rustdesk/rustdesk-server:latest hbbr

客户端配置里填上hbbs的地址和hbbr的地址,Key用cat id_ed25519.pub获取。实测下来,局域网内延迟在5ms以内,外网通过DDNS访问也能控制在50ms左右。

注意:DDNS配合动态公网地址访问NAS和RustDesk是常见做法,但一定要改默认端口、开防火墙白名单、禁用密码登录只用密钥。我见过太多人因为图省事被扫端口,最后NAS里数据全被加密。

4. GPU节点与AI工作负载的落地实践

4.1 RTX 4060 Laptop GPU在K8s中的直通

实验集群里有一台笔记本改的节点,带NVIDIA RTX 4060 Laptop GPU。要在K8s里用上这块卡,需要装NVIDIA Device Plugin和GPU Operator。但笔记本GPU有个坑:它默认是Optimus动态切换,Linux下需要手动指定用独显。

先在宿主机上确认GPU可见:

lspci | grep -i nvidia nvidia-smi

如果nvidia-smi报错,可能需要装nvidia-driver-535以上的版本,并在BIOS里关掉Secure Boot。然后装NVIDIA Container Toolkit:

distribution=$(. /etc/os-release;echo $ID$VERSION_ID) curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add - curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit sudo nvidia-ctk runtime configure --runtime=containerd sudo systemctl restart containerd

然后在K8s里部署Device Plugin:

kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.0/nvidia-device-plugin.yml

部署完成后,kubectl describe node <gpu-node>应该能看到nvidia.com/gpu: 1的资源。注意:笔记本GPU不支持vGPU,所以一个Pod独占整块卡,不能像数据中心卡那样切分。

4.2 PyTorch GPU环境的容器化封装

在K8s里跑PyTorch,最省心的方式是自己打一个基础镜像,把CUDA、cuDNN、PyTorch都装好,然后所有实验都基于这个镜像。我的Dockerfile:

FROM nvidia/cuda:12.1.0-cudnn8-runtime-ubuntu22.04 RUN apt-get update && apt-get install -y \ python3.10 python3-pip git wget \ && rm -rf /var/lib/apt/lists/* RUN pip3 install --no-cache-dir \ torch==2.1.0 torchvision==0.16.0 torchaudio==2.1.0 \ --index-url https://download.pytorch.org/whl/cu121 RUN pip3 install --no-cache-dir \ numpy pandas matplotlib jupyterlab WORKDIR /workspace CMD ["jupyter", "lab", "--ip=0.0.0.0", "--allow-root", "--no-browser"]

构建好推送到私有Registry,然后在K8s里创建Pod:

apiVersion: v1 kind: Pod metadata: name: pytorch-gpu spec: containers: - name: pytorch image: registry.local/pytorch:2.1.0-cu121 resources: limits: nvidia.com/gpu: 1 volumeMounts: - mountPath: /workspace name: workspace volumes: - name: workspace persistentVolumeClaim: claimName: pytorch-pvc nodeSelector: node-role.kubernetes.io/gpu: "true"

这样Jupyter Lab就跑在GPU节点上,浏览器访问NodePort就能写代码。实测下来,RTX 4060 Laptop在FP16精度下跑ResNet-50的推理,batch size=32时延迟约8ms,比CPU快20倍以上。

4.3 GPU算子与Cooperative Thread Array的概念澄清

热词里有人问“Cooperative Thread Array在GPU计算中是什么概念,和warp是什么关系”。这个问题很典型,我在这里展开说一下。

Warp是GPU硬件调度的基本单位,在NVIDIA架构里通常是32个线程一组。同一个warp里的线程执行相同的指令,但可以访问不同的数据,这就是SIMT(单指令多线程)模型。Warp的调度是硬件自动完成的,程序员不需要手动管理。

Cooperative Thread Array(CTA)则是软件层面的概念,它对应CUDA里的thread block。一个CTA里的线程可以通过shared memory通信,可以用__syncthreads()做屏障同步。CTA的大小由程序员指定,比如blockDim = (16, 16)就是一个256线程的CTA。

两者的关系是:一个CTA包含多个warp。比如256线程的CTA,在NVIDIA GPU上就是8个warp。硬件调度器以warp为单位发射指令,但CTA内的warp可以通过shared memory和屏障同步协作。理解这个层次关系,对写高效的GPU kernel至关重要。比如做矩阵乘法的时候,你要让同一个CTA内的warp共享tile数据,减少全局内存访问。

在K8s里跑GPU任务的时候,这些概念直接影响你的资源申请。一个Pod申请1块GPU,里面跑的CUDA程序可以启动多个CTA,但CTA之间不能跨GPU通信(除非用NCCL做多卡)。所以如果你要跑Foldseek这种需要大量并行比对的工具,要么单卡多CTA,要么多Pod多卡。

4.4 Foldseek在GPU上的部署实录

Foldseek是生物信息学里做蛋白质结构比对的工具,原生支持GPU加速。在K8s里部署的步骤:

# 拉取镜像 docker pull ghcr.io/steineggerlab/foldseek:latest # 测试GPU是否可用 docker run --gpus all ghcr.io/steineggerlab/foldseek:latest \ foldseek createdb /data/pdb100 -o /data/db

在K8s里封装成Job:

apiVersion: batch/v1 kind: Job metadata: name: foldseek-gpu spec: template: spec: containers: - name: foldseek image: ghcr.io/steineggerlab/foldseek:latest command: ["foldseek", "search", "/data/db", "/data/query", "/data/result", "/tmp", "--gpu", "1"] resources: limits: nvidia.com/gpu: 1 volumeMounts: - mountPath: /data name: data volumes: - name: data persistentVolumeClaim: claimName: foldseek-pvc restartPolicy: Never

实测下来,用RTX 4060跑1000条蛋白质序列的比对,GPU模式比CPU模式快约15倍。但要注意显存占用,如果序列太长或者数据库太大,会OOM。我的经验是显存至少8GB起步,4060 Laptop刚好够用,但跑大规模库的时候要分批。

5. 常见问题与排查技巧实录

5.1 K8s节点NotReady的排查路径

节点NotReady是最常见的问题,排查顺序应该是:网络 → kubelet → 容器运行时 → 资源。

先看节点状态:

kubectl describe node <node-name> | grep -A 10 Conditions

如果Ready是False,看Message字段。常见原因和解决:

现象可能原因解决方法
Network plugin not readyCNI未安装或配置错误检查Flannel/Calico的Pod日志
kubelet stopped posting statuskubelet进程挂了systemctl restart kubelet
container runtime not readycontainerd/docker异常systemctl restart containerd
DiskPressure磁盘使用率超过85%清理镜像和日志

我遇到最多的是DiskPressure,因为实验集群的节点硬盘小,镜像一多就爆。解决办法是配置kubelet的垃圾回收:

# 在/var/lib/kubelet/config.yaml里加 imageGCHighThresholdPercent: 70 imageGCLowThresholdPercent: 50 evictionHard: imagefs.available: "15%" nodefs.available: "10%"

然后systemctl restart kubelet。注意:改完配置后要观察一段时间,确保不会误杀正在用的镜像。

5.2 ExternalIPs不生效的坑

K8s的ExternalIPs是个很老的功能,但用起来坑不少。我一开始想用它把Service暴露到局域网,结果发现ExternalIPs只是把流量路由到节点,但节点上的kube-proxy必须能处理这个IP。

配置示例:

apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: my-app ports: - port: 80 targetPort: 8080 externalIPs: - 10.0.0.100

关键点:10.0.0.100这个IP必须属于某个节点,或者至少能被节点ARP响应。如果这个IP不在任何节点的网卡上,流量根本到不了。我的做法是把ExternalIP设成节点的真实IP,然后通过端口区分不同服务。但这样端口会冲突,所以后来我改用MetalLB,省心得多。

提示:ExternalIPs在云环境里通常被安全组挡住,家庭环境里如果用了VLAN隔离也要注意ACL。能用MetalLB就别用ExternalIPs,这是血泪教训。

5.3 GPU崩溃与D3D设备移除的应对

热词里有人问“GPU发生崩溃或D3D设备已移除”。这在Windows下常见,但Linux下跑CUDA也可能遇到类似问题,表现为nvidia-smi报GPU has fallen off the bus。

原因通常是供电不足、散热不良、或者驱动bug。我的排查步骤:

  1. 检查电源功率是否够。RTX 4060 Laptop TGP约115W,加上CPU和硬盘,整机功耗可能超过笔记本电源适配器的额定值。换一个功率更大的适配器。
  2. 检查散热。笔记本改的节点散热差,GPU温度超过85度就容易降频甚至掉卡。加一个外置散热底座,或者在机箱里加风扇。
  3. 更新驱动。NVIDIA的Linux驱动更新频繁,用ubuntu-drivers devices看推荐版本,不要盲目追新。
  4. 如果还不行,在/etc/modprobe.d/nvidia.conf里加options nvidia NVreg_EnableGpuFirmware=0,关掉GPU固件加载,有时候能解决兼容性问题。

在K8s里,如果GPU掉了,Pod会一直Pending。这时候要先把节点标记为不可调度:

kubectl cordon <gpu-node> kubectl drain <gpu-node> --ignore-daemonsets --delete-emptydir-data

然后重启节点,等nvidia-smi正常后再uncordon。

5.4 存储性能不达标的调优清单

60TB存储听起来很爽,但如果配置不当,性能可能还不如一块SSD。我整理了一个调优清单:

问题检查项优化方法
顺序读写慢硬盘是否SMR换CMR硬盘,SMR的写入放大严重
随机读写慢是否有SSD缓存加NVMe做L2ARC或SLOG
网络瓶颈是否万兆至少2.5G起步,万兆最佳
ZFS内存不足ARC大小每TB存储配1GB内存,60TB至少64GB
Ceph恢复慢恢复优先级调低osd_recovery_max_active,避免影响业务

我踩过最大的坑是SMR硬盘。当初图便宜买了几块大容量SMR盘做ZFS,结果 resilver 的时候速度只有10MB/s,一个4TB的盘重建要十几个小时。后来全换成CMR,重建速度直接到150MB/s。买硬盘前一定要查型号,SMR的坑太深了。

5.5 NAS挂载网盘与多IP观看的实践

热词里有人问“NAS如何实现挂载网盘资源多IP观看”。这个需求在家庭媒体库里很常见。我的做法是用rclone把网盘挂载到NAS的本地目录,然后通过Nginx做反向代理,绑定多个局域网IP。

rclone挂载:

rclone mount remote: /mnt/cloud \ --allow-other \ --vfs-cache-mode full \ --vfs-cache-max-size 50G \ --daemon

然后Nginx配置:

server { listen 10.0.0.100:80; location / { root /mnt/cloud; autoindex on; } } server { listen 10.0.0.101:80; location / { root /mnt/cloud; autoindex on; } }

这样局域网内两个IP都能访问同一份网盘内容。注意:rclone的VFS缓存要设大一点,否则拖动进度条会卡。我设了50G缓存,实测下来1080P视频可以流畅拖动,4K原盘还是建议先下载到本地。

6. 日常运维与自动化的小技巧

6.1 用CronJob做自动备份和清理

K8s的CronJob特别适合做家庭实验室的日常维护。我配了两个:

每日备份PVC到对象存储:

apiVersion: batch/v1 kind: CronJob metadata: name: backup-pvc spec: schedule: "0 3 * * *" jobTemplate: spec: template: spec: containers: - name: backup image: amazon/aws-cli command: - /bin/sh - -c - | aws s3 sync /data s3://backup/pvc/$(date +%Y%m%d) \ --endpoint-url http://minio.local:9000 volumeMounts: - mountPath: /data name: data volumes: - name: data persistentVolumeClaim: claimName: app-pvc restartPolicy: OnFailure

每周清理未使用的镜像:

apiVersion: batch/v1 kind: CronJob metadata: name: cleanup-images spec: schedule: "0 4 * * 0" jobTemplate: spec: template: spec: containers: - name: cleanup image: bitnami/kubectl command: - /bin/sh - -c - | kubectl get nodes -o name | xargs -I {} kubectl debug {} --image=alpine -- crictl rmi --prune restartPolicy: OnFailure

注意:crictl rmi --prune会删除所有未使用的镜像,包括你手动pull的。如果有些镜像不想被删,要打上标签或者放在单独的namespace里。

6.2 监控告警:Prometheus + Grafana + Alertmanager

家庭实验室也需要监控,不然硬盘快满了都不知道。我用kube-prometheus-stack一键部署:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm install monitoring prometheus-community/kube-prometheus-stack \ --namespace monitoring \ --create-namespace \ --set grafana.adminPassword=admin123

然后配几个关键告警规则:

groups: - name: home-lab rules: - alert: DiskWillFillIn24Hours expr: predict_linear(node_filesystem_avail_bytes[6h], 24*3600) < 0 for: 1h labels: severity: warning annotations: summary: "磁盘将在24小时内写满" - alert: GPU温度过高 expr: nvidia_gpu_temperature_celsius > 85 for: 5m labels: severity: critical

Alertmanager可以推到企业微信或者Telegram,我用的Telegram Bot,配置简单,通知及时。注意:不要把Alertmanager暴露到公网,否则会被滥用发垃圾告警。

6.3 远程访问的安全加固

家庭实验室最大的风险是暴露到公网的服务被攻击。我的加固清单:

  • SSH只允许密钥登录,禁用密码:PasswordAuthentication no
  • 改默认端口,虽然不能防定向攻击,但能挡掉90%的扫描
  • fail2ban,自动封禁多次失败的IP
  • 防火墙只开必要端口,比如443、21115-21119(RustDesk)、6443(K8s API,只允许内网)
  • 所有Web服务上HTTPS,用Let's Encrypt自动续期
  • 定期更新,unattended-upgrades自动打安全补丁

提示:DDNS配合动态公网地址访问NAS是常见需求,但一定要把NAS的管理端口和SMB端口关掉公网访问,只通过反向代理暴露必要的Web服务。我见过太多人把群晖的5000端口直接映射出去,结果被勒索病毒加密了全部照片。

6.4 用GitOps管理K8s配置

手动kubectl apply在实验阶段没问题,但时间长了会忘记哪个文件对应哪个服务。我后来用ArgoCD做GitOps,所有YAML推到私有Git仓库,ArgoCD自动同步到集群。

安装ArgoCD:

kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

然后创建一个Application:

apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: home-lab namespace: argocd spec: project: default source: repoURL: https://git.local/home-lab.git targetRevision: HEAD path: manifests destination: server: https://kubernetes.default.svc namespace: default syncPolicy: automated: prune: true selfHeal: true

selfHeal=true意味着如果有人手动改了集群里的资源,ArgoCD会自动改回去。这在多人协作或者自己手滑的时候特别有用。prune=true则会删除Git里已经不存在的资源,保持集群和仓库一致。

6.5 硬件选型与功耗控制的平衡

18台服务器跑起来,电费是个现实问题。我的做法是分层供电:

  • 核心节点(生产模拟集群的6台)常开,功耗约300W
  • 实验集群的x86节点按需开机,通过Wake-on-LAN远程唤醒
  • ARM节点常开,单台功耗不到10W
  • GPU节点只在跑任务时开机,平时关机

Wake-on-LAN的配置:

# 在目标机器上启用 ethtool -s eth0 wol g # 在控制机上发送魔术包 wakeonlan -i 10.0.1.255 00:11:22:33:44:55

实测下来,全部常开的功耗约800W,按需开机可以降到400W左右。一年电费差大概2000度,按居民电价算就是1000多块钱。这个钱花得值不值,取决于你用实验室产出了多少价值。对我来说,靠这套环境学到的K8s和存储知识,早就把电费赚回来了。

6.6 从玩客云到飞牛NAS:低成本入门路径

不是每个人都需要一上来就搞60TB和18台服务器。热词里有人问“玩客云刷机做NAS”和“飞牛NAS安装Dify”,这其实是很好的入门路径。

玩客云(现在叫网心云)二手只要几十块钱,刷上Armbian之后可以跑Docker。虽然性能弱,但用来跑一个轻量级的Dify或者Home Assistant完全够用。刷机步骤:

  1. 拆机,短接主板上的触点进入USB Burning模式
  2. 用Amlogic USB Burning Tool刷入Armbian镜像
  3. 启动后apt update && apt install docker.io
  4. 然后就可以docker run各种服务了

飞牛NAS(fnOS)是国产的NAS系统,对小白最友好的是它的iStock相册管理和Docker应用商店。安装Dify:

# 在飞牛NAS的Docker里拉取Dify docker pull langgenius/dify:latest # 运行 docker run -d --name dify \ -p 8080:8080 \ -v /vol1/docker/dify:/app/data \ langgenius/dify:latest

然后浏览器访问http://nas-ip:8080就能用Dify搭建AI工作流。飞牛NAS的优势是中文界面和本地化服务,适合不想折腾Linux命令的用户。但如果你要跑K8s,还是得回到标准的Linux发行版。

我的建议是:先用玩客云或飞牛NAS入门,熟悉Docker和基本运维,然后再逐步升级到K8s集群。不要一上来就搞双集群,那样挫败感太强。我当初从一台玩客云到现在的规模,花了三年时间,中间踩的坑足够写一本书了。

6.7 时间服务器与集群时钟同步

K8s集群对时间同步要求很高,etcd的Raft协议依赖精确的时钟。我在生产模拟集群里跑了一个Chrony作为内部NTP服务器:

apt install chrony # 编辑/etc/chrony/chrony.conf server ntp.aliyun.com iburst allow 10.0.0.0/24 local stratum 10

然后其他节点配置:

server 10.0.0.10 iburst

验证同步状态:

chronyc sources -v chronyc tracking

如果时钟偏移超过1秒,etcd会报错甚至选举失败。我遇到过因为NTP没配好导致API Server频繁重启的情况,排查了半天才发现是时间问题。家庭环境里路由器自带的NTP服务往往不靠谱,自己搭一个Chrony是最稳妥的。

6.8 服务器虚拟化:PVE与K8s的共存

我的底层虚拟化用的是Proxmox VE(PVE),18台服务器里有6台是PVE宿主机,上面跑虚拟机。为什么不用裸金属直接跑K8s?因为PVE的快照和备份功能太方便了,升级K8s之前先打个快照,搞坏了秒回滚。

PVE上跑K8s的虚拟机,关键是CPU类型要选host,否则嵌套虚拟化会有性能损失。网络用VirtIO,磁盘用SCSI,开启IO Thread。实测下来,PVE里的K8s节点性能损失在5%以内,完全可以接受。

共享存储方面,PVE可以用NFS或者Ceph RBD做虚拟机磁盘。我用的是Ceph RBD,这样虚拟机可以在不同PVE节点之间在线迁移。迁移的时候K8s节点会短暂NotReady,但Pod不会重建,因为kubelet只是心跳超时,恢复后会自动重新连接。

注意:PVE的Ceph和K8s的Ceph可以共用同一个集群,但建议分池。PVE用pve池,K8s用replicapool池,避免互相影响。我一开始混用,结果PVE做快照的时候把K8s的IO拖垮了,后来分池就再没出过问题。

6.9 从EMC存储Service Mode看企业级存储的降维

热词里有人搜“EMC存储service mode”,这其实是企业级存储的维护模式。家庭实验室虽然用不上EMC,但理解它的设计思路对用好Ceph和ZFS很有帮助。

EMC的Service Mode允许在不中断业务的情况下更换控制器、升级固件。Ceph的对应机制是osd set noout和osd set norebalance,在维护前先暂停数据再平衡,维护完再恢复。ZFS的对应机制是zpool scrub和zpool replace,可以在线替换故障盘。

企业级存储的核心思想是“冗余+在线维护”,家庭实验室虽然硬件没那么高端,但软件层面的思路是一样的。我每次换硬盘之前,都会先ceph osd set noout,换完再ceph osd unset noout,这样数据不会在换盘期间大量迁移,减少性能抖动。

6.10 K8s学习路径与权威指南的取舍

热词里有人搜“k8s权威指南第五版pdf下载”和“k8s学习”。我的建议是:书要看,但更重要的是动手。《Kubernetes权威指南》适合当字典查,但不要指望看完就会。真正的学习路径是:先跑起来一个单节点k3s,然后部署一个Nginx,然后尝试滚动更新,然后加一个PVC,然后加一个Ingress,然后加一个HPA。每一步都会遇到问题,解决问题就是学习。

我自己的学习顺序:

  1. 用k3d或者minikube跑单节点,熟悉kubectl基本命令
  2. 用kubeadm装三节点集群,理解etcd和API Server
  3. 部署有状态服务,理解StatefulSet和PVC
  4. 配置Ingress和Cert-Manager,理解七层路由和证书管理
  5. 部署Prometheus和ArgoCD,理解可观测性和GitOps
  6. 最后才是多集群和GPU调度

不要跳步。我见过太多人一上来就搞多集群,结果连Service和Deployment的区别都没搞清。K8s的复杂度是指数级的,但核心概念就那么几个,把基础打牢,后面都是组合。

6.11 服务器Linux系统的日常维护

18台服务器跑的都是Linux,发行版主要是Ubuntu 22.04和Debian 12。日常维护的核心是自动化:

  • 批量执行命令:用Ansible,ansible all -m shell -a "apt update"
  • 日志收集:用Loki + Promtail,所有节点的日志集中到Grafana里查
  • 安全更新:用unattended-upgrades,只自动打安全补丁
  • 配置管理:用Git管理/etc下的关键配置,改之前先commit

我踩过的坑是手动改配置忘了同步。有一次在节点A上改了/etc/hosts,忘了在节点B上改,结果Pod调度到B就解析失败。后来所有配置都走Ansible,改一次全集群生效,再没出过这种问题。

6.12 对象存储服务的选型对比

家庭实验室里对象存储的选型,我实际用过MinIO、Garage和SeaweedFS。对比如下:

方案优势劣势适用场景
MinIOS3兼容性最好,生态成熟资源占用高,许可证变更需要完整S3 API的场景
Garage轻量,多站点复制生态较新,文档少跨集群备份,资源受限环境
SeaweedFS海量小文件性能好配置复杂,运维门槛高图片、日志等小文件存储

我的选择是Garage做主存储,MinIO做兼容层。Garage负责实际的数据存储和跨集群复制,MinIO只在前端做S3网关,这样既省资源又保证了兼容性。实测下来,Garage在3节点上的写入吞吐能到200MB/s,读能到400MB/s,跑家庭备份完全够用。

6.13 高清录播服务器与在线转码

热词里有人搜“高清录播服务器在线”。我在实验集群里跑了一个Jellyfin + FFmpeg的转码服务,用GPU加速。Jellyfin的Pod配置:

apiVersion: apps/v1 kind: Deployment metadata: name: jellyfin spec: replicas: 1 selector: matchLabels: app: jellyfin template: metadata: labels: app: jellyfin spec: containers: - name: jellyfin image: jellyfin/jellyfin:latest ports: - containerPort: 8096 resources: limits: nvidia.com/gpu: 1 volumeMounts: - mountPath: /config name: config - mountPath: /media name: media volumes: - name: config persistentVolumeClaim: claimName: jellyfin-config - name: media nfs: server: 10.0.0.20 path: /volume1/media

关键是在Jellyfin的设置里开启硬件加速,选NVENC。这样转码4K HEVC的时候,GPU占用率约60%,CPU几乎不动。实测下来,RTX 4060可以同时转3路4K,或者8路1080P,家庭使用绰绰有余。

6.14 服务器集群的散热与噪音控制

18台服务器放在家里,噪音是个大问题。我的做法是分区放置:

  • 核心节点放在地下室或者储藏间,用机柜集中管理,噪音传不到生活区
  • 实验节点用迷你主机和笔记本,本身噪音就小
  • 硬盘笼加隔音棉,但要注意散热,隔音和散热是矛盾的

风扇策略:BIOS里设置风扇曲线,低温时停转,高温时才启动。PVE和Linux下可以用fancontrol做更精细的控制:

apt install fancontrol pwmconfig # 按提示生成/etc/fancontrol systemctl enable fancontrol

我实测下来,把风扇启动温度从40度调到55度,噪音降低了一半,硬盘温度只上升了3度。注意:硬盘长期超过50度会缩短寿命,所以55度启动是底线,不能再高了。

6.15 从家庭实验室到生产环境的经验迁移

最后说点务实的。家庭实验室里踩的坑,90%在生产环境里也会遇到。比如:

  • 存储性能瓶颈:家里Ceph恢复慢,公司里也可能因为OSD配置不当导致恢复风暴
  • GPU调度冲突:家里一个Pod独占GPU,公司里多租户抢GPU更复杂
  • 网络策略失效:家里Flannel简单,公司里Calico的NetworkPolicy写错一条就断网
  • 证书过期:家里Cert-Manager自动续期,公司里可能因为DNS问题续期失败

我的经验是:在家庭实验室里养成的好习惯,到了生产环境直接能用。比如用GitOps管理配置、用Prometheus做监控、用Ansible做批量运维、用Ceph做分布式存储。这些技能在简历上比“熟悉K8s”四个字有说服力得多。

如果你也想搞家庭实验室,我的建议是从一台迷你主机开始,装个PVE,跑一个k3s,然后慢慢加硬盘、加节点、加GPU。不要一开始就追求规模,规模是结果,不是目标。真正的目标是:通过这套环境,把你想学的技术真正用起来、搞坏、修好、再优化。这个过程里积累的经验,才是家庭实验室最大的价值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询