我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

家庭实验室实战:18台服务器、60TB存储与双K8s集群的架构与运维

家庭实验室实战:18台服务器、60TB存储与双K8s集群的架构与运维 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 recordsize16K tank/vm # 媒体库数据集保持128K默认值 zfs create tank/media # 数据库数据集recordsize设为8K并开启logbiasthroughput zfs create tank/db zfs set recordsize8K tank/db zfs set logbiasthroughput tank/dbashift参数更关键。现代硬盘的物理扇区基本都是4K所以ashift必须设为122的12次方等于4096。如果你设成9512字节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 128size2而不是3是因为家庭环境只有三节点如果设成3坏一台机器整个池就不可写了。设成2的话坏一台还能降级运行但要注意min size1只在紧急恢复时用平时保持2否则数据一致性没保障。在K8s里对接Ceph我用的是Rook Operator。装完之后创建StorageClassapiVersion: 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_BROWSERoff关掉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 checkkubeadm初始化的时候用--control-plane-endpoint指向这个VIPkubeadm init --control-plane-endpoint 10.0.0.10:6443 \ --upload-certs \ --pod-network-cidr 10.244.0.0/16为什么用Flannel而不是Calico因为家庭环境不需要复杂的NetworkPolicyFlannel的VXLAN模式配置简单跨节点通信稳定。Calico的BGP模式在只有三层交换机的家庭网络里反而容易出问题。工作节点加入后我给每个节点打了标签kubectl label node worker1 node-role.kubernetes.io/storagetrue kubectl label node worker2 node-role.kubernetes.io/gputrue kubectl label node worker3 node-role.kubernetes.io/ingresstrue这样在部署有状态服务的时候可以用nodeSelector精确调度。比如MinIO的Pod就只调度到storage节点Ingress Controller只跑在ingress节点。3.2 实验集群k3s的轻量化与ARM混部实验集群的12台机器里有4台是ARM架构的迷你主机RK35888台是x86的老旧设备。这种混合架构用kubeadm装会很痛苦因为镜像要分架构。k3s原生支持多架构镜像而且自带SQLite作为默认存储省掉了etcd的运维负担。安装k3s servercurl -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 ServiceMetalLB就会从池子里分配一个IP局域网内直接访问。ARM节点加入的时候要注意k3s的agent参数curl -sfL https://get.k3s.io | K3S_URLhttps://10.0.1.10:6443 K3S_TOKENxxx sh -ARM节点上跑的Pod必须是多架构镜像否则会报exec format error。我一般用docker buildx构建多架构镜像推送到私有Registry。私有Registry也是必须的因为实验集群经常断网测试不能依赖Docker Hub。3.3 两个集群的互联与隔离两个集群虽然K8s层面隔离但底层网络是通的。我在两个集群各放了一个RustDesk中继节点这样在外面可以随时连回家里调试。RustDesk自建服务器的好处是不依赖第三方中继延迟低而且可以自己控制密钥。部署RustDesk Serverdocker 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 Toolkitdistribution$(. /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 --runtimecontainerd sudo systemctl restart containerd然后在K8s里部署Device Pluginkubectl 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都装好然后所有实验都基于这个镜像。我的DockerfileFROM 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 \ torch2.1.0 torchvision0.16.0 torchaudio2.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, --ip0.0.0.0, --allow-root, --no-browser]构建好推送到私有Registry然后在K8s里创建PodapiVersion: 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 size32时延迟约8ms比CPU快20倍以上。4.3 GPU算子与Cooperative Thread Array的概念澄清热词里有人问“Cooperative Thread Array在GPU计算中是什么概念和warp是什么关系”。这个问题很典型我在这里展开说一下。Warp是GPU硬件调度的基本单位在NVIDIA架构里通常是32个线程一组。同一个warp里的线程执行相同的指令但可以访问不同的数据这就是SIMT单指令多线程模型。Warp的调度是硬件自动完成的程序员不需要手动管理。Cooperative Thread ArrayCTA则是软件层面的概念它对应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里封装成JobapiVersion: 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 kubeletcontainer runtime not readycontainerd/docker异常systemctl restart containerdDiskPressure磁盘使用率超过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。我的排查步骤检查电源功率是否够。RTX 4060 Laptop TGP约115W加上CPU和硬盘整机功耗可能超过笔记本电源适配器的额定值。换一个功率更大的适配器。检查散热。笔记本改的节点散热差GPU温度超过85度就容易降频甚至掉卡。加一个外置散热底座或者在机箱里加风扇。更新驱动。NVIDIA的Linux驱动更新频繁用ubuntu-drivers devices看推荐版本不要盲目追新。如果还不行在/etc/modprobe.d/nvidia.conf里加options nvidia NVreg_EnableGpuFirmware0关掉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至少64GBCeph恢复慢恢复优先级调低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 {} --imagealpine -- 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.adminPasswordadmin123然后配几个关键告警规则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: criticalAlertmanager可以推到企业微信或者Telegram我用的Telegram Bot配置简单通知及时。注意不要把Alertmanager暴露到公网否则会被滥用发垃圾告警。6.3 远程访问的安全加固家庭实验室最大的风险是暴露到公网的服务被攻击。我的加固清单SSH只允许密钥登录禁用密码PasswordAuthentication no改默认端口虽然不能防定向攻击但能挡掉90%的扫描fail2ban自动封禁多次失败的IP防火墙只开必要端口比如443、21115-21119RustDesk、6443K8s API只允许内网所有Web服务上HTTPS用Lets Encrypt自动续期定期更新unattended-upgrades自动打安全补丁提示DDNS配合动态公网地址访问NAS是常见需求但一定要把NAS的管理端口和SMB端口关掉公网访问只通过反向代理暴露必要的Web服务。我见过太多人把群晖的5000端口直接映射出去结果被勒索病毒加密了全部照片。6.4 用GitOps管理K8s配置手动kubectl apply在实验阶段没问题但时间长了会忘记哪个文件对应哪个服务。我后来用ArgoCD做GitOps所有YAML推到私有Git仓库ArgoCD自动同步到集群。安装ArgoCDkubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml然后创建一个ApplicationapiVersion: 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: trueselfHealtrue意味着如果有人手动改了集群里的资源ArgoCD会自动改回去。这在多人协作或者自己手滑的时候特别有用。prunetrue则会删除Git里已经不存在的资源保持集群和仓库一致。6.5 硬件选型与功耗控制的平衡18台服务器跑起来电费是个现实问题。我的做法是分层供电核心节点生产模拟集群的6台常开功耗约300W实验集群的x86节点按需开机通过Wake-on-LAN远程唤醒ARM节点常开单台功耗不到10WGPU节点只在跑任务时开机平时关机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完全够用。刷机步骤拆机短接主板上的触点进入USB Burning模式用Amlogic USB Burning Tool刷入Armbian镜像启动后apt update apt install docker.io然后就可以docker run各种服务了飞牛NASfnOS是国产的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 VEPVE18台服务器里有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。每一步都会遇到问题解决问题就是学习。我自己的学习顺序用k3d或者minikube跑单节点熟悉kubectl基本命令用kubeadm装三节点集群理解etcd和API Server部署有状态服务理解StatefulSet和PVC配置Ingress和Cert-Manager理解七层路由和证书管理部署Prometheus和ArgoCD理解可观测性和GitOps最后才是多集群和GPU调度不要跳步。我见过太多人一上来就搞多集群结果连Service和Deployment的区别都没搞清。K8s的复杂度是指数级的但核心概念就那么几个把基础打牢后面都是组合。6.11 服务器Linux系统的日常维护18台服务器跑的都是Linux发行版主要是Ubuntu 22.04和Debian 12。日常维护的核心是自动化批量执行命令用Ansibleansible 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。不要一开始就追求规模规模是结果不是目标。真正的目标是通过这套环境把你想学的技术真正用起来、搞坏、修好、再优化。这个过程里积累的经验才是家庭实验室最大的价值。
返回列表