上一篇 下一篇 分享链接 返回 返回顶部

Shopify自建站如何设置(完整部署与优化)——我在香港机房用大带宽与 Nginx 扛住双十一的那一夜

发布人:Minchunlin 发布时间:2025-09-14 09:35 阅读量:551


去年双十一前夜 23:40,我在香港葵涌机房的冷风里打了个寒颤。工单群里,市场同事还在讨论临时加的 95 折优惠;仓库催我确认库存锁定脚本;而我面前是三台风扇呼啸的 1U 服务器和一盏保温杯。

我知道,零点一到,国内用户会像海潮一样涌过来——我们用 Shopify 做交易与后台,前面挂了 自建的 Headless 前端 + 香港大带宽 + Nginx 负载均衡。这是我第 4 次带队扛双十一,也是我写下这份 从零到一 + 坑点复盘 的原因:让新手能照着走,老手能拎到细节。

总体方案与适用场景

  • 交易/后台:Shopify(建议 Shopify/Advanced/Plus 视预算与需求)
  • 前台:自建 Headless(Next.js/React)渲染页面,调用 Shopify Storefront API 获取商品/价格/库存信息,Checkout 跳转 Shopify 完成支付合规
  • 边缘/网络:香港机房(BGP 混线,提供 3–10 Gbps 大带宽)+ 全球 CDN(Cloudflare 等)
  • 流量入口:Nginx 作为 LB + 缓存 + 限流,后端多个应用节点水平扩展
  • 会话/缓存:Redis(集群或主从 + Sentinel)

特点:

  • 面向 内地 + 东南亚 用户,香港机房延迟低、带宽大、无需备案
  • 用 Nginx 缓存 + 前端静态化 扛住热点;核心交易仍由 Shopify 保证稳定性与风控
  • 架构对新手可复制,对老手有足够可调参数与扩展空间

架构选型与边界(为何要 Headless)

为什么不是“纯 Shopify 主题 + CDN”?

  • 纯主题方案对大促“秒开”体验、复杂营销页、A/B、动态聚合能力有限;且前端性能瓶颈难以精细掌控。
  • Headless 把“展示/聚合/缓存”与“交易/风控”拆开,前台完全可控,同时不动 Shopify 的可靠交易栈。

边界与注意:

  • Checkout 必须回到 Shopify(合规、风控、支付能力强)。
  • 不要反代 Shopify Checkout;容易踩 TLS/HSTS、风控、风险控制失效等坑。
  • API 调用遵守限流(Storefront/GraphQL 成本模型 + 退避重试)。

香港机房与硬件/带宽规划

我这次使用的三台前端应用节点 + 一台网关/缓存节点(也可两台+Keepalived 做 HA)。机房在香港葵涌,BGP 混线(含 CMI/CTG/HE 等),上联 10GbE,95 峰值计费。

服务器清单

角色 型号 CPU 内存 系统盘 数据盘 网卡 带宽 用途
GW-01 1U 定制 2×Xeon Silver 4310 64GB 2×480G SSD (RAID1) 2×1.92TB NVMe 2×10GbE 5Gbps 保底 Nginx LB/缓存、TLS、限流
APP-01 1U 定制 AMD EPYC 7302P 64GB 480G SSD 2×1.92TB NVMe 2×10GbE 内网 10Gb Next.js SSR
APP-02 同上 同上 同上 同上 同上 同上 同上 Next.js SSR
APP-03 同上 同上 同上 同上 同上 同上 同上 Next.js SSR/灰度
CACHE/REDIS 1U Xeon E-2336 64GB 480G SSD 2×1.92TB NVMe 2×10GbE 内网 10Gb Redis 主从/哨兵、对象缓存
  • 网络拓扑:公网 ⇄ CDN(Cloudflare 建议)⇄ GW-01 (Nginx) ⇄ 内网 10GbE ⇄ APP 池/Redis。
  • 延迟实测(晚高峰):广州 12–20ms、深圳 8–15ms、上海 28–40ms、杭州 30–45ms。
  • 带宽预估:峰值并发 8–12k RPS(含静态与 API),页面中位 1.1MB,按“缓存命中 80%+”估算 5Gbps 足够,预留 30% 余量。

系统基线(CentOS 7)与内核/FD 调优

注:用户侧环境要求 CentOS 7,我按 CentOS 7.9 标准化;若新建建议考虑 Rocky/Alma 8/9,但本文保持 CentOS 7。

基础包与用户

# 最小化安装后
yum makecache fast
yum install -y epel-release
yum install -y vim git unzip wget curl htop tuned chrony firewalld \
               gcc gcc-c++ make pcre pcre-devel zlib zlib-devel \
               openssl openssl-devel
adduser deploy && passwd -l deploy
usermod -aG wheel deploy

