为什么我没有给每个项目都起一个 Nginx 容器

预计阅读时间:12 分钟

这是「把自己部署到互联网上」系列的第 22 篇。

上一篇讨论了 Nginx 的部署方式:系统安装,还是放进 Docker 容器。

我的结论是:

如果只是单项目、传统部署,系统 Nginx很好;如果像我这样准备在一台服务器上跑多个 Docker 项目,并希望统一入口、减少公网端口暴露、提升迁移清晰度,容器化 Nginx更适合。

比如博客一个 Nginx,官网一个 Nginx,作品站一个 Nginx,后台服务一个 Nginx。每个项目都自带入口,看起来独立,也符合“项目自包含”的直觉。

我一开始也考虑过这个方案,之前也确实这么干过,一个应用配套一个 Nginx, 但最后没有这么做。

单看一个项目,每个项目一个 Nginx容器确实有好处。项目有自己的 Nginx 配置、静态资源、反向代理规则,迁移时目录里包含应用、数据库、Nginx和配置,看起来像一个完整包。对于只有一个博客项目的场景,这样做完全可以。

问题在于:

我的规划不是一个项目。后面还会有个人官网、作品站和其他项目,它们都要通过这一台服务器、同一组公网端口对外访问。

我想大多数企业和个人都是同一台服务器跑多个应用。

那么问题就来了:既然 Nginx 可以容器化,那是不是每个项目都应该起一个自己的 Nginx 容器

如果每一个项目都起一个 NGINX 容器,起多了对服务器有什么影响

端口

服务器对外常用 Web 端口默认是 80 和 443,浏览器会自动省略。也可以通过指定端口访问,比如1080、2080等这些端口。

简单说,80 和 443 就像网站的“正门”,其他端口是“侧门”,正门大家都走所以不用特意标注,走侧门时才需要写清楚门牌号。

同一台服务器上,同一个端口不能被多个容器同时绑定,如果博客 Nginx 已经写了:

ports:
  - "80:80"
  - "443:443"

官网 Nginx 就不能再绑定同样的宿主机端口。当然可以让不同项目映射到不同端口,比如博客 8081、官网 8082、作品站 8083。但对用户不友好,普通用户可记不住你的这些端口。

所以不管项目内部有没有 Nginx,最外层仍然需要一个统一入口接管 80 和 443。既然最终一定需要统一入口,让每个项目都直接面对公网就不合适。

证书管理会变复杂

HTTPS 是网站基础配置。如果每个项目都有自己的 Nginx 容器,证书申请、续期、路径、挂载、reload 和备份就可能分散到各个项目里。短期看问题不大,长期维护时会增加出错点。

如果后面使用通配符证书,或者多个子域名共用一套证书,集中管理会更清楚。我更希望证书放在哪里、Nginx 怎么引用、续期后怎么重载,都在一个地方处理。这样能降低证书过期和配置混乱的风险。

每个容器都要挂载证书目录
每个项目都要处理 certbot  acme
证书续期后要 reload 对应容器
容易出现证书散落、权限混乱

维护成本

每个项目一个 Nginx,意味着每个项目都要维护一份 Nginx配置。项目多了以后,差异会越来越多:

  • 有的启用了 gzip
  • 有的忘了 HTTPS 跳转
  • 有的日志路径不同
  • 有的代理头没写全
  • 有的上传大小限制不一致
  • 有的改完配置忘了 reload。

如果只有一个人维护服务器,我不希望把同一种入口逻辑复制到多个项目里。更合理的方式是:

  • 把通用入口能力集中到全局 Nginx
  • HTTP 到 HTTPS 跳转
  • 证书配置
  • 反向代理通用头
  • 日志格式
  • 静态资源缓存策略
  • 上传大小限制和基础安全头

项目只关心自己的应用服务。

Nginx 本身很轻,多个容器不会特别吃 CPU。 但数量多了会增加一些固定成本:

内存:每个 Nginx 容器通常几十 MB 左右
进程数:每个容器内有 master/worker 进程
日志:每个容器单独产生日志
网络:多一层 Docker 网络转发
磁盘:镜像复用,但配置和日志会增多

日志和排查会变分散

