1. 项目概述:为什么我们需要一个家庭Kubernetes实验室?

如果你和我一样,对容器编排、微服务架构和云原生技术充满好奇,但又苦于没有合适的实验环境,那么“PhilipSchmid/k8s-home-lab”这个项目可能就是为你量身定做的。它不是一个简单的脚本集合,而是一个经过精心设计的、旨在将几台普通的家用电脑(甚至是树莓派)打造成一个功能完备、生产级可用的Kubernetes集群的完整解决方案。这个项目的核心价值在于,它为你提供了一个“沙盒”,让你可以在不产生任何云服务费用、不担心影响线上业务的前提下,去实践、去试错、去深入理解Kubernetes的每一个组件和概念。

我最初接触这个项目,是因为厌倦了在单节点的Minikube或Docker Desktop上做实验的局限性。那些环境虽然方便,但无法模拟多节点、高可用、网络策略、存储编排等真实生产场景。而直接在公有云上搭建集群,成本又是个问题。于是,一个运行在家庭网络中的、由真实物理机或虚拟机组成的Kubernetes集群,就成了最佳选择。PhilipSchmid的这个项目,正是解决了从零开始搭建这样一个集群的复杂性和一致性问题。它通过Ansible自动化了从操作系统初始化、Kubernetes组件安装、到网络插件、存储方案、Ingress控制器乃至监控日志全家桶的部署,让你可以像部署一个应用一样,一键部署你的整个实验室环境。

这个项目适合谁呢?我认为有三类人:第一类是正在学习Kubernetes的开发者或运维工程师,你需要一个可以随意“折腾”的环境来巩固知识;第二类是希望将家庭服务(如媒体服务器、智能家居中枢、个人网盘)容器化并统一管理的技术爱好者;第三类是小型团队或初创公司,希望在将应用迁移到生产云环境前,先在内部进行充分的架构验证和压力测试。无论你是哪一类,这个项目都能为你提供一个坚实、可靠的起点。

2. 核心架构与设计哲学解析

2.1 技术栈选型:为什么是K3s + Ansible?

打开项目的配置文件,你会发现它的核心是K3s,而不是原生的K8s。这是一个非常关键且明智的选择。K3s是Rancher Labs推出的一个轻量级Kubernetes发行版,它将所有Kubernetes组件打包成了一个不到100MB的二进制文件,并且默认使用containerd而非Docker作为容器运行时。对于资源受限的家庭环境(比如使用树莓派或旧笔记本),K3s的内存和CPU占用要低得多,启动速度也更快。同时,它内置了Traefik作为Ingress控制器,并默认使用sqlite3作为数据存储(也支持etcd),这大大简化了部署复杂度。

项目选择Ansible作为自动化工具,则是另一个亮点。Ansible基于SSH,无需在目标节点安装代理,采用声明式的YAML语法来描述状态,这与Kubernetes的理念一脉相承。通过Ansible,我们可以将集群的“期望状态”——包括系统配置、软件包、服务状态——定义在Playbook中。这意味着你的整个实验室环境都是可版本化、可重复构建的。今天你在一台机器上实验,明天换一台机器,或者集群被你不小心“玩坏”了,你都可以通过重新执行Ansible Playbook,快速恢复到已知的良好状态。这种“基础设施即代码”的实践,本身就是云原生运维的核心技能之一。

注意 :虽然项目默认使用K3s,但其架构设计是模块化的。如果你需要体验原生Kubernetes组件(如kube-apiserver、kube-controller-manager等),完全可以修改Ansible角色,替换为kubeadm或其他工具的部署方式。这体现了项目良好的可扩展性。

2.2 网络与存储:家庭环境下的特殊考量

在家庭网络中搭建Kubernetes,网络规划是第一个挑战。项目通常假设你的所有节点在同一个局域网段内。你需要为集群规划一个独立的Pod CIDR(如 10.244.0.0/16 )和Service CIDR(如 10.96.0.0/12 ),确保它们不会与你的家庭路由器网段(通常是 192.168.1.0/24 )冲突。项目默认集成了Flannel作为CNI(容器网络接口)插件,它使用VXLAN后端,能很好地工作在大多数家庭网络环境中,实现跨节点的Pod通信。

