如何通过Docker Compose环境变量配置Vite React应用的后端服务名以解决跨服务通信的域名解析问题?
我来帮你理清这个问题的核心,以及给出几个实用的解决方案:
问题根源:你搞错了前端代码的运行环境!
你以为前端代码是在Docker容器里运行的,所以能直接用Docker服务名backend来访问后端,但实际上React代码最终是在用户的浏览器里执行的,浏览器不在你的Docker网络skystack_net里,自然没法解析Docker内部的服务名,这就是net::ERR_NAME_NOT_RESOLVED报错的原因。而你用宿主机内部IP(比如172.25.176.1)能行,是因为这个IP是浏览器能访问到的宿主机地址,后端容器的5000端口已经映射到了宿主机上。
解决方案一:本地开发时用Vite代理转发请求(最推荐)
既然本地开发时前端跑在浏览器,后端跑在Docker里,我们可以让Vite帮我们把API请求转发到后端容器,这样前端代码不用改,还能避开跨域问题。
- 在你的前端项目根目录下修改
vite.config.js,添加代理配置:
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { proxy: { // 匹配所有以/api开头的请求 '/api': { target: 'http://localhost:5000', // 后端容器映射到宿主机的地址 changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') // 去掉/api前缀再转发给后端 } } } })
- 修改你的前端API请求代码,用相对路径代替硬编码的地址:
// 原来的写法 // const BACKEND_HOST_URL = import.meta.env.VITE_BACKEND_HOST_URL || 'http://localhost:5000'; // 现在的写法,直接用相对路径 const fetchData = async () => { const response = await fetch('/api/my-endpoint'); // ...处理响应 };
这样本地开发时,Vite会把/api/my-endpoint的请求转发到http://localhost:5000/my-endpoint,完全适配你的Docker后端。
解决方案二:Docker部署前端时用反向代理(适合生产环境)
如果要把前端也部署到Docker容器里对外提供服务,推荐用Nginx作为反向代理,把前端和后端的请求都统一处理,这样Nginx在Docker网络里能直接解析backend服务名。
- 创建一个Nginx配置文件
nginx.conf:
server { listen 80; server_name localhost; # 处理前端静态文件请求 location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; } # 处理API请求,转发到后端服务 location /api { proxy_pass http://backend:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
- 修改你的
docker-compose.yml,添加Nginx服务,调整前端和后端的端口配置:
version: '3' services: frontend: build: context: ../frontend # 去掉端口映射,让Nginx访问 environment: # 前端打包时用相对路径指向代理地址 - VITE_BACKEND_HOST_URL=/api networks: - skystack_net depends_on: - nginx backend: build: context: ../backend # 不需要对外暴露端口,只让Nginx访问 # ports: # - "5000:5000" depends_on: - mongodb environment: - PORT=5000 - DOCKER_ENV=true networks: - skystack_net mongodb: image: mongo:latest ports: - "27017:27017" networks: - skystack_net volumes: - mongo-data:/data/db nginx: image: nginx:alpine ports: - "80:80" volumes: # 挂载Nginx配置文件 - ./nginx.conf:/etc/nginx/conf.d/default.conf # 挂载前端打包后的静态文件 - ../frontend/dist:/usr/share/nginx/html networks: - skystack_net depends_on: - backend volumes: mongo-data: networks: skystack_net:
- 前端打包时确保用相对路径,修改
vite.config.js添加base: './':
export default defineConfig({ plugins: [react()], base: './' })
这样用户访问Nginx的80端口时,前端静态文件由Nginx提供,API请求会被转发到Docker网络里的backend:5000,完美解决服务名解析问题。
额外小技巧:兼容本地和Docker两种开发模式
如果你想让前端代码同时支持本地跑(不用Docker)和Docker跑,可以保留环境变量的逻辑,但调整一下:
// 本地开发时用Vite代理的/api,Docker部署时用Nginx代理的/api const BACKEND_HOST_URL = import.meta.env.VITE_BACKEND_HOST_URL || '/api'; const fetchData = async () => { const response = await fetch(`${BACKEND_HOST_URL}/my-endpoint`); // ...处理响应 };
这样不管是本地开发还是Docker部署,代码都能正常工作。
备注:内容来源于stack exchange,提问作者Casper Karlsson

