在云上的 277 天

这是一段长达277天的云原生实践复盘。它始于我构建的一个自建PaaS平台,该平台横跨多家云服务商并以K3s为核心;最终,它演进为一个基于开放标准、将复杂性策略性交由专业平台处理的轻量化个人技术生态。此文旨在记录这一过程中的架构决策、挑战,以及最终形成的“关注点分离”理念。

初始架构:一个全功能自建PaaS

我最初的目标是构建一个功能完备、高可用的云原生平台,以承载各类第三方项目和个人应用。该平台的架构设计如下:

  • 基础设施:由分布在不同云服务商的7台 2c2g/4g KVM 虚拟机组成。
  • 集群网络:通过 WireGuard 构建安全的跨云内网。
  • 服务编排:以 K3s 为核心,并引入 Rancher 进行统一管理。
  • 流量入口:采用 Traefik DaemonSet 模式,实现去中心化的服务暴露。
  • 可观测性:部署了完整的 Prometheus、Grafana 和 Loki 堆栈,以实现全面的监控与日志聚合。

这个平台相当强大,其中不仅包括一个核心的 Clojure 单体应用,还混合部署了多个基于 Spring Boot、Quarkus 和 Python 等不同技术栈实现的服务。通过统一的 GitDevOps 进行 CI/CD 集成,实现了无缝的版本更新和流量切换。然而,这个“大而全”的系统在实践中也暴露出了两个核心的内在矛盾。

  1. 资源开销与节点规格的矛盾:Rancher、Prometheus 等管理和监控组件本身就是资源消耗大户。在 2c2g/4g 这样资源受限的节点上,系统管理组件与业务应用之间形成了资源争抢,导致整体资源水位居高不下。
  2. 有状态服务与跨云网络的矛盾:在高延迟的广域网(WAN)环境下,部署 Ceph、Longhorn 等分布式存储方案并不可行。因此,有状态服务(如数据库)不得不采用本地存储卷(Local Persistent Volume)并固定于特定节点。这牺牲了 Kubernetes 核心的动态调度能力,并引入了单点故障的风险。

理念转变:将通用能力交由专业平台

上述矛盾的根源在于,我试图用一个单一的、自包含的系统去解决所有类型的问题。而真正的突破点在于转变思路:识别系统中的“核心价值”与“通用能力”,并将后者通过开放标准接口,策略性地交由专业的云服务处理。

  • 核心价值:独特的业务逻辑代码以及将系统粘合起来的自动化工具。
  • 通用能力:可观测性后端、对象存储、容器制品库、内容分发网络等。这些是通用的、商品化的能力,自建成本高昂且无法超越专业服务。

基于这一理念,整个技术体系开始了彻底的解耦和重构。

架构解耦案例分析

以下通过几个具体的例子,来论证这一架构选择。

1. 核心架构:从K8s到基于定制网关的松散节点联邦

原始状态:通过 Traefik Ingress Controller、Ingress、Service 等多层 Kubernetes 抽象来管理流量。使用 Kubernetes 的Deployment 和 DaemonSet 来管理容器生命周期。

转移的复杂性:服务发现、动态路由、容器调度与健康状态维护的整个控制平面。

保留的核心资产:一个轻量级的网关和一套直接的容器管理脚本,同时保留了 Docker 容器化带来的环境隔离与标准化交付的优势。

连接标准/工具HTTP/SDocker API。我转向使用 Caddy 作为核心网关,它能自动管理HTTPS证书;同时,我利用自己的Go开发经验,集成了一个 JWT插件 来保护内部服务间的通信。对于容器管理,简单的 Docker + Shell脚本 就足以满足启动、失败重试和健康检查的需求。

收益:对于我的需求——服务数量固定、无弹性伸缩要求——Kubernetes提供的强大调度和自愈能力反而成了一种“过度设计”。其资源开销和维护心智负担远超过了它带来的好处。回归到更直接的控制方式,让我能将精力聚焦在应用本身,而非平台运维。

2. 开发哲学:从单体架构到AI友好的云原生微服务

原始状态:一个庞大的 Clojure 单体应用,以及多个基于 Spring Boot、Quarkus、Python 等不同技术栈的服务,运行在 JVM 等资源基线较高的运行时上。

转移的复杂性:多技术栈带来的维护成本、JVM 的运行时开销和单体应用内部的高度耦合。

保留的核心资产:拆分后的、职责单一的业务逻辑。

技术选型:我逐步将原有服务统一并重构为独立的 Go微服务。选择 Go,不仅因为它资源占用低、性能好,更重要的是,在 AI 辅助编程时代,Go 简洁、规整的语法对 AI 模型和人类审核者都极其友好,显著降低了开发和代码审查的认知负荷。

