原创 idoubi 2024-08-14 21:07 广东
弃坑 Vercel,白嫖 Cloudflare
前言
在上一篇文章讲到,我做了一个 AI 搜索引擎,部署在 Vercel,每个月几十万访问量。前几天登录 Vercel 看了一下账单,好家伙!!!一个月给我干到了 5000 多刀的支出,合计人民币 3 万 7 千多元。为什么要用 Vercel
在这篇文章:出海第一周,我的 GPTs 导航站🔥了 讲到,去年 11 月,我开始做独立出海项目,当时做的 GPTs 导航站,第一次用到了 Vercel 进行部署。使用 Vercel 部署,主要图的是方便。Vercel 集成了 Github 做 CI/CD,代码提交到 Github 自动发布上线,可以滚动更新,回退版本,DevOps 相当便利;Vercel 内置域名服务,为你部署的每个项目,生成一个:xxx.vercel.app 的子域名,可以公网访问,方便新项目快速上线 Demo 版本;Vercel 可以指定分支部署项目,为每一次部署生成一个独立的访问地址,方便正式上线前进行调试和验证,还支持团队协作,划线评论;Vercel 集成了很多常用的功能,比如网站访问统计 / 页面性能分析 / 服务运行日志 / 环境变量管理 / 防火墙等,还支持存储套件,比如文件存储 / Postgres 数据存储 / KV 等;最最关键的一点,Vercel 和流行全栈开发框架 nextjs 同属于一家公司,对 nextjs 项目支持非常到位。Vercel 模板中心有大量使用 nextjs 开发的模板,生态做的很完善:开发框架 + 模板组件 + 运维部署一条龙服务,刚接触全栈开发的朋友,简直不要太爱。Vercel 有哪些坑
Vercel 最大的坑,是收费太贵了。首先,免费版本只能部署 Github 个人项目,如果你的项目是放在某个组织下面,要在 Vercel 部署,就必须要升级成团队版,20 美金 / 月。export const maxDuration = 60; // This function can run for a maximum of 60 seconds
export const dynamic = 'force-dynamic';
export function GET(request: Request) {
return new Response('Vercel', {
status: 200,
});
}
就算花 20 美金升级到了 Pro 版本,很多功能依然受限制。Vercel 几乎每项功能都是单独计费的,比如网页统计 Analytics,给一定的免费额度,访问量大一点就得付钱。比如数据存储,也是按照存储空间和访问流量计费的,计费规则一大堆,给你搞得迷迷糊糊,到了月初再给你出个账单,“惊喜值”拉满。云函数调用是最大的支出,按流量计费:$0.18 / 1 GB Hrs。如果你在 nextjs 项目中使用了 nextjs 的 Image 组件:import Image from 'next/image'
export default function Page() {
return (
<Image
src="/profile.png"
width={500}
height={500}
alt="Picture of the author"
/>
)
}
Vercel 会帮你做图片裁剪,做 CDN 加速,对于图片型网站,体验是好了,但是这个费用,也是很大的一块支出,妥妥的”羊毛出在羊身上“。有哪些可替代的部署方案
部署 nextjs 项目,除了 Vercel 之外,还有很多可替代的部署方案。包括跟 Vercel 类似的云部署平台,开源的部署方案,自建服务器部署以及使用 Cloudflare Pages 部署几种。跟 Vercel 类似的云部署平台Netlify:这是Vercel的直接竞争对手,提供类似的功能。Netlify还额外提供每个站点每月1000个已识别的活跃用户和站点分析。(月访问2.7M)Railway:这个平台可以部署大部分项目,包括Docker容器。它支持项目内Dockerfile和公开打包好的docker镜像,但不支持docker-compose。(月访问1.5M)Zeabur:与Railway类似,可以部署多种项目类型,包括Docker容器。(月访问59.9K)Render:另一个流行的云部署平台,提供类似Vercel的服务。(月访问1.6M)Firebase:这是Google提供的一个平台,可用于部署和托管web应用。(月访问16.4M)Heroku:虽然它的定位略有不同,但Heroku也是一个受欢迎的应用部署平台。(月访问1.9M)开源部署方案Coolify:这是一个引人注目的开源项目,旨在成为Heroku、Netlify和Vercel等流行平台的自托管替代方案。Coolify提供了一系列功能来简化应用程序部署过程。(27.7k star)Dokku:Dokku是一个轻量级的开源PaaS(平台即服务)实现。它可以让你在自己的服务器上创建类似Heroku的环境,支持多种编程语言和框架。(26.5k star)SST:SST是一个用于构建全栈无服务器应用的框架,专注于无服务器架构和AWS生态系统。(21.2k star)Dokploy:Dokploy是一个开源的部署平台,旨在成为Vercel、Netlify和Heroku的替代方案。(5.2k star)自建服务器部署买台服务器,安装宝塔面板,部署 nextjs 项目买台服务器,安装 pm2 做进程管理,部署 nextjs 项目买台服务器,安装 docker,使用容器部署 nextjs 项目买台服务器,使用 minikube 或 k3s 自建 K8S 集群,部署 nextjs 项目在 xx 云上面买托管的 K8S 集群,部署 nextjs 项目使用 Cloudflare Pages 部署Cloudflare 的定位为一家全球性的互联网基础设施提供商,提供了一系列的网络安全和性能优化服务,包括内容分发网络(CDN)、DDoS防护、SSL/TLS加密、DNS管理等。Cloudflare Workers(serverless计算平台)和Cloudflare Pages 可以用来部署 nextjs 应用。以上几种方案,都可以用来替代 Vercel 部署 nextjs 项目,至于要选择哪一个,关键要考虑两点:服务费用和运维复杂度。我选择从 Vercel 迁移,主要是为了降低成本,最关心的是费用问题,所以不会再选择其他云部署平台和托管的 K8S 集群,而是选择成本相当较低的自建服务器部署和 Cloudflare 的托管方案。我实践了以下 3 种部署方案,分享一下具体的部署步骤。在云服务器上用 pm2 部署 nextjs 项目
在 AWS 上购买一台 4c8g 的 EC2 服务器,选择 Ubuntu 操作系统,使用 pm2 做进程管理,部署 nextjs 项目。先确保安装了 nodejs 和 npm,并使用 pnpm 作为 nextjs 项目的依赖管理工具npm install -g pnpm
安装 pm2 做进程管理npm install -g pm2
在项目根目录下安装依赖,构建输出产物pnpm install
pnpm build
使用 pm2 启动服务pm2 start pnpm --name sorafm -- start --port 8015
使用 nginx 做反向代理先确保安装和启动了 nginx:sudo apt install nginx
sudo systemctl start nginx
为 nextjs 项目创建 nginx 配置:sudo vi /etc/nginx/conf/sorafm.conf
server {
listen 80;
location / {
proxy_pass http://127.0.0.1:8015/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_log /var/log/nginx/sorafm.error;
}
nginx 重载新的网站配置:sudo nginx -s reload
DNS 解析域名到服务器的公网 ip在 DNS 控制台添加一条 A 记录,指向服务器的公网 ip,比如我这里使用的是子域名:sorafm.trys.aihttp://sorafm.trys.ai
访问 nextjs 项目了。配置 https 访问可以在 Ubuntu 安装 certbot 生成域名证书:sudo apt update
sudo apt install certbot python3-certbot-nginx
为新域名生成新的证书,并使用 https 访问sudo certbot --nginx -d sorafm.trys.ai
配置完成后,就可以通过:https://sorafm.trys.ai
安全访问 nextjs 项目了。在云服务器上用 Docker 部署 nextjs 项目
同样是使用 Ubuntu 云服务器,使用 pm2 部署 nextjs 更简单直接,轻量级部署,服务资源占用少。使用 docker 部署,系统隔离性更好,更方便移植,适用于微服务架构。要使用 docker 部署 nextjs 应用,先确保在服务器安装好了 docker,再来改造 nextjs 项目修改项目根目录下的next.config.mjs
,使用 standalone
模式输出编译产物/** @type {import('next').NextConfig} */
const nextConfig = {
output: "standalone",
};
export default nextConfig;
在 deploy 文件夹下新建 Dockerfile
文件,写入 docker 镜像构建内容FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat && yarn global add pnpm
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json pnpm-lock.yaml* ./
RUN pnpm i --frozen-lockfile
# Rebuild the source code only when needed
FROM deps AS builder
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY . .
RUN pnpm build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs && \
mkdir .next && \
chown nextjs:nodejs .next
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 8080
ENV NODE_ENV production
ENV PORT 8080
ENV HOSTNAME "0.0.0.0"
# server.js is created by next build from the standalone output
CMD ["node", "server.js"]
在项目根目录下新建 .dockerignore
文件,写入构建镜像时要忽略的内容.next
.vercel
.vscode
data
debug
node_modules
开始构建 docker 镜像sudo docker build -f deploy/Dockerfile -t sorafm:latest .
使用 docker 运行服务sudo docker run -itd -p 127.0.0.1:8014:8080 --restart=always sorafm:latest
服务运行成功后,再通过 nginx 配置公网访问,DNS 解析公网域名,certbot 配置 https 证书,这三个步骤跟上面使用 pm2 部署 nextjs 的方案一致。“具体改造点参考:https://github.com/all-in-aigc/sorafm/commit/63802c832bf9a26dfe93e0964876c918c7132af2
使用 Cloudflare 部署 nextjs
上述两种方案:使用 pm2 和使用 docker 部署 nextjs 应用,需要先有一台服务器。如果不想买服务器,而是通过托管的方式部署 nextjs 项目,可以选择 Cloudflare Pages,几乎免费的云部署方案。按照 Cloudflare 的官方文档,使用 Cloudflare Pages 部署 nextjs 项目,主要的步骤:安装部署依赖pnpm add -D @cloudflare/next-on-pages
在项目根目录创建一个配置文件 wrangler.toml
name = "sorafm"
compatibility_date = "2024-07-29"
compatibility_flags = ["nodejs_compat"]
pages_build_output_dir = ".vercel/output/static"
更新 next.config.mjs
文件import { setupDevPlatform } from "@cloudflare/next-on-pages/next-dev";
/** @type {import('next').NextConfig} */
const nextConfig = {};
if (process.env.NODE_ENV === "development") {
await setupDevPlatform();
}
export default nextConfig;
修改服务端路由运行时对所有的 api 路由文件 route.ts 和所有的页面路由文件 page.tsx 都添加一行代码,指定使用 edge 运行时:export const runtime = "edge";
修改 package.json
文件,添加编译指令"pages:build": "npx @cloudflare/next-on-pages",
"preview": "pnpmb pages:build && wrangler pages dev",
"deploy": "pnpm pages:build && wrangler pages deploy"
在项目根目录通过命令行部署项目到 Cloudflare Pagesnpm run deploy
在第一次运行 deploy 命令时,需要填写项目名称,跳转 Cloudflare 进行授权验证等。发布完成后,就可以在 Cloudflare Workers and Pages 管理后台看到项目了:xxx.pages.dev
,部署成功可直接公网访问,方便项目快速上线验证。使用 Cloudflare 部署 nextjs 项目需要注意的点
Cloudflare 部署 nextjs 项目,只支持 edge 运行时首先,我们使用thinkany.ai
来搜一下什么是 nodejs 的 edge 运行时:export const runtime = "edge";
Cloudflare 部署 nextjs 项目,需要改造 pg 客户端如果 nextjs 项目用到了 postgres 数据库,客户端连接使用的是 pg 这个包,是不支持 edge 运行时的import { Pool } from "pg";
我们需要更换一个支持 edge 运行时的 postgres 客户端,比如 neon,把操作 postgres 数据库的逻辑改成这样:import { neon } from '@neondatabase/serverless';
async function getData() {
const sql = neon(process.env.DATABASE_URL);
const response = await sql`SELECT version()`;
return response[0].version;
}
如果是原生的 postgres 数据库,这种改造是 OK 的,部署到 Cloudflare 也没问题。如果数据库用的是 supabase,这种方案就行不通,原因是 neon 并不兼容 supabase。我们需要使用 supabase 官方客户端进行改造:import { createClient } from "@supabase/supabase-js";
export function getSupabaseClient() {
const client = createClient(
process.env.SUPABASE_URL || "",
process.env.SUPABASE_ANON_KEY || ""
);
return client;
}
@supabase/supabase-js
是支持 edge 运行时的,改造完可以部署到 Cloudflare,但是我遇到了一个问题,就是这个客户端不支持 select * from xxx order by random()
随机排序操作。Cloudflare 部署 nextjs 项目,需要改造对 fs / http 等 nodejs API 的依赖因为 edge 运行时不支持 fs / http 这种 nodejs API,所有依赖这两个 API 的逻辑都需要改造:比如读取本地文件的逻辑:const data = fs.readFileSync(dataFile, "utf8");
依赖了 fs API,edge 运行时不支持,可以改成用 fetch API 读取远端文件。比如 google-one-tap
这个 npm 包,依赖了 axios 请求 google 登录 API,而 axios 依赖 http API,不兼容 edge 运行时,我们只能把这个 npm 拉到本地,再把对应的 http 请求换成 fetch API。“具体改造点可以参考:https://github.com/all-in-aigc/sorafm/compare/main...feature/deploy-to-cloudflare
Cloudflare 有哪些可白嫖的真香服务
Cloudflare 提供了很多常用的网站管理服务,其中大部分功能都有免费的使用额度,对于小网站而言,白嫖就够了。DNS 解析我们对 Cloudflare 使用最多的场景,可能就是 DNS 解析了。Cloudflare 的 DNS 服务被认为是世界上最快的 DNS 解析器之一,平均响应时间为 11 毫秒,覆盖超过 330 座城市。这种快速的 DNS 解析可以提高网站的整体访问速度。比如在 namecheap / godaddy 这些平台注册的域名,我们可以选择接入到 Cloudflare 做 DNS 解析。总结
本文用实际的例子介绍了把 nextjs 项目从 Vercel 迁移到 Cloudflare 的全过程。拥抱赛博菩萨 Cloudflare(cf),节省好几万的服务成本。感谢 「1024 全栈开发社群」成员 @Deniffer 和 @7 提供的建议,我们这个群的技术讨论氛围真好。“欢迎新朋友加入「1024 全栈开发社群」,跟艾逗笔一起学全栈开发,让你的创意更快落地成产品。