glasses.dev
#Docker#FastAPI#Troubleshooting#DevOps#Python

FastAPI 프로젝트의 Docker 도입과 트러블슈팅 로그

2025-11-21
5 min read

맨땅에 Docker: 로컬에서 클라우드까지의 험난한 여정

Swifty Backend API를 개발하며 로컬 환경(localhost)에서는 잘 동작하던 코드가 컨테이너 환경으로 넘어가는 순간 수많은 문제에 봉착했습니다. 특히 Google Cloud Run 배포를 목표로 Docker를 도입하면서 겪었던 주요 이슈들과 해결 방법을 공유합니다.


1. 의존성 지옥 (Dependency Hell): httpx 버전 충돌

가장 먼저 마주친 벽은 pip install 단계에서의 실패였습니다.

문제 상황

Gemini API를 사용하기 위한 google-generativeai 라이브러리와, Toss 인증을 위한 라이브러리가 공통적으로 httpx를 의존성으로 가지고 있었습니다. 하지만 서로 요구하는 버전 범위가 달랐습니다.

ERROR: Cannot install httpx<0.26.0 and >=0.25.0 and httpx>=0.27.0 because these package versions have conflicting dependencies.

해결

단순히 requirements.txt에 패키지명만 적는 것이 아니라, 두 라이브러리가 공통적으로 허용하는 교집합 버전을 찾아 명시적으로 지정해주어야 했습니다.

# requirements.txt
# HTTP Client (for Gemini API and Toss Auth)
httpx>=0.25.0,<0.28.0

이 경험을 통해 라이브러리 간의 의존성 트리를 이해하고, 명시적인 버전 관리의 중요성을 깨달았습니다.


2. 환경 변수(Env)와 배포 스크립트의 불협화음

로컬에서는 .env 파일 하나로 모든 것이 해결되었지만, gcloud 명령어로 배포할 때는 이야기가 달랐습니다.

문제 상황

gcloud run deploy 명령어의 --env-vars-file 옵션은 표준 .env 포맷(KEY=VALUE)이 아닌 YAML 포맷을 기대했습니다. 이로 인해 배포 시 환경 변수 파싱 에러가 발생했습니다.

ERROR: (gcloud) Failed to parse YAML from [.env.production]: expected '<document start>', but found '<scalar>'

해결

배포 전용 설정 파일인 .env.yaml을 별도로 생성하여 관리했습니다.

# .env.yaml
DATABASE_URL: "postgresql+asyncpg://user:pass@..."
LOG_LEVEL: "INFO"
# ...

또한, 배포 스크립트(deploy-cloud-run.sh)를 작성하여 로컬의 .env 값을 읽어와 포맷팅하는 복잡한 과정 대신, 명시적인 YAML 파일을 바라보게 하여 배포 프로세스를 단순화했습니다.


3. 특수 문자가 포함된 DB 비밀번호

문제 상황

PostgreSQL 연결 URL(postgres://user:password@host...)을 구성할 때, 비밀번호에 포함된 특수문자(@, :, / 등)가 URL 파싱을 방해했습니다. ValueError: invalid literal for int() with base 10 같은 엉뚱한 에러가 발생하여 원인을 찾는데 시간이 걸렸습니다.

해결

비밀번호를 URL 인코딩(Percent-encoding)하여 처리했습니다.

# Before (Error)
password = "p@ssword"
 
# After (Fixed)
password = "p%40ssword" 

결국 운영 편의성을 위해 특수문자가 없는 비밀번호로 변경하는 타협안을 선택했지만, URL 구성 요소로서의 비밀번호는 반드시 인코딩이 필요함을 배웠습니다.


4. PostgreSQL 확장(Extension) 누락

문제 상황

로컬 DB에서는 잘 동작하던 uuid_generate_v4() 함수가 클라우드 DB에 배포된 스키마를 실행하자 "function does not exist" 에러를 뱉었습니다.

해결

UUID 생성 함수는 PostgreSQL의 기본 내장 함수가 아니라 uuid-ossp 확장이 필요합니다. SQL 덤프 파일 최상단에 확장 설치 명령어를 추가하여 해결했습니다.

-- 스키마 생성 전 확장 기능 활성화
CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA public;

결론: "내 컴퓨터에서는 되는데요"를 넘어서

Docker를 도입하는 과정은 단순히 애플리케이션을 포장하는 것을 넘어, 환경(Environment)을 코드로 정의하는 과정이었습니다.

  1. 명시적 의존성 관리: 호환되는 버전을 정확히 파악하기
  2. 환경 격리: 로컬과 배포 환경의 설정 파일 포맷 차이 인지하기
  3. 데이터베이스 초기화: 스키마뿐만 아니라 필요한 확장(Extension)까지 챙기기

이러한 시행착오 끝에 현재는 deploy-cloud-run.sh 스크립트 실행 한 번으로 안전하게 프로덕션 배포가 가능한 파이프라인을 구축하게 되었습니다.

댓글이나 피드백을 남겨주세요

(Giscus 또는 Utterances 댓글 컴포넌트가 들어갈 자리)