deploy(compose): app healthcheck + 优雅停 + 日志策略 + README 部署章节
- Dockerfile:alpine 安装 wget;加 HEALTHCHECK(/v1/health 探测,
interval 15s、start_period 20s、retries 3)
- docker-compose.yml app:
· stop_grace_period 30s(配合 fiber ShutdownWithContext 10s
+ cron 任务收尾,避免 SIGKILL 截断在途请求)
· restart unless-stopped
· json-file logging:单文件 50MB、保留 5 个滚动文件
· 业务日志卷 ./logs:/app/logs
- docker-compose.yml postgres:同样配 restart + logging
- logs/ 目录用 .gitkeep 保留;.gitignore 加 logs/* + !logs/.gitkeep
- README 新增'一键部署(docker compose)'整章:3 步上线、启动顺序图、
healthcheck 说明、优雅停机、日志、回填历史、生产 checklist
所有 12 条守卫保持无输出;build / vet 干净。
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -17,6 +17,10 @@ vendor/
|
|||||||
.env.*
|
.env.*
|
||||||
!.env.example
|
!.env.example
|
||||||
|
|
||||||
|
# Runtime logs(容器 bind mount,目录用 .gitkeep 保留)
|
||||||
|
logs/*
|
||||||
|
!logs/.gitkeep
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|||||||
@@ -8,9 +8,15 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o /out/app ./cmd/app \
|
|||||||
&& CGO_ENABLED=0 GOOS=linux go build -o /out/backfill ./cmd/backfill
|
&& CGO_ENABLED=0 GOOS=linux go build -o /out/backfill ./cmd/backfill
|
||||||
|
|
||||||
FROM alpine:3.20
|
FROM alpine:3.20
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
# wget 用于容器 healthcheck;ca-certificates 用于 TLS;tzdata 用于本地时区
|
||||||
|
RUN apk add --no-cache ca-certificates tzdata wget
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=builder /out/app /app/app
|
COPY --from=builder /out/app /app/app
|
||||||
COPY --from=builder /out/backfill /app/backfill
|
COPY --from=builder /out/backfill /app/backfill
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
|
# 容器级 healthcheck:拒绝把 unhealthy 容器纳入负载均衡
|
||||||
|
HEALTHCHECK --interval=15s --timeout=3s --start-period=20s --retries=3 \
|
||||||
|
CMD wget --spider -q http://127.0.0.1:8080/v1/health || exit 1
|
||||||
|
|
||||||
ENTRYPOINT ["/app/app"]
|
ENTRYPOINT ["/app/app"]
|
||||||
|
|||||||
87
README.md
87
README.md
@@ -100,6 +100,93 @@ curl -s 'localhost:8080/v1/market/context?symbol=BTCUSDT' | jq 'keys'
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 一键部署(docker compose)
|
||||||
|
|
||||||
|
适合生产或测试环境快速起服务。**3 步上线**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. 准备环境变量
|
||||||
|
cp .env.example .env
|
||||||
|
# 编辑 .env,至少修改 POSTGRES_PASSWORD(不修改 compose 直接拒绝启动)
|
||||||
|
# 国内机器还要填 HTTP_PROXY / HTTPS_PROXY
|
||||||
|
|
||||||
|
# 2. 启动(自动建表)
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 3. 验证
|
||||||
|
curl -s http://localhost:8080/v1/health | jq .
|
||||||
|
docker compose ps # 应看到 3 行:postgres healthy / migrate exited(0) / app running
|
||||||
|
```
|
||||||
|
|
||||||
|
### 启动顺序
|
||||||
|
|
||||||
|
`docker compose up` 会按如下依赖链推进,**任何一环失败都会阻断**:
|
||||||
|
|
||||||
|
```
|
||||||
|
postgres 起 → pg_isready healthcheck 通过
|
||||||
|
→ migrate 容器跑 migrations/ up → 退出 0
|
||||||
|
→ app 起,连 DB,启 cron
|
||||||
|
```
|
||||||
|
|
||||||
|
migrate 是一次性 init container(image `migrate/migrate:v4.18.1`),跑完立即退出。app 用 `depends_on: condition: service_completed_successfully` 等它结束,保证 app 启动时表一定存在。
|
||||||
|
|
||||||
|
### 容器 healthcheck
|
||||||
|
|
||||||
|
- **postgres**:`pg_isready`,5s 间隔
|
||||||
|
- **app**:`wget --spider /v1/health`,15s 间隔,start_period 20s
|
||||||
|
|
||||||
|
`docker compose ps` 会显示 `(healthy)` 或 `(unhealthy)`。k8s / 反向代理 / LB 可直接根据 healthcheck 状态做流量切换。
|
||||||
|
|
||||||
|
### 优雅停机
|
||||||
|
|
||||||
|
`docker compose stop` 默认 10s,本项目设 `stop_grace_period: 30s`:
|
||||||
|
|
||||||
|
- app 收到 SIGTERM
|
||||||
|
- fiber `ShutdownWithContext(10s)` 等当前 HTTP 请求结束
|
||||||
|
- cron Scheduler.Stop() 等当前 collector 任务结束
|
||||||
|
- 30s 超时后 docker 才发 SIGKILL
|
||||||
|
|
||||||
|
### 日志
|
||||||
|
|
||||||
|
容器 stdout 走 docker json-file driver:
|
||||||
|
|
||||||
|
- 单文件最大 50 MB
|
||||||
|
- 保留 5 个滚动文件
|
||||||
|
- 应用业务日志额外挂载到宿主机 `./logs/`
|
||||||
|
|
||||||
|
查实时日志:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f app
|
||||||
|
docker compose logs -f postgres
|
||||||
|
```
|
||||||
|
|
||||||
|
### 回填历史
|
||||||
|
|
||||||
|
启动后 DB 是空的;调用 `/v1/market/context` 会 fallback 到 Binance(带 warnings)。要立刻获得满载历史,进容器跑 backfill:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose exec app /app/backfill --symbol BTCUSDT --interval 1h --limit 500
|
||||||
|
docker compose exec app /app/backfill --symbol ETHUSDT --interval 1h --limit 500
|
||||||
|
# 其它 interval 同理
|
||||||
|
```
|
||||||
|
|
||||||
|
或等 cron 自然落库(15m K 线每 15 分钟一次,1h 每整点一次)。
|
||||||
|
|
||||||
|
### 生产 checklist
|
||||||
|
|
||||||
|
合并前再过一遍:
|
||||||
|
|
||||||
|
- [ ] `.env` 中 `POSTGRES_PASSWORD` 已改为强密码
|
||||||
|
- [ ] 国内机器:`.env` 中 `HTTP_PROXY` / `HTTPS_PROXY` 已配
|
||||||
|
- [ ] `docker compose config` 渲染后无 `${VAR:?}` 报错
|
||||||
|
- [ ] `docker compose up -d` 后 `docker compose ps` 全部 healthy / exited(0)
|
||||||
|
- [ ] `curl /v1/health` 200,`curl /v1/market/context?symbol=BTCUSDT` 返回非空 `technical`
|
||||||
|
- [ ] 宿主机 `./logs/` 目录有 app 写入权限(默认 alpine root 用户写没问题)
|
||||||
|
- [ ] `make guard` 在仓库根目录全绿(即使是 deploy 修改,守卫规则也必须不破)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 国内网络
|
## 国内网络
|
||||||
|
|
||||||
`fapi.binance.com` 国内直连不稳定,通过环境变量挂代理即可(Go 默认尊重 `HTTPS_PROXY`):
|
`fapi.binance.com` 国内直连不稳定,通过环境变量挂代理即可(Go 默认尊重 `HTTPS_PROXY`):
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ services:
|
|||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 10
|
retries: 10
|
||||||
start_period: 10s
|
start_period: 10s
|
||||||
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "50m"
|
||||||
|
max-file: "5"
|
||||||
|
|
||||||
# init container:跑完 migration 立即退出。
|
# init container:跑完 migration 立即退出。
|
||||||
# 由 app 依赖它 service_completed_successfully,保证表存在再启动 app。
|
# 由 app 依赖它 service_completed_successfully,保证表存在再启动 app。
|
||||||
@@ -58,6 +64,16 @@ services:
|
|||||||
NO_PROXY: ${NO_PROXY:-}
|
NO_PROXY: ${NO_PROXY:-}
|
||||||
volumes:
|
volumes:
|
||||||
- ./config:/app/config:ro
|
- ./config:/app/config:ro
|
||||||
|
- ./logs:/app/logs
|
||||||
|
# SIGTERM → 等 app 内置 10s graceful shutdown(fiber.ShutdownWithContext)
|
||||||
|
# + cron 任务结束的缓冲 = 30s。超时后 docker 发 SIGKILL。
|
||||||
|
stop_grace_period: 30s
|
||||||
|
restart: unless-stopped
|
||||||
|
logging:
|
||||||
|
driver: json-file
|
||||||
|
options:
|
||||||
|
max-size: "50m"
|
||||||
|
max-file: "5"
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
hermes_pg_data:
|
hermes_pg_data:
|
||||||
|
|||||||
0
logs/.gitkeep
Normal file
0
logs/.gitkeep
Normal file
Reference in New Issue
Block a user