简介:面向需要自建定制Nginx镜像的开发者与运维人员,这份nginx-1.24.0 Docker镜像资源提供了完整的Dockerfile体系与启动脚本,解决直接使用官方镜像时难以自定义编译参数或添加模块的问题。压缩包内共48个文件,约64KB,主体是11个Dockerfile模板与23个shell脚本,同时包含5个template模板、若干README说明及license等文件,覆盖debian、alpine等不同基础镜像变体。已有952人学习下载,适合有一定Docker基础、希望深入Nginx镜像构建细节的读者。借助内置的多版本Dockerfile模板可快速生成alpine、debian、perl等镜像;entrypoint脚本内置了模板渲染、IPv6监听、worker进程数自动调整等逻辑,方便理解官方镜像结构并按需二次开发,是一份轻量但实用的Docker构建参考。
1. 从 nginx-1.24.0 到 Docker 镜像:为什么这个版本值得你为它单独构建
线上服务跑了三年,还在用 nginx 1.18,安全扫描报告里躺着一堆 CVE。你想升级,但又怕apt upgrade把当前配置和第三方模块冲掉。这时候,nginx-1.24.0 的 Docker 镜像就是那个“后悔药”:版本固定、环境隔离、参数可复现,测试完直接推到生产。这篇笔记就是把这个镜像从拉取、构建、运行到踩坑的完整闭环讲透,适合运维、后端和做容器化的同学。你不需要是全职 Docker 工程师,只要手头有 Linux 或 Docker Desktop,就能照着把 1.24.0 跑起来。
2. 选型与准备:nginx-1.24.0 镜像的三条路,以及我为什么选官方镜像做底座
2.1 官方镜像、发行版镜像、自编译镜像的取舍
拿到 nginx-1.24.0 的镜像,第一时间想到的肯定是 Docker Hub 上的nginx:1.24.0。但“能用”和“好用”之间有三个选择。第一条路是直接用官方镜像,它基于 Debian 或 Alpine,默认编译好了常见的 HTTP、SSL、v2 模块,适合绝大多数 Web 和反向代理场景。第二条路是踩在发行版镜像上装 nginx,比如 CentOS 7 镜像里yum install nginx,优点是跟公司现有配置基线一致,缺点是体积大、版本可能被系统源锁住,而且 nginx 的编译参数不可控。第三条路是自编译,把--with-stream、--with-http_v3_module这些官方默认没开的模块塞进去,适合做 TCP 四层转发、灰度流量和边缘网关。
我一般会先拉官方nginx:1.24.0跑通业务,只有遇到“官方镜像缺模块”或“要固定 OpenSSL 版本”时才切换到自编译。理由很简单:官方镜像的 Dockerfile 是公开的,基础镜像经过大量生产环境验证,安全补丁跟进也快。如果你直接以 CentOS 为底座,后续 CVE 修复得自己盯系统包,反而更累。下表是我常用来评估的维度:
| 选型维度 | 官方镜像 | 发行版镜像 | 自编译镜像 |
|---|---|---|---|
| 模块覆盖 | 常用模块够用 | 依赖发行版源 | 完全自定义 |
| 体积 | 小(几十MB到几百MB) | 大(带整个OS) | 可控 |
| 安全更新 | 镜像发布方跟进 | 自己维护 | 自己维护 |
| 维护成本 | 最低 | 中等 | 最高 |
2.2 拿到 nginx-1.24.0 官方镜像的最小命令与版本核对
先别急着写 Dockerfile,把基础镜像拉下来做一次“体检”。下面是两条我每次都会执行的最小命令:
docker pull nginx:1.24.0 docker run --rm nginx:1.24.0 nginx -Vdocker pull指定版本标签1.24.0,这比拉latest安全得多,因为latest会漂移,昨天测试通过的镜像今天可能就变了。docker run --rm表示容器退出后立刻删除,不会残留临时容器。最后面的nginx -V会输出编译参数和版本号,我习惯用这一步确认镜像里的 nginx 确实是你想要的 1.24.0,同时看清它编译了哪些模块。
如果当前机器是 Windows 或 macOS,得靠 Docker Desktop 来跑这两条命令。装完 Docker Desktop 后先看右下角引擎图标是不是绿的,再执行命令。很多人卡在启动阶段,报错信息里往往有 “virtualization support not detected”,这种一般是 BIOS 里没开虚拟化,或者 WSL2 没启用。先把 Docker Desktop 的基础环境搞定,再谈拉镜像。
2.3 本地没有 Docker 环境时:先用二进制包验证配置再上镜像
有时候你手头只有一台裸机,没法立刻装 Docker,但又要保证 nginx-1.24.0 的配置能平滑迁移到容器里。我的做法是先在宿主机上解压一个 nginx 1.24.0 的二进制包,用它验证配置语法,再同步到镜像里。因为容器内外的 nginx.conf 路径不一样,直接复制很可能在nginx -t这一步翻车。
wget http://nginx.org/download/nginx-1.24.0.tar.gz tar xzf nginx-1.24.0.tar.gz cd nginx-1.24.0 ./configure --prefix=/usr/local/nginx && make -j$(nproc) /usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf这里没有用容器,目的是把 nginx.conf 里的pid、error_log、include路径都理清楚。-c指定配置文件路径,-t只做语法校验,不启动进程。等你把路径改成容器里的/etc/nginx结构后,再进 Dockerfile 就少踩很多路径坑。这个步骤虽然多花十分钟,但能省下后面“配置找不到”“日志写不进”的血泪时间。
3. 用 Dockerfile 构建 nginx-1.24.0 镜像:从最小可跑做到可控可维护
3.1 最小 Dockerfile:基于官方镜像改配置
如果只是把宿主机上的配置搬进容器,基于官方镜像改是最快的。下面这个 Dockerfile 是我给内部测试环境用的最小版本:
FROM nginx:1.24.0 COPY nginx.conf /etc/nginx/nginx.conf COPY conf.d/ /etc/nginx/conf.d/ RUN rm /etc/nginx/conf.d/default.conf EXPOSE 80 443 CMD ["nginx", "-g", "daemon off;"]这里逻辑很简单:FROM锁定基础镜像;COPY把宿主机的配置覆盖进去;RUN rm删掉官方镜像自带的default.conf,避免它和你自己的 server 块抢80端口。EXPOSE只是声明容器会监听哪些端口,真正映射靠docker run的-p参数。CMD里的daemon off;是 nginx 在容器里必须开的选项,让进程在前台运行,否则容器会因为没有存活进程而立刻退出。
参数说明:如果你不覆盖nginx.conf,官方镜像默认配置已经能直接跑,但/etc/nginx/conf.d/下默认只有一个default.conf,行业标准做法是删掉它,把自己的conf.d目录放进去。注意COPY conf.d/后面的斜杠很关键,不带斜杠会复制文件夹本身,带斜杠会把里面文件复制到目标目录。
3.2 自编译 nginx-1.24.0 的 Dockerfile 要点
当你需要--with-stream做四层 TCP 转发,或者要自定义 OpenSSL 版本时,就得走自编译。这里给一套多阶段构建的模板:
FROM nginx:1.24.0 AS base FROM debian:bullseye-slim AS builder RUN apt-get update && apt-get install -y \ curl \ build-essential \ libpcre3-dev \ libssl-dev \ zlib1g-dev RUN curl -O http://nginx.org/download/nginx-1.24.0.tar.gz \ && tar xzf nginx-1.24.0.tar.gz WORKDIR /nginx-1.24.0 RUN ./configure \ --prefix=/usr/share/nginx \ --sbin-path=/usr/sbin/nginx \ --modules-path=/usr/lib/nginx/modules \ --conf-path=/etc/nginx/nginx.conf \ --http-log-path=/var/log/nginx/access.log \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx.pid \ --with-http_ssl_module \ --with-http_v2_module \ --with-stream \ --with-stream_ssl_module \ && make -j$(nproc) \ && make install FROM debian:bullseye-slim COPY --from=builder /usr/sbin/nginx /usr/sbin/nginx COPY --from=builder /etc/nginx /etc/nginx COPY --from=builder /usr/lib/nginx /usr/lib/nginx RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates tzdata curl \ && rm -rf /var/lib/apt/lists/* EXPOSE 80 443 CMD ["nginx", "-g", "daemon off;"]这段的关键是configure参数。--prefix决定配置文件的默认目录,--sbin-path和--conf-path要把路径写死,不然 make install 会把文件散到多个目录,最终镜像里要到处COPY。--with-stream是四层转发的开关,--with-stream_ssl_module则支持 stream 里的 SSL 终结。生产上我还习惯加--with-http_stub_status_module,后面配/nginx_status做监控非常方便。
多阶段构建的智慧在于:builder阶段里有一整套编译工具链,体积很大,但最终镜像只继承第二阶段需要的二进制和配置,把 gcc、make、源码全丢掉了。为什么不用 Alpine?因为 musl libc 对部分configure脚本不友好,编译第三方模块时经常要额外打补丁。如果你不是对体积极度敏感,Debian slim 比 Alpine 省心得多。
3.3 构建参数与镜像瘦身的三个必调项
构建镜像不是docker build一把梭就完事。我踩过几次亏之后,固定下三个必调项。第一,--build-arg把版本号透传进去,不要在 Dockerfile 里写死。比如:
docker build --build-arg NGINX_VERSION=1.24.0 -t my-nginx:1.24.0 .这样后续升 1.26 不用改 Dockerfile。第二,--network=host只在编译阶段用,能避免拉取依赖时的 DNS 问题,但最终运行时不要用。第三,--squash或--label两个参数,前者压缩镜像层数,后者给镜像打上构建日期和负责人信息,排查问题能少很多玄学。
体积瘦身还有一个容易忽略的点:编译完的 nginx 二进制会带有调试符号。我一般会在make install后再执行strip /usr/sbin/nginx,能把二进制从几 MB 压到 1MB 左右。但注意,strip 之前最好用nginx -V确认模块都编译进去了,因为 strip 之后虽然功能不受影响,但如果你想用strings去排查某些隐藏模块,信息会变少。
4. 运行 nginx-1.24.0 容器:端口、挂载、日志与健康检查的落地配置
4.1 一条 docker run 命令跑起来的参数拆解
镜像构建完,最直接的方式是用docker run把它起起来。下面这条命令覆盖了生产环境最基本的参数:
docker run -d \ --name nginx-124 \ -p 80:80 \ -p 443:443 \ -v /data/nginx/conf.d:/etc/nginx/conf.d:ro \ -v /data/nginx/html:/usr/share/nginx/html \ -v /data/nginx/logs:/var/log/nginx \ -e TZ=Asia/Shanghai \ --restart unless-stopped \ my-nginx:1.24.0-d是后台运行,--name给容器起名,方便后续docker exec和docker logs。-p把宿主机的 80/443 映射到容器,注意左侧是宿主机端口,右侧是容器内端口,写反了会直接导致访问不通。-v是挂载,:ro表示只读,防止容器里误改宿主机文件。-e TZ设置时区,否则日志时间戳会差 8 个小时。--restart unless-stopped让 Docker 在守护进程重启或容器意外退出时自动拉起。
这里最容易忽略的是conf.d挂载成只读后,你仍然可以用docker exec进容器改/etc/nginx/conf.d里的文件,但改的是宿主机/data/nginx/conf.d下的副本。所以我会把宿主机目录的权限调成与容器内运行用户一致,避免出现“能读不能写”的权限报错。
4.2 挂载配置与日志落盘:怎么改都不重建容器
常见做法是把整个/etc/nginx挂载出来,但我更推荐只挂载conf.d和日志目录。原因有两个:一是官方镜像的/etc/nginx/nginx.conf里有一堆默认include路径,整个目录挂载会把默认配置也盖掉,容易触发“无法加载共享库”的诡异问题;二是nginx.conf属于全局配置,变更频率极低,不值得作为挂载点。
日志落盘有个坑:nginx 的 worker 进程以nginx用户运行,但/var/log/nginx挂载到宿主机后,目录属主可能变成 root。此时容器内 nginx 写日志会提示 permission denied。我的做法是挂载前在宿主机执行:
mkdir -p /data/nginx/logs chown -R 101:101 /data/nginx/logs101 是官方 nginx 镜像里nginx用户的 UID。如果你用自编译镜像,先docker exec nginx-124 id看一下 UID,再回宿主机改权限。这个步骤不做,日志文件永远起不来。
配置改完不用重建容器,执行docker exec nginx-124 nginx -s reload就能平滑重载。reload 的原理是向 master 进程发送 HUP 信号,worker 进程一个个退出再拉起新配置,所以线上流量不会中断。注意-s reload只校验并重载配置,不做二进制升级;如果需要升级 nginx 版本,还是得换镜像重建容器。
4.3 健康检查与优雅重启:容器编排下的 nginx 生命周期
在 Kubernetes 或 Compose 环境里,健康检查决定了流量要不要打到这个容器上。官方 nginx 镜像默认没有 HEALTHCHECK,我习惯在 Dockerfile 里补一段:
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \ CMD curl -fs http://127.0.0.1/healthz || exit 1--interval是检查间隔,--timeout是单次检查超时,--start-period给容器启动预留的时间,避免刚启动还没起来就连续报错。curl -fs表示如果请求返回非 200,curl 就返回非零退出码,健康检查直接失败。注意/healthz这个路径必须在你的 nginx server 块里先定义好,否则检查会一直失败,直到容器被编排系统重启。
最后一个技巧是优雅停机。直接docker kill会立刻杀进程,可能切断正在处理的请求。应该在停止前让 master 进程走nginx -s quit:
docker exec nginx-124 nginx -s quit docker stop nginx-124quit会等待所有 worker 处理完当前请求后再退出,stop默认再等 10 秒强制杀。两条命令配合,能把连接掉线率降到最低。如果你用的是 Docker Compose,可以在stop_grace_period里加长等待时间,给 nginx 足够时间完成优雅退出。
5. nginx-1.24.0 镜像实战避坑:从拉取失败到 502 的五个常见问题
5.1 现象:docker pull 卡在等待,或者反复超时
第一次在公司网络下拉nginx:1.24.0,进度条经常卡在 Pulling fs layer。原因大家都懂,Docker Hub 的访问在部分网络环境下非常不稳定。解决方法是给 Docker daemon 配置国内镜像源。Linux 下修改/etc/docker/daemon.json:
{ "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"] }改完执行systemctl restart docker再重新 pull。注意镜像源地址会有变动,如果这个源失效,可以换其他公开源,或者让运维同学搭一个内网 registry。配置完成后可以用docker info检查 mirror 是否生效,里面会列出 registry-mirrors 条目。
5.2 现象:容器起来了,但宿主机上访问不了 80 端口
docker ps能看到容器是 Up 状态,curl http://localhost却一直拒绝连接。我先docker port nginx-124确认端口映射,如果返回80/tcp -> 0.0.0.0:80,说明映射没问题。接着看宿主机防火墙:firewall-cmd --list-ports或iptables -L,常见是 firewalld 挡住了 80。解决是放行端口:
firewall-cmd --permanent --add-port=80/tcp firewall-cmd --reload还有一种翻车原因是容器内端口不是 80。比如自编译镜像里 listen 8080,但-p 80:80映射的是容器 80,当然不通。这时候不要瞎猜,直接docker exec nginx-124 ss -tlnp看容器内真正监听的端口。
5.3 现象:改了挂载的配置,reload 后没变化,甚至nginx -t报错
宿主机上编辑了/data/nginx/conf.d/app.conf,然后docker exec nginx-124 nginx -s reload显示成功,但请求回来的还是旧响应。原因往往是你的nginx.conf里最后一行的include /etc/nginx/conf.d/*.conf;被自己覆盖掉,或者挂载时把conf.d这个目录挂到了错误路径。我的排查习惯是:
docker exec nginx-124 nginx -Tnginx -T会输出当前进程实际加载的完整配置,包含所有include展开后的内容。看到里面既没有你新加的 server 块,也没有报错,那就是 include 路径没覆盖到。解决方法是检查nginx.conf里的 include 后缀是*.conf还是*,如果只写了*,目录下所有文件都会被加载,可能把备份文件也加载进来,导致 server 块重复定义。
5.4 现象:自编译镜像里加了--with-stream,但启动时 module 加载失败
我首次自编译 1.24.0 时,nginx -V明明能看到--with-stream,但启动日志里一直报:ngx_stream_module.so" not found。原因是我用了--modules-path又把模块编译成.so,但运行时没有设置LD_LIBRARY_PATH或者在nginx.conf里用load_module指向了错误路径。解决方法是两个:要么在nginx.conf顶部显式写load_module /usr/lib/nginx/modules/ngx_stream_module.so;,要么干脆在 configure 时去掉--modules-path,把模块静态编进二进制。对于 stream 这种常用模块,直接用静态编译,别折腾动态加载。
5.5 现象:容器内日志时间比宿主机慢 8 小时
业务日志、错误日志的时间戳全部是 UTC,跟数据库和监控系统对不上。这个问题源于 Docker 容器默认继承宿主机的/etc/localtime,但基础镜像里TZ环境变量没设置。解决方法是启动参数加:
-e TZ=Asia/Shanghai如果已经起来了,可以docker exec -it nginx-124 bash -c "ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime"然后 reload,但这只是临时手段,容器重建后会恢复。最稳的做法还是写在 Dockerfile 里:
ENV TZ=Asia/Shanghai注意不要用挂载/etc/localtime的方式,因为宿主机上是符号链接,挂载成文件后反而会报错。日志时间问题虽然不影响业务,但排障时会让你对不上监控曲线,值得提前处理。
6. 进阶:给 nginx-1.24.0 镜像做多架构推送与版本标签管理
6.1 用 buildx 构建 amd64/arm64 多架构镜像
现在服务器有 x86 也有 ARM,如果只用本地docker build,换到 ARM 机器上就得重新拉取一次镜像。我现在的做法是用docker buildx一次性打多个架构的包:
docker buildx create --name multiarch --use docker buildx build \ --platform linux/amd64,linux/arm64 \ -t your-registry/nginx-1.24.0:latest \ --push .buildx会在后台为每个平台启动独立的构建器,然后生成一份 manifest 列表,推送后同一镜像能按平台拉取相应架构。需要注意,这种跨构构建必须开启 Docker 的 experimental 特性,而且--push需要目标 registry 的登录权限。如果你想先验证本地,不加--push改成--load,但--load只能加载当前架构,跨架构验证还是得靠 push 后拉取。
多架构构建最大的坑是基础镜像不一致。如果你FROM nginx:1.24.0,buildx 会为 amd64 拉 amd64 的 nginx,为 arm64 拉 arm64 的 nginx,这没问题。但如果你FROM debian:bullseye-slim又要执行编译步骤,就得确保 Dockerfile 里没有硬编码路径和架构相关的依赖。我踩过 arm64 编译时libssl-dev版本不一致的坑,后来在 Dockerfile 里固定 Debian 版本,并加DEBIAN_FRONTEND=noninteractive环境变量,构建才稳定。
6.2 标签规范与镜像版本一致性验证
镜像标签是一张长期维护的负担,我一般固定三套标签:精确版本nginx-1.24.0、大版本nginx-1.24、可变标签latest。避免latest在测试环境里不知不觉漂移。另外强烈建议记录基础镜像的 digest:
docker image inspect your-registry/nginx-1.24.0:latest --format '{{.RepoDigests}}'把输出的 digest 记到部署文档里,下次拉取时对比一下,如果变了就知道上游镜像被重新构建过。这个习惯能帮你少踩“昨天还能用,今天莫名报错”的玄学问题。
验证镜像是否满足业务预期,不要只测nginx -v。我会在容器启动后跑一轮真实的转发测试:
docker run --rm -d --name nginx-test -p 18080:80 your-registry/nginx-1.24.0:latest curl -I http://127.0.0.1:18080看到 HTTP 200 和 Server: nginx/1.24.0 响应头,才说明镜像可用。如果是自编译镜像,还会检查nginx -V里的编译参数是否和期望一致。这是我个人的习惯:把这条烟囱测试放进 CI 里,每次构建后自动执行,它比任何静态检查都更能发现问题。希望这些细节能帮你在 nginx-1.24.0 的容器化路上少走弯路,也欢迎你按照自己的业务场景把超时、健康检查和挂载策略调成顺手的样子。
本文还有配套的精品资源,点击获取