这是「把自己部署到互联网上」系列的第 23 篇。
前两篇连续讨论了 Nginx:第 21 篇讲 Nginx 应该装在系统里,还是放进 Docker 容器;第 22 篇讲为什么我没有给每个项目都起一个 Nginx 容器。到这里,思路已经明确。
我的个人服务器后面不只跑博客,还可能跑个人官网、作品站、小工具和实验项目。如果每个项目都各自暴露端口、处理 HTTPS、维护 Nginx 配置,后面会越来越难管理。
所以我的最终方案是:全局 Nginx 统一管理多个项目的公网入口。
这篇不是最终配置文件,而是后续部署博客和官网时遵循的整体架构。下一篇再进入公共网络、配置文件和启动检查。
主要解决的问题
公网入口不能乱
一台服务器可以有多个项目,但对外访问入口最好集中。用户访问博客或官网,不应该记端口,最终都应该通过标准的 80 和 443 访问。
证书不能散
HTTPS 证书如果分散到每个项目里,后面续期、挂载、reload 和备份都会变麻烦。
数据库和内部服务不能暴露公网
项目内部服务应该留在内部网络里,由 Nginx 统一代理,数据库只给项目内部访问。
多项目要能扩展
今天只有博客,明天可能有官网,后天还有其他工具。新增项目时,接入方式应该固定,而不是每次重新设计。
迁移要清楚
以后换服务器时,我要知道 Nginx 配置在哪里,证书在哪里,项目在哪里,数据在哪里。
这些问题合起来,就是一句话:外部入口集中,内部项目独立。
整体架构设计
整体架构大概是:
公网用户
-> 域名 DNS
-> 服务器 80/443
-> 全局 Nginx 容器
-> 不同项目服务
更具体一点:
blog.aicultiv.com -> 全局 Nginx -> blog-app 容器
www.aicultiv.com -> 全局 Nginx -> site-app 容器或静态目录
api.aicultiv.com -> 全局 Nginx -> api-service 容器
数据库不对公网开放:
blog-app 容器 -> blog-db 容器
每个项目内部可以用自己的 Docker Compose 管理应用、数据库、缓存和后台任务。
但公网入口统一交给全局 Nginx。
全局 Nginx 管外部访问,项目 Compose 管内部服务,数据库只服务项目内部,HTTPS 证书集中放在统一位置。
服务器目录
为了长期维护,我会先规划服务器目录,例如:
/opt/coduty/
nginx/
compose.yml
conf.d/
certs/
logs/
projects/
blog/
compose.yml
.env
data/
site/
compose.yml
.env
dist/
backups/
scripts/
这不是唯一标准,大家可按照自己的习惯,自主规划。核心是:
- 全局
Nginx 单独一个目录,每个项目单独一个目录,配置、证书、日志、数据分开放,备份和脚本有明确位置 - 想改
Nginx,就去nginx/conf.d - 想看证书,就去
nginx/certs - 想看博客项目,就去
projects/blog - 目录结构会直接影响后续排查和迁移
全局 Nginx 的第一职责是接管公网入口,所以它会绑定宿主机的 80 和 443。示意配置如下:
services:
nginx:
image: nginx:alpine
container_name: global-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./conf.d:/etc/nginx/conf.d:ro
- ./certs:/etc/nginx/certs:ro
- ./logs:/var/log/nginx
networks:
- public
networks:
public:
external: true
这里有几个要点:
- 只有全局 Nginx 绑定公网 80 和 443
- 其他项目尽量不直接映射公网端口
- Nginx 配置通过挂载进入容器,方便编辑、备份和迁移
- 证书也通过挂载进入容器
- Nginx 加入一个外部公共网络,后面需要被代理的项目服务也加入这个网络
公共网络
全局 Nginx 要代理到不同项目容器,就必须能访问它们。不同 Compose 项目默认不一定能互相访问,所以我会创建一个公共网络,例如:
docker network create coduty-public
全局 Nginx 加入这个网络,需要被 Nginx 访问的应用容器也加入这个网络。项目内部服务仍然使用自己的默认网络。数据库通常不加入公共网络,只给项目内部应用访问。
项目服务示意:
services:
app:
networks:
- default
- coduty-public
networks:
coduty-public:
external: true
这样外部入口和内部数据服务就分开了。
域名转发由全局 Nginx 的 server 配置处理。例如博客:
server {
listen 80;
server_name blog.example.com;
location / {
proxy_pass http://blog-app:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
这只是 HTTP 示例。后面配置 HTTPS 时,还要加 443、证书路径和 HTTP 到 HTTPS 跳转。
重点是:域名匹配放在全局 Nginx,项目服务只在内部提供端口,Nginx 根据域名转发到对应项目。
新增项目流程
新增项目时,流程会比较固定:
- 项目服务加入公共网络
- 确认服务名和端口
- 在全局
Nginx增加 server 配置 - 检查
Nginx语法 reload Nginx- 配置 DNS 和 HTTPS。
SSL证书管理
证书也要集中管理。我的倾向是放在全局 Nginx 目录下,例如:
/opt/coduty/nginx/certs/
example.com/
fullchain.pem
privkey.pem
Nginx 配置引用这些证书。这样证书申请、部署、续期和 reload 都集中在一个地方。如果后面使用通配符证书,比如 *.example.com,多个子域名可以共用同一套证书。具体通配符证书通常涉及 DNS 验证和私钥保护,后面需要单独处理。
小结
全局 Nginx 统一入口,并不意味着项目混在一起。相反,项目内部要保持独立。
博客项目有自己的 compose.yml、.env、应用容器、数据库容器、数据卷、初始化脚本和备份目录。
官网项目也可以有自己的构建流程、静态文件目录和部署容器。
全局 Nginx 只关心一件事:这个域名应该转发到哪个内部服务。它不直接管理项目内部数据库,也不承担业务逻辑。
这个方案的好处很明确:
- 公网入口清晰,只有全局 Nginx 占用 80 和 443
- 端口暴露更少,项目内部服务不直接暴露公网
- 证书集中管理
- 新增项目有固定接入流程
- 访问失败时可以按 DNS、安全组、防火墙、全局 Nginx、项目容器、应用日志逐层排查
- 迁移时 Nginx 配置、证书和项目目录都有固定位置。
风险也要提前规避:
- 全局 Nginx 会成为关键节点,一旦配置错误,可能影响多个项目
- 所以需要操作严格规范:改配置前备份;改完先检查语法;通过后再 reload;
- 每个域名一个配置文件,不把所有规则堆在一个大文件里
- 日志要能查。
容器化 Nginx 可以这样检查配置:
docker exec global-nginx nginx -t
检查通过后再 reload:
docker exec global-nginx nginx -s reload
配置文件建议拆开:
conf.d/
blog.aicultiv.com.conf
www.aicultiv.com.conf
api.aicultiv.com.conf
最终方案可以概括为:
- 公网只进入全局
Nginx的80 和 443 Nginx根据域名转发到不同项目- 项目内部仍然独立,用 Docker Compose 管理应用、数据库和数据卷
- 数据库不暴露公网
- 证书集中管理
- 新增项目按统一规则接入
这个方案不是最复杂的,也不一定是正确的,条条大路通罗马,挺适合我的应用场景:一台机器,多个项目,一个人长期维护。
下一篇会把方案落到操作层面:全局 Nginx 容器实战,公共网络、配置文件与启动检查。
本文由 楸木 原创,转载请注明出处。