Docker容器无法通过反向代理访问其他容器。

4

我正在使用docker-compose.yml在Ubuntu VPS上的nginx代理后面运行几个服务。

我遇到的问题是,我的docker容器无法使用其公开可用的代理URL相互访问。它们只能使用其docker服务名称相互访问。

最简单的演示方法如下。我可以使用以下命令从任何地方连接到我的Postgres实例:

psql -Atx postgres://username:pw@postgres.mysite.com/database

但是,如果我进入其中一个正在运行的容器:

docker compose exec backend sh

然后尝试使用相同的连接字符串连接,它就会卡住...甚至代理端也没有日志。但是,如果我使用docker服务名称,它可以正常工作:

psql -Atx postgres://username:pw@postgres:5432/database

我应该能够从容器内外的任何地方使用postgres.mysite.com。但由于某种原因,我只能在容器外部使用它。这是与此相关的nginx配置部分:

   upstream docker-postgres {
      server postgres:5432;
   }

   server {
      listen 443 ssl;
      server_name postgres.mysite.com;

      location / {
         proxy_pass http://docker-postgres;
      }
   }

但这与postgres无关。无论服务如何,似乎都会发生这种情况。每当容器A(应用代码)尝试使用容器C中运行的公共可用URL连接到容器B(服务)时。
例如,对于我的MinIO服务,在上传到https://assets.mysite.com时会出现上传挂起的情况,但在上传到http://minio:9001时不会出现。
部分docker-compose.yml:
proxy:
    image: nginx:alpine    
    ports:
      - '80:80'
      - '443:443'    
    networks:
      - myNetwork
backend:  
    ports:
      - '3000:3000'
    volumes:
      - ./:/app
      - /app/node_modules    
    networks:
      - myNetwork
postgres:
    image: postgres:10.4
    ports:
      - "5432:5432"   
    networks:
      - myNetwork   
minio:
    image: minio/minio
    command: server --console-address ":9090" --address ":9001" ./minio_data 
    ports:
      - '9090:9090'
      - '9001:9001'
    networks:
      - myNetwork

networks:
  myNetwork:
    external: true
 

附加信息

我在代理服务器下的assets.mysite.com server_name下设置了一个/test端点。

location /test { return 200 'success!!'; }

我可以从服务器(不在Docker容器内部)访问此端点:

curl https://assets.mysite.com/test ----> 成功

然而,我无法从任何容器内部访问它:

docker compose exec backend curl https://assets.mysite.com/test ---> 挂起!

尽管如此,我仍然可以成功ping通assets.mysite.com,即使是从容器内部:

docker compose exec backend ping assets.mysite.com

编辑:这是我几乎完整的nginx配置(仅省略了SSL证书)

http { 
   server {
      listen 80 default_server;
      listen [::]:80 default_server;
      server_name _;

      location / {
         return 301 https://$host$request_uri;
      }
   }

   upstream docker-backend {
      server backend:9000;
      keepalive 100;
   }

   upstream docker-postgres {
      server postgres:5432;
   }

   upstream docker-redis {
      server redis:6379;
   }

   upstream docker-minio-assets {
      server minio:9001;
   }

   server {
      listen 443 ssl;
      server_name mysite.com;
      root /var/www/frontend;
      index index.html;
      
      location /test {
         return 200 'success!!';
      }
      
      location /backend {
         rewrite ^/backend(/.*)$ $1 break;
         proxy_pass http://docker-backend;
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_set_header X-Forwarded-Proto $scheme;
         proxy_pass_request_headers on;
         client_max_body_size 3m;
      }

      location / {
         include /etc/nginx/mime.types;
         try_files $uri $uri/ /index.html =404;
      }
   }

   server {
      listen 443 ssl;
      server_name shopadmin.mysite.com;
      root /var/www/admin;
      index index.html;

      location /a/ {
         try_files $uri /index.html;
      }

      location / {
         include /etc/nginx/mime.types;
         try_files $uri /index.html =404;
      }
   }

   server {
      listen 443 ssl;
      server_name assets.mysite.com;
      client_max_body_size 20m; # Allow bigger file uploads.

      location /test {
         return 200 'success!!';
      } 

      location / {
         proxy_pass http://docker-minio-assets;
      }
   }

   server {
      listen 443 ssl;
      server_name redis.mysite.com;
    
      location / {
         proxy_pass http://docker-redis;
      }
   }

   server {
      listen 443 ssl;
      server_name postgres.mysite.com;
    
      location / {
         proxy_pass http://docker-postgres;
      }
   }
}



