Enhance Docker deployment: structured logging, health checks, resource limits, entrypoint script

- Add structured logging config with JSON/text format support
- Create docker-entrypoint.sh with dependency health checks
- Enhance /health endpoint with DB and Redis connectivity checks
- Add resource limits (CPU/memory) for all services
- Add network aliases for service discovery
- Add container hostnames for better identification
- Reduce health check timeout from 20s to 5s
- Add build metadata labels to Dockerfile
- Use ENTRYPOINT for dependency checking before app startup
- Log rotation: 50m per file, 5 files max
This commit is contained in:
2026-08-25 03:14:30 +00:00
parent ad037de48d
commit 0c805b1442
7 changed files with 618 additions and 60 deletions
+37 -33
View File
@@ -21,32 +21,14 @@ from typing import AsyncGenerator
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import text
from app.core.settings import get_settings
from app.core.database import engine, mtg_engine, async_session, mtg_async_session
from app.routers import auth, users, decks, rooms, admin, card_router, interactions, refresh, user_data, card_import
from app.routers.games import router as games_router
from app.services.mtgjson_manager import MTGJSONManager
def setup_logging(debug: bool = False) -> None:
"""Configure application logging with verbose support."""
level = logging.DEBUG if debug else logging.INFO
logging.basicConfig(
level=level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
]
)
if debug:
logging.getLogger('sqlalchemy.engine').setLevel(logging.WARNING)
logging.getLogger('sqlalchemy.pool').setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
logger.info(f"Logging initialized at level {logging.getLevelName(level)}")
from app.core.logging_config import configure_logging
async def run_initial_download():
@@ -91,8 +73,8 @@ def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
"""Application lifespan events for startup and shutdown."""
settings = get_settings()
# Startup
setup_logging(debug=settings.DEBUG)
# Configure logging
configure_logging()
logger = logging.getLogger(__name__)
logger.info(f"MTG Online Backend starting (v{settings.APP_VERSION})")
logger.info(f"Database: {settings.DATABASE_URL.split('@')[1] if '@' in settings.DATABASE_URL else 'configured'}")
@@ -146,24 +128,46 @@ app.include_router(card_import.router, prefix="/api/v1/card-import", tags=["Card
@app.get("/health", tags=["Health"])
async def health_check():
"""Health check endpoint with MTGJSON data status."""
"""Health check endpoint with full service status."""
from app.services.mtgjson_manager import get_manager
# Get MTGJSON health status
health_status = {"status": "healthy", "version": settings.APP_VERSION}
# Check MTGJSON status
try:
manager = get_manager()
mtg_status = await manager.get_health_status()
health_status["mtgjson"] = mtg_status
if mtg_status.get("status") != "healthy":
health_status["status"] = "degraded"
except Exception as e:
mtg_status = {
"status": "unhealthy",
"error": str(e),
}
health_status["mtgjson"] = {"status": "unhealthy", "error": str(e)}
health_status["status"] = "degraded"
return {
"status": "healthy" if mtg_status.get("status") == "healthy" else "degraded",
"version": settings.APP_VERSION,
"mtgjson": mtg_status,
}
# Check primary database
try:
from app.core.database import engine
async with engine.connect() as conn:
await conn.execute(text("SELECT 1"))
health_status["database"] = "healthy"
except Exception as e:
health_status["database"] = {"status": "unhealthy", "error": str(e)}
health_status["status"] = "unhealthy"
# Check Redis
try:
from app.core.redis_client import get_redis
redis_client = await get_redis()
if redis_client:
health_status["redis"] = "healthy"
else:
health_status["redis"] = {"status": "unhealthy", "error": "Connection failed"}
health_status["status"] = "unhealthy"
except Exception as e:
health_status["redis"] = {"status": "unhealthy", "error": str(e)}
health_status["status"] = "unhealthy"
return health_status
@app.get("/", tags=["Root"])