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

去年双十一前夜 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:冻结/预热/限流/扩容/回滚表