当多个项目都有 Nginx 时,需要回答很多问题:

  1. 哪个 Nginx 接公网流量?
  2. 哪个只在内部用?
  3. 域名在哪一层匹配?
  4. HTTPS 在哪一层终止?
  5. 请求转发经过几层?
  6. 日志看哪一个?
  7. 出现 502 时,是外层 Nginx 的问题,还是项目内 Nginx 的问题?

如果请求链路变成:

公网用户 -> 全局 Nginx -> 项目 Nginx -> 应用服务

不是不能做,有些场景确实需要这样。但对我的个人服务器来说,这一层通常不必要。我更希望链路保持简单:

公网用户 -> 全局 Nginx -> 项目应用服务

能少一层代理,就少一层排查成本。

项目独立,入口不一定要独立

博客可以有自己的代码、数据库、环境变量、数据卷和 Compose 文件。官网也可以有自己的构建流程、静态文件和部署目录。这些都应该独立。但它们对外访问时,可以共享同一个入口层。

也就是说:

公网入口统一
项目内部独立

每个服务不需要都直接暴露公网。它们只需要在内部网络里提供服务,由统一入口根据域名或路径转发过去。

对我的个人服务器来说,这个入口就是全局 Nginx

所以我的最终架构设计是:全局 Nginx 统一管理公网入口,每个项目只负责自己的应用服务。

公网层:

80 / 443 -> 全局 Nginx

项目层:

全局 Nginx -> 博客应用容器
全局 Nginx -> 官网应用容器或静态目录
全局 Nginx -> 其他项目服务

数据库层:

项目应用容器 -> 数据库容器

数据库不直接暴露公网,项目内部服务不随便暴露公网,公网只认全局 Nginx

这并不表示每个项目内部绝对不能有 Nginx。有些项目内部仍然可能需要 Nginx,比如前端静态资源需要单独服务,项目镜像自带 Nginx,应用需要内部 Nginx 做特殊路由,或者希望项目镜像完全自包含。

但要区分它的角色:项目内部 Nginx 不直接占用公网 80/443,它只是项目内部服务的一部分。真正面对公网的,仍然是全局 Nginx。不是不能有多个 Nginx,而是不能让多个 Nginx 同时抢公网入口

好处与代价

这样做有几个好处:

  1. 80 和 443 只由一个地方管理
  2. HTTPS 证书集中管理
  3. 域名和子域名配置集中管理
  4. 请求路径更短
  5. 新增项目时,只需要在全局 Nginx 增加配置,并让项目加入公共网络或提供可访问地址
  6. 排查访问问题时入口明确

全局 Nginx 也有代价。它会成为关键节点,所有公网流量都经过它,配置必须谨慎。

  1. 新增项目时,要修改全局配置
  2. Nginx 容器访问不同 Compose 项目时,需要接入到公共 Docker network,或者通过宿主机端口连接。
  3. 证书和配置备份也要做好,因为入口集中后,这些文件就很关键。

这些代价可以接受。它们是集中管理带来的明确维护责任,而不是多个入口分散后产生的隐性成本。对个人服务器来说,更愿意维护一个清楚的入口,而不是多个边界模糊的入口。

小结

如果你只有一个项目,每个项目一个 Nginx没什么问题,直接系统安装 Nginx也完全可以。但如果准备在一台服务器上部署多个项目,建议先想清楚入口架构设计。

  1. 不要每个项目随手暴露一个端口
  2. 不要每个项目各自管理 HTTPS
  3. 不要让多个 Nginx 同时承担公网入口

更稳的方式是:公网入口统一,项目内部独立,数据库不暴露公网,HTTPS 集中管理,新增项目按统一规则接入

这也是我为什么我没有给每个项目都起一个 Nginx 容器的原因?

因为项目可以独立,但公网入口应该统一。每个项目一个公网 Nginx 看起来隔离,实际会带来:

  • 端口冲突
  • 证书分散
  • 入口混乱
  • 维护成本上升

下一篇,我会继续写这个方案的落地:我的最终方案,使用全局 Nginx 统一管理多个项目。


本文由 楸木 原创,转载请注明出处。

相关推荐

发现更多