Docker Compose 通过 localhost 访问另一个容器

3

我正在努力理解如何访问作为 docker-compose 服务的一部分运行的其他容器。

我看到了很多答案谈论在其他容器中通过服务名称访问容器,但也有很多教程仅使用暴露端口的本地主机。

所以我只是想搞清楚什么时候使用哪个方法,以及为什么它能够工作。

我的样例应用程序是:https://github.com/daniil/full-stack-js-docker-tutorial

其中,我有一个将ui和api服务映射在一起的NGINX服务器,但事后我意识到,在我的React容器(3000:3000)中,我实际上可以通过对http://localhost:5050进行axios请求来访问Express容器(5050:5050)。

但同时,如果我尝试通过localhost连接到我的MySQL容器(9906:3306),它不起作用,我必须使用db作为主机,即容器名称。

有人能帮我理解它是如何工作的吗:

  • 什么时候可以使用 http://localhost:SERVICE_PORT,它是否适用于React服务,因为它是浏览器请求?即:axios
  • 为什么我不能在React / axios请求中使用 http://api:5050,是因为没有主机解析吗?
  • 为什么我不能使用http://localhost:9906|3306来连接我的db服务?
  • NGINX反向代理将客户端和api绑定在一起的目的或好处是什么,如果你实际上不需要在两者之间有任何东西,因为本地主机似乎可以工作?
  • 如果容器应该是隔离的,那么为什么从我的React容器中的localhost:5050仍然可以看到在另一个容器中运行的API服务器在5050上运行?
  • 其他可帮助我理解跨容器通信方式的一般规则
1个回答

9

这里的重要细节是,你的React应用程序实际上并没有运行在容器中。它运行在最终用户的浏览器中,这是在Docker空间之外的,因此无法访问Docker的内部网络。

假设您有一个典型的应用程序:

version: '3.8'
services:
  frontend: { ... }
  backend: { ... }
  database: { ... }
  proxy: { ... }

当一个容器直接调用另一个容器时,使用Compose服务名称和服务的默认端口。例如,backend容器可能配置了 database:5432 作为其数据库URL;Nginx proxy 可能被配置为 proxy_pass http://frontend:3000。如果存在ports:,则不需要指定 networks:container_name:,并且会被忽略。对于大多数简单应用程序,这可以直接使用,无需指定任何选项。
当浏览器应用程序调用容器时,请使用主机的DNS名称或IP地址和该容器的第一个ports:号码。
当浏览器应用程序调用容器时,并且您非常确定浏览器位于与容器相同的主机上时,只有在这种情况下才可以使用http://localhost:12345,并再次匹配目标容器的第一个ports:号码。
NGINX反向代理将客户端和API连接起来的目的或好处是什么?
为了避免需要知道主机名。假设您的Nginx配置如下:
location / {
  proxy_pass http://frontend:3000 ;
}
location /api {
  proxy_pass http://backend:3000 ;
}

那么浏览器应用程序只需向/api发出HTTP GET请求,无需知道服务器的主机名;它将使用与当前页面URL相同的主机名。

这也意味着您可以避免直接发布其他所涉及的容器,甚至可能为不同目的使用多个后端容器。

一个完整但最简化的设置可能如下所示:

version: '3.8'
services:
  frontend:
    build: ./frontend
  backend:
    build: ./backend
    environment:
      PGHOST: database
      # PGUSER, PGPASSWORD
  database:
    image: 'postgres:14'
    volumes:
      - 'pgdata:/var/lib/postgresql/data'
    environment: {} # POSTGRES_USER, POSTGRES_PASSWORD
  proxy:
    image: 'nginx:1.21'
    volumes:
      - './default.conf:/etc/nginx/conf.d/default.conf'
    ports:
      - '12345:80'
volumes:
  pgdata:

使用这个设置和上面展示的Nginx配置,浏览器访问http://localhost:12345 将获取主要应用程序。如果浏览器应用程序请求/api/foo,那么它将被翻译为http://localhost:12345/api/foo,它会被代理到Docker空间中的http://backend:3000/foo

David,非常感谢你抽出时间向我解释,这让我对很多事情有了更清晰的认识,我真的很感激你的细致和举例。现在这个问题困扰我的程度少了很多,我的头脑也终于把所有的零散知识点拼凑成了一个完整的体系。 - daniil

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