容器重启后 Nginx 陷入崩溃循环:基础镜像在 discourse.conf 中包含 brotli_static,但从未加载该模块(未知指令)

摘要
在常规容器重启(主机重启 / docker restart)之后,app 容器内的 nginx 无法启动,并出现以下错误:

nginx: [emerg] unknown directive "brotli_static" in /etc/nginx/conf.d/discourse.conf:172

这导致整个站点宕机(没有回退机制——nginx 始终无法绑定到 80/443 端口),直到手动修补配置。

环境

  • 基础镜像:discourse/base:2.0.20260812-0036
  • nginx:1.26.3-3+deb13u7(Debian 13/trixie 软件包,非自定义编译)
  • 已安装 libnginx-mod-http-brotli-static/-filter 1.0.0~rc-6,且 /etc/nginx/mods-enabled/*.conf 符号链接(包含正确的 load_module 指令)存在并指向有效的 .so 文件。

根本原因
/etc/nginx/nginx.conf(由 nginx-common 软件包拥有,未做修改)中没有 include /etc/nginx/modules-enabled/*.conf; 指令,因此已安装的 Brotli 模块从未被加载——而 discourse.conf(由 Discourse 模板生成)仍然输出 brotli_static on;,假设这些模块已加载。

为什么不会立即失败
只有当 nginx 实际(重新)启动时,配置才会被重新解析。一个新构建的容器可以正常运行数天或数周,直到某些事件触发 nginx 重启(主机重启、docker restart、OOM 等),此时它会无声地进入崩溃循环(约每秒 1 次),且没有自动恢复机制。

临时解决方案
/etc/nginx/nginx.conf 的第一行(主上下文,在 events {} 之前)添加 include /etc/nginx/modules-enabled/*.conf;。已确认此操作可恢复 nginx -t 的成功执行及正常操作。

请求
基础镜像能否做到以下任一操作:(a) 在其 nginx.conf 中添加此 include 指令,或 (b) 如果发行版的 nginx 软件包构建不再默认捆绑 Brotli 支持,则从随附的 discourse.conf 模板中移除 brotli_static/brotli

2 个赞

更正我之前的报告:基础镜像没有问题。问题的根源在于我们本地的安装环境。

实际发生的情况

我们主机上的一个本地 cron 任务会将过时的、手动维护的 nginx 配置文件复制到正在运行的容器中。其中一个文件早于 include /etc/nginx/modules-enabled/*.conf; 的引入,另一个仍然设置了 brotli_static on;。这两者共同导致了 unknown directive "brotli_static" 错误,从而阻止 nginx 启动。

我为什么误诊了

我检查了正在运行的容器内的 /etc/nginx/nginx.conf,发现缺少 modules-enabled 的 include 语句。该文件在本地已被覆盖,因此运行中的容器从未反映 Discourse 实际生成的内容。改为检查构建好的镜像后,问题就显而易见了。include 语句是存在的:

docker create --name check local_discourse/app
docker cp check:/etc/nginx/nginx.conf ./
docker rm check

为什么问题延迟暴露

这部分可能对其他人有帮助。该任务以 nginx -s reload 结束,并丢弃其输出。当配置损坏时,重新加载会静默失败,而正在运行的 nginx 会继续使用其已加载的配置提供服务。一切看起来都正常。损坏只有在下一次真正的 nginx 启动时才会显现出来,对我们来说,那是几天后一次无人值守的 kernel 升级导致主机重启时发生的。

因此,如果一个 Discourse 容器一直正常处理流量,但在一次无关的重启后停止工作,请检查在此期间是否有东西覆盖了它的 nginx 配置。

为造成的干扰致歉,感谢所有花时间查看此问题的人。

1 个赞