Упаковка FastAPI приложения в Docker, работа с источниками данных и очереди (Celery + Redis).
Научиться упаковывать FastAPI приложение в Docker, интегрировать парсер данных с базой данных и вызывать парсер через API и очередь (Celery + Redis). Освоить многосервисную архитектуру с оркестрацией через Docker Compose.
| Сервис | Контейнер | Порт | Назначение |
|---|---|---|---|
| app | fastapi_app_lr3 | 8000 | FastAPI — Personal Finance API + парсер |
| db | finance_db_lr3 | 5432 | PostgreSQL 16 — основная БД |
| redis | redis_lr3 | 6379 | Redis 7 — брокер Celery |
| celery-worker | celery_worker_lr3 | — | Celery worker — фоновая обработка парсинга |
| Компонент | Технология | Назначение |
|---|---|---|
| Веб-фреймворк | FastAPI | REST API приложения |
| ORM | SQLAlchemy 2.0 async | Асинхронный доступ к PostgreSQL |
| База данных | PostgreSQL 16 | Хранение данных (финансы + parsed_page) |
| Асинхронная очередь | Celery | Фоновая обработка задач парсинга |
| Брокер сообщений | Redis 7 | Хранение задач Celery и результатов |
| Контейнеризация | Docker + Compose | Упаковка и оркестрация сервисов |
| HTTP (async) | aiohttp | Асинхронные HTTP-запросы для парсинга |
| HTTP (sync) | requests | Синхронные запросы в Celery-задачах |
| Парсинг HTML | beautifulsoup4 | Извлечение <title> из HTML |
| Аутентификация | PyJWT | JWT-токены |
# Запрос
curl -X POST "http://localhost:8000/parse/direct?url=https://www.python.org"
# Ответ
{
"id": 1,
"url": "https://www.python.org",
"title": "Welcome to Python.org"
}
# Запрос — ставит URL в очередь Redis
curl -X POST "http://localhost:8000/parse/async?url=https://fastapi.tiangolo.com"
# Ответ
{
"message": "Task queued",
"task_id": "a1b2c3d4-...",
"status": "PENDING"
}
# Проверка статуса
curl "http://localhost:8000/parse/async/status/a1b2c3d4-..."
# Ответ (после выполнения)
{
"task_id": "a1b2c3d4-...",
"status": "SUCCESS",
"result": {
"id": 2,
"url": "https://fastapi.tiangolo.com",
"title": "FastAPI"
}
}
# Запрос
curl -X POST "http://localhost:8000/parse/batch" \
-H "Content-Type: application/json" \
-d '["https://www.python.org", "https://github.com"]'
# Ответ
{
"parsed": 2,
"time_seconds": 1.234,
"results": [...]
}
curl "http://localhost:8000/parse/history?limit=5"
parse_url_task.delay(url) — задача помещается в очередь Redistask_id и статус PENDING# app/models/models.py (добавлено)
class ParsedPage(Base):
__tablename__ = "parsed_page"
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
url: Mapped[str] = mapped_column(Text, nullable=False)
title: Mapped[str] = mapped_column(String(500), nullable=False)
parsed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
# app/tasks/celery_tasks.py
from celery import Celery
celery_app = Celery("parser_tasks", broker=REDIS_URL, backend=REDIS_URL)
@celery_app.task(bind=True, name="parse_url")
def parse_url_task(self, url: str) -> dict:
"""Fetch URL, extract , save to DB."""
resp = requests.get(url, timeout=10, headers={"User-Agent": "Mozilla/5.0"})
soup = BeautifulSoup(resp.text, "html.parser")
title = soup.title.string.strip() if soup.title else "No title"
# Save to PostgreSQL (sync)
page = ParsedPage(url=url, title=title, parsed_at=datetime.now(timezone.utc))
session.add(page); session.commit()
return {"id": page.id, "url": url, "title": title}
# docker-compose.yml
services:
db: # PostgreSQL 16 — основная БД
redis: # Redis 7 — брокер Celery
app: # FastAPI (Personal Finance API + парсер)
celery-worker: # Celery worker — фоновая обработка
# Запуск
$ docker compose up -d
# Сервисы:
# - http://localhost:8000/docs — Swagger UI
# - http://localhost:8000/parse/... — эндпоинты парсера
# - PostgreSQL на localhost:5432
# - Redis на localhost:6379
В рамках лабораторной работы №3 были решены следующие задачи:
Освоены навыки контейнеризации веб-приложений, настройки асинхронных очередей задач (первый шаг к MLOps) и многосервисной архитектуры.