KEDA缩容至零实战:让自托管应用按需唤醒节省资源

从常开到按需:一次自托管思路的转变
对于运行 homelab 的爱好者来说,一个长期存在的默认假设是:应用必须始终在线。毕竟,谁也不想在访问自己的服务时看到加载失败。但一位 Reddit 用户分享了他的实践——不再让所有自托管应用彻夜运行,而是让「第一个请求」来唤醒它们。
Homelab 是指技术爱好者在家中搭建的个人服务器实验室,用于运行各类自托管应用,如文件同步(Nextcloud)、密码管理(Vaultwarden)、媒体服务器(Jellyfin)、智能家居控制(Home Assistant)等。与依赖云服务不同,自托管强调数据主权和技术自主。典型的 homelab 硬件从树莓派、Intel NUC 迷你主机到二手企业级服务器不等,功耗从几瓦到数百瓦。随着 Docker 和 Kubernetes 的普及,越来越多的爱好者从简单的 Docker Compose 部署进阶到完整的 Kubernetes 集群管理,这使得企业级的资源调度能力下沉到了个人基础设施层面。
这位用户运行了九个采用「缩容至零」(Scale to Zero)策略的应用。当没有人使用时,这些应用会自动将副本数缩减为零;而在他分享的时刻,其中三个应用正处于关闭状态,全部九个则会在夜间下线。这不是故障,而是精心设计的资源管理策略。
缩容至零是云原生架构中的一个重要概念,指当应用没有活跃流量时,将其运行实例数从至少一个缩减为零个。这与传统的自动伸缩(Autoscaling)有本质区别——后者通常在"至少一个副本"和"多个副本"之间动态调整,而缩容至零则彻底释放所有计算资源。这一理念最早在 Serverless/FaaS(Function as a Service)平台中被广泛采用,如 AWS Lambda、Google Cloud Functions 等,后来被 Knative、OpenFaaS 和 KEDA 等项目引入到 Kubernetes 生态中,使得容器化的长运行服务也能享受类似的按需启停能力。
核心收益:302 个 Pod 小时
最直观的成果体现在数字上:在其监控面板记录的两天时间里,这套方案节省了约 302 个 Pod 小时。
Pod 是 Kubernetes 中最小的可部署计算单元,通常包含一个或多个容器。"Pod 小时"是衡量容器运行时间的单位,即一个 Pod 运行一小时所消耗的计算资源时长。302 个 Pod 小时意味着,如果这些应用一直保持运行,它们将累计占用相当于 302 小时的 CPU 和内存资源。以一台典型的 homelab 迷你主机(如搭载 Intel N100 处理器、16GB 内存)为例,每个闲置 Pod 虽然 CPU 使用率低,但仍会占用 100-500MB 内存,九个应用全天候运行可能消耗数 GB 内存,而缩容至零后这些资源可完全释放给其他工作负载。
这意味着大量本应被闲置进程占用的 CPU、内存资源被释放出来。对于运行在有限硬件(如迷你主机、软路由或单台服务器)上的 homelab 而言,这种节流不仅降低了功耗和发热,也让同一套硬件能够承载更多服务。
KEDA 如何实现缩容与按需唤醒
整套机制的核心是 KEDA(Kubernetes Event-Driven Autoscaling),一个基于事件驱动的 Kubernetes 自动伸缩组件。它承担了两个关键角色:负责将闲置应用缩减到零的「缩容」,以及在请求到来时重新拉起应用的「唤醒」。
KEDA 由微软和红帽于 2019 年联合发起,现为 CNCF(云原生计算基金会)毕业级项目。它的核心设计哲学是将外部事件源作为伸缩的触发信号。KEDA 不替代 Kubernetes 原生的 Horizontal Pod Autoscaler(HPA),而是作为其补充,提供了从零到一的伸缩能力(HPA 原生不支持缩到零)以及丰富的事件源支持。截至目前,KEDA 支持超过 60 种事件源(Scalers),包括 Kafka 消息队列深度、Prometheus 指标、Cron 定时计划、HTTP 请求数、PostgreSQL 查询结果等。其架构包含三个核心组件:Operator(控制器)负责管理 ScaledObject 资源并协调伸缩逻辑;Metrics Server 向 Kubernetes API 暴露外部指标;以及可选的 HTTP Add-on 提供 HTTP 流量感知能力。
HTTP 拦截器:请求的中转站
实现按需唤醒的关键在于 KEDA 的 HTTP 拦截器(HTTP Interceptor)。它的巧妙之处在于流量路由的设计:每个应用的路由并不直接指向应用本身,而是指向拦截器。
KEDA 的 HTTP Add-on(也称为 HTTP Interceptor Proxy)本质上是一个轻量级反向代理,部署为 Kubernetes 集群中始终运行的服务。它的设计借鉴了服务网格(Service Mesh)中 Sidecar 代理的思想,但更加专注于解决缩容至零场景下的流量路由问题。当 Ingress Controller(如 Nginx Ingress、Traefik)将流量路由到拦截器时,拦截器会检查目标 Deployment 的当前副本数。如果副本数大于零,请求直接被透明转发;如果为零,拦截器会通过 Kubernetes API 触发扩容,同时利用 HTTP 长连接(keep-alive)或请求缓冲机制保持客户端连接不中断。整个过程的超时时间通常可配置,一般设置为 30-60 秒,足以覆盖大多数容器化应用的启动时间。
当第一个请求到达时,工作流程如下:
- 请求命中拦截器,而非已经下线的应用;
- 拦截器识别到目标应用当前副本为零,触发 KEDA 拉起对应的 Pod;
- 请求被「挂起」(held open),一直保持连接直到 Pod 完成启动;
- Pod 就绪后,请求被转发到应用,用户得到正常响应。
这种设计的优雅之处在于,用户几乎无需感知底层的伸缩过程——只是第一次访问会稍慢(即经典的「冷启动」延迟),后续访问则恢复正常速度。
关于冷启动延迟,一个 Pod 从零启动到就绪需要经历多个阶段:Kubernetes 调度器选择节点、容器运行时拉取或验证镜像、创建容器、执行 Init Container(如果有)、启动主进程、通过 Readiness Probe 健康检查。对于轻量级应用(如 Go/Rust 编写的单二进制程序),整个过程可能只需 2-5 秒;但对于 Java/JVM 类应用,启动时间可能长达 30-60 秒。减轻冷启动影响的常见手段包括:使用更小的容器镜像、预拉取镜像到所有节点、采用 GraalVM Native Image 编译 Java 应用、以及下文提到的 Cron 保温策略。
用 Cron 触发器保温常用应用
冷启动虽然节省资源,但每次访问都要等待启动显然影响体验。为此,该方案引入了 Cron 触发器(Cron Trigger),针对每天都会使用的应用,在白天时段保持其「温热」(warm)状态。
Cron 触发器是 KEDA 内置的 Scaler 之一,它允许用户基于时间计划定义伸缩行为。其命名来源于 Unix/Linux 系统中历史悠久的 Cron 定时任务调度器(名称源自希腊语 chronos,意为"时间")。在 KEDA 的 ScaledObject 中,可以定义多个触发器(Triggers),它们之间是"或"(OR)关系——只要任意一个触发器满足条件,就会保持应用运行。典型配置如:工作日 8:00-23:00 保持最少 1 个副本,其余时间允许缩至零。这种设计使得用户可以将 Cron 触发器与 HTTP 触发器组合使用:白天由 Cron 保温,夜间完全依赖 HTTP 请求按需唤醒,实现精细化的资源分配。
这实际上是一种权衡策略:对于高频使用的服务,用少量的常驻资源换取即时响应;对于低频或纯粹的夜间闲置服务,则完全缩容至零。这种精细化的分级管理,正是这套方案的成熟之处——它没有走「全开」或「全关」的极端,而是根据实际使用模式动态调整。
一个可复用的开源实践
值得称道的是,作者将整套配置以单一公开仓库的形式开源,包含 21 个应用和 22 个基础设施组件,托管于 github.com/mortennordbye/homelab。KEDA 相关的配置位于 k8s/talos/apps/<app>/scaledobject.yaml。
ScaledObject 是 KEDA 引入的 Kubernetes 自定义资源定义(CRD),用于声明式地描述应用的伸缩策略。一个典型的 ScaledObject YAML 文件包含以下关键字段:scaleTargetRef(指向要伸缩的 Deployment)、minReplicaCount(最小副本数,设为 0 即启用缩容至零)、maxReplicaCount(最大副本数)、cooldownPeriod(缩容冷却期,即最后一个事件后多久开始缩容)、以及 triggers(触发器列表,定义伸缩的条件和事件源)。CRD 是 Kubernetes 的扩展机制,允许第三方项目在不修改 Kubernetes 核心代码的情况下,引入新的资源类型和控制逻辑。
从路径可以看出,整套环境构建在 Talos Linux 之上。Talos Linux 是一个专门为运行 Kubernetes 而设计的不可变(immutable)操作系统。与传统 Linux 发行版不同,Talos 没有 SSH 访问、没有 Shell、没有包管理器,所有管理操作都通过其专有的 API(talosctl)进行。系统采用只读的 SquashFS 根文件系统,启动后直接运行 Kubernetes 组件,极大地减少了攻击面和维护负担。这种设计理念与 CoreOS(现 Fedora CoreOS)和 Bottlerocket 类似,但 Talos 更加极端地移除了所有非 Kubernetes 必需的组件。对于 homelab 用户而言,Talos 的优势在于集群的一致性和可重复性——整个操作系统的配置都可以用一个 YAML 文件描述,实现真正的"基础设施即代码"。
整套方案采用声明式的 ScaledObject 资源来定义每个应用的伸缩行为。这种「基础设施即代码」的方式,让配置具备高度的可复现性和可审计性。
对 homelab 玩家的启示
这一实践对广大自托管爱好者具有明确的参考价值:
- 资源利用最大化:在硬件资源有限的场景下,KEDA 缩容至零能让你在同等硬件上运行更多服务;
- 无感知的用户体验:通过 HTTP 拦截器的请求挂起机制,冷启动不会导致访问失败,只是略有延迟;
- 分级保温策略:结合 Cron 触发器,可以为高频应用保留响应速度,兼顾效率与体验;
- 开源可借鉴:完整的 Kubernetes 伸缩配置对外开放,社区可以直接学习、复用甚至改进。
作者本人也在帖子中邀请社区参与讨论,询问「你会怎么做得更好」。这种开放的态度恰恰是 homelab 社区的魅力所在——每一份配置分享都是集体知识的沉淀。
结语:从 Always On 到 On Demand
长期以来,「服务器必须 7×24 小时在线」几乎成了不假思索的默认设置。但这个案例提醒我们,随着 Kubernetes 生态中 KEDA 等事件驱动伸缩工具的成熟,「按需唤醒」已经成为技术上完全可行且体验良好的选择。
对于生产环境的大型服务,这套思路同样适用——它本质上与 Serverless(无服务器)的「冷启动」理念一脉相承。Serverless 计算是云计算的一种模型,用户只需提供代码逻辑,底层基础设施的管理完全由平台负责。AWS Lambda(2014 年发布)是这一模型的开创者,其核心特征之一就是"按调用付费"——函数在没有请求时不消耗任何计算资源。KEDA 的缩容至零本质上将这种 Serverless 范式带入了标准的 Kubernetes 工作负载:你仍然使用传统的容器化应用(而非受限的函数格式),但享受类似的按需伸缩能力。Knative Serving 是另一个实现类似功能的项目,它由 Google 发起,提供了完整的缩容至零和流量路由能力,但架构更为复杂。KEDA 则以其轻量级和模块化设计在 homelab 和中小规模集群中更受欢迎。
而对于个人 homelab 而言,它更是一种优雅的资源哲学:让机器只在需要时工作,其余时间安静地等待第一个请求的敲门声。
相关推荐

MLOps实战项目:衣物洗涤识别系统端到端构建全解析
通过一个衣物洗涤识别系统,详解MLOps端到端实战流程,涵盖自动化数据采集、模型再训练、Docker容器化、AWS云端部署以及Grafana+Prometheus监控,为MLOps初学者和求职者提供完整参考范本。

Row-Bot多智能体编排架构深度解析:父子Agent协作与并发控制
深入解析Row-Bot开源项目的多智能体编排架构,详解父子Agent分工模式、Git worktree并发安全机制、状态持久化与容错恢复设计,为AI Agent工程化落地提供可借鉴的协作范式。

Unsloth Desktop 发布:本地模型运行与训练一体化桌面应用
Unsloth Desktop 是一款开源跨平台桌面应用,集模型运行、微调训练、部署于一体,支持Mac/Windows/Linux,实现2倍训练加速与70%显存节省,零遥测保护隐私。