乐于分享
好东西不私藏

AI 时代,怎么发布自己的 Web 应用?

AI 时代,怎么发布自己的 Web 应用?

AI 已经能在很短的时间里写出一个能跑的 Web 应用。需求说清楚,页面、接口、数据库迁移和测试都可以交给 Coding Agent 逐步完成。到了发布这一步,事情却突然变得很传统。域名指向哪里,进程怎样常驻,证书谁来续期,日志去哪里看,服务挂了怎样恢复,这些问题一个都不会因为代码由 AI 写完而消失。

发布方式很多,常用的可以归成三种。第一种方式是买一台云服务器,自己管理运行环境。第二种方式是使用 Vercel、云函数或 Cloudflare Workers 这类 Serverless 服务。第三种方式适合准备建设内部平台的团队,把构建、镜像、域名和扩缩容统一封装,让开发者交付代码以后直接拿到一个可访问地址。

三种 Web 应用发布方式

这三种方式没有固定的高低顺序。选择主要取决于应用需要多大的运行自由、团队愿意承担多少运维工作,以及同一套发布流程会不会被很多项目重复使用。

动手以前先看应用需要什么

先不要急着选云产品。把应用的运行条件写下来,答案通常已经很接近了。

  • • 只有静态页面,还是带服务端渲染和 API
  • • 是否需要常驻进程、定时任务或长连接
  • • 数据放在托管数据库,还是必须使用本机磁盘
  • • 是否依赖系统软件、GPU、特定网络或自定义运行时
  • • 流量平稳,还是长期空闲、偶尔突然升高
  • • 发布由一个人偶尔操作,还是几十个项目每天都在重复

静态站和常见前端框架通常很适合托管平台。需要完整操作系统、特殊依赖或多个常驻服务时,直接使用云服务器通常更省事。项目多到每个团队都在重复配置 CI、镜像、域名和监控时,自建平台才开始有意义。

第一种方式是买一台云服务器

阿里云 ECS、腾讯云 CVM 和其他云厂商的虚拟机都属于这种方式。买到实例以后,会得到一台带 CPU、内存、磁盘、网络和操作系统的远程计算机。应用可以直接装在系统里,也可以放进 Docker 容器。

这种方式最大的好处是运行边界宽。Node.js、Java、Python、数据库、消息队列和自定义二进制都能装,监听端口、文件目录和进程模型也由自己决定。需要固定公网 IP、私有网络、特殊系统包或稳定的常驻进程时,云服务器很直接。

选择云服务器以后,下面这些工作都要自己处理。

  1. 1. 创建实例,选择地域、系统、CPU、内存和磁盘
  2. 2. 用 SSH 密钥登录,关闭不需要的公网入口
  3. 3. 配置安全组,只开放必要的端口
  4. 4. 安装运行时或 Docker,启动应用进程
  5. 5. 配置域名、反向代理和 HTTPS
  6. 6. 接入日志、监控、告警和备份
  7. 7. 安排系统补丁、应用升级和故障恢复

直接部署适合简单而稳定的环境

直接部署的做法很朴素。服务器安装 Node.js、JDK 或 Python,代码拉下来以后安装依赖,再用 systemd 之类的进程管理方式让应用常驻。机器上只有一个应用,运行时版本长期稳定,团队也熟悉 Linux 时,这套办法完全能用。

问题出在项目增加以后。不同应用需要不同版本的运行时和系统包,部署脚本会逐渐依赖机器当前状态。某次手工修改没有写进文档,下一次迁移就很难复现。

Docker 把运行环境跟着应用一起交付

Docker 会把应用、依赖、运行时和启动方式打进容器镜像。开发机、CI 和云服务器运行同一个镜像,环境差异会少很多。一次常见的发布通常只需要运行下面几条命令。

docker compose up -d --build
docker compose ps
curl -I https://example.com

Docker 解决的是应用如何被打包和运行。域名、证书、宿主机补丁、磁盘备份、容器重启、日志轮转和监控仍然需要有人负责。把进程放进容器以后,云服务器也不会自动变成托管平台。

数据库要格外谨慎。个人项目可以把数据库也放进 Compose,前提是已经设置持久卷和备份。只要数据重要,优先考虑云数据库或另一套经过验证的持久化方案。应用容器随时可以重建,数据库不能随便删掉重来。

云服务器适合想掌握完整环境、应用依赖比较特殊,或者希望用一台机器承载多个小服务的人。代价也很清楚,以后机器的维护和故障处理都要自己负责。

第二种方式是使用 Serverless

