Docker化Rails应用向同环境Rails服务发起HTTP请求失败
drmanabase ➜ /app (002-build-seed-file ✗) $ ruby -v ruby 3.1.0p0 (2021-12-25 revision fb4df44d16) [x86_64-linux] drmanabase ➜ /app (002-build-seed-file ✗) $ rails -v Rails 7.0.2.3
通过Dockerfile、docker-compose.yml和devcontainer.json搭建了两个Rails/Postgres容器化应用:
- 「public应用」:存放无需保密的业务逻辑;
- 「private应用」:存放需保密的业务逻辑,作为HTTP服务接收请求并返回响应。
private应用的基础Rails结构已搭建完成,本地浏览器访问localhost:3000可正常显示页面,但从public应用容器内执行以下代码发起请求时失败:
require 'net/http' require 'uri' uri = URI('http://localhost:3000/') Net::HTTP.get(uri)
报错信息
/usr/local/lib/ruby/3.1.0/socket.rb:1214:in `__connect_nonblock': Failed to open TCP connection to localhost:3000 (Cannot assign requested address - connect(2) for [::1]:3000) (Errno::EADDRNOTAVAIL) from /usr/local/lib/ruby/3.1.0/socket.rb:1214:in `connect_nonblock' from /usr/local/lib/ruby/3.1.0/socket.rb:56:in `connect_internal' from /usr/local/lib/ruby/3.1.0/socket.rb:137:in `connect' from /usr/local/lib/ruby/3.1.0/socket.rb:642:in `block in tcp' from /usr/local/lib/ruby/3.1.0/socket.rb:227:in `each' from /usr/local/lib/ruby/3.1.0/socket.rb:227:in `foreach' from /usr/local/lib/ruby/3.1.0/socket.rb:632:in `tcp' from /usr/local/lib/ruby/3.1.0/net/http.rb:998:in `connect' from /usr/local/lib/ruby/3.1.0/net/http.rb:976:in `do_start' from /usr/local/lib/ruby/3.1.0/net/http.rb:965:in `start' from /usr/local/lib/ruby/3.1.0/net/http.rb:627:in `start' from /usr/local/lib/ruby/3.1.0/net/http.rb:503:in `get_response' from /usr/local/lib/ruby/3.1.0/net/http.rb:474:in `get' from /app/lib/mtg_json/tempCodeRunnerFile.rb:6:in `<main>' /usr/local/lib/ruby/3.1.0/socket.rb:1214:in `__connect_nonblock': Cannot assign requested address - connect(2) for [::1]:3000 (Errno::EADDRNOTAVAIL) from /usr/local/lib/ruby/3.1.0/socket.rb:1214:in `connect_nonblock' from /usr/local/lib/ruby/3.1.0/socket.rb:56:in `connect_internal' from /usr/local/lib/ruby/3.1.0/socket.rb:137:in `connect' from /usr/local/lib/ruby/3.1.0/socket.rb:642:in `block in tcp' from /usr/local/lib/ruby/3.1.0/socket.rb:227:in `each' from /usr/local/lib/ruby/3.1.0/socket.rb:227:in `foreach' from /usr/local/lib/ruby/3.1.0/socket.rb:632:in `tcp' from /usr/local/lib/ruby/3.1.0/net/http.rb:998:in `connect' from /usr/local/lib/ruby/3.1.0/net/http.rb:976:in `do_start' from /usr/local/lib/ruby/3.1.0/net/http.rb:965:in `start' from /usr/local/lib/ruby/3.1.0/net/http.rb:627:in `start' from /usr/local/lib/ruby/3.1.0/net/http.rb:503:in `get_response' from /usr/local/lib/ruby/3.1.0/net/http.rb:474:in `get' from /app/lib/mtg_json/tempCodeRunnerFile.rb:6:in `<main>'
核心原因
容器内的localhost指向容器自身,而非宿主机或其他容器。因此public应用容器中请求localhost:3000时,实际是访问自身的3000端口,而非private应用的服务。
具体修复步骤
确认网络归属
确保两个应用处于同一个Docker网络中。默认情况下,docker-compose会为项目创建专属网络,所有服务自动加入;如果是分开的docker-compose配置,需手动指定共享网络。替换访问地址为服务名
在public应用的请求代码中,将localhost替换为private应用在docker-compose.yml中定义的服务名。例如private应用的服务名为private-app,修改后的代码如下:require 'net/http' require 'uri' uri = URI('http://private-app:3000/') Net::HTTP.get(uri)确保private应用绑定正确地址
确认private应用的Rails服务绑定到0.0.0.0(允许所有网卡访问),而非默认的127.0.0.1(仅本地访问)。启动命令需改为:rails server -b 0.0.0.0 -p 3000也可在Dockerfile的
CMD指令或docker-compose.yml的command字段中指定该启动命令。验证网络连通性
进入public应用容器,执行ping命令测试与private应用的连通性:ping private-app若能ping通则网络配置正常;若不通,检查docker-compose的网络配置是否正确。
内容的提问来源于stack exchange,提问作者Justin Simpson

