4、后端项目初始化:FastAPI项目骨架搭建、项目目录结构设计、配置文件管理、日志系统集成

好,咱们正式开始动手了。

前面几章聊了架构设计和技术选型,说白了都是在纸上谈兵。这一章,我们要把想法落地成代码。我个人的习惯是,项目初始化这一步绝对不能马虎。骨架搭得正,后面写代码才顺溜。要是上来就乱写一气,后面重构的成本会让你哭出来。

今天我们要搞定四件事:FastAPI项目骨架搭建、目录结构设计、配置文件管理、日志系统集成。这四个东西是任何一个后端项目的基石。咱们一个一个来。

4.1 项目骨架搭建:从零开始

先说说FastAPI。我为什么选它?说白了,就是快。性能接近Go写的框架,开发效率又高,异步支持原生就有。我在之前的项目中用Flask写过运维平台,到了后期,异步任务一多,代码就变得很难看。FastAPI的依赖注入系统和Pydantic模型,简直就是为运维平台这种需要强类型校验的场景量身定做的。

先创建一个项目根目录,比如叫 ops-platform。然后我们初始化Python项目环境:

mkdir ops-platform
cd ops-platform
python -m venv venv
source venv/bin/activate  # Windows下用 venv\Scripts\activate
pip install fastapi uvicorn pydantic-settings loguru

嗯,这里要注意。我习惯用 pydantic-settings 来管理配置,而不是直接用环境变量或者yaml文件。为什么?因为Pydantic的校验能力太强了,你配置写错了,启动时直接报错,不会等到运行时才炸。我曾经在生产环境遇到过因为配置文件少了个逗号导致服务挂掉的情况,从那以后,我对配置校验就特别敏感。

接下来,创建一个最基础的入口文件 main.py

from fastapi import FastAPI

app = FastAPI(title="运维平台", version="0.1.0")

@app.get("/health")
async def health_check():
    return {"status": "ok"}

运行一下:

uvicorn main:app --reload --port 8000

看到 {"status": "ok"} 了吗?骨架搭好了。但这才刚开始。

4.2 项目目录结构设计:别让代码变成一锅粥

很多新手喜欢把所有代码都塞在一个文件夹里。我见过一个项目,一个 utils.py 文件写了三千行,里面既有数据库操作,又有发邮件,还有日志格式化。你想想看,这种项目谁敢接手?

我推荐一个经过多个项目验证的目录结构,专门针对运维平台这种多模块的场景:

ops-platform/
├── app/
│   ├── __init__.py
│   ├── main.py              # 应用入口
│   ├── core/                # 核心模块
│   │   ├── __init__.py
│   │   ├── config.py        # 配置管理
│   │   ├── logger.py        # 日志集成
│   │   └── exceptions.py    # 全局异常处理
│   ├── modules/             # 业务模块
│   │   ├── __init__.py
│   │   ├── cmdb/            # 配置管理数据库
│   │   ├── deploy/          # 部署模块
│   │   ├── monitor/         # 监控模块
│   │   └── user/            # 用户模块
│   ├── schemas/             # Pydantic模型
│   ├── services/            # 业务逻辑层
│   └── utils/               # 工具函数
├── logs/                    # 日志目录
├── tests/                   # 测试
├── .env                     # 环境变量
├── requirements.txt
└── Dockerfile

这个结构有什么好处?我简单说三点:

  • 模块隔离:每个业务模块(cmdb、deploy等)都是独立的,互不干扰。你甚至可以单独把某个模块抽出来做成微服务。
  • 分层清晰schemas 管数据校验,services 管业务逻辑,modules 管路由和接口。各司其职。
  • 核心统一:配置、日志、异常处理都放在 core 里,全局只初始化一次,避免到处重复写。
我的小技巧:在 app/__init__.py 里不要写任何业务代码。只做一件事:把 main.py 里的 app 对象暴露出来。这样其他模块导入时不会产生循环引用。

为了让你更直观地理解这个结构,我画了一张图:

ops-platform/ (根目录) app/ (应用主目录) core/ (核心模块) config.py, logger.py modules/ (业务模块) cmdb/ deploy/ monitor/ user/ schemas/ (数据模型) logs/ tests/ .env ← 核心配置,全局共享 ← 业务模块,独立开发 ← 数据校验,统一管理 ← 模型定义,前后端对齐

4.3 配置文件管理:别再硬编码了

我见过最离谱的代码,数据库连接信息直接写在 main.py 里。换环境就得改代码,然后忘了改回来,直接把测试库给连上了生产环境。嗯,这种事发生过不止一次。

我们用 pydantic-settings 来管理配置。在 app/core/config.py 里写:

