Homepage 的 Kubernetes 集成实战:集群模式连接、自动服务发现与资源监控 Widget
Homepage 原生支持 Kubernetes 集群连接,允许在自建首页中实时展示集群节点状态、CPU/内存统计,并通过 Ingress、Traefik IngressRoute 或 Gateway API HttpRoute 注解实现服务自动发现。本文以仓库根目录的 kubernetes.md 与 docs/configs/kubernetes.md 为骨架,结合 安装文档 与 src/utils/kubernetes 源码实现,完整讲解连接模式、配置项、服务发现注解体系与 Widget 用法,帮助你在一小时内把 Homepage 接入现有集群。
前置要求
Kubernetes 集成并非开箱即用,集群侧需要满足以下条件:
| 要求 | 说明 |
|---|---|
| Kubernetes 1.19+ | 集群 API 版本下限,确保 networking.k8s.io/v1 Ingress 与客户端库兼容 |
| Metrics Service | 提供 metrics.k8s.io 聚合 API,用于获取节点 / Pod 的 CPU 与内存指标 |
| Ingress controller | 承载 Ingress 资源,供服务发现读取(如 Nginx、Traefik、Cilium 等) |
| Gateway-API(可选) | 若需使用 HttpRoute 服务发现,需集群安装了 Gateway API CRD |
Metrics Service 尤为重要:Homepage 的服务统计 Widget 与集群 Widget 的资源数据均来源于 metrics.k8s.io,未部署 Metrics Server 时相关指标将不可用。
部署方式
方式一:使用非官方 Helm chart(推荐)
官方未维护 Helm chart,社区维护的 chart 位于 jameswynn/helm-charts。只需两步:
helm repo add jameswynn https://jameswynn.github.io/helm-charts
helm install my-release jameswynn/homepage
安装完成后,需要将 kubernetes.yaml 中的连接模式设置为 cluster(详见下文"连接模式"):
mode: cluster
如需启用 Gateway API 兼容性,追加:
gateway: true
方式二:原生 Kubernetes manifests
如果不想依赖 Helm chart,可以自行编写清单并用 kubectl apply -f filename.yaml 应用。完整可运行的示例见 Kubernetes 安装文档,核心资源清单如下:
- ServiceAccount:名为
homepage,供 Pod 携带集群身份; - Secret:类型
kubernetes.io/service-account-token,供 ServiceAccount 挂载令牌; - ConfigMap:承载全部配置文件(
kubernetes.yaml、settings.yaml、services.yaml、widgets.yaml、bookmarks.yaml、custom.css/js); - ClusterRole 与 ClusterRoleBinding:授权 Homepage 读取集群资源,规则覆盖
namespaces、pods、nodes、ingresses、ingressroutes(traefik.io)、httproutes/gateways(gateway.networking.k8s.io)以及metrics.k8s.io的nodes/pods; - Service:ClusterIP 类型,暴露 3000 端口;
- Deployment:镜像
ghcr.io/gethomepage/homepage:latest,以非 root(UID 1000)运行,通过subPath将 ConfigMap 中每个文件挂载到/app/config/对应路径; - Ingress:对外暴露,同时可附加
gethomepage.dev/*注解实现自发现(见下文)。
多副本注意:若副本数大于 1,建议在入口路由上开启会话粘滞(sticky session),避免页面加载与窗口 / 标签聚焦时产生不必要的重复渲染。以 Traefik 为例,通过 sticky.cookie 配置(httpOnly: true、secure: true、sameSite: none),示例见 docs/installation/k8s.md。
连接配置:kubernetes.yaml 的三种模式
Kubernetes 连接在 kubernetes.yaml 中配置(未创建时会在首次启动自动生成副本,可参考 src/skeleton/kubernetes.yaml),共三种模式:
| 模式 | 行为 |
|---|---|
disabled | 完全禁用 Kubernetes 连接 |
default | 使用默认的 kubeconfig 解析(本地开发、集群外部运行时使用,解析规则见 Kubernetes 官方 kubeconfig 组织文档) |
cluster | 使用集群内的 ServiceAccount 令牌(生产部署推荐) |
mode: default
从源码 src/utils/config/kubernetes.js 可以清晰看到三种模式的分流逻辑:cluster 调用 kc.loadFromCluster() 加载挂载的 ServiceAccount 令牌,default 调用 kc.loadFromDefault() 走本地 kubeconfig,而 disabled 或未配置时返回 null,后续所有 Kubernetes 相关功能整体关闭。
连接建立后,可通过开关决定启用哪些服务发现来源。以下配置仅启用 Ingress(这也是默认行为):
ingress: true # 默认,仅启用 ingress
同时启用 Traefik IngressRoute 与 Gateway API:
ingress: true # 默认,启用 ingress
traefik: true # 启用 traefik ingressRoute
gateway: true # 启用 gateway-api
源码层面对应三个独立的后端列表获取器:ingress 走 NetworkingV1Api.listIngressForAllNamespaces()(见 ingress-list.js),traefik 通过 CustomObjectsApi.listClusterCustomObject 分别探测 traefik.containo.us 与 traefik.io 两个 API 组的 ingressroutes(见 traefik-list.js),gateway 则拉取 gateway.networking.k8s.io/v1 的 httproutes(见 httproute-list.js)。
Kubernetes Widget:集群资源总览
Kubernetes Widget 可展示集群整体概览、各节点明细或两者兼有,配置写在 widgets.yaml:
- kubernetes:
cluster:
# 是否显示集群节点
show: true
# 是否显示聚合的 CPU 统计
cpu: true
# 是否显示聚合的内存统计
memory: true
# 是否显示自定义标签
showLabel: true
label: "cluster"
nodes:
# 是否显示节点列表
show: true
# 是否显示每个节点的 CPU
cpu: true
# 是否显示每个节点的内存
memory: true
# 是否显示标签(始终为节点名称)
showLabel: true
其中 cluster 区块展示集群聚合视图,nodes 区块逐节点展示。二者的 cpu / memory 数据均来自集群 Metrics Service;showLabel 在 cluster 模式下展示自定义 label 文案,在 nodes 模式下则固定展示节点名。该 Widget 的前端组件位于 src/components/widgets/kubernetes,配套的状态/统计 API 端点见 src/pages/api/kubernetes。
Service Widgets:手动关联服务与 Pod 统计
Kubernetes 连接就绪后,可以为 services.yaml 中的任意服务手动开启 Pod 统计(当前仅支持 CPU 与内存)。关键是在服务条目上配置 namespace 与 app 两个字段:
- Emby:
icon: emby.png
href: "http://emby.home/"
description: Media server
namespace: media # 应用所在的 kubernetes 命名空间
app: emby # 已部署应用的名称
app 字段用于构造标签选择器:上例将匹配带有 app.kubernetes.io/name=emby 标签的 Pod。这通常能同时命中 Ingress 与 Deployment,但对复杂或不规则的应用部署并不总是成立,此时可用 podSelector 字段弥补——它接受任意字段选择器(field selector),选择能力非常强大。
例如,将多个底层 Deployment 聚合为一个应用,展示高层级的汇总资源占用:
- Element Chat:
icon: matrix-light.png
href: https://chat.example.com
description: Matrix Synapse Powered Chat
app: matrix-element
namespace: comms
podSelector: >-
app.kubernetes.io/instance in (
matrix-element,
matrix-media-repo,
matrix-media-repo-postgresql,
matrix-synapse
)
重要提示:podSelector 置为空白字符串并不会停用该字段,反而会选中命名空间内的全部 Pod。这一行为非常适合捕获"被隔离在单个命名空间内的复杂应用"(如 Longhorn)的整体资源使用情况。
自动服务发现
Ingress 注解发现
Homepage 通过 Ingress 注解实现自动服务发现,所有配置项均以 gethomepage.dev/ 为前缀,使用标准注解语法,与手动配置的选项一一对应:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: emby
annotations:
gethomepage.dev/enabled: "true"
gethomepage.dev/description: Media Server
gethomepage.dev/group: Media
gethomepage.dev/icon: emby.png
gethomepage.dev/name: Emby
gethomepage.dev/widget.type: "emby"
gethomepage.dev/widget.url: "https://emby.example.com"
gethomepage.dev/widget.headers.X-Auth-Key: "your-secret-here"
gethomepage.dev/pod-selector: ""
gethomepage.dev/weight: 10 # 可选
gethomepage.dev/instance: "public" # 可选
spec:
rules:
- host: emby.example.com
http:
paths:
- backend:
service:
name: emby
port:
number: 8080
path: /
pathType: Prefix
集群连接配置正确后,该服务会被自动发现并加入首页。无需再手动指定 namespace 与 app——它们会被自动推断。
各注解的语义:
| 注解 | 含义 |
|---|---|
gethomepage.dev/enabled | 是否启用该服务的自动发现 |
gethomepage.dev/name / description / group / icon | 服务卡片显示的名称、描述、所属分组与图标 |
gethomepage.dev/widget.type / widget.url / widget.headers.* | 为发现的服务直接附加 Widget 配置(如 Emby 的类型、地址、请求头) |
gethomepage.dev/pod-selector | 指定健康检查所用的 Pod(见下文) |
gethomepage.dev/weight | 可选,影响服务在组内的排序权重 |
gethomepage.dev/instance | 可选,限定服务仅出现在特定 Homepage 实例上 |
多实例过滤:如果部署了多个 Homepage 实例,可用 instance 注解将服务限定到特定实例;未标注时服务对所有实例可见。若某个服务需要在多个指定实例(而非全部)上展示,可叠加多个 instance.<name> 注解,例如同时标注 gethomepage.dev/instance.public: "" 与 gethomepage.dev/instance.internal: "",则该服务只会出现在名为 public 和 internal 的实例上。
健康检查 Pod 选择:使用 gethomepage.dev/pod-selector 指定健康检查对应的 Pod。例如标注 gethomepage.dev/pod-selector: app.kubernetes.io/name=deployment 会将检查链接到带有 app.kubernetes.io/name: deployment 标签的 Pod。
从源码看,注解前缀常量 ANNOTATION_BASE = "gethomepage.dev" 与 ANNOTATION_WIDGET_BASE = "gethomepage.dev/widget." 定义在 src/utils/config/kubernetes.js,被资源解析逻辑(resource-helpers.js)统一消费。此外,URL 的推导逻辑也在该文件中:Ingress 依据 spec.tls 是否存在决定 https/http,而 HTTPRoute 则需向关联的 Gateway 查询 listener 的 protocol 才能确定协议前缀(见 resource-helpers.js)。
Traefik IngressRoute 支持
在 kubernetes.yaml 中设置 traefik: true 后,Homepage 可读取 Traefik IngressRoute 自定义资源定义的 Ingress。由于 Traefik 路由规则非常复杂,必须设置 gethomepage.dev/href 注解,Homepage 才能确定该服务的访问地址:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: emby
annotations:
gethomepage.dev/href: "https://emby.example.com"
gethomepage.dev/enabled: "true"
gethomepage.dev/description: Media Server
gethomepage.dev/group: Media
gethomepage.dev/icon: emby.png
gethomepage.dev/app: emby-app # 可选,当 app.kubernetes.io/name != ingress metadata.name 时可能需要
gethomepage.dev/name: Emby
gethomepage.dev/widget.type: "emby"
gethomepage.dev/widget.url: "https://emby.example.com"
gethomepage.dev/pod-selector: ""
gethomepage.dev/weight: 10 # 可选
gethomepage.dev/instance: "public" # 可选
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(`emby.example.com`)
services:
- kind: Service
name: emby
namespace: emby
port: 8080
scheme: http
strategy: RoundRobin
weight: 10
注意:缺少 href 注解的 IngressRoute 会被 Homepage 直接忽略。这一行为在源码 traefik-list.js 中有明确实现——最终返回的列表仅保留带有 gethomepage.dev/href 注解的 IngressRoute。同时该文件还会在拉取前通过 checkCRD 检查 ingressroutes.traefik.containo.us 与 ingressroutes.traefik.io 两个 CRD 是否存在,并在权限不足(403)时给出清晰的 RBAC 报错提示(kubernetes.js)。
Gateway API HttpRoute 支持
Homepage 同样支持 Gateway API 的自动服务发现。在 kubernetes.yaml 中开启:
gateway: true # 启用 gateway-api
即可读取 HttpRoute 自定义资源上的注解,注解格式与上文 Ingress 示例完全一致(Automatic Service Discovery)。若使用的是非官方 Helm chart,请务必确认其 ClusterRole 已包含 gateway.networking.k8s.io 相关权限,RBAC 完整示例见 docs/installation/k8s.md。
Longhorn Widget:存储统计
针对 Longhorn 提供专用 Widget,在 widgets.yaml 中配置,展示集群存储统计:
- longhorn:
# 是否展开显示
expanded: true
# 是否显示代表聚合值的节点
total: true
# 是否将节点名称显示为标签
labels: true
# 是否显示节点
nodes: true
# 显式指定要展示的节点列表;nodes 为 true 时默认展示全部节点
include:
- node1
- node2
各选项说明:expanded 控制卡片展开形态;total 展示聚合汇总;labels 控制节点名称标签的显隐;nodes 控制节点列表;include 则可在 nodes: true 的基础上收窄到指定节点。需要注意的是,Longhorn 场景通常配合前文提到的 podSelector: "" 技巧使用——通过选中命名空间内全部 Pod 来聚合出整体存储用量。
注意事项
与 Docker 服务发现类似,自动发现的服务目前没有严格的排序规则,且会显示在 services.yaml 中手动配置的服务上方。若对顺序有要求,可通过 weight 注解参与排序,但无法完全控制与手动服务的相对顺序。
额外配置文件:通过 ConfigMap 挂载
部分 Homepage 功能(例如 Proxmox)需要额外的配置文件(如 proxmox.yaml)。在 Kubernetes 上运行时,这些文件必须通过 ConfigMap 提供并挂载到容器的 /app/config 目录。
ConfigMap 示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: homepage
data:
proxmox.yaml: |
pve:
url: https://proxmox.host.or.ip:8006
token: username@pam!Token ID
secret: secret
随后在 Deployment 中通过 subPath 将文件挂载到 /app/config 下:
volumeMounts:
- mountPath: /app/config/proxmox.yaml
name: homepage-config
subPath: proxmox.yaml
该模式与 安装文档 中 Deployment 示例对 kubernetes.yaml、services.yaml、widgets.yaml 等的挂载方式完全一致,新增任何配置文件都遵循同一套路。
本地开发与测试:基于 k3d 的集群环境
仓库在 k3d/README.md 中提供了完整的本地开发与测试方案:利用 k3d 在 Docker 内拉起 k3s 集群,再通过脚本或 Tilt 部署 Homepage,便于验证上述全部 Kubernetes 功能。
所需工具:docker、kubectl、helm、k3d(可选 k9s、tilt、direnv)。一次性测试部署:
./k3d-up.sh # 创建集群
./k3d-deploy.sh # 构建并部署
xdg-open http://homepage.k3d.localhost:8080/ # 打开 Homepage
持续开发模式则用 Tilt 建立本地 CI 循环,改动代码后自动更新部署:
./k3d-up.sh
tilt up # 按空格打开 Tilt Web UI
xdg-open http://homepage.k3d.localhost:8080/
所有命令需在 k3d 目录下执行。该环境配合 src/utils/kubernetes 目录下的单元测试(ingress-list.test.js、traefik-list.test.js、httproute-list.test.js、resource-helpers.test.js 等),为验证服务发现、URL 推导与选择器解析逻辑提供了完整的开发闭环。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