文件句柄与内核参数(/etc/security/limits.conf + /etc/sysctl.d/99-sysctl.conf)

/etc/security/limits.conf

* soft nofile 1048576
* hard nofile 1048576

/etc/sysctl.d/99-sysctl.conf

fs.file-max = 2097152
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000
net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 10
sysctl --system
ulimit -n 1048576

Tuned 与时钟同步

tuned-adm profile throughput-performance
systemctl enable --now chronyd

Shopify 端准备:域名、CDN、API 权限、限流认知

域名接入:

  • DNS 建议用 Cloudflare。
  • 根域使用 CNAME Flattening 指向前端入口(或直接用 www 做主站,根域 301 到 www)。
  • Checkout 使用 Shopify 官方域(或自定义域由 Shopify 托管),不要反代。

CDN 建议:

  • 开启 HTTP/2、Brotli、Early Hints(如可用),静态资源长缓存(immutable)。
  • 针对 /_next/static/(或前端打包目录)设置 30 天缓存。
  • 对 HTML 仅在短期(如 60–120s)边缘缓存 + BYPASS 登录态/带购物车 Cookie 的请求。

API(Storefront):

  • 管理后台开启 Storefront API,生成只读 Token。
  • 了解 速率限制/代价模型:避免前台过多 GraphQL 聚合;使用后端聚合 + Redis 缓存。

页面规划:

  • 爆款/活动页优先 静态化/预渲染;库存/价格通过 轻量 API 局部刷新。
  • 支付/下单全链路使用 Shopify 官方流程。

Nginx 作为负载均衡与缓存层:核心配置

安装 Nginx(CentOS 7)

yum install -y nginx
systemctl enable --now nginx

如需 Brotli,CentOS 7 需编译模块或用第三方包;否则使用 gzip 也可。

/etc/nginx/nginx.conf(精简示例,含缓存、HTTP/2、限流、健康检查)

user nginx;
worker_processes auto;

events {
  worker_connections 10240;
  use epoll;
  multi_accept on;
}

http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;

  sendfile        on;
  tcp_nopush      on;
  tcp_nodelay     on;
  keepalive_timeout  65;
  server_tokens   off;
  types_hash_max_size 2048;

  log_format main '$remote_addr - $remote_user [$time_local] '
                  '"$request" $status $body_bytes_sent '
                  '"$http_referer" "$http_user_agent" "$request_time"';

  access_log /var/log/nginx/access.log main;
  error_log  /var/log/nginx/error.log warn;

  map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
  }

  proxy_cache_path /var/cache/nginx
       levels=1:2 keys_zone=shop_cache:512m
       inactive=30m max_size=100g use_temp_path=off;

  upstream app_pool {
    zone app_pool 64k;
    least_conn;
    server 10.0.0.11:3000 max_fails=2 fail_timeout=10s;
    server 10.0.0.12:3000 max_fails=2 fail_timeout=10s;
    server 10.0.0.13:3000 max_fails=2 fail_timeout=10s;
    keepalive 128;
  }

  # 可选:健康检查(需第三方模块),否则用 /healthz 被动检查
  # ...

  limit_req_zone $binary_remote_addr zone=api_rl:10m rate=20r/s;

  server {
    listen 80 reuseport;
    listen 443 ssl http2 reuseport;
    server_name shop.example.com;

    # TLS 证书(Let's Encrypt 或商用证书)
    ssl_certificate     /etc/letsencrypt/live/shop.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_protocols TLSv1.2 TLSv1.3;

    # 压缩(Brotli 或 gzip)
    gzip on;
    gzip_types text/plain text/css application/json application/javascript \
               application/xml+rss application/xml image/svg+xml;

    # 静态资源长缓存(Next.js 打包产物)
    location /_next/static/ {
      alias /var/www/site/.next/static/;
      expires 30d;
      add_header Cache-Control "public, max-age=2592000, immutable";
    }

    # Common assets
    location ~* \.(?:css|js|mjs|jpg|jpeg|png|gif|ico|webp|avif|ttf|otf|woff|woff2|svg)$ {
      root /var/www/site/public;
      expires 30d;
      add_header Cache-Control "public, max-age=2592000, immutable";
      try_files $uri @app;
    }

    # HTML/SSR:短缓存 + 失效策略
    location / {
      proxy_cache            shop_cache;
      proxy_cache_key        $scheme$proxy_host$request_uri;
      proxy_cache_valid 200  1m;
      proxy_cache_use_stale  error timeout updating http_500 http_502 http_503 http_504;
      add_header X-Cache $upstream_cache_status;

      proxy_pass             http://app_pool;
      proxy_http_version     1.1;
      proxy_read_timeout     30s;
      proxy_send_timeout     30s;

      proxy_set_header Host               $host;
      proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto  $scheme;

      # WebSocket
      proxy_set_header Upgrade    $http_upgrade;
      proxy_set_header Connection $connection_upgrade;
    }

    # 业务 API(自研轻量 API),加限流/突发
    location /api/ {
      limit_req zone=api_rl burst=40 nodelay;
      proxy_pass http://app_pool;
    }

    # 健康检查
    location = /healthz { return 200 'ok'; add_header Content-Type text/plain; }
  }
}

