Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

python-service-kit

CI Python FastAPI Kubernetes

Production-ready FastAPI microservice template. Clone, rename, build features — structured logging, Prometheus metrics, health probes, and Kubernetes manifests are already wired up.

~2,000 req/s on a laptop, p50 12 ms — through the full middleware stack and a real Postgres round-trip:

Latency distribution under load

What's inside

Concern Solution
Logging structlog → JSON lines, correlation_id auto-propagated via contextvars
Metrics Prometheus: request count, latency histogram, in-flight gauge — at /metrics
Health /health (liveness) and /ready (readiness) with different semantics — see below
Config pydantic-settings — bad config kills the process at startup, not mid-request
DB Async SQLAlchemy 2.0, tuned connection pool
Deploy Dockerfile (non-root, multi-stage) + K8s Deployment / Service / HPA

Request flow

flowchart LR
    C[Client] --> M[ObservabilityMiddleware<br/>correlation_id + metrics + timing]
    M --> H["/health — liveness"]
    M --> R["/ready — readiness<br/>checks Postgres"]
    M --> B[your endpoints]
    M -.-> P[(Prometheus<br/>/metrics)]
    B --> D[(PostgreSQL<br/>async pool)]
Loading

One correlation ID, every log line — pass X-Correlation-ID (or let the service generate one) and grep the whole request story:

Structured logs with correlation ID

Quick start

git clone git@github.com:loguntsovae/python-service-kit.git my-service
cd my-service && cp .env.example .env
pip install -e ".[test]"
uvicorn app.main:app --reload
pytest tests/ -v --cov=app    # 3 tests, <1s

Architecture decisions

Liveness ≠ readiness. /health returns 200 while the process is alive — K8s restarts the pod when it fails. /ready checks real dependencies (Postgres) — K8s stops routing traffic but does not restart. When a downstream dies, pods go unready instead of crash-looping. That distinction prevents cascading failures.

Fail at startup, not at 3 a.m. pydantic-settings validates every env var's type before the app starts. A missing DATABASE_URL or non-integer DB_POOL_SIZE exits immediately with a clear error instead of surfacing 10 minutes into production traffic.

contextvars over parameter-threading. structlog carries request_id / correlation_id into every log line emitted anywhere in the request — including deep business logic — without passing a logger through function signatures.

Non-root container. Runs as UID 1000; runAsNonRoot: true compatible. A container escape from root is an incident, from UID 1000 it's a bad day.

Project structure

app/
  config.py      — settings from environment (validated at startup)
  logging.py     — structlog JSON configuration
  metrics.py     — Prometheus counters, histograms, gauge
  middleware.py  — ObservabilityMiddleware (metrics + correlation IDs)
  health.py      — /health and /ready
  db.py          — async SQLAlchemy engine + session factory
  main.py        — app factory and lifespan
k8s/             — Deployment, Service, HPA
tests/           — ASGI test client, probe tests

Reproduce the benchmark

docker run -d -p 5432:5432 -e POSTGRES_PASSWORD=pw -e POSTGRES_DB=service postgres:16-alpine
DATABASE_URL=postgresql+asyncpg://postgres:pw@localhost:5432/service uvicorn app.main:app
# then: 5000 requests, 32 concurrent against /ready (numbers above are from an M-series MacBook)

About

Production-ready FastAPI microservice template — Prometheus, structured logging, Vault secrets, async SQLAlchemy, K8s manifests

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages