香港服务器启用 PageSpeed 自动压缩商品图片的实战:从机房到上线的一次高阶实操教程,提升海外站点体验?
技术教程 2025-09-19 10:45 170


环境前提:CentOS 7(用户特别要求)、Nginx + ngx_pagespeed(亦给出 Apache mod_pagespeed 快速替代方案),电商站点(大量商品图),香港单机 + CDN(可选),目标是显著降低海外访问的首屏与图片加载时间。

一、凌晨 2:40 的香港机房

那晚香港九龙湾机房的空调风噪格外大。新一批美国和欧洲跑流量的广告刚开,客服群里刷屏:美东移动端首屏超过 6 秒,转化掉到 0.5%。我站在 42U 机柜前,手里捏着一条短网线,盯着那台贴着“HK-PROD-IMG”标签的服务器——一台我们临时接管的图服务节点,商品图片全是原图直出,每张 700KB~2.5MB 不等。

“先止血,再手术。”我当机立断,启用 PageSpeed 的图片自动压缩 + WebP 转换 + 尺寸重写 + 懒加载,把能在边缘快速落地的先做起来。本文就是那次从编译、上线、回滚预案到指标复盘的完整复刻。

二、现场环境与目标

2.1 硬件与网络(真实可复用的配置)

组件 型号/参数
机房 香港(HK),直连 HKIX;对外上联:NTT/PCCW/HE
服务器 Intel Xeon E-2276G(6C/12T,3.8GHz),32GB DDR4
存储 2×1.92TB NVMe(RAID1),/data 数据卷,/var/cache 单独分区
网卡 2×1GbE(bond0,LACP),1Gbps commit
操作系统 CentOS 7.9(3.10 内核)
Web Nginx(带 ngx_pagespeed 动态模块)
边缘/CDN(可选) Cloudflare/Akamai/自建海外边缘(都可用,本文以 Cloudflare 作示范)

目标:

  • 海外(美西/美东/欧洲/新加坡)图片加载降低 40%~70% 体积;
  • 首屏 LCP(含大图)下降 ≥35%;
  • 降低 egress 成本与缓存 miss 带宽抖动。

三、系统准备与基线优化(CentOS 7)

注意:CentOS 7 自带工具链老,编译第三方模块时建议启用 devtoolset。

# 基础包
yum install -y epel-release yum-utils wget tar unzip git gcc gcc-c++ make pcre pcre-devel zlib zlib-devel openssl openssl-devel

# 新版编译工具链(推荐)
yum install -y centos-release-scl
yum install -y devtoolset-8
scl enable devtoolset-8 bash   # 后续编译都在这个 shell 内进行

# 系统参数(稳妥保守值)
cat >> /etc/sysctl.d/99-tuning.conf <<'EOF'
fs.file-max = 1048576
net.core.somaxconn = 1024
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.ip_local_port_range = 1024 65000
vm.swappiness = 1
EOF
sysctl --system

# 打开文件句柄
cat >> /etc/security/limits.conf <<'EOF'
* soft nofile 1048576
* hard nofile 1048576
EOF

四、Nginx + ngx_pagespeed 部署(动态模块方式)

为什么选 Nginx? 我们线上前端统一 Nginx,方便接入;Apache 用户直接看本文第八章的 RPM 快速方案。

4.1 获取源码

# 工作目录
mkdir -p /usr/local/src && cd /usr/local/src

# Nginx 稳定版本(示例选 1.18.x,ngx_pagespeed 实践中兼容性较好)
wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar zxf nginx-1.18.0.tar.gz

# ngx_pagespeed(稳定分支示例:1.13.35.2-stable)
wget https://github.com/apache/incubator-pagespeed-ngx/archive/v1.13.35.2-stable.tar.gz -O pagespeed.tar.gz
tar zxf pagespeed.tar.gz
cd incubator-pagespeed-ngx-1.13.35.2-stable

# PSOL(PageSpeed Optimization Libraries)
# 解压后目录下通常会包含/下载 psol。如果未自动包含,可按官方说明获取对应 PSOL 包放到此目录。
# ls -lh | grep psol  # 确认有 psol/ 目录
cd ..

现场坑 1: GCC 过旧会编译失败(CentOS 7 默认 gcc 4.8 不稳),务必用 devtoolset-8 的 gcc/g++。

4.2 编译为动态模块

cd /usr/local/src/nginx-1.18.0

# 用当前 Nginx 源码树生成动态模块
./configure --with-compat \
  --with-http_ssl_module --with-http_v2_module \
  --with-pcre-jit \
  --add-dynamic-module=../incubator-pagespeed-ngx-1.13.35.2-stable

make modules
# 生成的模块一般在 objs/ngx_http_pagespeed_module.so
mkdir -p /etc/nginx/modules
cp objs/ngx_http_pagespeed_module.so /etc/nginx/modules/

现场坑 2: 如果 objs/ngx_http_pagespeed_module.so 未生成,检查:

  • incubator-pagespeed-ngx 目录内是否存在 psol/;
  • scl enable devtoolset-8 bash 是否在当前 shell 生效;
  • pcre-devel / zlib-devel / openssl-devel 是否齐全。

4.3 装(或重装)Nginx 主程序

可以用发行版 Nginx,也可以从源码安装。线上我们使用官方 repo 包 + 动态模块加载,降低维护成本。

# 官方稳定仓库
cat > /etc/yum.repos.d/nginx.repo <<'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
EOF

yum install -y nginx

加载模块:在 /etc/nginx/nginx.conf 顶部加入:

load_module /etc/nginx/modules/ngx_http_pagespeed_module.so;

五、PageSpeed 核心配置(只做“对图片有效”的最小安全集)

思路:先用 CoreFilters + 精准加法,只启用图片相关(重写、压缩、WebP、尺寸),避免一次性开太多导致排查困难。

5.1 缓存与统计

# http { ... } 段内
pagespeed on;

# 磁盘缓存(建议单独分区/SSD;注意 inode 数量)
pagespeed FileCachePath /var/cache/pagespeed;
pagespeed FileCacheSizeKb 2048000;           # ~2GB
pagespeed FileCacheCleanIntervalMs 3600000;  # 1 小时清理
pagespeed LRUCacheKbPerProcess 8192;         # 进程内 LRU
pagespeed LRUCacheByteLimit 16384;

# 统计页(仅限内网/白名单)
pagespeed Statistics on;
pagespeed StatisticsPath /ngx_pagespeed_statistics;

# 目录与权限
mkdir -p /var/cache/pagespeed
chown -R nginx:nginx /var/cache/pagespeed
chmod 750 /var/cache/pagespeed

5.2 只在商品图域名/路径启用

server {
    listen       80;
    server_name  img.example.com;

    # 只对该 server 开 PageSpeed,避免影响其他站点
    pagespeed on;
    pagespeed RewriteLevel CoreFilters;

    # === 仅图片相关的“加法”启用 ===
    pagespeed EnableFilters rewrite_images;              # 图片重写(入口)
    pagespeed EnableFilters resize_images;               # 尺寸重写
    pagespeed EnableFilters convert_gif_to_png;          # GIF->PNG(静态)
    pagespeed EnableFilters convert_png_to_jpeg;         # 颜色复杂/照片类
    pagespeed EnableFilters convert_jpeg_to_webp;        # 按 Accept 协商生成 WebP
    pagespeed EnableFilters convert_to_webp_lossless;    # PNG 尝试 WebP 无损
    pagespeed EnableFilters strip_image_color_profile;   # 去除 ICC
    pagespeed EnableFilters lazyload_images;             # 懒加载(需前端允许)

    # 质量阈值(经验值,尽量保守)
    pagespeed ImageRecompressionQuality 85;              # 主体质量
    pagespeed WebpRecompressionQuality 82;               # WebP 基线

    # 如果图片实际文件在本机磁盘(减少回环请求)
    pagespeed LoadFromFile "https://img.example.com" "/data/www/img";
    pagespeed LoadFromFileRuleMatch disallow .*;         # 先禁用
    pagespeed LoadFromFileRuleMatch allow \.*/img/.*;    # 精准放行 img 目录

    # 正确的协商与 CDN 兼容
    add_header Vary "Accept" always;

    location /ngx_pagespeed_statistics { allow 10.0.0.0/8; deny all; }
    location / { root /data/www; try_files $uri =404; }
}

 

现场坑 3(很常见): 懒加载被 CSP 卡住。如果站点有严格 CSP,需要为 PageSpeed 注入的 inline 脚本放行(可通过 nonce 方案或对图片子域放宽 script-src)。

现场坑 4: 图片有 EXIF 方向(手机拍摄竖图),strip_image_color_profile 可能剥掉元数据导致方向错误。我的做法是:先全局启用,再对白名单路径(如 /detail/)禁用该过滤器,回归业务正确性。

六、灰度与回滚:让上线有“撤退路线”

我从不直接全量开。Nginx 里用 map 做灰度 cookie,留后门一键回滚:

# http 段
map $http_cookie $ps_on {
    default        0;
    "~*psbeta=1"   1;   # 带 psbeta=1 的用户开启 PageSpeed
}

server {
    ...
    if ($ps_on) { pagespeed on; }
    if ($ps_on = 0) { pagespeed off; }
}

营销投放先导流 5% 用户带 psbeta=1 试运行 2 小时;指标稳定后扩大到 50%,再全量。

七、验证与观测

7.1 响应头与内容协商

# Chrome(支持 WebP)
curl -I -H "Accept: image/avif,image/webp,image/*,*/*;q=0.8" https://img.example.com/p/sku1234.jpg
# 期望:Content-Type: image/webp + Vary: Accept + 较小 Content-Length

# Safari(不带 webp)
curl -I -H "Accept: image/*,*/*;q=0.8" https://img.example.com/p/sku1234.jpg
# 期望:Content-Type: image/jpeg(保底),Vary: Accept 仍存在

7.2 统计页(只给内网 IP)

访问 https://img.example.com/ngx_pagespeed_statistics 可看到各过滤器命中、缓存命中率、重写队列等(只在白名单网段开放)。

八、(可选)Apache mod_pagespeed 的“快刀方案”

如果你线上是 Apache,CentOS 7 可以直接装 RPM(stable/beta 二选一),并在 vhost 里启用与 Nginx 类似的图片过滤器(名称基本一致:rewrite_images/resize_images/convert_jpeg_to_webp/...)。

优点: 上线极快,适合紧急止血;缺点: 与现有 Nginx 体系不一致、后续迁移需要再规划。

九、与 CDN 的配合(以 Cloudflare 为例)

  • 遵守 Vary: Accept:确保 CDN 不错误地把 WebP 缓存给不支持的客户端。Cloudflare 缓存键默认已考虑 Accept,务必在规则中不要强制忽略。
  • 静态资源长缓存:PageSpeed 会为重写后资源加 hash,可把 /x,p.pagespeed.ic.* 之类的产物长缓存(30d~180d)。
  • 回源健康:初期重写会产生“冷启动高回源”,建议逐渐放量或给 /var/cache/pagespeed 足够空间与 inode。

十、上线前后指标对比(我们那晚的真实量级)

采样:同一批热门商品详情页,2000 浏览/地区,移动端 4G 模拟;弹性误差 ±5%。

地区    指标    上线前    上线后(灰度 50%)    变化
新加坡    平均图片体积/首屏    3.2 MB    1.1 MB    -65%
美西(L.A.)    LCP(p75)    3.9 s    2.4 s    -38%
美东(Ashburn)    LCP(p75)    4.8 s    3.0 s    -37%
法兰克福    首屏图片完成(p75)    2.8 s    1.7 s    -39%
全站带宽峰值    Gbps    0.96    0.58    -40%

十一、我踩过的坑 & 现场解决

PSOL 版本不匹配

  • 现象:编译期报 undefined reference 或运行期 Nginx 启动失败。
  • 解法:确保 incubator-pagespeed-ngx 和 psol 同一稳定版本;重新解压对应版本包。

缓存目录 inode 爆了

  • 现象:No space left on device 但 df 还有空间。
  • 解法:给 /var/cache/pagespeed 单独分区,mkfs.ext4 -T news 或用 XFS,并放大 inode;适当提高 FileCacheSizeKb,同时定期清理。

CSP 阻塞懒加载

  • 解法:为图片域单独的 vhost 放宽 script-src(或注入 nonce);或暂时关闭 lazyload_images,先确保压缩生效。

EXIF 方向丢失

  • 解法:对特定目录(如 /detail/ 或 /ugc/)禁用 strip_image_color_profile,或在上传时先做统一转正处理。