from pydantic_settings import BaseSettings
from typing import Optional

class Settings(BaseSettings):
    # 应用配置
    APP_NAME: str = "运维平台"
    APP_VERSION: str = "0.1.0"
    DEBUG: bool = False
    
    # 数据库
    DB_HOST: str = "localhost"
    DB_PORT: int = 3306
    DB_USER: str = "root"
    DB_PASSWORD: str = ""
    DB_NAME: str = "ops_platform"
    
    # Redis
    REDIS_HOST: str = "localhost"
    REDIS_PORT: int = 6379
    REDIS_PASSWORD: Optional[str] = None
    
    # 日志
    LOG_LEVEL: str = "INFO"
    LOG_FILE: str = "logs/app.log"
    
    class Config:
        env_file = ".env"
        env_file_encoding = "utf-8"

settings = Settings()

然后在项目根目录创建 .env 文件:

DB_HOST=192.168.1.100
DB_PORT=3306
DB_USER=ops_admin
DB_PASSWORD=your_secure_password
DB_NAME=ops_platform
LOG_LEVEL=DEBUG
警告.env 文件里不要写生产环境的真实密码!我建议用环境变量或者密钥管理服务(比如Vault)来管理敏感信息。.env 只适合开发环境。

使用的时候,直接导入 settings 对象就行:

from app.core.config import settings

print(f"连接数据库: {settings.DB_HOST}:{settings.DB_PORT}")

你看,多干净。而且Pydantic会自动帮你做类型转换和校验。比如 DB_PORT 你写了个字符串 "abc",启动时直接报错,不会等到运行时才暴露问题。

4.4 日志系统集成:让问题无处遁形

日志这东西,平时没人关心。一旦出了故障,它就是你的救命稻草。我选择 loguru 而不是Python自带的 logging 模块。为什么?因为loguru用起来太舒服了,配置简单,输出漂亮,还自带轮转功能。

app/core/logger.py 里配置:

from loguru import logger
import sys
from app.core.config import settings

# 移除默认的handler
logger.remove()

# 添加控制台输出
logger.add(
    sys.stdout,
    format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level:<8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
    level=settings.LOG_LEVEL,
    colorize=True
)

# 添加文件输出,自动轮转
logger.add(
    settings.LOG_FILE,
    rotation="500 MB",    # 每个文件最大500MB
    retention="30 days",  # 保留30天
    compression="zip",    # 压缩旧日志
    format="{time:YYYY-MM-DD HH:mm:ss} | {level:<8} | {name}:{function}:{line} - {message}",
    level=settings.LOG_LEVEL,
    enqueue=True          # 异步写入,不阻塞主线程
)

然后在 main.py 里引入:

from app.core.logger import logger

@app.on_event("startup")
async def startup_event():
    logger.info("运维平台启动成功")
    logger.debug(f"当前配置: DEBUG={settings.DEBUG}")
重点:loguru的 enqueue=True 参数一定要加上。运维平台通常要处理大量并发请求,如果日志写入是同步的,高并发下会拖慢接口响应。异步写入能有效避免这个问题。

我曾经遇到过一个线上故障,排查了半天才发现是日志文件写满了磁盘。从那以后,我养成了两个习惯:一是给日志加上轮转和压缩,二是监控日志目录的磁盘使用率。你想想看,如果日志把磁盘撑爆了,服务挂了,你连查日志的机会都没有——这多讽刺。

4.5 整合:让所有模块协同工作

现在我们把所有东西串起来。在 app/main.py 里:

from fastapi import FastAPI
from app.core.config import settings
from app.core.logger import logger

app = FastAPI(
    title=settings.APP_NAME,
    version=settings.APP_VERSION,
    debug=settings.DEBUG
)

@app.on_event("startup")
async def startup():
    logger.info(f"{settings.APP_NAME} v{settings.APP_VERSION} 启动")
    # 这里后面会连接数据库、初始化Redis等

@app.get("/health")
async def health():
    logger.debug("健康检查被调用")
    return {
        "status": "ok",
        "version": settings.APP_VERSION
    }

运行一下看看效果:

uvicorn app.main:app --reload --port 8000

你会看到控制台输出漂亮的彩色日志。打开 logs/app.log,也能看到同样的内容被记录下来了。

嗯,到这里,我们的后端项目骨架就搭好了。目录结构清晰,配置集中管理,日志系统就绪。这就像盖房子打地基,地基稳了,后面往上盖多少层都不怕。

记住一句话:项目初始化阶段的每一分钟投入,都会在后期节省你十倍的调试时间。别嫌麻烦,别图省事。把基础打牢,后面写业务代码的时候,你会感谢现在的自己。


专注资料整理