1
你能从后端容器中ping通 https://assets.mysite.com 吗?问题可能与DNS有关。 - Rob
1
你能从后端容器中ping通 https://assets.mysite.com 吗?问题可能与DNS有关。 - Rob
我可以写一篇关于Node.js的答案,详细说明上述内容。 - John
也许这个问题与 SSL 证书有关。尝试忽略它,执行 docker compose exec backend curl -k https://assets.mysite.com/test。如需进一步调试,请使用 -v 标志运行 curl。 - Slava Kuravsky
它是仅支持IPv6吗?我现在注意到一些关于启用了用户空间代理的IPv6服务器的问题。我也正在尝试修复一个部署中的问题,可能会与你的问题有很多交集。 - Marcel
显示剩余52条评论
4个回答

1
为了解决容器A在解析"b.domain.com"时被重定向到主机而不是直接找到容器B的问题,您可以在Docker Compose文件中为容器B添加一个别名。修改您的Docker Compose文件,包含以下内容:
networks:
  my-net:
    aliases:
      - b.domain.com

通过在“my-net”网络中为容器B指定别名“b.domain.com”,容器A将能够在解析主机名时直接定位到容器B。
但是,请注意,如果您的容器不属于同一个网络,Docker将安装一个可能阻止容器A和容器B之间连接的UFW(简化防火墙)规则。因此,请确保两个容器在同一个网络中,或者如果它们在不同的网络中,则采取适当的措施允许它们之间的通信。

0
我过去遇到过类似的情况,其中有一个特别让我想起你的情况,因为你在处理子域名时遇到了问题,而我也曾经遇到过同样的情况。如果我的解决方案对你没有用,请忽略我的回答,但我会把它留在这里,以防对其他人有所帮助,避免浪费他们的时间。 我的情况 我正在运行一个使用docker网络的Kubernetes(KIND)部署,与docker compose设置相同。我需要从容器中连接到AWS端点,这些是外部域名:
- authsamples.com(AWS Cloudfront) - login.authsamples.com(AWS Cognito) - web.authsamples.com(AWS Cloudfront) - api.authsamples.com(AWS API Gateway)
在部署的容器内部,我可以向https://authsamples.com的基本域和https://google.com等常规互联网URL发出curl请求,但当调用AWS子域名https://login.authsamples.com时,我却一直得不到响应。所有的URL都可以从主机计算机解析。 解决方案

我在KIND工作节点和容器上尝试了很多关于DNS和resolv.conf设置的更新。奇怪的是,容器的一般互联网访问正常,问题仅针对基于我的子域URL。

我的设置没有任何特殊之处,比如防火墙。问题原来是在主机网络层面上。对我有用的方法是确保在主机计算机上使用谷歌的两个主要名称服务器,而不是使用自动选项。

DNS servers

操作系统

我只在Windows和macOS上遇到过这种问题,尽管现在我主要使用Ubuntu作为我的桌面系统。如果没有这些DNS名称服务器,有时我也会遇到移动开发问题,例如模拟器上的Android WiFi网络无法工作。迄今为止,我还没有读到任何解释我所遇到行为的文章。

尝试的方法

当遇到这类问题时,我会尝试以下快速操作,看看是否对您有所帮助。

