009 add docker compose

This commit is contained in:
Раис Юсупалиев
2026-03-08 15:18:46 +03:00
parent 34d7a4c336
commit df2f132c05
7 changed files with 178 additions and 6 deletions
+16
View File
@@ -0,0 +1,16 @@
FROM python:3.14-slim
ENV PYTHONUNBUFFERED=1
WORKDIR /app
RUN pip install --no-cache-dir poetry
COPY pyproject.toml poetry.lock ./
RUN poetry config virtualenvs.create false \
&& poetry install --no-interaction --no-root
COPY app ./app
COPY config.yaml ./config.yaml
CMD ["poetry", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
+25 -1
View File
@@ -1,6 +1,25 @@
services:
app:
image: yusupal1ev/g2s-aggregator:0.0.0
environment:
G2S_CONFIG_FILE: /app/config.yaml
G2S_REPOSITORY__REDIS_DSN: redis://redis:6379/0
G2S_OBSERVABILITY__OTLP_ENDPOINT: http://signoz:4317
PYTHONUNBUFFERED: "1"
ports:
- "8000:8000"
depends_on:
- redis
- signoz
redis:
image: redis:7-alpine
command: ["redis-server", "--save", "", "--appendonly", "no"]
ports:
- "6379:6379"
signoz:
image: signoz/signoz:0.82.0
image: signoz/signoz:latest
container_name: signoz
environment:
G2S_ALERTS__TELEGRAM_BOT_TOKEN: ${G2S_ALERTS__TELEGRAM_BOT_TOKEN:-}
@@ -9,5 +28,10 @@ services:
SIGNOZ_ALERTS_RULES_FILE: /etc/signoz/alerts/signoz_alert_rules.yaml
ports:
- "3301:3301"
- "4317:4317"
volumes:
- signoz_data:/var/lib/signoz
- ./infra/alerts:/etc/signoz/alerts:ro
volumes:
signoz_data:
+42
View File
@@ -0,0 +1,42 @@
# Local Infrastructure Stack
## Services
- `app`: FastAPI service (`uvicorn app.main:app`) built from root `Dockerfile`
- `redis`: price cache backend
- `signoz`: observability backend (UI + OTLP receiver)
## Environment wiring
### `app` service
- `G2S_CONFIG_FILE=/app/config.yaml`
- `G2S_REPOSITORY__REDIS_DSN=redis://redis:6379/0`
- `G2S_OBSERVABILITY__OTLP_ENDPOINT=http://signoz:4317`
- `PYTHONUNBUFFERED=1`
### `signoz` service
- `G2S_ALERTS__TELEGRAM_BOT_TOKEN` (optional for Telegram alerts)
- `G2S_ALERTS__TELEGRAM_CHAT_ID` (optional for Telegram alerts)
## Startup instructions
1. Validate compose syntax:
- `docker compose config`
2. Start dependencies:
- `docker compose up -d redis signoz`
3. Verify running containers:
- `docker compose ps`
4. Start application:
- `docker compose up -d app`
5. Stop local stack:
- `docker compose down`
## Smoke command sequence
```bash
docker compose config
docker compose up -d redis signoz
docker compose ps
```
+5 -4
View File
@@ -1,7 +1,7 @@
# Spec Tasks Index
> ⚠️ This file is generated. Do not edit manually.
> Generated at (UTC): `2026-03-08T09:04:29+00:00`
> Generated at (UTC): `2026-03-08T12:18:20+00:00`
## Tasks
@@ -16,10 +16,11 @@
| 006 | DONE | 2026-03-07 | Add delivery price controller endpoint | `spec/tasks/006_add_delivery_price_controller.md` |
| 007 | DONE | 2026-03-07 | Add observability correlation and telemetry | `spec/tasks/007_add_observability_correlation.md` |
| 008 | DONE | 2026-03-07 | Add SigNoz to Telegram alerting configuration | `spec/tasks/008_add_signoz_telegram_alerting.md` |
| 009 | TODO | 2026-03-07 | Add local infrastructure stack | `spec/tasks/009_add_local_infra_stack.md` |
| 009 | DONE | 2026-03-07 | Add local infrastructure stack | `spec/tasks/009_add_local_infra_stack.md` |
| 010 | TODO | 2026-03-08 | Migrate to YAML-only configuration loading | `spec/tasks/010_migrate_to_yaml_only_configuration.md` |
## Summary
- Total: **10**
- Total: **11**
- TODO: **1**
- DONE: **9**
- DONE: **10**
+1 -1
View File
@@ -1,7 +1,7 @@
---
id: 009
title: Add local infrastructure stack
status: TODO
status: DONE
created: 2026-03-07
---
@@ -0,0 +1,39 @@
---
id: 010
title: Migrate to YAML-only configuration loading
status: TODO
created: 2026-03-08
---
## Context
В проекте используется смешанный подход конфигурации с переменными окружения. Требуется унифицировать источник конфигурации и оставить только `config.yaml`.
## Goal
Убрать использование переменных окружения из runtime-конфигурации приложения и перевести загрузку всех параметров на чтение из `config.yaml`.
## Constraints
- Соблюдать layered architecture из `AGENTS.md`.
- Scope задачи: только механизм загрузки конфигурации и места её потребления.
- Запрещено менять бизнес-правила и поведение use-case, не связанное с конфигурацией.
- Не изменять файлы в `spec/`.
## Acceptance criteria
- Runtime-конфигурация приложения загружается только из `config.yaml`.
- Переменные окружения не используются для чтения параметров Controller, Service, Business Logic, Repository, Adapter, Observability и Alerts.
- При отсутствии обязательных полей в `config.yaml` приложение завершает запуск с детерминированной ошибкой валидации конфигурации.
- Тесты конфигурации больше не опираются на `monkeypatch` переменных окружения и проверяют чтение из YAML.
## Definition of Done
- [ ] Удалено чтение переменных окружения из конфигурационного слоя.
- [ ] Все компонентные секции конфигурации продолжают читаться из `config.yaml`.
- [ ] Обновлены тесты конфигурации под YAML-only поведение.
- [ ] Smoke test старта приложения проходит с валидным `config.yaml`.
## Tests
- Обновить `tests/config/test_config_sections.py` под проверку YAML-only загрузки.
- Добавить/обновить негативные тесты отсутствующих обязательных YAML-полей.
- Проверить app startup smoke test с валидным `config.yaml`.
## Commands
- `poetry run pytest tests/config/test_config_sections.py -q`
- `poetry run pytest tests/smoke/test_app_import.py -q`
+50
View File
@@ -0,0 +1,50 @@
from pathlib import Path
import yaml
PROJECT_ROOT = Path(__file__).resolve().parents[2]
COMPOSE_FILE = PROJECT_ROOT / "docker-compose.yml"
INFRA_README = PROJECT_ROOT / "infra" / "README.md"
def _load_compose() -> dict:
content = yaml.safe_load(COMPOSE_FILE.read_text(encoding="utf-8"))
assert isinstance(content, dict)
return content
def test_compose_defines_required_services() -> None:
compose = _load_compose()
services = compose.get("services")
assert isinstance(services, dict)
assert {"app", "redis", "signoz"}.issubset(services.keys())
def test_app_service_contains_cache_and_observability_wiring() -> None:
compose = _load_compose()
app_service = compose["services"]["app"]
build_config = app_service.get("build")
assert isinstance(build_config, dict)
assert build_config["context"] == "."
assert build_config["dockerfile"] == "Dockerfile"
environment = app_service.get("environment")
assert isinstance(environment, dict)
assert environment["G2S_REPOSITORY__REDIS_DSN"] == "redis://redis:6379/0"
assert environment["G2S_OBSERVABILITY__OTLP_ENDPOINT"] == "http://signoz:4317"
assert environment["G2S_CONFIG_FILE"] == "/app/config.yaml"
depends_on = app_service.get("depends_on")
assert isinstance(depends_on, list)
assert "redis" in depends_on
assert "signoz" in depends_on
def test_smoke_command_sequence_is_documented() -> None:
readme = INFRA_README.read_text(encoding="utf-8")
assert "docker compose config" in readme
assert "docker compose up -d redis signoz" in readme
assert "docker compose ps" in readme