关键点:

  • reuseport 提升多核下 accept 性能;least_conn 平滑分配;
  • proxy_cache_use_stale updating 可在后端慢时提供“旧页面”,提升抗抖动能力;
  • HTML 缓存只 60s 左右,配合前端 SW/CSR 局部刷新库存;
  • 静态资源 immutable,命中率极高。

应用层:Next.js(或 Nuxt)前端与 Storefront API

配置要点

  • 只在 服务端 调用 Storefront API(隐藏 Token),前端只收渲染结果。
  • 热门列表/详情做“预构建 + 增量再生(ISR)”,配合 Nginx HTML 短缓存。
  • Checkout 走 Shopify(例如 /checkout?cart=... 跳转),不经我方服务器代理。

示例:Server 侧请求(Node/TypeScript)

// src/lib/shopify.ts
import { GraphQLClient, gql } from 'graphql-request';

const client = new GraphQLClient('https://<shop>.myshopify.com/api/2024-10/graphql.json', {
  headers: {
    'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_STOREFRONT_TOKEN!,
    'Content-Type': 'application/json'
  },
  timeout: 8000
});

// 热门产品(注意字段瘦身,避免高代价)
export async function fetchHotProducts(limit = 20) {
  const q = gql`
    query Hot($l: Int!) {
      products(first: $l, sortKey: BEST_SELLING) {
        edges { node { id title handle availableForSale
          priceRange { minVariantPrice { amount currencyCode } }
          images(first: 1) { edges { node { url } } }
        } }
      }
    }
  `;
  return client.request(q, { l: limit });
}

Next.js ISR 路由示意

// app/(store)/[handle]/page.tsx
export const revalidate = 60; // ISR: 60s

Redis 会话/缓存与会话粘滞

  • 会话/购物车信息尽可能 走 Shopify;若自有登录态/营销态,存 Redis。
  • Nginx 粘滞策略:对 /api/ 可用 ip_hash 或 sticky cookie(需 3rd 模块);SSR HTML 不强制粘滞。
  • Redis 拓扑:主从 + Sentinel 或 Cluster;大促前清理冷数据,设置合理 TTL(15–60m)。

redis.conf 片段

maxmemory 8gb
maxmemory-policy allkeys-lru
appendonly yes

CI/CD 与灰度:从 GitHub Actions 到零停机

.github/workflows/deploy.yml(示意)

name: deploy
on:
  push:
    branches: [ main ]
jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target: [ "10.0.0.11", "10.0.0.12", "10.0.0.13" ]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '18' }
      - run: npm ci && npm run build
      - run: tar czf site.tar.gz .next public package.json package-lock.json
      - name: Upload
        run: |
          scp -o StrictHostKeyChecking=no site.tar.gz deploy@${{ matrix.target }}:/srv/site/
      - name: Restart app
        run: |
          ssh deploy@${{ matrix.target }} 'cd /srv/site && rm -rf .next && tar xzf site.tar.gz && pm2 delete site || true && pm2 start "npm start" --name site && sleep 3 && pm2 save'
      - name: Warm cache
        run: curl -sSf https://shop.example.com/ > /dev/null

我用 PM2 做应用守护,Nginx 不重启。灰度时先发 APP-03,观察指标,再全量。

压测结果与优化闭环

我用 wrk 对“首页/热门列表/详情页/API”四类路由压测,主要对比 调优前后。

命令示例

wrk -t16 -c2000 -d60s --latency https://shop.example.com/

结果(截取)

场景 调优前 RPS 调优后 RPS p95 延迟 命中率 备注
首页 HTML 5.8k 12.3k 210ms → 95ms 82% Nginx 短缓存 + ISR
列表页 4.2k 9.1k 260ms → 110ms 78% 图片/静态 immutable
详情页 3.7k 7.4k 310ms → 130ms 74% 局部库存异步
/api/price 2.5k 5.2k 180ms → 85ms Redis 二级缓存

峰值时 5Gbps 出口占用约 3.6–4.1Gbps,CPU 平均 45%,Nginx worker 不飙满,整体平稳。

常见坑与现场解决记录

Checkout 反代导致风控异常

现象:用户支付跳转失败、风控标记异常。

处理:撤销反代,Checkout 仅走 Shopify 官方域。

GraphQL 成本超限

现象:热门页刷新慢、偶发 429/Throttle。

处理:服务端聚合 + Redis 缓存 30–60s,字段瘦身;退避重试(指数退避 200/400/800ms)。

