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

dan*_*iil 3 docker docker-compose docker-network

我正在尝试思考如何访问作为docker-compose服务一部分运行的其他容器。

我见过很多关于通过其他容器内的服务名称访问容器的答案,但也有很多教程只是使用带有公开端口的本地主机。

所以我只是想弄清楚何时使用哪个以及为什么它会这样工作。

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

在其中,我有一个 NGINX 服务器将 ui 和 api 服务映射在一起,但事后我意识到,在我的 React 容器 (3000:3000) 内部,我实际上可以通过以下方式访问 Express 容器 (5050:5050)向 发送 axios 请求http://localhost:5050

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

有人可以帮我理解这一切是如何运作的吗:

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

Dav*_*aze 9

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

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

version: '3.8'
services:
  frontend: { ... }
  backend: { ... }
  database: { ... }
  proxy: { ... }
Run Code Online (Sandbox Code Playgroud)

当一个容器调用另一个容器时,直接使用 Compose 服务名称和服务的默认端口。容器backend可以配置为其database:5432数据库 URL;Nginxproxy可能配置为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 ;
}
Run Code Online (Sandbox Code Playgroud)

然后浏览器应用程序只需发出 HTTP GET 请求即可,而/api无需知道服务器的主机名;它将使用与当前页面 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:
Run Code Online (Sandbox Code Playgroud)

通过此设置和上面显示的 Nginx 配置,浏览器调用http://localhost:12345将检索主应用程序。如果浏览器应用程序随后请求/api/foo,则该请求将被转换为http://localhost:12345/api/foo,并http://backend:3000/foo在 Docker 空间内被代理。