收益:应用架构的演进与基础设施的精简相辅相成。对于个人开发者而言,快速迭代和低资源占用是关键。Go 微服务不仅降低了单个服务的故障半径,其对 AI 辅助编程的友好性也切实地提升了开发效率,这比维护一个多语言、多框架的复杂系统所带来的管理成本要低得多。

3. 可观测性:从自建后端到拥抱 Remote Write

原始状态:在集群内部署完整的 Prometheus Server、Grafana 和 Loki。

委托管理的复杂性:时间序列数据库的高可用、长期存储和性能扩展。

保留的核心资产:Grafana 面板和 PromQL 查询表达式。

连接标准Prometheus Remote Write 协议。通过在节点上部署轻量级的采集代理(Vector),将 cAdvisor 和 Host 指标数据发送至阿里云 Prometheus 托管服务。

收益:自建并维护一个高可用的监控后端是一项专业且繁重的运维任务。对于我而言,其核心价值在于数据和可视化面板,而非后端本身。通过将后端存储与计算委托给云服务,我保留了核心资产,同时消除了一个巨大的资源消耗点和潜在的故障源,且并未被厂商锁定。

4. 公开站点:利用专业托管平台

实践:将内容型网站从后端剥离,托管至 Vercel,采用 Jamstack 架构,再套一层阿里云 CDN。我保留了内容本身和更新 API,而将全球分发、缓存、安全的复杂性完全交由专业平台处理。

收益:对于内容分发这类高度标准化的工作负载,利用专业的托管平台是最高效的选择。Vercel 和阿里云 CDN 不仅提供了自建方案难以企及的全球网络和弹性资源,还带来显著的安全性优势。它们提供了强大的 DDoS 攻击防护能力,并隐藏了源站 IP,极大地降低了安全风险。同时,全球分布的边缘节点确保了用户可以从最近的位置加载资源,从而获得稳定、低延迟的访问体验。对我而言,这意味着我可以零成本、零运维地获得世界一流的基础设施和安全保障,这远比在自己的VM上搭建相关服务要明智。

5. CI/CD:构建自动化的交付流程

实践:使用 Github Actions,将异构构建环境(macOS/Windows/Ubuntu)的复杂性完全剥离。我的自动化交付流程如下:

  1. 代码提交至 Github,自动触发 Action 执行编译和构建。
  2. 后端服务被打包成容器镜像,并推送到阿里云容器仓库。
  3. 使用 ci-transfer,将前端和客户端归档文件上传至阿里云 OSS,并同时通过 SSH 触发后端节点的更新脚本。
  4. 节点上的脚本从容器仓库拉取最新的镜像,并完成服务的重启和更新。

收益:这套基于标准 API(OCI, S3)和自研工具的流程,实现了高效且可移植的自动化部署,既利用了 Github Actions 强大的托管构建能力,又通过自定义工具链保留了对部署流程的完全控制,整个过程与底层基础设施解耦。

最终的技术生态版图

277天后,我选择下云并从头开始搭建一个职责清晰、高度解耦的个人技术生态系统:

  • 核心业务层:数台轻量级VM,运行由 Caddy 网关和 Docker/Shell 脚本管理的 Go 微服务。
  • 内容交付层:由 Vercel 和阿里云 CDN 组成的全球边缘网络。
  • 定时任务层:由阿里云提供的函数计算 FC 服务,定时执行基于容器的浏览器模拟等重负载任务。
  • 统一构建层:由 Github Actions 提供完全托管的构建环境,并由自研工具链驱动。
  • 标准服务层:由阿里云提供的、基于标准协议的容器仓库 Registry、对象存储 OSS 和 Prometheus 实例。

这个最终形成的生态系统,其每一个组件都由最适合的平台承载,通过开放标准连接,协同工作。系统的复杂性并未消失,而是被策略性地分解并转移到了不同的专业领域。

这趟旅程并非为了否定 Kubernetes——它依然是解决规模化、多团队协作问题的强大工具。这趟旅程的真正意义在于,它让我深刻理解到架构设计的核心是取舍(Trade-off),是根据自身的核心需求,在系统的复杂度、维护成本和功能收益之间找到最佳平衡点。 从构建一个“无所不包”的平台,到设计一个“恰到好处”的生态,这标志着关注点从“实现技术”到“解决问题”的转变。最终,开发者的核心精力得以回归到最根本的价值创造,而非浸淫在无穷尽的技术之潮中,力竭并被无声淹没。