FastAPI(Asyncpg + Docker)에서 “invalid literal for int() with base 10: 'None'” 오류 수정
Source: Dev.to
번역할 텍스트가 제공되지 않았습니다. 번역이 필요한 전체 내용을 알려주시면 한국어로 번역해 드리겠습니다.
문제 개요
Docker 환경에서 Aiven PostgreSQL을 사용해 FastAPI 애플리케이션을 배포할 때 다음과 같은 트레이스백이 발생할 수 있습니다:
ValueError: invalid literal for int() with base 10: 'None'
SQLAlchemy의 URL 파서는 문자열 -None을 데이터베이스 URL의 포트 부분으로 해석합니다. 이 URL은 다음과 같이 표시됩니다:
postgresql+asyncpg://user:pass@host:None/dbname
원인
SQLAlchemy의 make_url() 함수는 포트 구성 요소를 정수로 변환하려고 시도합니다. URL을 만들 때 사용된 변수가 None으로 평가되면, f‑string 보간이 이를 문자열 "None"으로 바꾸어 오류가 발생합니다.
전형적인 코드 패턴:
import os
DB_USER = os.getenv("DB_USER")
DB_PASSWORD = os.getenv("DB_PASSWORD")
DB_HOST = os.getenv("DB_HOST")
DB_PORT = os.getenv("DB_PORT")
DB_NAME = os.getenv("DB_NAME")
ASYNC_DB_URL = f"postgresql+asyncpg://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
engine = create_async_engine(ASYNC_DB_URL)
DB_PORT(또는 다른 구성 요소)가 컨테이너 환경에 없으면 None이 되어 :None이 포함된 잘못된 URL이 생성됩니다.
전체 DATABASE_URL과 구성 요소 기반 구성을 혼합하는 대체 로직도 이 문제를 일으킬 수 있습니다:
ASYNC_DB_URL = os.getenv("DATABASE_URL") or f"postgresql+asyncpg://{DB_USER}:{DB_PASSWORD}@{DB_HOST}:{DB_PORT}/{DB_NAME}"
DATABASE_URL이 없을 경우, 구성된 URL에 None 포트가 그대로 포함됩니다.
Solution
Use a Single DATABASE_URL
Prefer a single environment variable that contains the complete connection string. Validate its presence early and avoid building the URL from separate components.
import os
from sqlalchemy.ext.asyncio import create_async_engine
DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
raise ValueError("DATABASE_URL environment variable is not set")
engine = create_async_engine(DATABASE_URL)
Adjust the Scheme for asyncpg
Aiven (and many cloud providers) supply URLs with the postgres:// scheme. For SQLAlchemy async support, replace it with postgresql+asyncpg://.
raw_url = os.getenv("DATABASE_URL")
if raw_url and raw_url.startswith("postgres://"):
raw_url = raw_url.replace("postgres://", "postgresql+asyncpg://", 1)
engine = create_async_engine(raw_url)
Full Example (database.py)
import os
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker, declarative_base
# Retrieve and validate the full URL
DATABASE_URL = os.getenv("DATABASE_URL")
if not DATABASE_URL:
raise ValueError("DATABASE_URL environment variable is required")
# Ensure the asyncpg driver scheme
if DATABASE_URL.startswith("postgres://"):
DATABASE_URL = DATABASE_URL.replace("postgres://", "postgresql+asyncpg://", 1)
# Create the async engine
engine = create_async_engine(DATABASE_URL, echo=True)
# Session factory
AsyncSessionLocal = sessionmaker(
bind=engine,
class_=AsyncSession,
expire_on_commit=False,
)
# Base class for models
Base = declarative_base()
Docker 구성
DATABASE_URL을 .env 파일을 통해 또는 docker‑compose.yml에 직접 지정하여 컨테이너에 전달합니다.
services:
app:
build: .
env_file:
- .env
.env 예시:
DATABASE_URL=postgresql+asyncpg://avnadmin:password@host:port/defaultdb?sslmode=require
모범 사례
- 단일 진실 소스: 전체 연결 문자열을 하나의 변수에 저장합니다.
- 조기 검증: 필수 변수가 누락된 경우 명확한 오류를 발생시킵니다.
- 방어적 프로그래밍: 조각난 구성 요소로 URL을 구성하는 것을 피합니다.
- 일관된 스키마: SQLAlchemy async를 사용할 때
postgresql+asyncpg://를 사용합니다. - 최소 환경 변수: 환경 간 값 불일치 또는 누락 위험을 줄입니다.
구성을 단일 검증된 DATABASE_URL로 통합하면 None 포트 오류를 제거하고 Docker, Kubernetes 또는 기타 플랫폼 전반에 걸친 배포를 간소화할 수 있습니다.