存储是另一个重点。在生产环境中,你可能会有Ceph、Longhorn等分布式存储方案。但在家庭实验室,我们更需要考虑简单、可靠和低功耗。项目的一个常见实践是使用NFS(网络文件系统)。你可以将家中一台NAS或一台始终开机的旧电脑配置为NFS服务器,然后在Kubernetes中通过 nfs-subdir-external-provisioner 这个动态存储供应器,为集群提供按需创建的持久卷。这样,像Nextcloud(个人网盘)、Plex(媒体服务器)这类需要持久化数据的应用,就能稳定地运行了。当然,对于非持久化的数据或缓存,使用节点本地存储( hostPath local 卷)也是完全可行的,性能更好。

2.3 安全与维护:最小权限与自动化运维

家庭实验室不等于可以忽视安全。项目在设计中遵循了最小权限原则。例如,它会创建一个专用的非root用户来运行K3s服务,并通过Ansible Vault或环境变量来管理敏感信息(如证书、令牌),避免将密码明文写在代码里。此外,它默认会部署 metrics-server ,为HPA(水平Pod自动扩缩容)提供资源指标,虽然家庭应用可能不需要自动扩缩,但这为学习该功能提供了环境。

在维护方面,项目的Ansible Playbook被设计成“幂等”的,这意味着你可以安全地多次运行它,它只会将系统改变到Playbook中定义的状态,而不会引起错误或重复配置。这对于长期维护至关重要。你可以将Playbook放入Git仓库,任何配置变更都通过提交代码、执行Playbook来完成,实现了实验室配置的版本控制和变更追溯。

3. 从零开始:硬件准备与基础环境搭建

3.1 硬件选择与规划

搭建家庭K8s实验室,硬件门槛并不高。以下是几种常见的方案:

  1. 树莓派集群 :最具性价比和乐趣的方案。3台树莓派4B(4GB或8GB内存)即可组成一个高可用集群(1个master,2个worker)。你需要为每个树莓派配备可靠的MicroSD卡(建议A1/V30级别)、电源和散热片。网络方面,一个千兆交换机是必须的,能让节点间通信更流畅。
  2. 旧笔记本/台式机 :如果你有淘汰的电脑,这是零成本方案。即使只有双核CPU和4GB内存,也能运行一个轻量级的集群。建议安装无图形界面的Linux发行版以节省资源。
  3. 微型主机/工控机 :如Intel NUC、Minisforum等,性能更强,体积小巧,功耗控制得也不错,是追求性能和静音平衡的选择。

我的建议是,至少准备3个节点:1个控制平面节点(Master),2个工作节点(Worker)。这样你才能实践高可用、节点调度、Pod迁移等概念。为每个节点分配静态IP地址(通过路由器DHCP绑定或静态配置),并确保所有节点之间可以通过主机名互相解析。我通常会在路由器的DNS服务器中为每个节点添加记录,或者在每个节点的 /etc/hosts 文件中手动配置。

3.2 操作系统安装与基础配置

项目推荐使用Ubuntu Server LTS(如22.04)或Raspbian OS(针对树莓派)。安装时,选择最小化安装即可,无需桌面环境。

系统安装完成后,有几项基础配置必须通过Ansible或手动完成:

  • 用户与SSH :为Ansible创建一个专用管理用户(如 ansible ),并配置SSH密钥登录,禁用密码登录以提升安全性。将你的公钥分发到所有节点。
    # 在控制机(你用来运行Ansible的电脑)上生成密钥对
    ssh-keygen -t ed25519 -C "k8s-lab"
    # 将公钥复制到所有目标节点
    ssh-copy-id ansible@<node-ip>
    
  • 系统优化 :关闭交换分区( swapoff -a 并注释掉 /etc/fstab 中的swap行),因为Kubernetes默认认为交换内存会影响性能和稳定性。加载必要的内核模块(如 br_netfilter , overlay ),并配置 sysctl 参数以启用IP转发和桥接流量。
  • 容器运行时准备 :K3s默认使用containerd。我们需要确保其所需的内核模块和cgroup驱动配置正确。虽然K3s安装脚本会处理大部分,但提前配置好cgroup驱动为 systemd (修改 /etc/containerd/config.toml )能与系统更好地集成。

这些步骤都可以编写到Ansible Playbook的“预处理”角色中,实现自动化。