Serverless 把服务器、扩缩容和一部分发布流程交给平台。开发者通常连接 Git 仓库或上传代码,平台负责构建并给出访问地址。流量上来时增加实例,空闲时减少实例,有些产品可以缩到零。

Serverless 的便利与边界

这种方式内部也有不同形态,不能把所有产品当成同一种运行环境。

Vercel 适合以 Web 框架为中心的项目

Vercel 可以连接 GitHub、GitLab 和 Bitbucket。分支推送会产生 Preview Deployment,生产分支的更新会产生 Production Deployment。对 Next.js 和常见前端框架来说,构建规则、CDN、函数运行环境、预览地址和回滚都已经连在一起。

它很适合快速发布网站、管理后台、内容站和带轻量服务端逻辑的全栈应用。需要留意的地方是函数运行时间、内存、包大小、区域和并发模型。应用还要把状态放进数据库、对象存储或其他外部服务,不能依赖某个函数实例会一直活着。

云厂商的函数产品适合接入已有云资源

阿里云函数计算、腾讯云云函数等产品会托管计算资源,并通过 HTTP 请求或事件触发代码。应用已经在使用同一厂商的对象存储、消息队列、数据库和权限体系时,这类产品更容易接进现有环境。

函数产品常常同时支持代码包、Web 函数或容器镜像,具体能力以产品和地域为准。部署以前要核对触发方式、运行时、并发、超时、网络、日志和计费规则。云函数省掉了机器维护,却会要求代码遵守平台的执行模型。

Cloudflare Workers 运行在边缘节点

Cloudflare Workers 在全球网络上运行,主要执行模型基于 V8 Isolates。请求进入某个节点以后调用 Worker 的 fetch 处理函数。它启动快,适合边缘 API、鉴权、请求改写和靠近用户执行的 Web 逻辑。

Workers 和普通 Node.js 服务器有差别。可用 API、状态模型、CPU 时间和资源限制都要按 Workers 文档检查。Cloudflare 也明确建议不要把可变状态寄托在全局作用域,因为两次请求没有保证落到同一个实例。

Serverless 最适合运行模型与平台契合的应用。代码一旦需要特殊系统包、长期占用本机资源、复杂网络拓扑或平台没有支持的运行时,后续迁移会很麻烦。选它以前,应当先查限制页,再看首页上的部署按钮。

三种方式对比

维度
云服务器
托管 Serverless
自建发布平台
开发者交付物
代码或容器镜像
Git 仓库、代码包或函数
Git 仓库和少量应用配置
运行自由度
受平台运行时与配额约束
由平台团队定义支持范围
日常运维
自己负责
云平台负责大部分工作
平台团队集中负责
上线速度
配好以后较快
通常最快
平台建成以后很快
空闲成本
实例通常持续计费
常见按调用或使用量计费
取决于集群和缩零策略
适合的起点
特殊运行环境和常驻服务
个人项目与常见 Web 框架
多团队重复交付同类服务

如果目标只是尽快把一个产品放到网上,优先试托管 Serverless。遇到明确的运行限制,再转向云服务器或容器平台。为了一个应用先搭 Kubernetes、CI 系统、镜像仓库和可观测性,通常会让发布工作比应用本身还大。

如果要做基建,可以怎样设计?

团队拥有很多服务以后,问题会发生变化。开发者都在重复写 Dockerfile、CI 配置和部署脚本,平台团队又要反复检查镜像、域名、证书、资源限制和日志接入。此时可以把这些共同步骤封装成一套 Source-to-Service 平台。

理想情况下,开发者只需要输入一条命令。

platform deploy --repo https://github.com/example/my-app --branch main

命令返回构建状态,成功后给出一个 URL。开发者提交源码和少量配置,不需要理解 Kubernetes 对象,也不必为常见语言手写 Dockerfile。

要实现这件事,可以把 Cloud Native Buildpacks、Tekton 和 Knative Serving 组合起来。

从源码到可访问服务

整个过程如下。

Git 仓库
  -> Tekton 拉取源码并执行构建任务
  -> Cloud Native Buildpacks 检测语言和依赖
  -> 生成并推送 OCI 镜像
  -> 控制面创建或更新 Knative Service
  -> Knative 生成 Revision、Route 和访问地址
  -> 状态、日志和 URL 返回给开发者

Buildpacks 负责把源码变成镜像

Cloud Native Buildpacks 会先检测源码,再选择参与构建的 Buildpack。Node.js 项目可以通过 package.json 和锁文件识别,Java、Python、Go 等语言也有各自的检测规则。随后 Buildpack 安装运行时与依赖、执行编译、设置启动进程,最后产出可运行的 OCI 镜像。

