You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker Compose中前端外部访问时如何正确调用后端服务

问题描述

我有如下Docker Compose配置:

version: '3'

networks:
  my-network:
    driver: bridge

services:
  frontend:
    build:
      context: ./frontend/
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    depends_on:
      - applets_backend
      - dummy_backend
    environment:
      - REACT_APP_API_URL=http://applets_backend:8000
      - REACT_APP_DUMMY_API_URL=http://dummy_backend:8400
      - REACT_APP_NODE_ENV=development
      - REACT_APP_TYPE=demo
    networks:
      - my-network

  applets_backend:
    build:
      context: ./applets_backend/
      dockerfile: Dockerfile
    ports:
      - "8080:8000"
    networks:
      - my-network

  dummy_backend:
    build:
      context: ./dummy_backend/
      dockerfile: Dockerfile
    ports:
      - "8484:8000"
    networks:
      - my-network

前端容器内的React应用需要向另外两个后端服务发起请求,但通过localhost:3000访问前端时,用容器名作为请求地址会报错。我尝试添加Nginx反向代理容器:

reverse-proxy:
    image: nginx:alpine
    restart: "always"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - "8000:8000"
      - "8400:8400"
    depends_on:
      - frontend
    networks:
      - my-network

对应的nginx.conf:

events {}

http {
    server {
        listen 8000;
        location / {
            proxy_pass http://applets_backend:8000;
            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;
        }
    }

    server {
        listen 8400;
        location / {
            proxy_pass http://dummy_backend:8400;
            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;
        }
    }
}

只有把前端环境变量改成下面这样才生效:

environment:
- REACT_APP_API_URL=http://localhost:8000
- REACT_APP_DUMMY_API_URL=http://localhost:8400

我觉得这不是正确的配置方式,想知道怎么实现前端外部访问时正确调用后端服务。


解决方案

核心问题是:React应用运行在用户浏览器中,不属于Docker网络,无法识别applets_backend这类容器名。浏览器会把容器名当作普通域名解析,自然失败。下面是两种更合理的配置方式:

方式一:用Nginx统一代理前端和后端(推荐)

把前端也放到Nginx代理下,让前端和后端使用同一个域名的不同路径,避免跨域和容器名解析问题。

1. 修改Docker Compose配置

移除前端的宿主机端口映射,让Nginx直接代理前端容器;同时移除后端的宿主机端口映射,只保留Docker网络内的访问:

version: '3'

networks:
  my-network:
    driver: bridge

services:
  frontend:
    build:
      context: ./frontend/
      dockerfile: Dockerfile
    depends_on:
      - applets_backend
      - dummy_backend
    environment:
      # 前端用相对路径请求,无需硬编码域名
      - REACT_APP_API_URL=/api
      - REACT_APP_DUMMY_API_URL=/dummy
      - REACT_APP_NODE_ENV=development
      - REACT_APP_TYPE=demo
    networks:
      - my-network

  applets_backend:
    build:
      context: ./applets_backend/
      dockerfile: Dockerfile
    networks:
      - my-network

  dummy_backend:
    build:
      context: ./dummy_backend/
      dockerfile: Dockerfile
    networks:
      - my-network

  reverse-proxy:
    image: nginx:alpine
    restart: "always"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    ports:
      - "80:80" # 只用一个端口对外提供服务
    depends_on:
      - frontend
      - applets_backend
      - dummy_backend
    networks:
      - my-network

2. 修改Nginx配置

让Nginx同时代理前端、两个后端,通过路径区分:

events {}

http {
    server {
        listen 80;

        # 代理前端静态资源
        location / {
            proxy_pass http://frontend:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }

        # 转发请求到applets_backend
        location /api/ {
            proxy_pass http://applets_backend:8000/; # 末尾的/需保留,避免路径拼接错误
            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;
        }

        # 转发请求到dummy_backend
        location /dummy/ {
            proxy_pass http://dummy_backend:8000/; # 末尾的/需保留
            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;
        }
    }
}

3. 验证

启动所有容器后,直接访问http://localhost即可:

  • 前端页面正常加载,请求/api路径时Nginx自动转发到applets_backend:8000
  • 请求/dummy路径时转发到dummy_backend:8000

方式二:利用React开发服务器的代理(仅开发环境)

如果是开发阶段,不想用Nginx,可以直接配置React的开发服务器代理,把请求转发到后端容器。

1. 在前端项目根目录创建setupProxy.js

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://applets_backend:8000',
      changeOrigin: true,
      pathRewrite: { '^/api': '' } // 可选,根据后端接口路径调整
    })
  );
  app.use(
    '/dummy',
    createProxyMiddleware({
      target: 'http://dummy_backend:8000',
      changeOrigin: true,
      pathRewrite: { '^/dummy': '' }
    })
  );
};

2. 修改前端环境变量

environment:
  - REACT_APP_API_URL=/api
  - REACT_APP_DUMMY_API_URL=/dummy
  - REACT_APP_NODE_ENV=development
  - REACT_APP_TYPE=demo

3. 验证

启动容器后,访问localhost:3000,React开发服务器会自动把/api和/dummy的请求转发到对应的后端容器。

为什么之前的配置能工作但不合理?

之前用localhost:8000和localhost:8400是因为Nginx把宿主机端口映射到了容器,浏览器能解析localhost,但这种方式需要暴露多个端口,而且前端硬编码了宿主机地址,部署到服务器时还要修改环境变量,灵活性差。上面两种方式都避免了硬编码,更符合Docker的网络隔离设计。

内容的提问来源于stack exchange,提问作者Aleh Debug

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 21:52:03