3.3 Ansible控制机与环境准备

你的笔记本电脑或其中一台集群节点可以作为Ansible控制机。首先安装Ansible( pip install ansible 或通过系统包管理器)。然后,为你的实验室项目创建一个工作目录,结构如下:

k8s-home-lab/
├── inventories/
│   └── production/
│       ├── hosts.ini        # 节点清单文件
│       └── group_vars/      # 组变量
│           └── all.yml      # 全局变量
├── site.yml                 # 主Playbook
└── roles/                   # Ansible角色目录
    ├── common/              # 通用配置(用户、SSH、内核参数)
    ├── k3s/                 # K3s安装与配置
    ├── network/             # CNI插件安装
    └── storage/             # 存储配置(如NFS provisioner)

hosts.ini 中定义你的节点:

[master]
k8s-master ansible_host=192.168.1.101 ansible_user=ansible

[worker]
k8s-worker1 ansible_host=192.168.1.102 ansible_user=ansible
k8s-worker2 ansible_host=192.168.1.103 ansible_user=ansible

[k3s_cluster:children]
master
worker

group_vars/all.yml 中定义全局变量,如K3s版本、Pod CIDR等。

4. 核心部署流程详解与踩坑实录

4.1 使用Ansible部署K3s集群

有了基础环境,核心的部署工作就集中在执行Ansible Playbook上。项目提供的 site.yml 可能类似于以下结构:

---
- name: 部署Kubernetes家庭实验室
  hosts: all
  become: yes
  roles:
    - role: common
      tags: common

- name: 部署K3s控制平面
  hosts: master
  become: yes
  roles:
    - role: k3s
      k3s_mode: server
      k3s_token: "{{ vault_k3s_token }}" # 从Ansible Vault读取
      tags: master

- name: 获取K3s节点令牌
  hosts: localhost
  gather_facts: no
  tasks:
    - name: 从master获取token
      slurp:
        src: /var/lib/rancher/k3s/server/node-token
      register: token_content
      delegate_to: "{{ groups['master'][0] }}"
    - set_fact:
        k3s_worker_token: "{{ token_content.content | b64decode }}"

- name: 部署K3s工作节点
  hosts: worker
  become: yes
  roles:
    - role: k3s
      k3s_mode: agent
      k3s_server: "https://{{ groups['master'][0] }}:6443"
      k3s_token: "{{ hostvars['localhost']['k3s_worker_token'] }}"
      tags: worker

执行部署:

# 首先加密你的敏感变量文件
ansible-vault create group_vars/vault.yml
# 在其中定义 vault_k3s_token 等变量

# 运行整个Playbook
ansible-playbook -i inventories/production/hosts.ini site.yml --ask-vault-pass

# 或者分步运行
ansible-playbook -i inventories/production/hosts.ini site.yml --tags common
ansible-playbook -i inventories/production/hosts.ini site.yml --tags master
ansible-playbook -i inventories/production/hosts.ini site.yml --tags worker

实操心得

  1. 令牌管理 :K3s集群加入需要令牌。上述Playbook展示了如何动态地从Master获取令牌并传递给Worker,这比写死在变量里更安全。务必使用Ansible Vault保护这个令牌。
  2. 镜像拉取 :在国内环境,K3s拉取镜像可能会很慢。你可以在Master节点上预拉取镜像,或者修改K3s的启动参数,使用国内镜像仓库。一个更彻底的方法是在 /etc/rancher/k3s/registries.yaml 中配置镜像仓库镜像。
  3. 部署验证 :部署完成后,在Master节点上运行 kubectl get nodes ,应该能看到所有节点状态为 Ready 。如果Worker节点没有加入,首先检查Master节点的6443端口是否对Worker开放(防火墙规则),然后检查Worker节点的 /var/log/syslog 中K3s agent的日志。

4.2 部署CNI插件与核心插件

K3s默认安装Flannel,但有时你可能想换用Calico以获得更强大的网络策略功能。这时,你需要在K3s安装完成后,手动部署Calico的Manifest。

# 在Master节点上
# 首先,如果Flannel已运行,需要先卸载(谨慎操作)
kubectl delete -f /var/lib/rancher/k3s/server/manifests/k3s-flannel.yaml