Dockerfile 因此可以从常见项目里拿掉。平台团队统一维护 Builder 和 Buildpack 版本,应用团队只关心源码、依赖声明和少量构建参数。Paketo Buildpacks 已经提供多种语言的生产级实现,可以作为起点。

免写 Dockerfile 仍然需要一份清楚的应用契约。Web 服务要监听平台注入的端口,启动命令要能被识别,运行状态要放到外部存储,未被 Builder 支持的系统依赖也要有声明方式。检测失败时,平台应当给出可读错误,并允许用户补充 Procfile、构建参数,或者改用项目自带的 Dockerfile。

Tekton 负责组织构建和交付步骤

Knative Serving 接收的是容器镜像,它不会从 Git 仓库构建源码。Buildpacks 能产出镜像,却不会替应用创建域名和流量规则。中间需要一个构建编排层。

Tekton 把拉取代码、运行测试、执行 Buildpacks、推送镜像和部署服务组织成 Task 与 Pipeline。每次发布对应一个 TaskRun 或 PipelineRun,平台控制面可以观察它的状态,把当前阶段和失败原因返回给 CLI 或 Web 页面。

初期没有必要立刻做完整 CI/CD。MVP 只要跑通源码、镜像和 URL 三段即可。等这个流程稳定以后,再加入测试、安全扫描、SBOM、PR Preview 和审批。

Knative 负责运行服务和管理版本流量

镜像进入仓库以后,平台控制面创建或更新 Knative Service。Service 模板里的镜像或配置发生变化时,Knative 会生成不可变的 Revision,Route 把访问地址映射到一个或多个 Revision。Knative Serving 可以根据请求自动扩缩容,在启用相关配置时把空闲服务缩到零,也能用流量比例完成灰度和蓝绿发布。

这让平台能够提供几项开发者直接感知的能力。每次发布有独立版本,失败后可以把流量切回旧 Revision,低流量应用可以减少空闲副本,测试版本也可以通过命名路由获得单独地址。

平台还要处理这些容易被忽略的工作

能够从代码生成一个可访问的 URL,只能说明基本发布流程已经跑通。准备长期使用,还要处理下面这些事情。

  • • Git 授权与私有仓库凭据
  • • 镜像仓库、镜像签名和清理策略
  • • 域名、DNS 与证书自动签发
  • • Secret、环境变量和数据库连接
  • • 构建日志、运行日志、指标与告警
  • • CPU、内存、并发构建和租户配额
  • • 构建缓存、Builder 预热和超时控制
  • • Revision 保留、回滚和故障排查入口
一键上线背后的平台工作

开发者不再处理这些细节,平台团队会统一负责。对常见应用来说,发布步骤应该尽量简单。构建失败时,平台也要说明原因,并允许开发者补充配置或改用 Dockerfile。持续运行的有状态组件则应当使用更合适的服务。

简单的构想

自建平台最容易犯的错,是一开始就支持所有语言、所有部署模型和所有云。更稳妥的 MVP 可以只支持两类无状态 HTTP 应用,例如 Node.js 和 Java,并规定统一的端口、健康检查和配置注入方式。

  1. 1. 参数 Git 仓库、分支和应用名
  2. 2. 触发 Tekton 构建任务
  3. 3. 用 Paketo Buildpacks 生成并推送镜像
  4. 4. 创建 Knative Service 并等待 Ready
  5. 5. 返回构建日志、错误原因和访问 URL
  6. 6. 用一个真实项目端到端验证重新发布与回滚

这个流程稳定以后,再增加 Preview 环境、自动测试、镜像扫描、自定义域名、多租户隔离和成本统计。对平台来说,重复发布是否省事,比功能多少更重要。

AI 让代码产出速度提高以后,发布系统会更频繁地被使用。个人项目通常应该先借用成熟平台,把时间留给产品。团队持续重复同一套交付动作时,再把经验做成内部平台。那时 Knative 和 Buildpacks 很合适,但用户看到的产品应该是一条清楚的发布命令、可追踪的状态,以及最终可以打开的 URL。

后续可以考虑扩展为 FaaS、BaaS 等,当然一般也不需要搞这些~

参考资料

  • • 阿里云 ECS 产品说明
  • • Docker 容器基础
  • • Vercel 的 Git 部署流程
  • • 阿里云函数计算产品说明
  • • Cloudflare Workers 的运行方式
  • • Cloud Native Buildpacks 的构建过程
  • • Paketo Buildpacks 的 Node.js 构建示例
  • • Tekton Tasks 与 Pipelines
  • • Knative Serving 概览
  • • Knative 流量管理