如何解决AWS ECS中Nginx无法识别同实例Web容器的问题
问题根因
报错是两个问题叠加导致的:
- Nginx默认启动时会强制校验
upstream块里的所有上游域名,解析不到直接崩溃退出。本地用docker compose跑的时候,compose会先创建共享网络、按依赖顺序启动容器,自动注入web服务名的DNS解析记录,所以nginx启动时能找到web:3000,不会报错。 - 你贴的
docker ps输出里压根没有nginx容器,只有web服务和ecs-agent。links是Docker Compose专属配置,AWS ECS不识别这套逻辑,既不会自动做容器间的DNS映射,也不会按compose里写的依赖顺序拉起nginx,nginx容器启动时解析不到web直接崩溃,所以ps里看不到对应进程。
修复步骤
1. 修改Nginx配置,去掉启动时的强解析依赖
删掉原来固定写死的upstream块,用变量传递上游地址,配置resolver让Nginx在请求到来时动态解析域名,不要在启动阶段就卡校验:
# 删除原有的 upstream rails_app { server web:3000; } 配置段 resolver 127.0.0.11 valid=30s ipv6=off; # Docker内置DNS地址,解析结果缓存30秒 set $rails_upstream web:3000; server { server_name example.com; # 把example.com的所有请求301跳转到www域名 return 301 $scheme://www.$server_name$request_uri; } server { listen 80; listen [::]:80; server_name xxxxx-xxxxx-xxxx-xxxx.us-west-2.elb.amazonaws.com; # 替换为你自己的ALB域名 root /example/public; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; # 禁止访问敏感类型文件 location ~ /\. { deny all; } location ~* ^.+\.(rb|log)$ { deny all; } # 直接响应静态资源请求 location ~ ^/(assets|images|javascripts|stylesheets|swfs|system)/ { try_files $uri @rails; access_log off; gzip_static on; expires max; add_header Cache-Control public; add_header Last-Modified ""; add_header ETag ""; break; } location / { try_files $uri @rails; } # 动态请求反向代理到Rails服务 location @rails { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://$rails_upstream; # 用变量形式触发动态解析 proxy_read_timeout 900; } }
如果你的ECS任务用的是awsvpc网络模式,把resolver地址改成169.254.169.253(AWS VPC内置DNS地址)即可
2. 调整ECS部署逻辑,不要依赖Docker Compose专属配置
两个方案选一个即可:
- 方案一(最省事,推荐):把nginx和web放到同一个ECS任务定义里,两个容器共享任务的网络命名空间。这种情况下直接把nginx里的上游地址改成
127.0.0.1:3000就行,不需要走容器名解析,也不用配置links。注意web容器不需要把3000端口映射到宿主机,只要在任务定义里声明容器暴露3000端口,同任务下的nginx走本地回环地址就能直接访问。 - 方案二:如果要把nginx和web拆成独立部署的服务,就开启ECS Service Connect或者用Cloud Map做服务发现,拿到web服务的固定内网域名后,替换掉nginx配置里的
web服务名即可。
3. 补充启动检查,规避启动顺序问题
就算DNS解析正常,如果nginx启动时web还没完成初始化、没开始监听3000端口,转发请求还是会报错。给nginx加个启动前的端口检测,等web端口通了再启动Nginx即可,把原来的Dockerfile.nginx改成:
FROM nginx:1.23-alpine RUN apk add --no-cache wait-for-port COPY ./nginx.conf /etc/nginx/conf.d/default.conf ENTRYPOINT ["/bin/sh", "-c", "wait-for-port --host web --port 3000 --timeout 60 && nginx -g 'daemon off;'"] EXPOSE 80
如果用了方案一的同任务共享网络,把--host web改成--host 127.0.0.1
验证方法
重新部署后先在ECS实例上执行docker ps,确认web和nginx两个容器都处于Up状态,再进nginx容器执行wget -O- http://web:3000(同任务部署就访问http://127.0.0.1:3000),能正常拿到Rails响应就说明连通性正常,最后再验证域名跳转和页面访问逻辑即可。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