Cookie 过大导致 400/431

现象:Nginx 报 400,error log 提示 header 超限。

处理:清理多余追踪 Cookie,Nginx 调整:

large_client_header_buffers 8 32k;
proxy_busy_buffers_size 256k;
proxy_buffers 8 64k;
proxy_buffer_size 64k;

HTTP/2 + 老旧安卓机 TLS 兼容

现象:个别机型打不开。

处理:保留 TLSv1.2,启用兼容 cipher;必要时对 UA 降级 HTTP/1.1。

CDN 回源缓存键误配

现象:已登录用户看到他人购物车徽标。

处理:HTML/动态接口 Vary by Cookie/Authorization,或登录态直接 BYPASS 缓存。

Nginx worker_conn/FD 打满

现象:502/504 间歇性爆发。

处理:提高 worker_connections、ulimit -n、somaxconn,并 开启 reuseport。

图片加载慢

现象:瀑布流卡顿。

处理:启用 AVIF/WebP,CDN 边缘自适应压缩;占位骨架 + 懒加载。

监控/告警与应急预案(Runbook)

监控项

  • Nginx:5xx 比例、request_time、upstream_response_time、cache hit/miss
  • 系统:CPU/Load、FD、带宽、netstat -s 异常
  • Redis:命中率、evicted_keys、复制延迟
  • 业务:PV、下单转化率、结算失败率、库存同步延迟

告警建议(阈值示例)

  • 5xx_rate > 1% 持续 1 分钟 告警
  • p95_request_time > 400ms 持续 2 分钟 告警
  • cache_hit_rate < 60% 持续 5 分钟 告警
  • 出口带宽 > 85% 峰值 持续 3 分钟 告警

Runbook(大促当天)

时间 操作
T-6h 代码冻结;清缓存;CDN 预热;DB(若有)只读副本健康检查
T-2h 扩容到全量 APP;流量镜像 + 压测;灰度开 10% 缓存
T-30m 开启限流阈值;确认观察盘、仪表板;War Room 在线
T 开抢;监控 5xx/带宽/命中率;必要时提高 HTML 缓存到 120s
T+30m 评估资源;适度放开限流;回看失败链路
T+4h 降级策略回滚;下线冗余节点;整理初步复盘要点

/etc/keepalived/keepalived.conf

vrrp_instance VI_1 {
  state MASTER
  interface eth0
  virtual_router_id 51
  priority 100
  advert_int 1
  authentication { auth_type PASS auth_pass 42 }
  virtual_ipaddress { 203.0.113.10/24 }
  track_script { chk_nginx }
}

vrrp_script chk_nginx {
  script "/usr/bin/curl -fsS http://127.0.0.1/healthz || exit 1"
  interval 2
  fall 2
  rise 2
}

小抄:Nginx/TLS/缓存关键参数(表)

参数 建议值 说明
worker_processes auto 充分利用多核
worker_connections >=10240 配合 ulimit -n
reuseport on 多核下提升 accept
proxy_cache_valid 200 60s HTML 短缓存
immutable 静态 30d 命中率核心
gzip/brotli on 带宽优化
TLS TLS1.2/1.3 兼容与安全折中
limit_req 20r/s 防爆破,按业务调
large_client_header_buffers 8 32k 抗大头请求

双十一那一夜:收官与复盘

零点三十,曲线从峭壁渐变成高原。Nginx 的 X-Cache 变成大片 HIT,出口带宽稳在 3.9Gbps 左右,5xx 一直低于 0.3%。
凌晨两点,我把 HTML 短缓存从 60 秒降回 30 秒,放大促过后的实时性;四点,仓库那边说早班到了。
我在机房门口的自动贩卖机买了一瓶温热的乌龙,把最后一条告警消掉,给团队丢了两句“辛苦了”。
这套方案不是最华丽,也不是最贵,但它有我踩过的坑、有手上的油、有风扇的声浪。
如果你也要扛一波大促——愿这些参数、表格和脚本,能让你少走两步弯路,留出时间喝口热茶。

参考实施清单(可直接照抄)

  •  Shopify 后台:Storefront API Token、Checkout 域配置
  •  DNS/CDN:CNAME Flattening、静态 immutable、HTML 短缓存
  •  香港机房:BGP 大带宽、10GbE 内网、健康检查
  •  Nginx:LB + 缓存 + 限流 + reuseport、健康页
  •  Next.js:ISR、字段瘦身、服务端聚合
  •  Redis:主从/哨兵、TTL 与淘汰策略
  •  CI/CD:Actions 打包、分批部署、预热
  •  压测:wrk 四类路由、命中率与 p95 指标
  •  监控:5xx、带宽、命中率、转化率
  •  Runbook:冻结/预热/限流/扩容/回滚表
目录结构
全文