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

