日志、容器与部署
日志、容器与部署 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
这一课把「自己电脑上能跑」升级成「随时能跑、出事能查」:规范 uvicorn 启动、配健康检查、用 logging 替代 print、环境变量注入配置,最后验证服务重启后 SQLite 数据原样都在。
问题场景
服务在你电脑上跑了两周,某天重启后:「数据怎么少了」——因为 DB 路径用了相对路径,换了个目录启动就新建了空库;「昨天那个报错是什么」——print 输出早被终端滚没了;「服务到底活着吗」——只能靠前端页面能不能打开来判断。部署的底线就三样:数据在、日志在、健康可查。
核心解释
启动与配置先定死:
# 启动命令:绑定地址、工作目录、日志级别全部显式
uvicorn main:app --host 127.0.0.1 --port 8100 --log-level info
# settings.py —— 配置全走环境变量,路径一律绝对化
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent
DB_PATH = Path(os.environ.get("DB_PATH", BASE_DIR / "data" / "reactlab.db"))
AI_API_KEY = os.environ.get("AI_API_KEY", "") # 可选能力,缺失不阻塞启动
LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO")
健康检查是部署的眼睛:
import logging
from fastapi import FastAPI
app = FastAPI()
logger = logging.getLogger("study-api")
logging.basicConfig(level=LOG_LEVEL,
format="%(asctime)s %(levelname)s %(name)s %(message)s")
@app.get("/healthz")
def healthz():
ok = DB_PATH.exists()
logger.info("healthz check db=%s", DB_PATH) # 参数化日志,别 f-string
return {"status": "ok" if ok else "degraded", "db": ok}
逐条钉住部署纪律:
- logging 替代 print。print 没有时间戳、级别、来源,出问题翻不到;
logging.basicConfig一次配置全服务受益。记日志用参数化格式(logger.info("x=%s", x))而不是 f-string,级别过滤时省掉无用求值。 - 路径绝对化 + 数据目录独立。本站把运行时数据放
data/database/(gitignore),代码放代码目录——重启、换目录、重装代码都不影响数据。相对路径是「重启后数据消失」的头号元凶。 - 健康检查查真依赖。
/healthz不只返回 ok,还要探数据库文件存在、可连接;前端或脚本用它判断服务状态,而不是猜端口通不通。 - Docker 的铁律:数据不进镜像。镜像里只放代码和依赖,SQLite 数据文件靠 volume 挂载到宿主机:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV DB_PATH=/data/reactlab.db
EXPOSE 8100
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8100"]
docker run -v %cd%/data:/data -p 8100:8100 study-api
# -v 把宿主机 data 目录挂进容器 /data:容器删了重建,数据还在
示例拆解
「重启后数据仍在」是部署验收的唯一硬标准,完整验一遍:
uvicorn main:app --port 8100 & # 启动
curl -X POST localhost:8100/notes -d '{"title":"部署验收","body":"x"}' -H "Content-Type: application/json"
curl localhost:8100/healthz # {"status":"ok","db":true}
# Ctrl+C 停掉,重新启动
uvicorn main:app --port 8100 &
curl localhost:8100/notes # "部署验收" 还在 —— 持久化验收通过
Docker 场景同理:docker run 里写一条数据,docker rm 容器后重新 run,数据仍在 volume 里。这两个实验做完,「容器与部署」就不是概念,是亲手验证过的事实。
动手任务
- 把服务所有 print 改成 logger 对应级别调用,重启后确认日志带时间戳和级别。
- 写一个
scripts/health.sh(或 .ps1):请求/healthz,非 200 时退出码 1,模拟监控探活。 - 给 Dockerfile 加 volume 挂载跑通完整流程:写数据 → 删容器 → 重建 → 验证数据还在。
常见错误
- 数据库用相对路径:从哪个目录启动就建在哪个目录,重启换目录数据「消失」;一律绝对路径或环境变量。
- 日志级别全 INFO 还刷屏:请求级细节用 DEBUG,关键动作用 INFO,失败用 WARNING/ERROR;全堆一个级别等于没有级别。
- 容器里存数据:镜像或容器层里的 SQLite 文件随容器删除消失;数据必须在挂载的 volume。
- 健康检查永远返回 ok:不探真实依赖的健康检查是装饰品;至少探数据库可连。
检查与复盘
自查清单:
- 启动命令、数据库路径、日志级别全部显式,不依赖默认值和启动目录
- 出问题时能从日志定位「何时、哪一级、发生了什么」
- 亲手验证过「重启(含容器重建)后数据仍在」
参考资料
- FastAPI 官方文档:Deployment —— uvicorn 启动参数与部署概念
- logging 标准库文档 —— 配置、级别与格式
- Docker 官方 Python 语言指南 —— 镜像构建与 volume
读完这一课了?
标记完成后,工作台会安排接下来的学习。