如何通过NGINX实现Docker动态端口下NodeJS+Socket.io应用的域名访问
动态Docker容器+Socket.IO的Nginx反向代理配置方案
嘿,这个需求我之前帮朋友落地过,核心难点就是动态端口的映射匹配和Socket.IO的WebSocket兼容配置,我给你整理了两种靠谱的方案,按需选就行:
核心思路先理清楚
你的Web应用会动态创建Docker容器并分配随机/动态端口,Nginx没法提前写死配置,所以核心是要让Nginx能实时获取「自定义域名 ↔ 容器端口/地址」的映射关系,再配合Socket.IO专属的反向代理配置,就能实现域名直接访问了。
方案一:用OpenResty(带Lua的Nginx)实时查询映射
这个方案最灵活,实时性拉满,不用频繁重启Nginx。
步骤1:准备OpenResty
直接装OpenResty就行,它自带Lua模块,省得自己编译Nginx加Lua:
# Ubuntu/Debian示例 apt update && apt install openresty
步骤2:存储「域名-端口/容器地址」映射
你的Web应用在创建Docker容器时,得把自定义域名和访问地址(宿主机端口或者Docker容器名+内部端口)存到一个快速查询的地方,比如Redis:
- 创建容器时执行:
redis-cli SET myapp.yourdomain.com 30001(假设映射到宿主机30001端口) - 如果用Docker网络,就存容器名:
redis-cli SET myapp.yourdomain.com myapp-123:3000 - 删除容器时执行:
redis-cli DEL myapp.yourdomain.com
步骤3:配置OpenResty
把下面的配置放到/usr/local/openresty/nginx/conf/nginx.conf(或你的Nginx配置目录):
http { # 开辟共享内存存临时缓存,减少Redis查询次数 lua_shared_dict addr_cache 10m; server { listen 80; server_name _; # 匹配所有域名,也可以写你的域名前缀比如 *.yourdomain.com location / { content_by_lua_block { -- 获取当前请求的域名 local domain = ngx.var.host -- 先查缓存,没有再查Redis local cache = ngx.shared.addr_cache local target_addr = cache:get(domain) if not target_addr then -- 连接Redis查询 local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.log(ngx.ERR, "Redis连接失败: ", err) ngx.exit(500) end target_addr, err = red:get(domain) if not target_addr or target_addr == ngx.null then ngx.exit(404) end -- 缓存10分钟,避免频繁查Redis cache:set(domain, target_addr, 600) end -- 把目标地址传给反向代理变量 ngx.var.proxy_target = "http://" .. target_addr } -- 实际反向代理配置 proxy_pass $proxy_target; # 敲黑板!Socket.IO必须的配置,少一个都可能连不上 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Real-IP $remote_addr; } } }
步骤4:重启OpenResty
openresty -s reload
方案二:用Consul+Consul-Template自动生成Nginx配置
如果你的架构已经用了服务注册中心,这个方案更规整,不用写Lua脚本。
步骤1:部署Consul和Consul-Template
Consul用来注册服务(域名+访问地址),Consul-Template监听Consul的变化,自动生成Nginx配置并reload。
步骤2:注册服务到Consul
你的Web应用创建容器时,向Consul注册服务:
# 示例:注册myapp.yourdomain.com,端口30001,标签标记是socketio服务 curl -X PUT http://localhost:8500/v1/agent/service/register -d '{ "Name": "myapp.yourdomain.com", "Port": 30001, "Tags": ["socketio"] }'
删除容器时注销服务:
curl -X PUT http://localhost:8500/v1/agent/service/deregister/myapp.yourdomain.com
步骤3:写Consul-Template模板
创建nginx.ctmpl文件:
http { server { listen 80; charset utf-8; {{ range services }} {{ if .Tags | contains "socketio" }} server_name {{ .Name }}; location / { proxy_pass http://127.0.0.1:{{ .Port }}; # Socket.IO必备配置 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } {{ end }} {{ end }} } }
步骤4:启动Consul-Template
让它自动生成配置并reload Nginx:
consul-template -consul-addr=localhost:8500 -template="nginx.ctmpl:/etc/nginx/conf.d/socketio.conf:nginx -s reload"
额外优化建议:用Docker网络代替宿主机端口映射
如果你的Nginx也是跑在Docker容器里,强烈建议把Nginx和应用容器放到同一个Docker网络里:
- 创建Docker网络:
docker network create app-network - 启动Nginx容器时加入这个网络:
docker run -d --network app-network ... nginx - 应用容器启动时也加入这个网络:
docker run -d --network app-network --name myapp-123 ... your-node-app - 这时候反向代理的目标就变成
http://myapp-123:3000(假设应用内部端口是3000),不用再暴露端口到宿主机,更安全也更省心。
内容的提问来源于stack exchange,提问作者Sajjad Ahmed Shaaz
相关产品推荐
相关产品推荐

