难度: 中级
适合人群: 运维工程师、后端开发、DevOps
在云原生时代,我们习惯了 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 |
01 为什么你需要 Zstd?
在 Nginx/OpenResty 的世界里,Gzip 统治了十几年。但现在,是时候引入 Zstd 了。
1.1 Zstd vs Gzip 性能对比
Zstd (Zstandard) 是 Facebook 开源的实时压缩算法:
| 指标 | Gzip (level 5) | Zstd (level 3) | 提升 |
|---|---|---|---|
| 压缩速度 | 50 MB/s | 350 MB/s | 7x |
| 解压速度 | 250 MB/s | 1000 MB/s | 4x |
| 压缩率 | 2.5:1 | 2.8:1 | 12% |
| 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 → make → make 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-stream | TCP/UDP 四层代理 | 代理 MySQL/Redis |
--with-stream_ssl_preread | 不解密读取 SNI | 多域名 TCP 代理 |
--with-http_v2_module | HTTP/2 支持 | 现代浏览器 |
--with-http_v3_module | HTTP/3 (QUIC) | 未来趋势 |
--with-http_realip_module | 获取真实客户端 IP | CDN 后端 |
--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天的踩坑时间。
完整内容请 微信搜索 技术修罗