# 然后部署Calico
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/tigera-operator.yaml -O
kubectl create -f tigera-operator.yaml
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/custom-resources.yaml -O
# 编辑custom-resources.yaml,将cidr改为你的Pod CIDR
kubectl create -f custom-resources.yaml

接下来,部署一些几乎必备的插件:

  • MetalLB :为家庭实验室提供LoadBalancer类型的服务。在没有云供应商的环境里,Service的 LoadBalancer 类型会一直处于 Pending 状态。MetalLB通过ARP或BGP协议,将外部IP地址“分配”给这些服务。
    kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml
    
    然后创建一个 IPAddressPool ,从你的家庭局域网中划分一段IP(例如 192.168.1.200-192.168.1.220 )给MetalLB管理。
  • NFS Subdir External Provisioner :如前所述,为集群提供动态NFS存储。
    helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
    helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
        --set nfs.server=192.168.1.99 \
        --set nfs.path=/srv/nfs/k8s
    

4.3 部署Ingress与证书管理

K3s内置了Traefik作为Ingress控制器。你可以直接使用它,也可以替换为更流行的Nginx Ingress Controller。我更喜欢后者,因为其文档和社区资源更丰富。

# 使用Helm安装Nginx Ingress Controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
    --namespace ingress-nginx \
    --create-namespace \
    --set controller.service.type=LoadBalancer \
    --set controller.service.annotations."metallb\.universe\.tf/address-pool"=default

安装后,通过 kubectl get svc -n ingress-nginx 查看MetalLB分配的外部IP(假设是 192.168.1.201 )。现在,你可以通过这个IP访问集群内的服务了。

为了让家庭服务拥有漂亮的HTTPS地址,我们需要证书。 cert-manager 是Kubernetes上自动化管理TLS证书的事实标准。虽然家庭环境没有公网域名,但我们可以使用自签名证书或内网DNS(如 home.lab )配合 cert-manager 的CA颁发者。

# 安装cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.2/cert-manager.yaml

# 创建一个自签名的CA Issuer
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned-issuer
spec:
  selfSigned: {}
EOF

然后,你就可以为你的Ingress资源申请证书了,所有HTTPS的繁琐流程都由 cert-manager 自动完成。

5. 应用部署实践:打造你的家庭服务生态

5.1 部署示例:家庭媒体中心与自动化工具

理论说再多,不如动手部署一个实际应用。让我们以部署“Jellyfin”(媒体服务器)和“Home Assistant”(智能家居)为例,串联起前面部署的所有组件。

首先,为这两个应用创建命名空间和持久化存储声明。

# storage/jellyfin-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: jellyfin-data
  namespace: media
spec:
  storageClassName: nfs-client # 对应之前部署的NFS provisioner
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 100Gi

然后,部署Jellyfin的Deployment和Service。

# apps/jellyfin-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jellyfin
  namespace: media
spec:
  selector:
    matchLabels:
      app: jellyfin
  template:
    metadata:
      labels:
        app: jellyfin
    spec:
      containers:
      - name: jellyfin
        image: jellyfin/jellyfin:latest
        ports:
        - containerPort: 8096
        volumeMounts:
        - name: data
          mountPath: /config
        - name: media
          mountPath: /media
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "2000m"
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: jellyfin-data
      - name: media
        hostPath:
          path: /mnt/nas/media # 假设你的媒体文件在NAS的这个路径
          type: Directory
---
apiVersion: v1
kind: Service
metadata:
  name: jellyfin
  namespace: media
spec:
  ports:
  - port: 8096
    targetPort: 8096
  selector:
    app: jellyfin

最后,创建Ingress,暴露服务到家庭网络,并配置HTTPS。

# networking/jellyfin-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jellyfin
  namespace: media
  annotations:
    cert-manager.io/cluster-issuer: selfsigned-issuer
    nginx.ingress.kubernetes.io/proxy-body-size: "0" # 允许大文件上传
spec:
  tls:
  - hosts:
    - jellyfin.home.lab
    secretName: jellyfin-tls
  rules:
  - host: jellyfin.home.lab
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: jellyfin
            port:
              number: 8096

部署Home Assistant的过程类似,但它的配置更复杂,可能需要访问宿主机的设备(如USB Zigbee网关)。这时就需要在Pod配置中使用 hostNetwork: true 或挂载 /dev 目录,但这会带来安全风险,需要仔细权衡。

