同网跨设备访问Docker部署前后端应用的配置问题
Docker容器化Vite+TS前端+Flask后端:同网设备无法访问后端解决方案
应用说明
这是一个简单的时间展示应用,后端将日期时间数据发送至前端展示,能正常显示日期即为运行正常。
问题详情
Docker Compose构建容器后,本地访问正常,但同网手机访问时仅前端能加载,后端无法响应。原因是前端fetch代码中生产环境的host硬编码为localhost:8000:
export const host = import.meta.env.MODE !== 'production' ? 'http://localhost:5001' : 'http://localhost:8000' export const get = ( route: string, callback: (response: object) => void, errorCallback?: (response: object) => void, ) => { fetch(`${host}/${route}`) .then((response) => response.json()) .then(callback) .catch((error) => errorCallback ? errorCallback(error as Error) : console.error(error), ) }
当手机访问时,localhost指向手机自身而非运行Docker的电脑,导致请求失败。手动将host改为电脑IP后,手机可正常访问,但这种硬编码方式不灵活。
核心问题
如何无需指定电脑IP,通过配置host变量或docker-compose.yaml,实现同网设备正常访问后端?
相关Docker文件
compose.yaml
services: frontend: build: context: . dockerfile: Dockerfile environment: NODE_ENV: production TZ: America/New_York ports: - 7023:7023 networks: - backend depends_on: - backend backend: build: context: ./backend dockerfile: Dockerfile environment: NODE_ENV: production TZ: America/New_York ports: - 8000:8000 networks: - frontend networks: frontend: backend:
./Dockerfile(前端)
# syntax=docker/dockerfile:1 ARG NODE_VERSION=18.18.0 FROM node:${NODE_VERSION}-alpine as base WORKDIR /usr/src/app FROM base as deps RUN --mount=type=bind,source=package.json,target=package.json \ --mount=type=bind,source=package-lock.json,target=package-lock.json \ --mount=type=cache,target=/root/.npm \ npm ci --omit=dev FROM deps as build RUN --mount=type=bind,source=package.json,target=package.json \ --mount=type=bind,source=package-lock.json,target=package-lock.json \ --mount=type=cache,target=/root/.npm \ npm ci COPY . . RUN npm run build FROM base as final ENV NODE_ENV production USER node COPY package.json . COPY --from=deps /usr/src/app/node_modules ./node_modules COPY --from=build /usr/src/app/dist ./dist EXPOSE 7023 CMD npx http-server ./dist -p 7023
./backend/Dockerfile(后端)
# syntax=docker/dockerfile:1 ARG PYTHON_VERSION=3.11.4 FROM python:${PYTHON_VERSION}-slim as base ENV PYTHONDONTWRITEBYTECODE=1 ENV PYTHONUNBUFFERED=1 ENV FLASK_ENV production WORKDIR /app ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser RUN --mount=type=cache,target=/root/.cache/pip \ --mount=type=bind,source=requirements.txt,target=requirements.txt \ python -m pip install -r requirements.txt USER appuser COPY . . EXPOSE 8000 CMD gunicorn 'app:app' --bind=0.0.0.0:8000
解决方案
方案1:Nginx反向代理(推荐)
通过Nginx统一对外端口,前端用相对路径请求后端,无需硬编码IP或域名,适配所有访问场景。
步骤1:修改前端代码,使用相对路径
去掉硬编码的host,直接用相对路径请求后端API:
export const get = ( route: string, callback: (response: object) => void, errorCallback?: (response: object) => void, ) => { // 用相对路径,Nginx会自动转发到后端 fetch(`/api/${route}`) .then((response) => response.json()) .then(callback) .catch((error) => errorCallback ? errorCallback(error as Error) : console.error(error), ) }
步骤2:更新docker-compose.yaml,添加Nginx服务
统一对外端口为80,将前端和后端的请求通过Nginx转发:
services: frontend: build: context: . dockerfile: Dockerfile environment: NODE_ENV: production TZ: America/New_York networks: - app-network depends_on: - backend backend: build: context: ./backend dockerfile: Dockerfile environment: NODE_ENV: production TZ: America/New_York networks: - app-network nginx: image: nginx:alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf networks: - app-network depends_on: - frontend - backend networks: app-network: driver: bridge
步骤3:创建Nginx配置文件(nginx.conf)
配置前端静态资源和后端API的转发规则:
server { listen 80; server_name _; # 转发前端静态资源请求到前端容器 location / { proxy_pass http://frontend:7023; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # 转发/api开头的请求到后端容器 location /api/ { proxy_pass http://backend:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
使用方式
启动容器后,本地访问http://localhost,同网设备访问http://[电脑IP]即可正常使用,无需任何IP配置。
方案2:环境变量动态配置后端地址
通过Vite环境变量传递后端地址,启动时自动获取电脑IP,避免硬编码。
步骤1:修改前端代码,读取环境变量
export const host = import.meta.env.MODE !== 'production' ? 'http://localhost:5001' : import.meta.env.VITE_BACKEND_URL || 'http://localhost:8000' export const get = ( route: string, callback: (response: object) => void, errorCallback?: (response: object) => void, ) => { fetch(`${host}/${route}`) .then((response) => response.json()) .then(callback) .catch((error) => errorCallback ? errorCallback(error as Error) : console.error(error), ) }
步骤2:启动容器时传递电脑IP
在启动Docker Compose时,自动获取并传递电脑IP:
# Linux/macOS HOST_IP=$(hostname -I | awk '{print $1}') docker-compose up -d # Windows(PowerShell) $env:HOST_IP=(Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.InterfaceAlias -notlike "Loopback*"} | Select-Object -First 1).IPAddress; docker-compose up -d
步骤3:更新compose.yaml中的前端环境变量
frontend: build: context: . dockerfile: Dockerfile environment: NODE_ENV: production TZ: America/New_York VITE_BACKEND_URL: "http://${HOST_IP}:8000" ports: - 7023:7023 networks: - app-network depends_on: - backend
原理说明
- 方案1通过Nginx统一入口,前端请求无需关心后端地址,所有请求都通过同一个IP/域名转发,彻底解决跨设备IP问题。
- 方案2通过环境变量动态注入电脑IP,避免硬编码,但需要启动时传递IP,灵活性不如反向代理。
内容的提问来源于stack exchange,提问作者Simon1
相关产品推荐
相关产品推荐

