还没用上 Zstd?教你手动编译一个"满血版" OpenResty 1.27

19 阅读7分钟

难度: 中级

适合人群: 运维工程师、后端开发、DevOps

op-0.webp

在云原生时代,我们习惯了 docker pull openresty/openresty 一键拉起服务。但当你深入生产环境时,往往会发现官方或社区的通用镜像总差那么一点意思:

  • 体积臃肿:官方镜像动辄 100MB+,塞了一堆不需要的模块。
  • 功能缺失:想要 Facebook 开源的 Zstandard (zstd) 压缩算法?对不起,默认没有。
  • 安全隐患:默认 root 用户运行,容器一旦被穿透,宿主机岌岌可危。
  • 配置粗糙:默认配置从未针对高并发场景进行内核参数调优。
  • 版本暴露Server: openresty/1.x.x 直接告诉黑客你的版本号。

今天,修罗就带大家从源码开始(From Scratch) ,基于极致轻量的 Alpine Linux 3.23,构建一个集 Zstd 压缩、非 Root 运行、TCP 代理、多路复用 于一身的"满血版" OpenResty 1.27.1.2 镜像。

本文你将学到

技能点说明
Zstd 压缩配置比 Gzip 快 3-7 倍的现代压缩算法
非 Root 运行容器安全最佳实践
TCP/UDP 代理四层负载均衡,代理 MySQL/Redis
多阶段构建Docker 镜像瘦身到 25MB
生产级调优epoll、keepalive、buffer 优化
完整配置文件可直接复制使用的 nginx.conf

op-1.webp


01 为什么你需要 Zstd?

在 Nginx/OpenResty 的世界里,Gzip 统治了十几年。但现在,是时候引入 Zstd 了。

1.1 Zstd vs Gzip 性能对比

Zstd (Zstandard) 是 Facebook 开源的实时压缩算法:

指标Gzip (level 5)Zstd (level 3)提升
压缩速度50 MB/s350 MB/s7x
解压速度250 MB/s1000 MB/s4x
压缩率2.5:12.8:112%
CPU 占用显著降低

实测数据:在 API 网关场景下,开启 Zstd 后,响应时间降低 15-20%,带宽节省 10-15%。

1.2 浏览器支持情况

Chrome 123+  ✅ 支持
Firefox 126+ ✅ 支持
Safari 18+   ✅ 支持
Edge 123+    ✅ 支持

1.3 智能回退配置

我们在 nginx.conf 中配置智能回退策略——优先 Zstd,不支持则回退 Gzip:

# ============================================
# Zstd 压缩配置(需要编译 zstd-nginx-module)
# ============================================
zstd on;                    # 启用 zstd 压缩
zstd_comp_level 3;          # 压缩级别 1-22,推荐 3(平衡点)
zstd_min_length 256;        # 小于 256 字节不压缩(压缩反而变大)
​
# 指定哪些 MIME 类型需要压缩
zstd_types
    application/json              # API 响应
    application/javascript        # JS 文件
    application/xml               # XML 数据
    text/css                      # CSS 样式
    text/plain                    # 纯文本
    text/xml                      # XML 文本
    image/svg+xml;                # SVG 图片
​
# ============================================
# Gzip 压缩配置(兜底方案)
# ============================================
gzip on;                    # 启用 gzip
gzip_comp_level 5;          # 压缩级别 1-9,推荐 5
gzip_min_length 256;        # 最小压缩大小
gzip_vary on;               # 添加 Vary: Accept-Encoding 头
gzip_proxied any;           # 对所有代理请求都压缩
gzip_types                  # 压缩类型(与 zstd_types 保持一致)
    application/json
    application/javascript
    text/css
    text/plain;

工作原理:Nginx 会根据客户端 Accept-Encoding 头自动选择:

  • 客户端发送 Accept-Encoding: zstd, gzip → 使用 Zstd
  • 客户端发送 Accept-Encoding: gzip → 使用 Gzip
  • 都不支持 → 不压缩

02 安全第一:非 Root (Non-Root) 运行

在 Kubernetes 或企业级 Docker 环境中, "最小权限原则" 是铁律。

2.1 为什么不能用 Root?

