变更内容: - 删除 `src` 子目录,将模块引用路径从 `src.functional_scaffold` 更新为 `functional_scaffold`。 - 修改相关代码、文档、测试用例及配置文件中的路径引用,包括 `README.md`、`Dockerfile`、`uvicorn` 启动命令等。 - 优化项目目录结构,提升代码维护性和可读性。
630 lines
16 KiB
Markdown
630 lines
16 KiB
Markdown
# CLAUDE.md
|
||
|
||
本文件为 Claude Code (claude.ai/code) 在此代码仓库中工作时提供指导。
|
||
|
||
为了方便团队交流,项目的自然语言使用中文,包括代码注释和文档等
|
||
|
||
## 项目概述
|
||
|
||
**FunctionalScaffold(函数式脚手架)** 是一个算法工程化 Serverless 解决方案的脚手架生成器。
|
||
|
||
### 核心目标
|
||
|
||
解决三大痛点:
|
||
1. **不确定的算力需求** - 需要动态扩缩容能力
|
||
2. **算法同学工程化能力不足** - 降低工程化门槛
|
||
3. **后端同学集成难度过高** - 标准化接口规范
|
||
|
||
## 技术架构
|
||
|
||
采用 **Docker 封装的 Serverless API 服务**方案:
|
||
|
||
- 算法代码 + 运行环境打包为 Docker 镜像
|
||
- 部署到云厂商 Serverless 平台实现自动扩缩容
|
||
- FastAPI 作为 HTTP 接口层
|
||
- 算法逻辑保持独立和专注
|
||
|
||
### 架构流程
|
||
|
||
```
|
||
用户请求 → API网关 → 容器实例(冷/热启动)→ FastAPI → 算法程序 → 返回结果
|
||
↓
|
||
外部服务(OSS/数据库)
|
||
```
|
||
|
||
### 代码架构
|
||
|
||
项目采用 **src layout** 结构(Python 最佳实践):
|
||
|
||
```
|
||
src/functional_scaffold/
|
||
├── algorithms/ # 算法层 - 所有算法必须继承 BaseAlgorithm
|
||
│ ├── base.py # 提供 execute() 包装器(埋点、错误处理)
|
||
│ └── prime_checker.py # 示例:质数判断算法
|
||
├── api/ # API 层 - FastAPI 路由和模型
|
||
│ ├── models.py # Pydantic 数据模型(使用 ConfigDict)
|
||
│ ├── routes.py # 路由定义(/invoke, /healthz, /readyz, /jobs)
|
||
│ └── dependencies.py # 依赖注入(request_id 生成)
|
||
├── core/ # 核心功能 - 横切关注点
|
||
│ ├── errors.py # 异常类层次结构
|
||
│ ├── logging.py # 结构化日志(JSON 格式)
|
||
│ ├── metrics.py # Prometheus 指标和装饰器
|
||
│ └── tracing.py # 分布式追踪(ContextVar)
|
||
├── utils/ # 工具函数
|
||
│ └── validators.py # 输入验证
|
||
├── config.py # 配置管理(pydantic-settings)
|
||
└── main.py # FastAPI 应用入口
|
||
```
|
||
|
||
**关键设计模式:**
|
||
|
||
1. **算法抽象层**:所有算法继承 `BaseAlgorithm`,只需实现 `process()` 方法。`execute()` 方法自动处理埋点、日志和错误包装。
|
||
|
||
2. **依赖注入**:使用 FastAPI 的 `Depends()` 机制注入 request_id,通过 `ContextVar` 在异步上下文中传递。
|
||
|
||
3. **配置管理**:使用 `pydantic-settings` 从环境变量或 `.env` 文件加载配置,支持类型验证。
|
||
|
||
4. **可观测性**:
|
||
- 日志:结构化 JSON 日志(pythonjsonlogger),自动包含 request_id
|
||
- 指标:Prometheus 格式(request_counter, request_latency, algorithm_counter)
|
||
- 追踪:request_id 关联所有日志和指标
|
||
- 日志收集:Loki + Promtail 自动收集和查询日志
|
||
|
||
## 开发命令
|
||
|
||
### 环境设置
|
||
|
||
```bash
|
||
# 创建虚拟环境并安装依赖(开发模式)
|
||
python -m venv venv
|
||
source venv/bin/activate # Windows: venv\Scripts\activate
|
||
pip install -e ".[dev]"
|
||
```
|
||
|
||
### 运行服务
|
||
|
||
```bash
|
||
# 方式1:使用辅助脚本(推荐)
|
||
./scripts/run_dev.sh
|
||
|
||
# 方式2:直接运行(开发模式,自动重载)
|
||
uvicorn functional_scaffold.main:app --reload --port 8000
|
||
|
||
# 方式3:生产模式
|
||
uvicorn functional_scaffold.main:app --host 0.0.0.0 --port 8000 --workers 4
|
||
```
|
||
|
||
访问地址:
|
||
- Swagger UI: http://localhost:8000/docs
|
||
- ReDoc: http://localhost:8000/redoc
|
||
- Metrics: http://localhost:8000/metrics
|
||
|
||
### 测试
|
||
|
||
```bash
|
||
# 运行所有测试
|
||
pytest tests/ -v
|
||
|
||
# 运行单个测试文件
|
||
pytest tests/test_algorithms.py -v
|
||
|
||
# 运行单个测试类
|
||
pytest tests/test_algorithms.py::TestPrimeChecker -v
|
||
|
||
# 运行单个测试方法
|
||
pytest tests/test_algorithms.py::TestPrimeChecker::test_prime_numbers -v
|
||
|
||
# 生成覆盖率报告
|
||
pytest tests/ --cov=src/functional_scaffold --cov-report=html
|
||
# 查看报告:open htmlcov/index.html
|
||
|
||
# 使用辅助脚本(包含代码检查)
|
||
./scripts/run_tests.sh
|
||
```
|
||
|
||
### 代码质量
|
||
|
||
```bash
|
||
# 代码格式化(自动修复)
|
||
black src/ tests/
|
||
|
||
# 代码检查(不修改文件)
|
||
black --check src/ tests/
|
||
|
||
# 代码检查
|
||
ruff check src/ tests/
|
||
|
||
# 自动修复可修复的问题
|
||
ruff check --fix src/ tests/
|
||
```
|
||
|
||
配置说明:
|
||
- Black: 行长度 100,目标 Python 3.9+
|
||
- Ruff: 行长度 100,目标 Python 3.9+
|
||
|
||
### Docker
|
||
|
||
```bash
|
||
# 构建镜像
|
||
docker build -f deployment/Dockerfile -t functional-scaffold:latest .
|
||
|
||
# 运行容器
|
||
docker run -p 8000:8000 functional-scaffold:latest
|
||
|
||
# 使用 docker-compose(包含 Prometheus + Grafana + Loki)
|
||
cd deployment
|
||
docker-compose up
|
||
# Grafana: http://localhost:3000 (admin/admin)
|
||
# Prometheus: http://localhost:9090
|
||
# Loki: http://localhost:3100
|
||
```
|
||
|
||
### 文档
|
||
|
||
```bash
|
||
# 导出 OpenAPI 规范到 docs/swagger/openapi.json
|
||
python scripts/export_openapi.py
|
||
```
|
||
|
||
## 添加新算法
|
||
|
||
### 1. 创建算法类(继承 BaseAlgorithm)
|
||
|
||
```python
|
||
# src/functional_scaffold/algorithms/my_algorithm.py
|
||
from typing import Dict, Any
|
||
from .base import BaseAlgorithm
|
||
|
||
class MyAlgorithm(BaseAlgorithm):
|
||
"""我的算法类"""
|
||
|
||
def process(self, input_data: Any) -> Dict[str, Any]:
|
||
"""
|
||
算法处理逻辑
|
||
|
||
Args:
|
||
input_data: 输入数据
|
||
|
||
Returns:
|
||
Dict[str, Any]: 处理结果
|
||
"""
|
||
# 实现算法逻辑
|
||
result = do_something(input_data)
|
||
return {"result": result}
|
||
```
|
||
|
||
### 2. 注册到 `__init__.py`
|
||
|
||
```python
|
||
# src/functional_scaffold/algorithms/__init__.py
|
||
from .my_algorithm import MyAlgorithm
|
||
__all__ = [..., "MyAlgorithm"]
|
||
```
|
||
|
||
### 3. 添加 API 端点(在 `api/routes.py`)
|
||
|
||
```python
|
||
@router.post("/my-endpoint")
|
||
async def my_endpoint(
|
||
request: MyRequest,
|
||
request_id: str = Depends(get_request_id)
|
||
):
|
||
"""我的算法端点"""
|
||
algorithm = MyAlgorithm()
|
||
result = algorithm.execute(request.data)
|
||
return MyResponse(request_id=request_id, **result)
|
||
```
|
||
|
||
### 4. 定义数据模型(在 `api/models.py`)
|
||
|
||
```python
|
||
class MyRequest(BaseModel):
|
||
"""我的请求模型"""
|
||
|
||
model_config = ConfigDict(
|
||
json_schema_extra={
|
||
"example": {"data": "示例数据"}
|
||
}
|
||
)
|
||
|
||
data: str = Field(..., description="输入数据")
|
||
```
|
||
|
||
### 5. 编写测试
|
||
|
||
```python
|
||
# tests/test_my_algorithm.py
|
||
def test_my_algorithm():
|
||
"""测试我的算法"""
|
||
algo = MyAlgorithm()
|
||
result = algo.process("测试数据")
|
||
assert result["result"] == expected
|
||
```
|
||
|
||
## 配置管理
|
||
|
||
配置通过 `src/functional_scaffold/config.py` 的 `Settings` 类管理:
|
||
|
||
- 从环境变量读取(不区分大小写)
|
||
- 支持 `.env` 文件
|
||
- 使用 `pydantic-settings` 进行类型验证
|
||
|
||
配置示例:
|
||
```bash
|
||
# .env 文件
|
||
APP_ENV=production
|
||
LOG_LEVEL=INFO
|
||
METRICS_ENABLED=true
|
||
```
|
||
|
||
访问配置:
|
||
```python
|
||
from functional_scaffold.config import settings
|
||
print(settings.app_env) # "production"
|
||
```
|
||
|
||
## 可观测性
|
||
|
||
### 日志
|
||
|
||
使用 `core/logging.py` 的 `setup_logging()`:
|
||
|
||
```python
|
||
from functional_scaffold.core.logging import setup_logging
|
||
|
||
# 设置日志
|
||
logger = setup_logging(level="INFO", format_type="json")
|
||
|
||
# 记录日志(自动包含 request_id)
|
||
logger.info("处理请求", extra={"user_id": "123"})
|
||
```
|
||
|
||
**日志特性:**
|
||
- 结构化 JSON 格式
|
||
- 自动包含 request_id(从 ContextVar 中提取)
|
||
- 支持文件日志(可选,通过环境变量启用)
|
||
- 日志轮转(100MB,保留 5 个备份)
|
||
|
||
### 日志收集(Loki)
|
||
|
||
项目集成了 Grafana Loki 日志收集系统,支持两种收集模式:
|
||
|
||
**模式 1: Docker stdio 收集(默认,推荐)**
|
||
- 自动收集容器标准输出/错误
|
||
- 无需修改应用代码
|
||
- 性能影响极小
|
||
|
||
**模式 2: 文件收集(备用)**
|
||
- 日志持久化到文件
|
||
- 支持日志轮转
|
||
- 需要设置 `LOG_FILE_ENABLED=true`
|
||
|
||
**查询日志:**
|
||
|
||
```bash
|
||
# 使用 Loki API
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app"}'
|
||
|
||
# 按 request_id 过滤
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app"} |= "request-id-here"'
|
||
```
|
||
|
||
**Grafana 仪表板:**
|
||
- 访问 http://localhost:3000
|
||
- 进入 "日志监控" 仪表板
|
||
- 使用 Request ID 输入框过滤特定请求的日志
|
||
|
||
**相关文档:**
|
||
- 完整文档:`docs/loki-integration.md`
|
||
- 使用说明:`docs/grafana-dashboard-usage.md`
|
||
- 快速参考:`docs/loki-quick-reference.md`
|
||
|
||
### 指标
|
||
|
||
使用 `core/metrics.py` 的装饰器:
|
||
|
||
```python
|
||
from functional_scaffold.core.metrics import track_algorithm_execution
|
||
|
||
@track_algorithm_execution("my_algorithm")
|
||
def my_function():
|
||
"""我的函数"""
|
||
pass
|
||
```
|
||
|
||
可用指标:
|
||
- `http_requests_total{method, endpoint, status}` - HTTP 请求总数
|
||
- `http_request_duration_seconds{method, endpoint}` - HTTP 请求延迟
|
||
- `algorithm_executions_total{algorithm, status}` - 算法执行总数
|
||
- `algorithm_execution_duration_seconds{algorithm}` - 算法执行延迟
|
||
|
||
### 追踪
|
||
|
||
Request ID 自动注入到所有请求和日志:
|
||
|
||
```python
|
||
from functional_scaffold.core.tracing import get_request_id
|
||
|
||
# 在请求上下文中获取 request_id
|
||
request_id = get_request_id()
|
||
```
|
||
|
||
**Request ID 特性:**
|
||
- 自动生成或从请求头 `X-Request-ID` 获取
|
||
- 通过 ContextVar 在异步上下文中传递
|
||
- 自动添加到所有日志记录中
|
||
- 可用于追踪单个请求的完整生命周期
|
||
- 在 Grafana 仪表板中可按 request_id 过滤日志
|
||
|
||
## 部署
|
||
|
||
### Kubernetes
|
||
|
||
```bash
|
||
kubectl apply -f deployment/kubernetes/deployment.yaml
|
||
kubectl apply -f deployment/kubernetes/service.yaml
|
||
```
|
||
|
||
配置说明:
|
||
- 3 个副本
|
||
- 资源限制:256Mi-512Mi 内存,250m-500m CPU
|
||
- 健康检查:存活探针 (/healthz),就绪探针 (/readyz)
|
||
|
||
### 阿里云函数计算
|
||
|
||
```bash
|
||
fun deploy -t deployment/serverless/aliyun-fc.yaml
|
||
```
|
||
|
||
### AWS Lambda
|
||
|
||
```bash
|
||
sam deploy --template-file deployment/serverless/aws-lambda.yaml
|
||
```
|
||
|
||
## 必须交付的三大组件
|
||
|
||
### 1. 接入规范
|
||
|
||
**API 端点标准:**
|
||
- `/invoke` - 同步调用接口
|
||
- `/jobs` - 异步任务接口(当前返回 501)
|
||
- `/healthz` - 存活检查
|
||
- `/readyz` - 就绪检查
|
||
- `/metrics` - Prometheus 指标
|
||
|
||
**Schema 规范:**
|
||
- 请求/响应 Schema(Pydantic 验证)
|
||
- 错误响应格式(统一的 ErrorResponse)
|
||
- 元数据和版本信息(每个响应包含 metadata)
|
||
|
||
### 2. Python SDK 运行时
|
||
|
||
**已实现的能力:**
|
||
- ✅ 参数校验(Pydantic + utils/validators.py)
|
||
- ✅ 错误包装和标准化(core/errors.py)
|
||
- ✅ 埋点(core/metrics.py - 延迟、失败率)
|
||
- ✅ 分布式追踪的关联 ID(core/tracing.py + RequestIdFilter)
|
||
- ✅ 日志收集和查询(Loki + Promtail)
|
||
- ⏳ Worker 运行时(重试、超时、DLQ - 待实现)
|
||
|
||
### 3. 脚手架生成器
|
||
|
||
**已包含的模板:**
|
||
- ✅ 示例算法函数(algorithms/prime_checker.py)
|
||
- ✅ Dockerfile(deployment/Dockerfile)
|
||
- ✅ CI/CD 流水线配置(.github/workflows/)
|
||
- ✅ Serverless 平台部署 YAML(deployment/serverless/)
|
||
- ✅ Grafana 仪表板模板(monitoring/grafana/dashboards/)
|
||
- ✅ 告警规则配置(monitoring/alerts/rules.yaml)
|
||
- ✅ Loki 日志收集配置(monitoring/loki.yaml, monitoring/promtail.yaml)
|
||
|
||
## 开发理念
|
||
|
||
**算法同学只需修改核心算法函数。** 所有基础设施、可观测性、部署相关的工作都由脚手架处理。
|
||
|
||
算法开发者只需:
|
||
1. 继承 `BaseAlgorithm`
|
||
2. 实现 `process()` 方法
|
||
3. 返回字典格式的结果
|
||
|
||
框架自动提供:
|
||
- HTTP 接口封装
|
||
- 参数验证
|
||
- 错误处理
|
||
- 日志记录
|
||
- 性能指标
|
||
- 健康检查
|
||
- 容器化部署
|
||
|
||
## 注意事项
|
||
|
||
1. **Pydantic V2**:使用 `ConfigDict` 而非 `class Config`,使用 `model_config` 而非 `Config`。
|
||
|
||
2. **异步上下文**:request_id 使用 `ContextVar` 存储,在异步函数中自动传递。
|
||
|
||
3. **测试隔离**:每个测试使用 `TestClient`,不需要启动真实服务器。
|
||
|
||
4. **Docker 构建**:Dockerfile 使用非 root 用户(appuser),包含健康检查。
|
||
|
||
5. **配置优先级**:环境变量 > .env 文件 > 默认值。
|
||
|
||
6. **Promtail 版本**:使用 Promtail 3.0.0 或更高版本,以支持较新的 Docker API(1.44+)。如果遇到 "client version too old" 错误,需要升级 Promtail 版本。
|
||
|
||
## 日志收集系统(Loki)
|
||
|
||
项目集成了 Grafana Loki 日志收集系统,提供强大的日志查询和分析能力。
|
||
|
||
### 架构
|
||
|
||
```
|
||
应用容器 (stdout/stderr)
|
||
↓
|
||
Docker Engine
|
||
↓
|
||
Promtail (日志采集器)
|
||
↓
|
||
Loki (日志存储)
|
||
↓
|
||
Grafana (可视化)
|
||
```
|
||
|
||
### 服务组件
|
||
|
||
**docker-compose 包含以下服务:**
|
||
- **app**: 应用服务(端口 8111)
|
||
- **loki**: 日志存储服务(端口 3100)
|
||
- **promtail**: 日志采集服务(端口 9080)
|
||
- **grafana**: 可视化服务(端口 3000)
|
||
- **prometheus**: 指标收集服务(端口 9090)
|
||
- **redis**: 缓存服务(端口 6380)
|
||
|
||
### 日志收集模式
|
||
|
||
#### 模式 1: Docker stdio 收集(默认)
|
||
|
||
**特点:**
|
||
- ✅ 无需修改应用代码
|
||
- ✅ 自动收集容器标准输出/错误
|
||
- ✅ 性能影响极小
|
||
- ✅ 推荐用于生产环境
|
||
|
||
**配置:**
|
||
应用容器需要添加标签(已配置):
|
||
```yaml
|
||
labels:
|
||
logging: "promtail"
|
||
logging_jobname: "functional-scaffold-app"
|
||
```
|
||
|
||
#### 模式 2: 文件收集(备用)
|
||
|
||
**特点:**
|
||
- ✅ 日志持久化到文件
|
||
- ✅ 支持日志轮转(100MB,5个备份)
|
||
- ✅ 适合需要本地日志文件的场景
|
||
|
||
**启用方式:**
|
||
```yaml
|
||
# docker-compose.yml
|
||
environment:
|
||
- LOG_FILE_ENABLED=true
|
||
- LOG_FILE_PATH=/var/log/app/app.log
|
||
```
|
||
|
||
### 日志格式
|
||
|
||
所有日志使用 JSON 格式,自动包含以下字段:
|
||
- `asctime`: 时间戳
|
||
- `name`: 日志器名称
|
||
- `levelname`: 日志级别(INFO, WARNING, ERROR)
|
||
- `message`: 日志消息
|
||
- `request_id`: 请求 ID(自动添加)
|
||
- `timestamp`: ISO 格式时间戳
|
||
|
||
### 查询日志
|
||
|
||
#### 使用 Loki API
|
||
|
||
```bash
|
||
# 查询所有日志
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app"}'
|
||
|
||
# 按 request_id 过滤
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app"} |= "request-id-here"'
|
||
|
||
# 查询错误日志
|
||
curl -G -s "http://localhost:3100/loki/api/v1/query_range" \
|
||
--data-urlencode 'query={job="functional-scaffold-app", level="ERROR"}'
|
||
```
|
||
|
||
#### 使用 Grafana 仪表板
|
||
|
||
1. 访问 http://localhost:3000(admin/admin)
|
||
2. 进入 "日志监控" 仪表板
|
||
3. 使用 Request ID 输入框过滤特定请求的日志
|
||
|
||
**仪表板面板:**
|
||
- **日志流(实时)**: 实时日志流
|
||
- **日志量趋势**: 按时间和级别统计
|
||
- **日志级别分布**: INFO/WARNING/ERROR 分布
|
||
- **错误日志**: 只显示 ERROR 级别
|
||
|
||
#### 使用 Grafana Explore
|
||
|
||
1. 访问 http://localhost:3000/explore
|
||
2. 选择 Loki 数据源
|
||
3. 使用 LogQL 查询语言
|
||
|
||
**常用查询:**
|
||
```logql
|
||
# 查询所有日志
|
||
{job="functional-scaffold-app"}
|
||
|
||
# 查询错误日志
|
||
{job="functional-scaffold-app", level="ERROR"}
|
||
|
||
# 按 request_id 过滤
|
||
{job="functional-scaffold-app"} |= "request-id-here"
|
||
|
||
# 使用 JSON 解析
|
||
{job="functional-scaffold-app"} | json | request_id="request-id-here"
|
||
|
||
# 统计日志量
|
||
sum by (level) (count_over_time({job="functional-scaffold-app"}[5m]))
|
||
```
|
||
|
||
### 验证和测试
|
||
|
||
```bash
|
||
# 验证 Loki 集成
|
||
./scripts/verify_loki.sh
|
||
|
||
# 测试 Request ID 过滤
|
||
./scripts/test_request_id_filter.sh
|
||
```
|
||
|
||
### 配置文件
|
||
|
||
- **Loki 配置**: `monitoring/loki.yaml`
|
||
- 日志保留期: 7 天
|
||
- 摄入速率限制: 10MB/s
|
||
- 自动压缩和清理
|
||
|
||
- **Promtail 配置**: `monitoring/promtail.yaml`
|
||
- Docker stdio 收集配置
|
||
- 文件收集配置
|
||
- JSON 日志解析规则
|
||
|
||
- **Grafana Provisioning**: `monitoring/grafana/`
|
||
- 数据源自动配置(datasources/)
|
||
- 仪表板自动加载(dashboards/)
|
||
|
||
### 故障排查
|
||
|
||
**看不到日志:**
|
||
1. 检查服务状态: `docker-compose ps`
|
||
2. 查看 Promtail 日志: `docker-compose logs promtail`
|
||
3. 验证容器标签: `docker inspect <container> | grep Labels`
|
||
|
||
**Docker socket 权限问题:**
|
||
```bash
|
||
sudo chmod 666 /var/run/docker.sock
|
||
```
|
||
|
||
**日志延迟:**
|
||
- Promtail 每 5 秒刷新一次
|
||
- 建议等待 5-10 秒后再查询
|
||
|
||
### 相关文档
|
||
|
||
- **完整文档**: `docs/loki-integration.md` - 包含查询示例、故障排查、性能优化
|
||
- **快速参考**: `docs/loki-quick-reference.md` - 常用命令和 LogQL 查询
|
||
- **仪表板使用**: `docs/grafana-dashboard-usage.md` - Grafana 仪表板使用说明
|
||
- **实施总结**: `docs/loki-implementation-summary.md` - 架构和实施细节
|
||
- **监控目录**: `monitoring/README.md` - 配置文件说明
|