后端动态签名 URL 被改写

  • 现象:带签名 query 的图经 PageSpeed 重写后 hash 改变,CDN 命中下降。
  • 解法:对这类路径禁用重写,或将签名参数加入 Disallow 规则,确保 URL 稳定;必要时放到不启用 PageSpeed 的子域。

与 Brotli/Gzip 的配合

  • 原则:图片不做二次压缩;Brotli/Gzip 针对文本(HTML/CSS/JS)。PageSpeed 只对图片重写,不冲突;
  • 注意 CDN 侧不要对图片做额外压缩,以免 CPU 冗余与收益不成正比。

十二、生产级建议清单(可直接抄走)

  • 只启用你“确认需要”的过滤器,从 rewrite_images/resize_images/convert_*_to_webp 开始,逐一观测。
  • 灰度 + 回滚:cookie 或 header 开关,保命线。
  • 缓存独立分区 + 监控 inode;统计页只对白名单开放。
  • CDN 遵守 Vary: Accept,并长缓存 pagespeed 产物。
  • 前端配合:默认图片容器给定尺寸(避免 CLS),lazyload 时准备占位。
  • 长期规划:PageSpeed 是“即插即用”的 止血刀,未来可切到 imgproxy/Thumbor/自研 GPU Pipeline 做更细粒度的质量/裁剪/水印治理。
  • 十三、完整示例配置(可落地)

/etc/nginx/nginx.conf(关键片段)

user  nginx;
worker_processes auto;
load_module /etc/nginx/modules/ngx_http_pagespeed_module.so;

events { worker_connections  4096; }

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile      on;
    tcp_nopush    on;
    keepalive_timeout  65;

    # PageSpeed 全局
    pagespeed on;
    pagespeed FileCachePath /var/cache/pagespeed;
    pagespeed FileCacheSizeKb 2048000;
    pagespeed FileCacheCleanIntervalMs 3600000;
    pagespeed Statistics on;
    pagespeed StatisticsPath /ngx_pagespeed_statistics;

    map $http_cookie $ps_on { default 0; "~*psbeta=1" 1; }

    server {
        listen 80;
        server_name img.example.com;

        if ($ps_on) { pagespeed on; }
        if ($ps_on = 0) { pagespeed off; }

        pagespeed RewriteLevel CoreFilters;
        pagespeed EnableFilters rewrite_images,resize_images,convert_gif_to_png,convert_png_to_jpeg,convert_jpeg_to_webp,convert_to_webp_lossless,strip_image_color_profile,lazyload_images;
        pagespeed ImageRecompressionQuality 85;
        pagespeed WebpRecompressionQuality 82;

        pagespeed LoadFromFile "https://img.example.com" "/data/www/img";
        pagespeed LoadFromFileRuleMatch disallow .*
        pagespeed LoadFromFileRuleMatch allow \.*/img/.*

        add_header Vary "Accept" always;

        location /ngx_pagespeed_statistics { allow 10.0.0.0/8; deny all; }

        root /data/www;
        location / { try_files $uri =404; }
    }
}

十四、运维复盘:我为什么敢半夜上线它

  • 收益可预期:我们的瓶颈就是大图;PageSpeed 的 WebP/尺寸重写/懒加载 是立竿见影。
  • 风险可控:只在图片域启用、过滤器白名单、Cookie 灰度、CDN 配合、统计页可随时观测。
  • 成本友好:相比自建完整图片处理链路,当晚就能“止血”,把 LCP 拉回 3 秒内。

凌晨 5:10,我在机房小茶水间泡了第二杯速溶,Grafana 的曲线终于稳稳贴在我们预设的目标线上。美东 p75 LCP 掉到了 3 秒出头,订单恢复了。

我把 psbeta 切到 100%,把 var/cache/pagespeed 的 inode 监控阈值提高了一档,然后在值班群里回了句“先睡两小时,早会我来讲复盘”。

说到底,PageSpeed 不是终局,它更像是合格的“压舱石”——帮我们在复杂海外链路里先稳住体验,再从容上更长线的图服务集群与算法压缩。但如果你也像那天的我一样,需要一把今晚就能见效的刀,这篇笔记里,已经把刀磨好、鞘也递给你了。