From 46a8bf3c07e0bbe86be4f025bd752f693f6addae Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 30 Jun 2026 10:57:21 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E4=BA=A7docker=E9=83=A8=E7=BD=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 12 +++++++ Dockerfile | 27 ++++++++++++++++ README.md | 36 +++++++++++++++++++++ docker-compose.yml | 13 ++++++++ docs/frontend-practices.md | 2 ++ nginx/default.conf.template | 62 +++++++++++++++++++++++++++++++++++++ 6 files changed, 152 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 nginx/default.conf.template diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3545ad1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +node_modules +dist +.git +.idea +.agents +.codex +.env.local +.env.*.local +dist.zip +*.log +Dockerfile +docker-compose*.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e3c1283 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM node:20-alpine AS build + +WORKDIR /app + +COPY package.json yarn.lock ./ +RUN yarn install --frozen-lockfile + +COPY . . + +ARG BUILD_MODE=prod +RUN case "$BUILD_MODE" in \ + test) yarn build:test ;; \ + prod|production) yarn build:prod ;; \ + *) echo "Unsupported BUILD_MODE: $BUILD_MODE. Use test or prod." && exit 1 ;; \ + esac + +FROM nginx:1.27-alpine + +ENV BACKEND_GATEWAY=http://192.168.3.211:9999 + +COPY nginx/default.conf.template /etc/nginx/templates/default.conf.template +COPY --from=build /app/dist /usr/share/nginx/html + +EXPOSE 10086 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=10s \ + CMD wget -qO- http://127.0.0.1:10086/ >/dev/null || exit 1 diff --git a/README.md b/README.md index 4042bf1..cf1f2ad 100644 --- a/README.md +++ b/README.md @@ -91,3 +91,39 @@ yarn build:prod # 默认生产打包 yarn build ``` + +## Docker 部署 + +镜像内使用 Nginx 托管前端静态资源,并代理 `/auth`、`/admin`、`/hotelStaff`、`/hotelBiz` 到后端网关。前端环境文件中的 `VITE_API_BASE_URL` 保持为空即可。 + +构建生产镜像: + +```bash +docker build --build-arg BUILD_MODE=prod -t hotel-biz-h5:prod . +``` + +构建测试镜像: + +```bash +docker build --build-arg BUILD_MODE=test -t hotel-biz-h5:test . +``` + +直接运行: + +```bash +docker run -d \ + --name hotel-biz-h5 \ + -p 10086:10086 \ + -e BACKEND_GATEWAY=http://192.168.3.211:9999 \ + hotel-biz-h5:prod +``` + +使用 compose: + +```bash +docker compose up -d --build +``` + +`BACKEND_GATEWAY` 是容器内 Nginx 代理到的后端网关地址,不要以 `/` 结尾。生产环境如果前面还有宿主机 Nginx,宿主机 Nginx 只需要负责域名、HTTPS 证书,并把流量转发到 `127.0.0.1:10086`。 + +生产扫码建议通过 HTTPS 访问,否则部分移动浏览器或微信内置浏览器可能无法打开摄像头。 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ce0f53a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,13 @@ +services: + hotel-biz-h5: + build: + context: . + args: + BUILD_MODE: prod + image: hotel-biz-h5:prod + container_name: hotel-biz-h5 + environment: + BACKEND_GATEWAY: http://192.168.3.211:9999 + ports: + - "10086:10086" + restart: unless-stopped diff --git a/docs/frontend-practices.md b/docs/frontend-practices.md index 9d5dd3a..1086f3f 100644 --- a/docs/frontend-practices.md +++ b/docs/frontend-practices.md @@ -33,6 +33,8 @@ - 前后端分离部署时,优先用 Nginx 把 `/auth`、`/admin`、`/hotelStaff`、`/hotelBiz` 等路径代理到后端网关,这样浏览器看到的是同源请求,跨域问题最少。 - 如果直接把 `VITE_API_BASE_URL` 配成后端网关 IP 或域名,跨域就需要后端网关正确返回 CORS 头;这不是前端代码能完全解决的。 - SPA 路由需要 Nginx `location / { try_files $uri $uri/ /index.html; }`。 +- Docker 部署时,容器内 Nginx 负责静态资源和接口前缀代理,运行时通过 `BACKEND_GATEWAY` 环境变量指定后端网关地址。 +- 如果生产服务器外层还有 Nginx,外层 Nginx 只负责域名、HTTPS 和转发到容器端口;接口前缀代理继续交给容器内 Nginx。 ## 移动端布局 diff --git a/nginx/default.conf.template b/nginx/default.conf.template new file mode 100644 index 0000000..82d0204 --- /dev/null +++ b/nginx/default.conf.template @@ -0,0 +1,62 @@ +server { + listen 10086; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + client_max_body_size 20m; + + gzip on; + gzip_types text/plain text/css application/javascript application/json image/svg+xml; + + location = /index.html { + add_header Cache-Control "no-cache"; + } + + location /assets/ { + expires 30d; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + location /auth/ { + proxy_pass ${BACKEND_GATEWAY}/auth/; + proxy_http_version 1.1; + 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; + } + + location /admin/ { + proxy_pass ${BACKEND_GATEWAY}/admin/; + proxy_http_version 1.1; + 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; + } + + location /hotelStaff/ { + proxy_pass ${BACKEND_GATEWAY}/hotelStaff/; + proxy_http_version 1.1; + 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; + } + + location /hotelBiz/ { + proxy_pass ${BACKEND_GATEWAY}/hotelBiz/; + proxy_http_version 1.1; + 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; + } + + location / { + try_files $uri $uri/ /index.html; + } +}