5.2 配置管理与GitOps初探

当你的家庭服务越来越多,手动 kubectl apply 会变得难以管理。这时可以引入GitOps工作流。Argo CD是一个流行的选择,它持续监控你的Git仓库,一旦仓库中的YAML文件发生变化,就自动同步到集群中。

  1. 在集群中安装Argo CD: kubectl create namespace argocd && kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
  2. 获取初始管理员密码: kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
  3. 通过端口转发访问Argo CD UI: kubectl port-forward svc/argocd-server -n argocd 8080:443
  4. 在UI中创建一个新的应用,指向存放你所有K8s YAML文件的Git仓库。

从此以后,你只需要向Git仓库提交代码,Argo CD就会自动帮你部署和更新应用。你的整个家庭实验室基础设施和应用状态,都通过Git仓库得到了版本化和自动化管理。

6. 监控、日志与日常运维

6.1 部署监控栈:Prometheus + Grafana

没有监控,集群就是在“裸奔”。Prometheus+Grafana是云原生监控的事实标准。

你可以使用 kube-prometheus-stack 这个Helm Chart一键部署整套监控系统,它包含了Prometheus、Grafana、Alertmanager以及一系列针对Kubernetes的监控规则和仪表盘。

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack -n monitoring --create-namespace

安装后,通过Ingress暴露Grafana服务。登录后,你就能看到集群节点、Pod、Service等资源的CPU、内存、网络流量等丰富指标。你还可以为Jellyfin、Home Assistant等自定义应用配置监控。

6.2 集中日志收集:Loki + Promtail

对于日志,EFK(Elasticsearch, Fluentd, Kibana)栈比较重。Grafana Labs推出的Loki是一个轻量级的日志聚合系统,它的设计理念是“只索引元数据,不索引日志内容”,因此资源消耗极低,非常适合家庭实验室。

helm repo add grafana https://grafana.github.io/helm-charts
helm install loki grafana/loki-stack -n logging --create-namespace --set promtail.enabled=true

promtail 是一个日志收集代理,它以DaemonSet形式运行在每个节点上,收集节点和容器日志,并发送给Loki。在Grafana中添加Loki数据源,你就可以在Grafana中像查询指标一样,使用LogQL查询语言来搜索和分析日志了。

6.3 日常运维命令与问题排查

家庭实验室也需要日常维护。以下是一些常用命令和排查思路:

  • 查看集群状态
    kubectl get nodes -o wide
    kubectl get pods -A
    kubectl get svc -A
    
  • 查看Pod详情和日志
    kubectl describe pod <pod-name> -n <namespace>
    kubectl logs <pod-name> -n <namespace> -f # -f 表示实时跟踪
    
  • 进入Pod调试
    kubectl exec -it <pod-name> -n <namespace> -- /bin/sh
    
  • 常见问题排查
    • Pod一直Pending kubectl describe pod 查看事件,通常是资源不足、节点选择器不匹配或PVC无法绑定。
    • Pod启动失败(CrashLoopBackOff) kubectl logs 查看应用日志,通常是配置错误、依赖服务未就绪或镜像拉取失败。
    • Service无法访问 :检查Service的Selector是否与Pod标签匹配;检查Endpoint是否正常( kubectl get endpoints <service-name> );如果通过Ingress访问,检查Ingress Controller的Pod日志。
    • 节点NotReady :SSH到该节点,检查 systemctl status k3s-agent (worker)或 k3s (master)服务状态;检查网络连通性;检查 /var/lib/rancher/k3s/agent/logs/ 下的日志。

一个我踩过的坑 :有一次重启家庭路由器后,所有节点的IP地址变了,导致集群瘫痪。因为K3s证书和配置里写死了旧的IP。解决方案是:要么为节点配置静态IP(推荐),要么在安装K3s时使用 --tls-san 参数添加一个域名或浮动IP,这样证书会包含这个SAN,IP变化后只需更新节点的 /etc/rancher/k3s/k3s.yaml 中的server地址并重启服务即可。这个教训让我深刻理解了在自动化脚本中,对IP地址等“可变”因素进行抽象(使用主机名、DNS)的重要性。

更多推荐