Docker 部署 Nginx 实现泛型二级域名自动分配 如果你手头恰好有一台云服务器、一个已备案的域名又不想每次上线新服务都去手动改Nginx配置那“Docker部署Nginx做泛型二级域名自动分配”这套方案值得看一下。我最近刚把个人项目从“一个服务一个端口”切到“一个域名通吃所有子服务”体验完全不一样新增一个演示站不用碰服务器上的任何配置文件只是起一个容器把域名按规则指给Nginx剩下的事情它自己分发。这篇就把完整过程、配置思路、坑位和排查方法都写清楚适合正在用Docker跑多个小项目、又对域名路由方式有点纠结的人参考。整个方案的核心就两句话DNS那边把*.example.com泛解析到你的服务器IPNginx这边通过泛域名规则把不同二级域名反向代理到不同容器。听起来简单真正落地时涉及的细节其实不少从证书签发到容器网络每步都有自己的脾气。下面按实际部署顺序拆开讲。1. 泛型二级域名分配是什么为什么要用 Nginx Docker1.1 一个域名通吃所有子服务的泛解析逻辑泛型二级域名分配简单说就是让a.example.com、b.example.com、c.example.com这类二级域名自动落到不同的服务上而你不需要为每个服务单独申请域名、单独配置DNS记录。DNS侧只要加一条*.example.com的A记录指向服务器IPHTTP服务侧再根据请求里的Host字段去匹配具体该交给哪个后端处理。这里的关键点在于域名解析只解决“这个域名指向哪台机器”解决不了“这个域名对应哪个服务”。所以真正做分配的是HTTP反向代理层。它收到请求后读取域名按预先定义的规则把流量转发到指定的容器或端口。这套机制在多服务场景下特别实用我举个例子你有一个博客系统、一个API服务、一个文件预览服务如果不用泛域名你得维护三个域名、三条DNS记录、三个证书用了泛域名之后全部收敛到一个域名下的二级域名里看起来整齐管理成本也低。DNS泛解析也不是新鲜东西但以前大家不愿意用一是因为证书签发麻烦二是动态增加服务时网关配置仍然要手工改。直到容器化普及、自动签发证书工具成熟之后泛域名分配才变成了一套很顺手的方案。你需要意识到DNS和Nginx是分工协作的关系DNS负责把流量引导到服务器Nginx负责把流量引导到具体服务两者缺一不可。1.2 为什么不用“每个服务一个端口”或“手动加站点配置”很多人在小规模阶段会用端口区分服务比如博客跑在8080API跑在8081文件服务跑在8082。这么做在一两台服务器、三五个服务的时候是能用的但问题会慢慢浮现浏览器默认只对80和443友好输入example.com:8080总显得不够正规有的网络环境会封非标准端口团队协作时记住每个服务的端口号本身就是额外负担。更重要的是端口方案没法在HTTPS证书上做到统一每个端口对应一个独立证书就很难看了。另一种常见的思路是每个新服务都手动写一个Nginx server块再reload。这也不算错但当一个项目组内大家都要频繁上线演示环境或分支预览时手动操作的效率就很低。我自己的实践感受是如果一个月只加一个新服务手写配置无所谓如果一周内可能反复创建、销毁临时环境就必须把“分配”这件事做简化。泛域名分配正好把“二级域名到容器”的映射收敛到一两行配置并且可以利用同一套正则规则去覆盖一类服务。其实也不一定非要用Nginx做网关像其他几个开源网关也能做类似的事。但Nginx的配置模型最简单直接容器生态下的镜像也成熟学习成本和调试成本比较低所以我个人更推荐用Nginx来承担泛域名分发这一层。1.3 Nginx 在整套容器化方案里的定位在这套方案里Nginx是作为请求入口存在的它承担三件事终止TLS、按Host路由、反向代理到内部容器。你可能会问为什么不让业务容器直接暴露端口非要绕一层Nginx因为入口统一之后无论是日志、访问控制、请求大小限制还是跨服务复用都能在同一个地方处理。从容器网络角度看Nginx容器和业务容器最好处于同一个Docker网络中这样Nginx可以用服务名或容器IP访问后端。我习惯的做法是让Nginx容器单独暴露80和443给宿主机其他业务容器一律不映射端口到宿主机只加入同一网络。这样做最大的好处是避免端口冲突也不会因为业务容器重启导致宿主机网络配置残留。还要强调的是Nginx处理泛域名分配时不是靠多个server_name一条条去写的而是利用正则匹配把一组域名归拢到同一个 server 块里。比如所有带-demo.前缀的三级场景或者不同二级域名指向同一组后端都可以用正则简化配置。这就是泛型分配和普通虚拟主机的本质区别。2. 部署前的核心准备域名、DNS 与服务器2.1 DNS 侧必须配置好的两条记录这里先说一条容易踩的坑只有泛解析记录没有根域名的记录很多服务和证书签发工具会不工作。我建议至少配两条A记录都是指向你的服务器IP一条是example.com本身另一条是*.example.com。前者用来访问主站后者用来匹配所有二级域名。如果你的域名是在常见的DNS面板里管理操作路径通常是在解析设置里添加记录记录类型选A主机记录填*或记录值填服务器公网IPTTL可以先设 600 秒调试期间方便生效稳定后再调高到 3600 甚至更高。注意*.example.com这种泛解析记录并不包含example.com本身所以根域名那条别省。TTL这个参数容易被忽略。改DNS解析后不是立刻全局生效的老的递归服务器会按原有TTL缓存。如果你调试时发现域名怎么都不解析到新IP先看TTL把值调低可以缩短等待时间。泛解析记录生效后可以用本机nslookup或dig命令验证一下例如dig a.demo.example.com如果返回你的服务器IP就说明DNS侧已经通了。2.2 服务器侧需要开放的端口与安全组策略服务器上的防火墙和安全组同样不能忘。你至少要确保80端口和443端口对外可达。80用于HTTP重定向到HTTPS以及一些验证场景443用于正式流量。如果云厂商有安全组概念记得在安全组入方向也放行这两个端口否则服务器内部防火墙开了也没用。除了入方向Nginx容器要访问业务容器通常走Docker内部网络不经过宿主机端口映射因此不需要额外开放业务端口。这条可以帮你减少暴露面也让整套方案在安全审计上更说得过去。我的建议是宿主机除22SSH、80、443外其他端口一律不放行。业务容器若需要调试可以临时用docker exec进入容器查看不需要把调试端口暴露出去。这里顺便说一句如果打算后续用容器方式自动签发HTTPS证书80端口在证书验证阶段会被用来响应挑战请求所以不要把80端口占给其他Web服务。我见过有人把80端口先给了一个静态站点结果证书签发失败排查了半天才发现是端口冲突。2.3 请求从浏览器到业务容器的完整路径理清数据流有助于后面排查问题。整套链路是这样的浏览器访问a.example.com先做DNS解析拿到服务器IP请求到达服务器80或443端口由Nginx容器接收Nginx根据$host变量判断是哪个二级域名查找匹配的 location 或 server 块然后把请求反向代理到业务容器的内部端口业务容器处理完成后把响应原路返回。知道这条链路之后遇到问题就能按段定位域名解析不通先查DNS记录HTTP能访问但HTTPS失败去查证书和TLS配置Nginx返回502多半是后端容器连不上或者容器名写错了。后面排查章节会详细展开这里只需要记住一句话流量是逐层转发的每一层都有日志顺着日志找断点。我在实际部署时还会在Nginx配置里加一个默认拒绝站点把未匹配到的域名直接返回444。这样做一方面可以快速发现异常流量另一方面避免误配置导致请求落到不该落的地方。这条细节虽然不影响正常功能但对线上环境很有价值。3. Docker 部署 Nginx 并启用泛域名路由3.1 用 Docker 跑 Nginx 而不是直接在宿主机安装的原因有朋友问我直接用apt install nginx不也能做泛域名转发吗为什么非要套一层Docker。我的理由有三个。第一隔离性宿主机上的Nginx如果配置失误可能会影响同一台机器上的其他组件容器化之后问题出在容器内删掉重建即可。第二可迁移性这套配置目录、容器编排文件可以整体带到另一台服务器不用重新装环境和处理依赖。第三和业务容器一致既然后端都已经容器化了网关也容器化网络模式、资源限制、日志收集都可以用同一套Docker工具链管理。当然容器化也有代价比如性能上会有极少量的损耗但在一台普通的云服务器上处理几千并发完全感受不到差别。我更看重的是运维体验的一致性。至少我现在新增一个服务不需要SSH到机器上再敲一遍nginx的安装命令所有东西都在Docker Compose项目里维护。3.2 目录挂载、网络规划与 Compose 文件骨架我的Nginx容器目录结构大概长这样宿主机上建一个nginx/目录下面放conf.d/存放各个server配置文件certbot/或ssl/存放证书html/放默认页面。用Docker挂载进去之后改配置只需要编辑宿主机文件再执行reload即可不需要进入容器改文件。Docker Compose是管理这套方案最舒服的方式。下面给一个骨架示例version: 3 services: nginx: image: nginx:stable-alpine container_name: gateway-nginx restart: always ports: - 80:80 - 443:443 volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/ssl:/etc/nginx/ssl:ro - ./nginx/html:/usr/share/nginx/html:ro networks: - app_net networks: app_net: external: true我特意把网络声明成external: true也就是预先创建一个名为app_net的Docker网络让Nginx和所有业务容器都加入这个网络。这样后续每个业务容器启动时只要用--network app_net或者Compose里声明同一个外部网络Nginx就能直接通过容器名访问它们。这种做法比每次docker run默认网络更可控也省去手动查IP的麻烦。创建外部网络只需一条命令docker network create app_net注意容器名就是Nginx访问后端的主机名。比如一个容器叫blog-apiNginx配置里写proxy_pass http://blog-api:8080;就可以了Docker内置DNS会自动解析。这个机制是整套方案能“自动分配”的基础。3.3 泛域名匹配的 Nginx 核心配置写法Nginx的泛域名匹配最核心的就是server_name支持前缀通配符、后缀通配符和正则。最简单的泛二级域名写法是server { listen 80; server_name *.example.com; ... }这样a.example.com和b.example.com都会进入这个块。那如何区分不同二级域名呢一种方式是写多个server块每个对应一个固定二级域名这适合服务数量很少的情况。另一种方式是只保留一个泛域名入口在 location 里用正则捕获域名前缀再动态选择后端这种方式适合同一类服务的批量分发。举例说明我想让blog.example.com转发到blog-container:8080让api.example.com转发到api-container:3000可以分别写两个server块每个块内部做反向代理。这种做法直观好读推荐给大多数场景。server { server_name blog.example.com; listen 443 ssl; # ssl 配置略 location / { proxy_pass http://blog-container:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } server { server_name api.example.com; listen 443 ssl; # ssl 配置略 location / { proxy_pass http://api-container:3000; } }如果服务太多或者希望用一个配置模板覆盖所有同类服务可以改用带正则的server_name。比如统一使用~^(?subdemo-\w)\.example\.com$然后在配置里通过变量构造后端地址server { server_name ~^(?sub[a-z0-9-])\.example\.com$; listen 443 ssl; location / { proxy_pass http://${sub}-service:8080; } }这里有个容易出错的地方proxy_pass如果使用变量作为目标地址Nginx要求目标地址至少包含域名和端口而且如果域名变量解析不了会在请求时直接报502。我一开始就被这个坑折磨过因为每次改完配置都要确认${sub}-service这个容器名真实存在。如果前缀和后缀对不上整个请求就会失败。因此用正则动态构造后端时命名规范必须统一比如所有后端容器都叫xxx-service并且这里的xxx必须和二级域名的前缀一致。如果服务数量不多我还是建议用显式server块虽然多几行配置但出错概率低日志也更好读。动态正则方案适合“一定范围的服务可以按规则枚举出来”的场景。3.4 容器启动与配置热加载的正确姿势配置文件写好之后先用docker compose config校验一下Compose文件格式然后启动Nginxdocker compose up -d nginx如果后续改了Nginx配置不需要整个容器重启执行docker exec gateway-nginx nginx -t docker exec gateway-nginx nginx -s reloadnginx -t这步必须先做它检查语法错误如果不过reload会失败并可能导致旧的配置继续运行。我习惯把这两条命令写成一个shell脚本叫做reload-nginx.sh放到项目根目录每次改配置就执行一下。这里要特别提醒Docker挂载配置目录用了:ro这是有意为之防止容器内部被意外篡改也提醒你在宿主机侧维护配置。但如果配置目录里的文件很多reload时会逐个解析文件太多会导致reload延迟尽量把无关的历史配置文件清理掉。我还会给Nginx容器配置restart: always这样即使服务器意外重启Nginx也会自动跟随Docker守护进程启动。如果你还加了证书自动续期的定时任务重启后要确认容器依赖顺序正常否则可能出现Nginx已经启动但证书还没挂上导致启动失败。个人经验是证书相关的问题后面第5章再展开这里先把容器跑起来再说。4. 泛域名到业务容器的实际分发实现4.1 固定二级域名到单一容器的标准配置最常见的场景就是“一个二级域名对应一个容器”。你只需要准备两个东西业务容器名和它的内部端口。然后在Nginx配置里新增一个server块监听80或443server_name写成固定的二级域名location里做proxy_pass。以博客系统为例假如容器名是my-blog内部端口是 8000配置可以是这样server { server_name blog.example.com; listen 80; location / { proxy_pass http://my-blog:8000; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto $scheme; } }这种配置的优点是清晰一个server块只服务于一个二级域名出现问题单独排查。但需要注意一点如果后端容器不在同一个Docker网络里proxy_pass http://my-blog:8000会解析不到主机名。遇到这个问题时不要急着改成IP先检查容器是否加入了app_net网络。把业务容器加入网络之后同一网络内部的容器名DNS解析立刻生效。4.2 用通配符统一入口并分流转发的进阶玩法如果业务模式是按用户或项目创建临时环境比如team-a.demo.example.com、team-b.demo.example.com写死server块就不现实了。这时候要用通配符或正则在入口层把二级域名接收下来再往后端分发。一个可行的设计是所有临时环境共用同一个后端网关容器这个容器接收到请求后从域名前缀里解析出项目名或用户ID再分发到对应的子服务。Nginx这层只负责把泛域名流量转交给网关不做具体业务判断。或者让Nginx把${sub}提取出来直接转发到对应容器。示例server { listen 80; server_name ~^(?sub[a-z0-9-])\.example\.com$; location / { proxy_pass http://${sub}:8080; } }这个写法要求你为每个项目启动一个以该项目名命名的容器容器内部服务监听8080。这样新增项目的步骤就变成了两步启动一个新容器命名和二级域名前缀一致。不需要改任何Nginx配置。这正是“泛型二级域名自动分配”最有吸引力的地方。当然这种动态模式也有限制如果二级域名前缀对应的容器不存在Nginx会返回502。所以你至少得写一个兜底的server块让没有对应容器的域名返回一个友好的提示页或者直接返回444。我个人会在默认server块里返回一个占位提示然后通过监控告警来发现是否有非预期域名访问。4.3 泛解析、默认 server 和正则之间怎么配合Nginx匹配server_name的顺序有讲究精确匹配优先以*开头的通配符其次以*结尾的通配符再次最后才是正则匹配。所以你可以同时配置几个固定的精确域名再配置一个正则泛域名Nginx会优先处理精确命中的域名。这个优先级在实际项目中很实用。比如你想让api.example.com固定走API网关其他所有二级域名走临时环境入口。那么api.example.com用精确server块临时环境用正则server块二者互不干扰。还要注意当域名没有匹配到任何server块时Nginx会使用默认的default_server。如果你没有显式配置它会把第一个监听的server块当作默认。这里我强烈建议你显式定义默认server块避免未匹配的域名把流量意外喂给某个业务容器。一个简单的默认块如下server { listen 80 default_server; listen 443 ssl default_server; server_name _; ssl_certificate /etc/nginx/ssl/default.crt; ssl_certificate_key /etc/nginx/ssl/default.key; return 444; }return 444是Nginx特有的非标准状态码含义是直接关闭连接不返回任何内容。对扫描工具来说这种响应方式很干净不暴露服务器指纹。不过我提醒一句HTTPS的默认server块需要至少一个合法证书否则SSL握手阶段就可能失败导致服务异常。4.4 后端容器容量与安全限制泛域名分配让新增服务变得很容易但也容易让人忽视安全策略。比如某个后端容器仅限内网访问却在配置里被泛域名规则意外暴露。为了减少这类风险我给每个业务容器起名时遵循统一约定的同时还会在Nginx层加一层访问控制比如仅允许特定IP段的请求访问某些敏感二级域名。如果你的服务分类很明确可以在Nginx里分开写多个泛域名匹配块比如*.public.example.com走公开入口*.internal.example.com走带有IP白名单的入口。虽然DNS泛解析是所有子域名都指向服务器但Nginx可以根据域名正则做更细粒度的分流。这其实也是一种基于Host的精细路由。容器侧资源限制别忘了加。泛域名分配的副作用是只要给一个二级域名起了一个容器它就在你的服务器上占资源。如果某个临时环境容器消耗资源过高会影响网关和其他服务。我用Docker Compose的deploy.resources.limits或者直接在docker run里限制内存和CPU至少保证单个容器不会把整个宿主机的资源吃满。这一步不要偷懒等真的出了故障再补就晚了。5. HTTPS 证书与泛域名证书自动续期5.1 为什么泛域名证书比单域名证书麻烦一点二级域名数量多不可能每个域名都手动申请一张证书。虽然Nginx支持配置多个证书文件但每新增一个二级域名就要签一张新证书维护成本立刻上来了。更合适的做法是使用泛域名证书它覆盖*.example.com以及example.com本身。一张证书就能让你所有二级域名都走HTTPS。泛域名证书的申请验证方式通常有两种HTTP-01和DNS-01。HTTP-01要求你有一个可访问的地址在域名指向的服务器上放置一个临时文件CA通过HTTP请求验证你对域名的控制权。这种方式对单个域名很方便但对泛域名来说有个问题HTTP-01挑战是针对具体域名的比如a.example.com虽然泛解析能让它指向服务器但你没法预知每个新二级域名会来验证配置起来比较别扭。所以泛域名证书一般用DNS-01验证也就是要求在DNS记录里添加一个指定的TXT记录证明你控制整个域名。你只需要给_acme-challenge.example.com添加一条TXT记录就能签出覆盖所有二级域名的证书。这也是目前最主流的泛域名证书申请方式。5.2 用 Certbot 容器处理泛域名申请与续期我习惯用某个开源证书客户端工具配合DNS插件来完成DNS-01验证前提是你的DNS服务商支持API操作TXT记录。流程大致如下启动一个证书客户端容器让它调用DNS服务商API创建TXT记录等验证通过后签发证书最后证书文件输出到宿主机目录Nginx里指向这个目录。如果你用的DNS厂商插件不是现成的也有变通办法手动添加TXT记录完成首次签发随后续期时再用定时脚本更新TXT记录。手动方式虽然繁琐但胜在不受插件限制适合域名解析在自建或小众平台上的场景只需要在每次续期前留出足够时间让TXT记录生效。证书签下来之后Nginx容器需要挂载证书目录并定期reload。具体到Nginx配置把ssl_certificate和ssl_certificate_key指向同一个泛域名证书文件路径即可。泛域名证书可以覆盖所有二级域名所以在每个server块里你其实只需要写同一组证书路径不用为每个域名单独准备证书。这极大简化了配置。5.3 证书续期的坑位清单第一个坑证书续期脚本运行的时间点要避开业务高峰因为DNS-01验证期间TXT记录会短暂存在如果脚本失败可能残留旧记录影响下一次验证。我一般把续期任务定在凌晨2点到4点之间并配置失败重试3次。第二个坑续期成功之后一定要让Nginx重新加载证书否则Nginx仍会继续使用旧证书直到进程重启或reload。这步没有自动做的话证书到期前你可能会看到客户端报警。解决方案很简单在续期脚本末尾加一条docker exec gateway-nginx nginx -s reload。第三个坑不要把证书文件放在容器内部直接改。因为容器可能被重建内部文件会丢失。正确做法是让证书目录挂载自宿主机续期脚本在宿主机上执行并把新证书写到该目录保证Nginx容器挂载的文件始终是最新的。如果挂载方式不当Nginx容器重建后证书丢失HTTPS直接瘫痪属于我最不想遇到的事故类型。5.4 有泛域名证书后Nginx 配置如何统一拿到泛域名证书后一个比较巧妙的做法是把SSL证书配置直接写到默认server块里因为Nginx的配置继承特性在同一个监听端口端口上子server块可以不重复写证书但这需要你整体设计一致。不过我为了减少配置耦合还是会每个业务server块都写上证书路径多几行但清晰。还有一点HTTP访问需要无条件跳转到HTTPS。我通常会在80端口的每个server块里写一个return 301 https://$host$request_uri;或者单独用一个只监听80的server块统一做跳转。因为80和443是同一个Nginx容器在监听不需要额外开其他服务。如果你某个二级域名暂时不想开放HTTPS可以保留80服务但建议至少把没有证书的域名严格限定在测试环境。注意使用return 301做强制跳转时$host保留了原始的二级域名这样跳到HTTPS后Nginx还能继续按域名路由。不会因为跳转后域名丢失导致Nginx匹配不到正确的server块。6. 踩坑排查与运维小技巧6.1 泛域名分配常见问题速查表下面整理了我实际操作中遇到的问题和解决思路按症状排查会比较快问题现象可能原因排查方法解决动作二级域名解析不到服务器DNS泛解析记录未生效dig 任意二级域名检查A记录等待TTL或调低TTL访问HTTP不通安全组或防火墙未放行80本地telnet 服务器IP 80放行安全组和防火墙重启Nginx访问HTTPS提示证书错误证书与域名不匹配浏览器查看证书详情确认泛域名证书覆盖*.example.comNginx返回502后端容器未加入同一网络docker network inspect将业务容器加入app_netNginx返回404未匹配到server块curl -H Host: xxx.example.com检查默认server与server_namereload失败配置文件语法错误nginx -t修正语法后重载证书自动续期后还是旧证书未执行reload查看证书签发时间续期脚本末尾加reload命令部分二级域名访问慢DNS解析跨运营商慢dig trace更换DNS服务商或调高TTL这张表解决90%的常规问题。关键在于不管哪个环节出了问题都要先确认流量走到哪一层了。如果连IP都ping不通就从防火墙和安全组查起如果IP通但HTTP打不开就查Nginx容器是否监听如果HTTP通但HTTPS拒绝就查证书和443端口。6.2 我个人验证过有效的几个配置习惯在把所有服务都纳入泛域名分配模式后我慢慢养成了一套操作习惯这里分享三条。第一每个泛解析二级域名默认都能访问到服务器但绝不代表每个域名都应该能访问到某个业务容器。我会在所有正则匹配的server块里加上请求头校验或拒绝无Host请求的规则把异常流量挡在外面。Nginx默认情况下如果请求没有带Host头会走默认server默认server我配置成444所以那些直接在服务器IP上发来的请求会被直接断开。第二给每个业务容器打上命名标签并且Nginx的server块和容器命名规则保持一致。这个看起来很小但能省很多事。比如我用blog-前缀命名所有和博客相关的容器用demo-前缀命名临时演示容器。这样配置里的正则表达式一目了然新同事接手的时候也容易理解。第三容器日志集中保留。Nginx容器默认日志输出到stdout如果你把所有容器的日志都交给某个日志收集工具统一采集排查问题时就不用挨个进容器看。我在宿主机上用一个简单的脚本把所有容器的日志按天归档至少保证问题发生后能回溯到当时的请求记录。6.3 一个容易被忽略的细节内部服务地址别用IP泛域名配置初期最容易犯的错就是在proxy_pass里写了一个固定IP比如proxy_pass http://172.20.0.5:8080;。当时能访问但容器一旦重建IP可能变成另一个整个服务就断了。换成容器名之后Docker的DNS自动解析新IP重建后无需修改Nginx配置。这一点在我早期的部署里吃亏不少所以现在只要看到proxy_pass里出现IP我就会敲响警钟。另外proxy_set_header里至少要设置上面提到的那三个头尤其Host头。很多后端框架会根据Host生成绝对链接如果这个头没有转发网站里生成的URL可能全部指向错误域名。我记得有一次后端生成了localhost链接差点以为是代码bug排查半天发现是Nginx里漏了Host头。6.4 关于自动化分配的一点扩展思路如果你已经掌握了手动泛域名配置可以继续往下做一个自动化管理入口。做法是给Nginx容器开发一个管理脚本这个脚本能接收参数自动生成server配置块并reload。比如输入test-project脚本就自动创建一个test-project.conf里面server_name是test-project.example.comproxy_pass指向test-project-container:8080。这样连打开编辑器都不需要完成整个“分配”动作。更进一步可以结合容器管理API在创建业务容器的同时自动调用这个脚本。我目前没有做这么重因为小项目手动执行脚本已经足够。如果你的环境里容器数量很多或需要频繁给外部用户开演示环境建议把配置生成做成一个小工具省下的时间远比写工具的时间多。这套方案的好用之处在于它把“域名 - 服务”的映射关系收敛到了一个可预期、可编排的模型里。我现在的服务器上新增一个演示站的时间和创建容器的时间几乎相等而且不会打扰正在运行的其他服务。如果你也经常在一台服务器上折腾多个小项目不妨按这个思路把Nginx的泛域名分配搭起来后续维护会轻松不少。