在 cloudflare serverless 部署 astro

最近将 astro 项目部署在 cloudfalre workers serverless 时, 同时接入google登录的功能, 碰了一鼻子灰.

主要包含以下内容

  • astro session 与 workers 的 kv 数据库关系
  • workers 构建资源使用限制
  • workers 数据库链接池限制
  • workers 与 wrangler 变量覆盖 与 astro 变量的冲突
  • astro 的认证框架选择
  • astro 年底被 cloudfalre 收购引起变动

前言

在大部分情况下, 如果仅仅需要部署一个落地页面, 对资源的消耗不大, 也没有什么重要商业数据, 也不是用于长期重构和关键性业务的服务区, 最好的选择就是部署在边缘云中. 以鄙人有限的眼界, 在 cloudflare, deno deploy, Vercel, Netlify ,AWS ,Azure ,Fastly 选择一个, 那一定会在 cloudflare 或者 deno deploy中选择部署.

原因也很简单, cloudflare 作为老牌的网络服务提供商, 我的云部署在cloudflare上, 我不担心出现什么大问题. 假设我选择 deno deploy , 更多原因是他是作为deno 生态的不二选择. 因为更喜欢deno.

在部署一个电商网站时, 需要用到google登录, 并使用Google账户登录为站点的用户凭借.

畅读本文所需要的知识储备

  1. web 全栈经验
  2. 了解 cloudflare workers
  3. 了解 astro

故事开始: googleapis

因为电商用 google 作为站点登录凭据, 因此第一时间当然是选择去看 google 的身份平台的开发文档. 经过一晚上的文档查阅, 发现Google 有个 npm 包: googleapis, 经过一通操作猛如虎后, 在那个基于 astro 技术基础, 结合 cloudfaler 部署的电商网站中安装了这个包. 后面发现这个包运行时各种问题, 原因在于 astro 和 cloudfalre workers 都只认 esm 模块, 而 googleapis是 commonjs的, 侧面说明 google 这样的大公司对于 dx 的前沿支持也是一言难尽.

登录状态的储存(SESSION)

既然引入了Google登录, 必然会存在会话功能, 又因为cloudflare workers本身定位为serverless, 此时就需要外挂一个数据库或者持久层来储存会话信息.

由于选择将 astro 部署在 cloudflare, 就一定会安装 astro的cloudfalre适配器1. 而 astro 在文档2中明确说明, cloudfalre 适配器会自动创建 cloudfalre 的 session kv 数据库, 如果之前不存在, 那么会默认创建一个.

在后来重构 astro 项目过程中, 发现这个 kv 数据库会根据在 cloudfalre 项目名称自动生成, 和 官方描述的的自动创建名称 sessioin 的kv数据库不一致, 是坑点之一. 比如项目的 cloudfalre 配置文件 wrangler.jsonc 中, 项目名称为 astro-blog, 可能不会生成名称为 session 的kv数据库, 而是会生成 astro-blog-session, 如果不在 cloudfalre 控制台手动与cloudfalre workers项目绑定, 那么就会在cloudfalre build 部署阶段失败.

认证框架

身份认证往往伴随会话或者身份认证和授权流程代码. 出于绝不重复造轮子的考虑, 会话管理一定存在现成的解决方案, 根据 ai 给出的适合astro的认证框架在官方文档中均给出了非常多的集成方案3, 例如后端集成的 supabase,firebase, prisma postgres, 还有 auth.js (原 NextAuth.js) 和 Lucia Auth , 最终选择了 astro 推荐的 better auth4.

后面因为升级到了 astro 6, 开发环境中直接模拟为 clouflare workers 生产环境, 其底层包为 workerd. 故事便由此开始

实施时发现问题

由于我的项目之前是 astro 5.x, 在年后3月10好 astro 便发布了 6.0 版本. cloudflare 成为了 astro 的大东家, 命运的齿轮就开始转动了起来. 主要是cloudflare workers的设计目标导致的各种限制.

无法使用长连接

首先是astro 6.0 自带 cloudflare 的 worker 运行时虚拟环境workd,

而 Cloudflare Workers 的设计目标是:

高并发、低延迟、无状态、可在全球边缘节点运行的 JavaScript 执行环境。

为了实现这个目标,它必须:

  1. 不允许长连接
  2. 不允许 TCP
  3. 不允许自定义 socket
  4. 不允许连接池
  5. 不允许阻塞 I/O
  6. 不允许共享状态

首先出现问题的是 astro 自带数据库的连接池就无法运行, 因为数据库连接池是长连接, 且大部分采用了较为底层的tcp协议. 因此像 better auth 的文档中写的, 类似于链接 postgresql 作为其持久层, 就没有作用了, workerd会直接把tcp链接掐断. 导致 better oauth 在文档中的常规的使用数据库连接池的方式就没法正常工作.

这是遇到的第一个坑.

构建环境128mb内存限制

astro 如果还用了服务端构建静态图像, 例如 rehypeMermaid 属性strategy设置为img-svg, 或者 astro 渲染模式使用 server 时, 在构建时就会占用大量内存, 直接导致构建阶段 oom, 可以购买付费版, 可能在构建阶段的构建内存提高到1gb.

mermaid构建图片问题

上文提到过 rehype-mermaid插件, 其可以在服务器构建阶段生成svg或图片, 依赖一个浏览器引擎 Playwright(一个浏览器引擎), 通常在cloudflare ci无法安装, 即使采用postinstall脚本也不行.

vite & wrangler & astro

这三者在处理环境变量的事情上非常不统一.

一般来讲, 纯 vite环境通常是使用 .env文件 + import.meta.env 使用 + env.d.ts 定义ts类型 的方式在项目中使用.

而 astro 则引入了一种新的方式:

astro.config.ts 配置环境变量schema , 结合虚拟包astro:env的方式达到环境变量使用效果.其同时具备环境变量的校验功能和ts类型的效果

现在astro 6, 因为cloudflare workers深度融合的原因. 因为某些原因, 要使用 wrangler或者workers的环境变量, 则可能同时会涉及到虚拟包 cloudflare:workers和cloudflare workers配置文件wrangler.jsonc

可谓混乱不堪. 初次接触让人十分凌乱

如果环境变量用于构建阶段生成硬编码, 建议采用 .env+import.meta.env的方式使用.

如果在纯astro的上下文中则建议 采用 astro.config.ts定义环境变量schema的方式. 这样可以在运行时第一时间校验环境变量是否满足条件. 省去了bug排查的难度.

什么情况使用wrangler.jsonc定义变量? 这个你只能让 astro和cloudflare之间权衡.

问题解决

workers不能使用长连接的问题无解. 但是可以找到一些云服务商提供的 http over tcp的方案链接postgresql. 具体详见各大数据库云服务了. 例如supabase和prima Accelerate这样的数据库云

worerksc 关于rehype-mermaid插件问题只能采用客户端渲染了, 而不是构建阶段生成svg. 这基本就是唯一办法

环境变量除了wrangler.jsonc还需要补充相关的知识

Footnotes

  1. astro的cloudflare适配器:@astro/cloudflare

  2. astro关于session管理的文档

  3. 后端集成, 身份认证

  4. astro与better auth