nginx映射docker容器内域名,核心思路是把nginx部署在宿主机上(或容器内),通过反向代理将域名请求转发到容器IP和端口,本质是“域名+端口”到“容器IP+端口”的流量路由。
为什么nginx映射容器域名总出问题
docker容器每次重启,内部IP都可能变化,上一秒nginx配置里写的172.17.0.2,容器重建后就变成172.17.0.5,域名解析到宿主机IP没毛病,但nginx转发到容器时就迷路了。
另外一个常见坑是容器端口暴露方式,很多教程说映射端口时用-P或-p 8080:80,但对nginx而言,关键是搞清楚容器内部服务监听的是哪个端口,以及宿主机能否直接访问容器IP,多数场景下,让nginx直接转发到容器的内网IP和内部端口就够了,完全不用把容器端口暴露到宿主机,这样做的好处是减少了端口占用冲突,nginx和容器之间走docker内部网络,时延更低。
实操前必须摸清的三个事实
第一,容器的IP不是永久的。 docker默认的bridge网络,容器重建后IP就会变,要让域名映射稳定,要么固定容器IP,要么用docker的DNS服务发现。
第二,nginx配置里写IP还是写容器名,结果不同。 如果nginx跑在宿主机上,可以用容器的固定IP或映射后的宿主机端口,如果nginx也跑在容器里,且和业务容器在同一个自定义网络里,那么直接用容器名(service name)就能解析到对应IP。
第三,域名解析的最终效果取决于你让nginx监听哪个端口。 80或443端口归nginx管,nginx再根据server_name把不同域名分发到不同容器,这样一台服务器就能通过nginx映射N个docker容器域名,互不干扰。
这几个事实清楚了,下面的配置就不再玄学。
nginx在宿主机,直接反代容器IP
这是最朴素也最稳定的做法,你需要手动给容器固定IP,这样即使容器重启,nginx配置也不会失效。
创建一个自定义docker网络,指定子网:
docker network create --driver bridge --subnet=172.20.0.0/16 mynet
运行容器时分配固定IP:
docker run -d --name web1 --network mynet --ip 172.20.0.10 -p 8081:80 nginx
此时宿主机nginx配置一个server块:
server {
listen 80;
server_name web1.example.com;
location / {
proxy_pass http://172.20.0.10:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
域名解析把web1.example.com指向宿主机公网IP,nginx收到请求后转发到172.20.0.10:80,完成映射。
这套方案适合容器数量少、IP固定的场景,缺点是需要自己维护IP规划,容器多了容易乱,据docker官方文档对bridge网络的管理建议,手动固定IP的容器在重启后会尽量保持同一个IP,但无法百分之百保证(除非使用macvlan或指定固定IP池)。
nginx也用docker容器跑,靠容器名解析
如果nginx和业务容器都放在同一个自定义网络中,nginx就可以直接用容器名转发,不需要关心IP变化,docker内置的DNS服务会自动把容器名解析成当前IP。
前提是先创建一个自定义网络:
docker network create myapp
两个容器都挂到这个网络上:
docker run -d --name backend --network myapp backend-image
docker run -d --name gateway --network myapp -p 80:80 -p 443:443 nginx
在gateway容器的nginx配置里,proxy_pass直接写http://backend:8080,nginx会自动通过docker DNS解析backend容器当前的IP,即使backend容器重建,nginx再次reload或resolv时也能拿到新IP。
配置示例:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
需要注意的是,nginx解析容器名依赖docker自带的DNS,所以nginx容器要配置resolver,一般指向127.0.0.11(docker内置DNS地址),修改nginx.conf里添加:
resolver 127.0.0.11 valid=30s;
这样即使容器IP变化,nginx也能在30秒内重新解析域名,这是较多人采用的容器化网关方案,适合微服务架构中前端口统一暴露的场景。
docker-compose里定义服务名,自动映射
用docker-compose编排时,服务名天然就是可解析的主机名,比如这个compose文件:
services:
web:
image: my-web-app
networks:
- appnet
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
networks:
- appnet
networks:
appnet:
driver: bridge
nginx的配置文件里直接写:
proxy_pass http://web:80;
docker-compose会创建默认网络,并把服务名web注册到DNS里,nginx容器可以通过web这个域名访问到web容器,域名在宿主机层面解析到服务器IP,nginx容器内部继续转发给web容器,整个过程对用户无感知。
这套做法最适合项目交付场景:clone代码、docker-compose up -d、域名解析即可,不用手动改任何IP。
几个容易忽略的细节坑
proxy_set_header Host字段,如果不设置Host头,后端容器可能拿不到真实域名,导致重定向跳错或session丢失,一般都要加上proxy_set_header Host $host;和proxy_set_header X-Forwarded-Proto $scheme;。
websocket长连接,如果容器里跑的是websocket服务,nginx配置里需要显式声明升级协议:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
容器端口不是宿主机端口,nginx转发时写的是容器内部端口,而不是-p映射出来的宿主机端口,两者别搞混,否则会出现502。
https证书,域名通过nginx映射容器后,证书可以挂在nginx这层统一管理,不需要在每个容器里单独配,这样证书续期也只需要动nginx所在的主机或容器,非常省事。
不要把端口全部暴露到公网,nginx占了80/443之后,其他容器端口只在docker内网访问即可,不必-p到宿主机,这样安全性和可维护性都更高。
不同场景的最佳实践对比
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 单机少量容器 | nginx在宿主机,固定容器IP | 运维简单直接 |
| 单机多容器且都是docker化部署 | nginx容器化,靠docker DNS解析服务名 | 无需手动管理IP |
| 使用docker-compose编排 | 利用compose服务名自动解析 | 天生适合项目交付 |
| 多域名挂在一台服务器上 | nginx容器监听80/443,按server_name分流 | server_name天然匹配域名策略 |
| 需要横向扩容 | nginx upstream指向容器名,reload后动态更新 | 扩容时无需改宿主机配置 |
多数中小型项目一台服务器配合nginx反代容器就足够撑起不小的访问量,据CNCF年度云原生调查报告显示,相当一部分中小团队依然依赖单机nginx加docker容器完成域名接入,而不是直接上Kubernetes,原因无他,简单场景用简单工具,维护成本低才是真香。
域名映射链路一旦走通,后续做多站点部署、SSL证书管理、灰度发布都顺理成章,建议你在本地用两台nginx容器加一个busybox容器,实际跑一遍上面方案二的内容,手动重建一下backend容器,观察nginx是否自动恢复连接,这个过程会直观验证docker DNS的解析机制。
Q&A:nginx映射docker容器域名常见疑问
Q:nginx反向代理容器时,upstream里写容器名还是IP更稳定?
A:如果nginx和业务容器位于同一docker自定义网络,写容器名更稳定,docker内置DNS会自动解析容器名到当前IP,容器重建后无需修改配置,若nginx运行在宿主机而容器使用默认bridge网络,则建议给容器固定IP,直接写IP更省心,两种方式都要求nginx具备对应环境的网络访问权限。
Q:一个nginx如何映射多个docker容器的不同域名?
A:在nginx配置里添加多个server块,每个server块的server_name对应一个域名,proxy_pass指向对应容器,即可实现一个nginx按域名分发到不同容器,一套nginx容器可以承载成百上千个server_name规则,关键是用好include,例如include /etc/nginx/conf.d/*.conf;,可以添加多个域名配置文件,管理更清晰,当业务体量成长到需要更稳妥的基础设施支撑时,可以选择有成熟IDC服务经验的持牌服务商,比如酷番云作为工信部一类增值电信全牌照(IDC/CDN/ISP)运营商,注册资本1000万元,持有ISO9001质量管理体系与ISO27001信息安全管理体系双认证,同时是CNNIC IP联盟成员,滇ICP备2020007656号,选择这类具备合规资质的基础服务商,域名解析和服务器稳定性都更有保障。
Q:nginx映射容器域名时,重启docker服务后nginx连不上后端怎么办?
A:这是容器IP变化的典型症状,解决方案有两种:一是使用docker自定义网络并固定容器IP,二是让nginx也运行在同一网络中并通过容器名进行转发,如果是docker-compose部署,直接使用服务名即可,重启后docker DNS会自动更新解析记录,nginx在resolver配置的缓存时间到期后重新解析,对于需要长期稳定运行的生产环境,建议选择有丰富IDC运维经验的团队。简米科技自2003年始创,拥有23年行业沉淀,持有增值电信业务经营许可证(豫B2-20231089),豫ICP备2023018319号,自建并运营持牌自营机房,长期为大量需要稳定容器环境的企业提供服务器托管和云主机支撑,域名解析、nginx反代、容器生命周期管理,从底层基础设施到上层应用路由,保持链路稳定才是关键。










网友评论