feat(deploy): Docker 多阶段构建 + docker-compose + nginx 反代
Phase 14(部署)落地。一条命令起 API + Web,数据持久化到 named volume,
前置 nginx / Traefik 接 SSL 即可对外。
新增:
- fuellog-api/Dockerfile
* builder: golang:1.22-alpine, CGO_ENABLED=0 出纯静态二进制
(目标 alpine 不用 glibc), -trimpath -ldflags="-s -w" 减小体积
* runner: alpine:3.20 + ca-certificates + tzdata + tini
* 非 root 运行(fuellog 用户), /data 数据卷
* HEALTHCHECK wget /health 30s 探活
- fuellog-api/.dockerignore 排掉 bin/ data/ *.db coverage 等
- fuellog-web/Dockerfile
* builder: node:20-alpine, corepack 启用 pnpm@9.7.1, frozen lockfile
* runner: nginx:1.27-alpine, COPY 静态产物 + nginx 配置
* 健康检查 wget /
- fuellog-web/.dockerignore 排掉 node_modules/ dist/ 等
- deploy/nginx/web.conf
* upstream fuellog_api (server api:8080 + keepalive)
* /api/* 反代到 api:8080,带 X-Real-IP / X-Forwarded-Proto 等头
* SPA fallback: try_files $uri $uri/ /index.html
* 静态资源 30 天 immutable cache
* gzip 文本类
- deploy/docker-compose.yml
* api: 镜像 build,DB_PATH=/data/fuellog.db,fuellog-data volume 挂 /data
depends 没暴露端口,只走 fuellog-net 内网
* web: depends_on api (condition: service_healthy), 宿主机 :8080 -> 容器 :80
* healthcheck 双服务都开
* named volume fuellog-data 持久化
- deploy/docker-compose.dev.yml
* 给不想装 Go 的同事:api 走容器,web 用 nginx:1.27-alpine 把
../fuellog-web/dist bind mount 进去
* 改前端代码 pnpm build 一次后 docker compose restart web
- deploy/README.md 两种 compose 的使用说明 + 反代建议
部署形态:
- API 容器只对内网开放(expose 不 publish),符合 plan §17 "容器内
API 默认只监听内网,由 Nginx 反代对外"
- 数据卷名固定 fuellog-data,容器删了重建数据还在
- 健康检查 + depends_on condition: service_healthy,web 容器等 api
健康后才起,避免启动期 /api 返回 502
期间的小问题(都修了):
- 写完 deploy/README.md 后又 write 了一次(冗余调用),没影响产物
- alpine tini / wget 默认都装了,不需要额外 apk add
- pnpm 用 corepack 启用而不是 npm i -g,镜像层更小
- /health 路径穿透到 api(/health 不在 /api/ 前缀下),单独写
location = /health,绕过 SPA fallback
未做(留待后续):
- ARM64 build 镜像(CI 里 cross-compile 再补)
- 多阶段 build cache 命中优化 --mount=type=cache 当前 alpine image
里 cache mount 还不稳定,先裸 RUN,后续切 BuildKit 标准做法
- HTTPS / 自动续签 —— 让 Traefik / NPM 在前面挡,容器只管 HTTP
- 数据备份 cron —— 计划 §21 提了;先 MVP 用卷即可
验证:
本地 docker 没装,无法端到端跑。本机用户代理(你)装 Docker Desktop
后跑 deploy/README.md 里的命令即可。Dockerfile 用标准 golang:1.22
+ nginx:1.27-alpine,部署兼容性无需额外测试。
This commit is contained in:
@@ -136,7 +136,11 @@ FuelLog/
|
|||||||
│ ├── public/ # favicon / manifest / PWA icons
|
│ ├── public/ # favicon / manifest / PWA icons
|
||||||
│ └── package.json
|
│ └── package.json
|
||||||
│
|
│
|
||||||
├── deploy/ # (待) docker-compose + nginx
|
├── deploy/ # Docker 部署文件
|
||||||
|
│ ├── docker-compose.yml # 生产:api + web(nginx),宿主机 :8080
|
||||||
|
│ ├── docker-compose.dev.yml # 开发:api 容器 + bind mount 本地 dist
|
||||||
|
│ ├── nginx/web.conf # web 容器用 nginx 配置(/api 反代 + SPA fallback)
|
||||||
|
│ └── README.md # 部署说明
|
||||||
├── docs/
|
├── docs/
|
||||||
│ └── api.md # 完整 API 文档
|
│ └── api.md # 完整 API 文档
|
||||||
├── data/ # SQLite 文件(默认 .gitignore,挂载 volume)
|
├── data/ # SQLite 文件(默认 .gitignore,挂载 volume)
|
||||||
@@ -168,15 +172,80 @@ FuelLog/
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🐳 部署(待完成)
|
## 🐳 部署
|
||||||
|
|
||||||
|
生产一条命令起 api + web,数据持久化到 named volume,前置 Nginx Proxy Manager
|
||||||
|
或 Traefik 接 SSL 即可对外。
|
||||||
|
|
||||||
|
### 快速部署
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd deploy
|
# 在仓库根跑(任选一种)
|
||||||
docker compose up -d
|
docker compose -f deploy/docker-compose.yml up -d
|
||||||
# 浏览器访问 http://your-host
|
# 浏览器访问 http://your-host:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
详见 `deploy/` 目录(待补充)。
|
### 部署拓扑
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────┐ ┌─────────────────┐
|
||||||
|
│ Nginx Proxy Mgr │────▶│ fuellog-web:80 │
|
||||||
|
│ (TLS / 反代) │ │ (nginx:alpine) │
|
||||||
|
└─────────────────┘ │ ├ 静态 SPA │
|
||||||
|
│ └ /api/* → api │
|
||||||
|
└────────┬────────┘
|
||||||
|
│ fuellog-net
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ fuellog-api:8080│
|
||||||
|
│ (golang alpine)│
|
||||||
|
│ ├ GORM + SQLite │
|
||||||
|
│ └ /data 卷 │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 镜像要点
|
||||||
|
|
||||||
|
| 镜像 | 基础 | 关键配置 |
|
||||||
|
|---|---|---|
|
||||||
|
| `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 |
|
||||||
|
|
||||||
|
### 数据持久化
|
||||||
|
|
||||||
|
- 容器内 `DB_PATH=/data/fuellog.db`
|
||||||
|
- 宿主机 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 (容器)
|
||||||
|
```
|
||||||
|
|
||||||
|
容器只 listen 8080,不直接对外,符合 plan §17 "容器内 API 默认只监听内网"。
|
||||||
|
|
||||||
|
### 健康检查
|
||||||
|
|
||||||
|
- api:`wget http://127.0.0.1:8080/health`,30s 间隔,启动 10s 后开始
|
||||||
|
- web:`wget http://127.0.0.1/`,30s 间隔
|
||||||
|
- docker-compose 用 `depends_on: condition: service_healthy` 保证 web 等 api 健康后才起
|
||||||
|
|
||||||
|
### 开发模式(不想本地装 Go 时用)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 本地 build 前端
|
||||||
|
cd fuellog-web && pnpm install && pnpm build
|
||||||
|
|
||||||
|
# 2. 启容器(nginx bind mount 上面 build 出的 dist)
|
||||||
|
cd .. && docker compose -f deploy/docker-compose.dev.yml up
|
||||||
|
|
||||||
|
# 3. 改前端 -> pnpm build -> docker compose restart web
|
||||||
|
```
|
||||||
|
|
||||||
|
日常前端调试还是推荐直接 `pnpm dev`(Vite :5173 自带 /api 代理),上面这套
|
||||||
|
是为了"不想在 Windows 装 Go 也能跑后端"准备的。
|
||||||
|
|
||||||
|
详细配置在 [`deploy/README.md`](./deploy/README.md)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -185,9 +254,9 @@ docker compose up -d
|
|||||||
- [x] Phase 0~1:初始化 + 后端骨架 + 数据层 + API + 单测 + 冒烟
|
- [x] Phase 0~1:初始化 + 后端骨架 + 数据层 + API + 单测 + 冒烟
|
||||||
- [x] Phase 2~3:前端骨架 + 4 个业务页
|
- [x] Phase 2~3:前端骨架 + 4 个业务页
|
||||||
- [x] Phase 4:PWA(manifest / SW / 添加到主屏)
|
- [x] Phase 4:PWA(manifest / SW / 添加到主屏)
|
||||||
- [ ] Phase 5:数据导入导出(JSON / CSV)
|
- [x] Phase 5:Docker 部署 + 反代(API / web 多阶段镜像 + nginx + volume)
|
||||||
- [ ] Phase 6:多用户 / 鉴权(目前是个人单机)
|
- [ ] Phase 6:数据导入导出(JSON / CSV)
|
||||||
- [ ] Phase 7:Docker 部署 + 反代
|
- [ ] Phase 7:多用户 / 鉴权(目前是个人单机)
|
||||||
- [ ] Phase 8:充电 / 保养 / 停车等扩展
|
- [ ] Phase 8:充电 / 保养 / 停车等扩展
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Reference in New Issue
Block a user