┌─────────────────────────────────────────────────────┐
│  容器以 root 运行的风险链                            │
├─────────────────────────────────────────────────────┤
│  1. 攻击者利用 CVE 漏洞进入容器                      │
│  2. 容器内 root = 宿主机 root(默认情况)            │
│  3. 攻击者逃逸到宿主机,获得完全控制权               │
│  4. 整个集群沦陷                                     │
└─────────────────────────────────────────────────────┘

2.2 非 Root 的经典难题

问题:Linux 禁止非 Root 用户监听 1024 以下端口(如 80、443)。

常见方案对比

方案优点缺点
改用 8080 端口简单需要额外端口映射
setcap 能力精确授权Alpine 支持有限
setuid 权限兼容性好需要理解原理

我们选择 setuid 方案,兼容性最好。

2.3 完整配置步骤

# ============================================
# 步骤 1:创建非 root 用户和组
# ============================================
# 安装 shadow 包(提供 useradd/groupadd 命令)
RUN apk add --no-cache shadow
​
# 定义用户信息(可根据需要修改)
ENV NON_ROOT_USER=wwj
ENV NON_ROOT_GID="103"
ENV NON_ROOT_UID="1003"# 创建用户组和用户
# -g 103: 指定组 ID
# -m: 创建 home 目录
# -u 1003: 指定用户 ID
RUN groupadd -g ${NON_ROOT_GID} nonroot && \
    useradd -m -u ${NON_ROOT_UID} ${NON_ROOT_USER} -g nonroot
​
# ============================================
# 步骤 2:修正目录权限
# ============================================
# 让非 root 用户可以写入必要的目录
RUN mkdir -p /usr/local/src/nginx/ && \
    chmod -R g+wx /var/run/openresty \
                  /usr/local/openresty \
                  /tmp/ \
                  /run && \
    chown -R ${NON_ROOT_USER}:nonroot /usr/local/src
​
# ============================================
# 步骤 3:解决 80 端口绑定问题(关键!)
# ============================================
# 设置 setuid 位,让普通用户执行时临时获得 root 权限
RUN chown -R root:${NON_ROOT_GID} \
        /usr/local/openresty/bin/openresty \
        /usr/local/openresty/nginx/sbin/nginx && \
    chmod 750 /usr/local/openresty/nginx/sbin/nginx && \
    chmod u+s /usr/local/openresty/nginx/sbin/nginx
​
# ============================================
# 步骤 4:切换到非 root 用户
# ============================================
USER ${NON_ROOT_USER}
EXPOSE 80

原理解释chmod u+s 设置 setuid 位后,任何用户执行该文件时,都会以文件所有者(root)的身份运行,从而可以绑定 80 端口。


03 核心构建:Dockerfile 深度解析

为了保证镜像体积足够小(最终约 25MB),我们采用 Docker 多阶段构建 (Multi-stage Build)

3.1 构建流程图

