diff --git a/README.md b/README.md index 017fdd1..88d77c0 100644 --- a/README.md +++ b/README.md @@ -185,6 +185,35 @@ docker compose -f deploy/docker-compose.yml up -d # 浏览器访问 http://your-host:8080 ``` +### 改端口(默认 8080 冲突时) + +如果宿主机 8080 已被占用(NAS、其它服务),用一个环境变量一起改 api +内部监听 + web 容器 upstream + 宿主机映射: + +```bash +# 例:把整套改成 8081 +API_PORT=8081 WEB_PORT=8081 \ + docker compose -f deploy/docker-compose.yml up -d --build +``` + +`API_PORT` 控制: +- `api.SERVER_ADDR`(api 容器内监听) +- `api.expose` / `healthcheck`(容器间网络) +- `web` build 阶段的 nginx upstream(`web.conf` 里 `server api:__API_PORT__` 在 + build 时 sed 替换) + +`WEB_PORT` 控制宿主机到 web 容器 80 端口的映射。 + +`.env` 文件也能放(放在仓库根,自动被 compose 读): + +``` +API_PORT=8081 +WEB_PORT=8081 +``` + +> **改完端口不重 build 是不生效的**,因为 web 镜像里的 nginx conf 是 +> build 时定的。`up -d --build` 才能拿到新镜像。 + ### 部署拓扑 ``` @@ -207,8 +236,8 @@ docker compose -f deploy/docker-compose.yml up -d | 镜像 | 基础 | 关键配置 | |---|---|---| -| `fuellog/api` | 多阶段 `golang:1.22-alpine` → `alpine:3.20` | CGO=0 出纯静态二进制 / tini 收僵尸 / 非 root / 时区 Asia/Shanghai / HEALTHCHECK | -| `fuellog/web` | 多阶段 `node:20-alpine` → `nginx:1.27-alpine` | corepack 启 pnpm / `pnpm build` → nginx 静态 / gzip + 30 天 immutable cache | +| `fuellog/api` | 多阶段 `golang:1.27-alpine` → `alpine:3.20` | CGO=0 出纯静态二进制 / tini 收僵尸 / 非 root / 时区 Asia/Shanghai / HEALTHCHECK | +| `fuellog/web` | 多阶段 `node:20-alpine` → `nginx:1.27-alpine` | corepack 启 pnpm / `pnpm build` → nginx 静态 / gzip + 30 天 immutable cache / `API_PORT` build arg 注入 upstream | ### 数据持久化 @@ -216,20 +245,44 @@ docker compose -f deploy/docker-compose.yml up -d - 宿主机 docker volume 名:`fuellog-data` - 容器删了重建,数据还在;真正要备份:`docker run --rm -v fuellog-data:/data -v $(pwd):/backup alpine cp /data/fuellog.db /backup/` -### 反代建议(不要直接把 8080 暴露公网) +### 反代建议(不要直接把对外端口暴露公网) ``` -Internet ──TLS──▶ Nginx Proxy Manager ──127.0.0.1:8080──▶ fuellog-web (容器) +Internet ──TLS──▶ Nginx Proxy Manager ──127.0.0.1:WEB_PORT──▶ fuellog-web (容器) ``` -容器只 listen 8080,不直接对外,符合 plan §17 "容器内 API 默认只监听内网"。 +容器内 API 不直接对外,符合 plan §17 "容器内 API 默认只监听内网"。 +`WEB_PORT` 是宿主机 listen 端口(默认 8080),跟 `API_PORT`(容器内 api +监听,默认 8080)是两个独立变量,大部分场景保持一致。 ### 健康检查 -- api:`wget http://127.0.0.1:8080/health`,30s 间隔,启动 10s 后开始 +- api:`wget http://127.0.0.1:${API_PORT}/health`,30s 间隔,启动 10s 后开始 - web:`wget http://127.0.0.1/`,30s 间隔 - docker-compose 用 `depends_on: condition: service_healthy` 保证 web 等 api 健康后才起 +### 排错:502 Bad Gateway + +最常见原因:`API_PORT` 与 `api` 容器实际 listen 端口不一致。 + +`fuellog-web/nginx/web.conf` 里 upstream 写的是 `server api:__API_PORT__`, +由 build 阶段用 `docker compose` 传入的 `API_PORT` 替换。如果改了 +`SERVER_ADDR` 端口但忘改 build arg,或者改了 build arg 但没重 build, +nginx 就会 upstream 连不上返 502。 + +排查步骤: +```bash +# 1. 看 api 实际 listen 端口 +docker exec fuellog-api sh -c 'wget -qO- http://127.0.0.1:8080/health 2>&1 || echo 8080_FAIL' +docker exec fuellog-api sh -c 'wget -qO- http://127.0.0.1:8081/health 2>&1 || echo 8081_FAIL' + +# 2. 看 web 容器内 nginx conf 写的是哪个端口 +docker exec fuellog-web cat /etc/nginx/conf.d/web.conf | grep 'server api:' + +# 3. 不一致就重 build(只 up -d 不会重 build) +API_PORT=8081 docker compose -f deploy/docker-compose.yml up -d --build +``` + ### 开发模式(不想本地装 Go 时用) ```bash diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index bee4e51..8c680c3 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -12,7 +12,9 @@ services: container_name: fuellog-api restart: unless-stopped environment: - SERVER_ADDR: ":8080" + # SERVER_ADDR 改端口时,记得 web 的 build.args.API_PORT 一起改 + # (compose 已通过 ${API_PORT:-8080} 自动串联) + SERVER_ADDR: ":${API_PORT:-8080}" GIN_MODE: release DB_PATH: /data/fuellog.db TZ: Asia/Shanghai @@ -22,11 +24,11 @@ services: - fuellog-data:/data networks: - fuellog-net - # 不直接对外暴露 8080,仅 web 容器能访问 + # 不直接对外暴露 api 端口,仅 web 容器能访问 expose: - - "8080" + - "${API_PORT:-8080}" healthcheck: - test: ["CMD", "wget", "-q", "-O-", "http://127.0.0.1:8080/health"] + test: ["CMD", "wget", "-q", "-O-", "http://127.0.0.1:${API_PORT:-8080}/health"] interval: 30s timeout: 3s retries: 3 @@ -36,15 +38,22 @@ services: build: context: ../fuellog-web dockerfile: Dockerfile + args: + # 跟 api.SERVER_ADDR 保持一致;默认 8080 不用传 + # - compose 改了 SERVER_ADDR,这里也要改 API_PORT + - API_PORT=${API_PORT:-8080} image: fuellog/web:latest container_name: fuellog-web restart: unless-stopped depends_on: api: condition: service_healthy + environment: + # web 容器需要知道 api 端口,镜像里 build 阶段就替换好了 + API_PORT: ${API_PORT:-8080} ports: # 宿主机 :8080 -> 容器 :80;改 :80 则直接用 80 - - "8080:80" + - "${WEB_PORT:-8080}:80" networks: - fuellog-net healthcheck: diff --git a/fuellog-web/Dockerfile b/fuellog-web/Dockerfile index 5ec6100..acb0aad 100644 --- a/fuellog-web/Dockerfile +++ b/fuellog-web/Dockerfile @@ -23,11 +23,17 @@ RUN pnpm build # Runner:用 nginx 提供静态资源 + 反代 /api FROM nginx:1.27-alpine +# API 容器端口 —— 跟 deploy/docker-compose.yml 的 SERVER_ADDR 保持一致 +# web.conf 里用 __API_PORT__ 占位,RUN sed 替换 +# 默认 8080(对应 Dockerfile 的 ENV SERVER_ADDR=:8080 / compose 默认) +ARG API_PORT=8080 + # 删掉默认配置 RUN rm /etc/nginx/conf.d/default.conf -# 我们的反代 + SPA fallback 配置 +# 我们的反代 + SPA fallback 配置 + 端口占位替换 COPY nginx/web.conf /etc/nginx/conf.d/web.conf +RUN sed -i "s|__API_PORT__|${API_PORT}|g" /etc/nginx/conf.d/web.conf # 静态产物 COPY --from=builder /src/dist /usr/share/nginx/html diff --git a/fuellog-web/nginx/web.conf b/fuellog-web/nginx/web.conf index 609e4c8..7d92acf 100644 --- a/fuellog-web/nginx/web.conf +++ b/fuellog-web/nginx/web.conf @@ -4,7 +4,7 @@ # - /api/* 反代到后端容器 api:8080 upstream fuellog_api { - server api:8080; + server api:__API_PORT__; keepalive 32; }