在单台云服务器上同时部署多个 Web 服务(如 Halo 博客、Wiselink 前端项目等)时,很多新手站长最常犯的错误就是把所有服务的端口(如 8090、8080)一股脑全部暴露给公网。这不仅导致网址后面带着丑陋的端口尾巴,更给黑客大军留下了无数个可以暴力破解的扫描后门。
今晚,我们将全盘复盘一套标准的企业级微服务网络架构。通过引入 Docker 自定义虚拟网络 与 Nginx Proxy Manager (NPM) 统一网关,实现全站仅对外暴露 80/443 端口,所有内部服务“隐身”运行、内网互通,彻底完成从“能运行”到“高安全”的架构蜕变!
⚠️ 写在前面的核心前置声明
在正式阅读本文的架构搭建前,必须明确一个技术大前提:我们目前手里只有一台纯公网 IP 的云服务器,还没有绑定或备案正规的域名。
很多人误以为只要装了 NPM 网关,就能在同一个 IP 的 80 端口下无限衍生出不同的网站。但由于 Nginx 的底层匹配机制,在没有域名作为“主机头(Host Header)”区分的情况下,NPM 面板中一个公网 IP 只能被普通 Proxy 规则绑定一次。这就导致 Halo 博客一旦占用了纯 IP 的 80 端口,Wiselink 前端服务在 80 端口上就会面临冲突。
因此,本文记录的架构,是在纯 IP 访问的限制下,如何通过“统一网关基础设施 + 内部虚拟网络 + 外部临时多端口”实现的过渡期妥协方案。它虽然没有在单 IP 的 80 端口上彻底解决业务冲突,但它为你铺设好了未来的微服务底座。一旦后续域名备案通过,这套架构将秒变无缝分流的完全体!
🎨 核心架构设计蓝图:流量是怎么流转的?
在开始动手之前,我们先在脑海中建立起这套经典的反向代理流量模型。请求就像是一个快递,一路上会经过三个关卡:
第一关:流量总入口(Gateway) 用户在浏览器输入标准的网址
[http://1.123.456.789/](http://1.123.456.789/)(默认 80 端口),请求最先到达部署在最外层的 Nginx Proxy Manager (NPM)。第二关:通信主管道(Docker 虚拟网络) 网关(NPM)在 80 端口接到请求后,根据我们在后台配置的路由规则(Proxy Host),通过一条专门的内部专属通道 ——
wiselink-net虚拟网络,精准地把请求分流转发。第三关:业务隔离层(Halo / Wiselink) Halo 博客和 Wiselink 前端容器作为独立的微服务节点,安心在小房间(沙箱环境)里监听各自的端口。因为有虚拟网络的存在,网关可以直接通过容器名(Container Name)找到它们,根本不需要暴露任何多余的公网端口!
🛠️ 第一阶段:打通内网,搭建 Docker 虚拟网络
在正式部署任何容器之前,我们必须先在 Linux 宿主机上打通这条通信管道。这就好比建房子前先铺设地下的自来水管网。
1. 创建外部自定义网络
我们需要创建一个名为 wiselink-net 的桥接网络。这样做的目的是为了实现跨项目通信,让不同 docker-compose.yml 文件里启动的容器能够无缝加入同一个局域网。
在终端执行以下 Linux 命令:
Bash
docker network create wiselink-net
2. 验证网络是否创建成功
执行以下命令查看当前 Docker 的网络列表,确认 wiselink-net 已经处于就绪状态:
Bash
docker network ls
🌐 第二阶段:搭建统一网关层(Gateway)
网关是整个架构的“门卫大爷”,负责拦截公网的所有请求,并提供可视化的 Web 管理界面。
1. 编写网关的 Docker 配置文件
我们在服务器的 /opt/my-server/gateway 目录下创建 docker-compose.yml 配置文件。
💡 避坑小贴士:在最初的测试调试阶段,我们可以在网关的
ports规则里临时加开8080端口用于排查前端问题。在后续生产环境完全稳定后,应将其移除,仅保留标准的 80、443 端口。
YAML
version: '3.8'
services:
gateway:
image: 'jc21/nginx-proxy-manager:latest'
container_name: wiselink-gateway
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
# 临时加开 8080 端口,用于 IP 环境下测试访问内部的前端项目
- '8080:8080'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- wiselink-net
networks:
wiselink-net:
external: true
2. 以后台守护进程模式启动网关
在 /opt/my-server/gateway 目录下执行以下标准 Docker 命令启动网关:
Bash
docker compose up -d
启动成功后,通过浏览器访问 http://你的服务器IP:81 就可以无缝进入 NPM 的可视化图形管理后台。
📝 第三阶段:应用层部署与接入(以 Halo 博客为例)
有了网关和虚拟网络这条“高速公路”后,我们的应用层服务(如 Halo 博客)就可以非常规范地加入这个生态圈了。
1. 编写 Halo 博客的 Docker 配置文件
请注意这里的核心逻辑:我们通过 ports 将宿主机的 8090 和容器内的 8090 建立映射,并且在 networks 节点明确声明加入 wiselink-net,从而让它和 NPM 网关处于同一个局域网内。
YAML
version: '3.8'
services:
halo:
image: halohub/halo:2.10.2
container_name: halo
restart: unless-stopped
ports:
- "8090:8090"
environment:
- halo.external-url=http://1.123.456.789/
volumes:
- ./halo2:/root/.halo2
networks:
- wiselink-net
networks:
wiselink-net:
external: true
2. 启动应用并实时监控日志
执行以下组合命令,先停止旧容器(如有),再拉起新容器,并持续跟踪控制台日志:
Bash
docker compose down
docker compose up -d
docker logs -f halo
⚠️ 高能预警:关于启动时
onErrorDropped报错的硬核科普 如果你在初始化 Halo 2.10.x 版本时,在日志顶部赫然看到一长串红色的LockObtainFailedException: Lock held by this virtual machine堆栈报错,请把心死死放回肚子里,完全不需要惊慌! 这是由于该版本在启动过程中,高并发的响应式主线程与内部插件初始化线程在极短时间内竞争 Lucene 索引文件锁而导致的“良性 Bug”。这纯粹是代码缺陷导致日志信息过于激进。只要你的日志最后输出了Initialized post indices(索引加载完毕),就说明你的博客功能是完美的,对后续运行毫无任何副作用!
3. 理解面子配置:halo.external-url 的真实身份
很多人在配置这个参数时一头雾水,甚至以为它是个安全防火墙(不符合就拒绝访问)。
真相是:它只是 Halo 的一个**“指路牌配置”**。它用来告诉 Halo 软件自己:“外部用户是用什么网址看到我的”。 它的核心作用是决定你的博客文章里生成的图片路径、超链接路径长什么样。如果你用其他带端口的地址溜进去,Halo 不仅不会拒绝你,反而会温和地让你进入,但由于生成的图片链接依然固执地走
external-url设定的地址,会导致网页出现跨域、图片全碎、样式乱套的“精神分裂”Bug 现场。因此,这个参数必须与你最终对外的访问地址严格保持一致!
🔗 第四阶段:纯 IP 环境下的端口分流局限与未来域名完全体展望
当所有的容器都成功跑在 wiselink-net 虚拟网络后,我们终于迎来了最核心的路由配置。
1. 为什么纯 IP 无法在 80 端口直接实现多业务共存?
在 NPM(Nginx Proxy Manager)中,有一个硬性底线:同一个公网 IP 作为 Domain Name 在普通面板里只能被绑定一次。 如果你的 Halo 博客已经占用了 1.123.456.789 这一条纯 IP 的 80 端口规则,那么当你在 NPM 里试图为 Wiselink 前端项目再次添加该 IP 时,系统就会无情地弹出 1.123.456.789 is already in use 报错。在没有域名作为“主机头”区分的情况下,纯 IP 无法在同一个 80 端口下衍生出多条普通代理规则。
2. 现阶段的曲线救国:外部多端口分流(当前方案)
为了打破这个僵局,我们目前采用的是“网关统一走内网,外部多端口迎客”的过渡方案:
Halo 博客:由网关承接 80 端口的纯 IP 流量,转发至内网
halo:8090。Wiselink 前端:我们在网关临时加开了
8080(或服务器直连8000)端口,走独立端口通道。 虽然网址后面暂时拖着一个端口尾巴,但这种通过物理端口切分流量的方式,是现阶段最稳妥的折中解法。
3. 🌟 极速内网直连:Docker 虚拟网络的威力
注意到了吗?因为网关和业务容器大家都身处在同一个 wiselink-net 虚拟网络中,我们在 NPM 的转发目标(Forward Hostname)里,可以直接填写容器的名字(如 halo 或 wiselink-frontend)!Docker 内部自带的 DNS 服务会自动帮我们做秒级解析,完全不需要填写繁琐、易变且不安全的内部局域网 IP。
4. 未来的终极形态:二级域名完全体分流(展望篇)
一旦你的域名购买并备案成功,这套架构将迎来不需要任何非常规端口的“完全体形态”。届时你只需要在云厂商后台将两个子域名解析到该 IP,然后在 NPM 里配置两条互不干扰的 80 端口规则,真正实现多业务共存:
路由规则 A:
blog.yourdomain.com➡️ 转发至内网http://halo:8090路由规则 B:
wiselink.yourdomain.com➡️ 转发至内网http://wiselink-frontend:8000
🔒 第五阶段:全面收紧安全组,完成线上安全闭环
恭喜你!当你通过上述步骤,让所有服务都能够通过规范的 80 端口或者专属端口正常访问后,就到了最后也是最重要的一步 —— 卸磨杀驴,收紧防火墙。
关闭公网隐患端口:立刻登录你的阿里云/腾讯云服务器控制台,进入安全组规则,毫不留情地删除 8090、81 以及其他测试用的内部端口的公网入站规则。
实现最小化暴露:整个服务器的公网,只留下
80(HTTP 标准端口)和443(HTTPS 加密端口)对公众开放。
💡 本阶段实战总结: 通过这波安全操作,我们成功将多余的漏洞端口彻底封死在公网之外。虽然在目前纯 IP 的限制下,博客和前端项目不得不借助 80 和 8080 两个不同的外部端口来打通访问,但通过 NPM 网关的统一收纳,所有恶意请求和黑客探针都必须经过网关这扇唯一的“正门”。我们不仅在安全层面上构建了坚固的闭环,更为主机铺设好了高扩展性的微服务网络,只静待域名的加入,即可一键解封完全体!