┌─────────────────────────────────────────────────────────────┐
│                    Stage 1: Builder                         │
├─────────────────────────────────────────────────────────────┤
│  Alpine 3.23 + 编译工具链 (gcc, make, perl...)              │
│         ↓                                                   │
│  下载源码: OpenSSL 3.4.3 + PCRE2 10.44 + Zstd 模块          │
│         ↓                                                   │
│  编译: ./configure → makemake install                    │
│         ↓                                                   │
│  产物: /usr/local/openresty/* (约 150MB)                    │
└─────────────────────────────────────────────────────────────┘
                          ↓ COPY --from=build
┌─────────────────────────────────────────────────────────────┐
│                    Stage 2: Runner                          │
├─────────────────────────────────────────────────────────────┤
│  Alpine 3.23 (纯净) + 运行时依赖 (libgcc, zstd, gd...)      │
│         ↓                                                   │
│  只拷贝编译产物,丢弃源码和编译器                            │
│         ↓                                                   │
│  最终镜像: 约 25MB                                          │
└─────────────────────────────────────────────────────────────┘

3.2 关键编译参数详解

ARG RESTY_CONFIG_OPTIONS="\
--add-module=/tmp/zstd-nginx-module \
--with-compat \
--with-file-aio \
--with-http_addition_module \
--with-http_auth_request_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_realip_module \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_v2_module \
--with-http_v3_module \
--with-stream \
--with-stream_ssl_module \
--with-stream_ssl_preread_module \
--with-threads \
"

参数说明

参数作用使用场景
--add-module=zstd添加 Zstd 压缩API 响应压缩
--with-streamTCP/UDP 四层代理代理 MySQL/Redis
--with-stream_ssl_preread不解密读取 SNI多域名 TCP 代理
--with-http_v2_moduleHTTP/2 支持现代浏览器
--with-http_v3_moduleHTTP/3 (QUIC)未来趋势
--with-http_realip_module获取真实客户端 IPCDN 后端
--with-threads线程池异步文件 IO

04 生产级配置调优

配置文件 nginx.conf 是性能的关键。以下是核心调优项:

4.1 隐藏服务器信息(安全)

# 隐藏 Nginx 版本号
server_tokens off;
​
# 完全隐藏 Server 头(需要 headers-more 模块)
more_set_headers 'Server: -';
more_clear_headers Server;

4.2 IO 多路复用(性能)

events {
    # epoll 是 Linux 高性能网络 IO 的基石
    # 比 select/poll 效率高几个数量级
    use epoll;
​
    # 单个 worker 进程的最大连接数
    # 需要配合 ulimit -n 调大系统限制
    worker_connections 2048;
​
    # 允许一个进程同时接受多个新连接
    # 高并发场景必开
    multi_accept on;
}

4.3 连接保活(减少握手)

http {
    # 长连接超时时间(默认 75s 太短)
    keepalive_timeout 300s;
​
    # 单个长连接最多处理的请求数(默认 100 太少)
    keepalive_requests 10000;
​
    # TCP 优化
    tcp_nopush on;   # 合并小包,减少网络开销
    tcp_nodelay on;  # 禁用 Nagle 算法,降低延迟
}

05 快速开始

5.1 目录结构

op_1.27.1.2/
├── Dockerfile              # 多阶段构建脚本
├── nginx.conf              # 主配置文件
├── nginx.vh.default.conf   # 默认 server 配置
├── index.html              # 默认首页
├── 403.html                # 403 错误页
├── 429.html                # 429 限流页
└── build.sh                # 一键构建脚本

5.2 一键初始化

# 创建工作目录
D_PATH=/data
mkdir -p $D_PATH/op_1.27.1.2 && cd $D_PATH/op_1.27.1.2

5.3 构建镜像

# 构建命令(使用宿主机网络加速下载)
docker build --network host --no-cache \
    -t sprinng/openresty:1.27.1.2-small-alpine3.23.0 .
​
# 后台构建(适合编译时间长的情况)
nohup sh build.sh &

5.4 验证安装

# 查看版本信息
docker run --rm -it sprinng/openresty:1.27.1.2-small-alpine3.23.0 \
    /usr/local/openresty/bin/openresty -v
​
# 预期输出(包含 zstd-nginx-module 说明编译成功)
# nginx version: openresty/1.27.1.2
# built with OpenSSL 3.4.3
# --add-module=/tmp/zstd-nginx-module ...

5.5 启动服务

# 基础启动
docker run -d --name openresty \
    -p 80:80 \
    sprinng/openresty:1.27.1.2-small-alpine3.23.0
​
# 挂载配置文件启动(生产推荐)
docker run -d --name openresty \
    -p 80:80 \
    -v /data/nginx/conf.d:/etc/nginx/conf.d:ro \
    -v /data/nginx/html:/usr/local/openresty/nginx/html:ro \
    sprinng/openresty:1.27.1.2-small-alpine3.23.0

免费内容到此结束

以上内容已经足够让你理解 OpenResty 极致镜像的核心理念。

但是,如果你真的想在生产环境跑起来,你还会遇到这些问题:

问题你可能花费的时间
Dockerfile 编译报错,缺依赖2天
OpenSSL 补丁下载失败1天
非 Root 权限配置不对,80 端口绑定失败1天
nginx.conf 配置不当,性能上不去1天
TCP 代理配置踩坑2天

我已经帮你踩过所有的坑了。

付费内容包含:

  • 完整可运行的 Dockerfile(300+ 行,含详细注释,复制即用)
  • 生产级 nginx.conf(含 WebSocket、日志格式、Buffer 调优)
  • TCP 代理配置示例(代理 MySQL/Redis,直接套用)
  • 非 Root 运行完整配置(setuid 方案,已验证)
  • 持续更新(版本升级、安全补丁第一时间同步)

一杯咖啡的钱,省下7天的踩坑时间。


​​

完整内容请 微信搜索 技术修罗