你从NGINX容器到子域名(例如https://assets.example.com)的呼叫是否遇到相同的问题?如果是的话,那么NGINX不是问题所在。
你能否从NGINX后面的服务向子域名(例如https://assets.example.com)发出curl请求(目前不能)?
你能否从NGINX后面的服务向一般的互联网URL发出curl请求?
你能否从NGINX后面的服务向基础域名(例如https://example.com)发出curl请求?
将主机网络更改为明确使用Google名称服务器是否会改变行为?

谢谢,但是这个答案不适用。我正在在Ubuntu VPS上设置这些服务,没有用户界面。我相信我已经在/etc/resolv.conf中添加了nameserver 9.9.9.9的更改,这确实有助于修复我的ping能力,但我的问题仍然存在。 - parliament
谢谢,但这个答案不适用。我正在为Ubuntu VPS设置这些服务,没有用户界面。我相信我已经在/etc/resolv.conf中进行了更改,包括nameserver 9.9.9.9,这确实有助于修复我的ping能力,但我的问题仍然存在。 - parliament
关于这个问题,我感兴趣的是你在连接DNS子域时遇到了问题,这与我曾经遇到的一个问题类似。为了更好地描述我的问题,我更新了我的答案,以防其中的任何信息对你有用。不过,如果你的问题是不同的,请忽略我的回答。 - Gary Archer
关于这个问题,我感兴趣的是你在连接DNS子域时遇到了问题,这与我曾经遇到的问题类似。为了更好地描述我的问题,我更新了我的答案,以防其中的任何信息对你有用。不过,如果你的问题是不同的,请忽略我的回答。 - Gary Archer
关于这个问题,我感兴趣的是你在连接DNS子域时遇到了问题,这和我之前遇到的问题很相似。我更新了我的回答,以更好地描述我的问题,以防你需要相关信息。不过,如果你的问题是不同的,请忽略我的回答。 - undefined

-2
根据提供的信息,似乎问题出在Docker网络配置上,以及容器如何解析其他容器的公共可用URL。
为了解决这个问题,您可以按照以下步骤更新您的docker-compose.yml文件和Nginx配置:
  1. 修改 docker-compose.yml 文件:

    • 从你的 docker-compose.yml 文件中删除 networks 部分,以允许容器使用其服务名称进行通信。
    • backend 服务中添加 depends_on 选项,以确保后端容器在所需服务启动后再启动:
    • backend:
        depends_on:
          - postgres
          - minio
      
  2. 修改 Nginx 配置:

    • 更新 Nginx 配置,使用 Docker 服务名称替代公开可用的 URL。例如,在你的 Nginx 配置文件中将 assets.mysite.com 替换为 docker-minio-assets,将 postgres.mysite.com 替换为 docker-postgres
    • 确保 Nginx 能够通过配置解析 Docker 服务名称。在 Nginx 配置文件的 http 块中添加以下行:
    • resolver 127.0.0.11 valid=30s;
      
    • 更新 Nginx 的 proxy_pass 指令,使用 Docker 服务名称。例如:
    • location / {
         proxy_pass http://docker-minio-assets;
      }
      
    • 对你的 Nginx 配置文件中的所有相关代理配置重复这些更改。
  3. 重新启动 Docker 容器:

    docker-compose down
    docker-compose up -d
    
应用这些更改后,您的容器应该能够使用Docker服务名称而不是公开的URL彼此通信。这样可以确保容器之间的连接按预期工作,即使通过Nginx代理访问服务也是如此。希望这对您有所帮助:)

这个回答看起来像是ChatGPT。 - DavidW

-7
更新Docker容器的DNS配置,使用一个能够正确解析域名的DNS解析器。然后,在每个服务的docker-compose.yml文件中,通过在服务定义下添加dns选项来指定DNS解析器。

1
嗨,Sreeram Nair。你最近的11个回答中,大部分或全部似乎都是由人工智能(例如ChatGPT)完全或部分撰写的。许多回答还有评论表明它们在某些方面是错误的。请注意,在这里发布由人工智能生成的内容是不允许的(//meta.stackoverflow.com/q/421831)。如果你在任何回答中使用了人工智能工具来辅助,请考虑删除它。我们希望你能继续留在这里,并通过发布你自己的高质量内容成为我们社区中宝贵的一员。谢谢! - NotTheDr01ds
1
读者应该仔细而批判地审查这个答案,因为由人工智能生成的信息往往包含基本错误和错误信息。如果您发现质量问题和/或有理由相信这个答案是由人工智能生成的,请相应地提供反馈。 - NotTheDr01ds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接