cloudflare + vercel + supubase 迁移至腾讯云 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
yekk
V2EX    分享创造

cloudflare + vercel + supubase 迁移至腾讯云

  •  
  •   yekk 151 天前 2153 次点击
    这是一个创建于 151 天前的主题,其中的信息可能已经有所发展或是发生改变。

    原因

    之前搞了个自动化播客,详情见字节篝火播客。因为播客本身需要处理一些 Hacker news 以及 github trending 的数据,为了保存下来后续做了个配套的网站 lumifire.io。起初为了快速上线,直接用了 vercel + supubase 的免费方案, 最近因 vercel + supubase 相继超出免费额度,所以搞了两个腾讯云轻量服务器部署应用, 一台部署 nextjs ,另一台部署 postgres 。最终确保从浏览器到 Cloudflare 以及从 Cloudflare 到你的服务器的全程流量都经过加密验证,下边会罗列大致流程以及会用到的配置文件,非傻瓜式教程,仅供参考。

    supubase 迁移至自建 postgres

    1. 生成配置文件(可以用 https://pgtune.leopard.in.ua/ 生成配置文件),配置文件见下文, 然后启动容器。(注意:请务必使用云服务商的防火墙,仅对你的应用服务器 IP 开放 5432 端口,避免数据库暴露在公网)
     services: db: image: postgres:15 restart: always env_file: - .env ports: - "5432:5432" volumes: - pgdata:/var/lib/postgresql/data - ./postgresql.conf:/etc/postgresql/postgresql.conf command: postgres -c config_file=/etc/postgresql/postgresql.conf healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] interval: 10s timeout: 5s retries: 5 logging: driver: "json-file" options: max-size: "10m" max-file: "3" volumes: pgdata: 
    # postgresql.conf listen_addresses = '*' max_cOnnections= 100 shared_buffers = 512MB effective_cache_size = 1536MB maintenance_work_mem = 128MB checkpoint_completion_target = 0.9 wal_buffers = 16MB default_statistics_target = 100 random_page_cost = 1.1 effective_io_cOncurrency= 200 work_mem = 18724kB huge_pages = off min_wal_size = 1GB max_wal_size = 4GB log_destination = 'stderr' logging_collector = off log_min_duration_statement = 200 log_line_prefix = '%m [%p]: [%l-1] user=%u,db=%d,client=%h ' log_statement = 'ddl' 
    1. 迁移数据
     #!/bin/bash # !!! 重要:请填写你的 Supabase 项目信息 !!! SUPABASE_PROJECT_REF="YOUR_SUPABASE_PROJECT_REF" # 在 Supabase 项目设置的 URL 中可以找到,例如 xyz.supabase.co 中的 xyz SUPABASE_HOST="db.${SUPABASE_PROJECT_REF}.supabase.co" SUPABASE_PASSWORD="YOUR_SUPABASE_DB_PASSWORD" # 在 Supabase 项目的 Database -> Password 中找到 # 你为新数据库设置的密码 LOCAL_POSTGRES_PASSWORD="YOUR_NEW_SUPER_STRONG_PASSWORD" SCHEMAS_TO_DUMP="public" DUMP_FILE="data_dump.sql" info() { echo -e "\033[0;32m[INFO]\033[0m $1" } warn() { echo -e "\033[0;33m[WARN]\033[0m $1" } error() { echo -e "\033[0;31m[ERROR]\033[0m $1" exit 1 } set -e set -o pipefail if [ "$SUPABASE_PROJECT_REF" == "YOUR_SUPABASE_PROJECT_REF" ] || [ "$SUPABASE_PASSWORD" == "YOUR_SUPABASE_DB_PASSWORD" ] || [ "$LOCAL_POSTGRES_PASSWORD" == "YOUR_NEW_SUPER_STRONG_PASSWORD" ]; then error "请先在脚本中填写 SUPABASE_PROJECT_REF, SUPABASE_PASSWORD, 和 LOCAL_POSTGRES_PASSWORD 变量!" fi info "检查并安装 postgresql-client (包含 pg_dump 和 psql)..." if ! command -v pg_dump &> /dev/null; then sudo apt-get update && sudo apt-get install -y postgresql-client info "postgresql-client 安装完成。" else info "postgresql-client 已安装。" fi info "正在从 Supabase 数据库导出数据..." info "主机: $SUPABASE_HOST" SCHEMA_ARGS="" for s in $SCHEMAS_TO_DUMP; do SCHEMA_ARGS+="--schema=$s " done export PGPASSWORD=$SUPABASE_PASSWORD pg_dump \ --host="$SUPABASE_HOST" \ --port=5432 \ --username="postgres" \ --dbname="postgres" \ $SCHEMA_ARGS \ --no-owner \ --no-privileges \ --format=plain \ --file="$DUMP_FILE" unset PGPASSWORD if [ -f "$DUMP_FILE" ]; then info "数据成功导出到 $DUMP_FILE" else error "数据导出失败!" fi info "正在将数据导入到新的本地 PostgreSQL 实例..." export PGPASSWORD=$LOCAL_POSTGRES_PASSWORD psql \ --host=localhost \ --port=5432 \ --username=postgres \ --dbname=db \ --file="$DUMP_FILE" unset PGPASSWORD info "数据导入完成。" info "进行简单验证..." export PGPASSWORD=$LOCAL_POSTGRES_PASSWORD TABLE_COUNT=$(psql --host=localhost --port=5432 --username=postgres --dbname=db --tuples-only -c "SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public';") unset PGPASSWORD info "验证完成。Public schema 中的表数量为: $(echo $TABLE_COUNT | xargs)" 

    部署 nextjs 应用

    1. nextjs 容器镜像 (Dockerfile)
     FROM node:20-alpine AS base # Install dependencies only when needed FROM base AS deps # Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. RUN apk add --no-cache libc6-compat WORKDIR /app # Install dependencies based on the preferred package manager COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ # Copy prisma schema for postinstall script COPY prisma ./prisma RUN \ if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ elif [ -f package-lockjson ]; then npm ci; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ else echo "Lockfile not found." && exit 1; \ fi # Rebuild the source code only when needed FROM base AS builder WORKDIR /app COPY --from=deps /app/node_modules ./node_modules COPY . . # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. # ENV NEXT_TELEMETRY_DISABLED=1 RUN \ if [ -f yarn.lock ]; then yarn run build; \ elif [ -f package-lock.json ]; then npm run build; \ elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ else echo "Lockfile not found." && exit 1; \ fi # Production image, copy all the files and run next FROM base AS runner WORKDIR /app ENV NODE_ENV=production # Uncomment the following line in case you want to disable telemetry during runtime. # ENV NEXT_TELEMETRY_DISABLED=1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=builder /app/public ./public # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static USER nextjs EXPOSE 3000 ENV PORT=3000 # server.js is created by next build from the standalone output # https://nextjs.org/docs/pages/api-reference/config/next-config-js/output ENV HOSTNAME="0.0.0.0" CMD ["node", "server.js"] 
    1. next.config.js 配置
     module.exports = { output: "standalone", }; 
    1. Cloudflare 生成 Origin Certificate 并下载, 然后配置 nginx 证书
    worker_processes auto; events { worker_connections 1024; } http { upstream nextjs_server { server nextjs-app:3000; } server { listen 80; server_name domain.com; # 强制跳转到 https if ($http_x_forwarded_proto != 'https') { return 301 https://$host$request_uri; } location / { proxy_pass http://nextjs_server; proxy_set_header Host $host; } } server { listen 443 ssl http2; server_name domain.com; ssl_certificate /etc/nginx/certs/domain.com.pem; ssl_certificate_key /etc/nginx/certs/domain.com.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; location / { proxy_pass http://nextjs_server; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $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; proxy_cache_bypass $http_upgrade; } location /_next/static { proxy_cache_valid 200 302 1y; proxy_pass http://nextjs_server; } } } 
    1. 容器编排配置 (docker-compose.yml)
    services: nextjs-app: build: context: ../ dockerfile: Dockerfile image: nextjs-app:latest container_name: nextjs-app restart: always env_file: - ../.env networks: - app-network nginx: image: nginx:stable-alpine container_name: nextjs-nginx restart: always ports: - "80:80" - "443:443" volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/certs:/etc/nginx/certs:ro # Cloudflare 生成的证书 depends_on: - nextjs-app networks: - app-network networks: app-network: driver: bridge 

    修改 Cloudflare DNS

    1. 登录你的 Cloudflare 账户,进入 domain.com 域名的 DNS 设置页面。
    2. 添加或修改 A 记录,名称为 domain.com(或 @),内容为你部署 Next.js 应用的腾讯云服务器公网 IP 地址
    3. 确保“代理状态 (Proxy status)”为“已代理 (Proxied)”,即云朵图标为橙色。
    4. 导航到 "SSL/TLS" -> "概述 (Overview)" 页面,将 SSL/TLS 加密模式设置为 "Full (Strict)"(完全-严格)。这是最安全的模式,因为它能确保从浏览器到 Cloudflare 以及从 Cloudflare 到你的服务器的全程流量都经过加密验证。
    9 条回复    2025-07-06 11:03:43 +08:00
    defunct9
        1
    defunct9  
       150 天前 via iPhone
    avenger
        2
    avenger  
       150 天前
    速度怎么样?
    迁过来要不要备案?
    yekk
        3
    yekk  
    OP
       150 天前 via iPhone
    @avenger 新加坡腾讯云
    yekk
        4
    yekk  
    OP
       150 天前 via iPhone
    @avenger 都 cloudflare 了,跟备案就没关系了
    ans
        5
    ans  
       150 天前
    auth 迁了没,自建好搞吗
    yekk
        6
    yekk  
    OP
       149 天前 via iPhone
    @ans 我用的 better auth
    fancypanda
        7
    fancypanda  
       148 天前
    其实/_next/static 这个可以不用单独配置的,cloudflare 是根据后缀名来决定是否缓存的
    yekk
        8
    yekk  
    OP
       148 天前 via iPhone
    @fancypanda 是的吧,你可以看下 Response Header Cf-Cache-Status 确认。
    cutchop
        9
    cutchop  
       128 天前
    为什么自建 postgres 而不是 supabase 呢? supabase 不是可以自托管吗?请指教。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5303 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 08:53 PVG 16:53 LAX 00:53 JFK 03:53
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86