🎯 프로젝트 소개
Teach AI는 인간이 AI에게 철학적 질문을 던지고, AI가 인간의 답변을 통해 배우는 프로젝트예요.
- URL: https://teach.chloe.ai.kr
- Stack: FastAPI + React + PostgreSQL + Docker
- 인프라: Caddy WAF → Traefik → Docker Containers
🚨 배포 시 마주친 3가지 문제
1️⃣ 첫 번째 장애물: nginx가 backend를 못 찾아요
증상:
nginx: [emerg] host not found in upstream "backend"
원인: Frontend 컨테이너가 teach-internal 네트워크에만 연결되어 있어서 backend에 접근 불가.
해결:
| |
nginx 설정에 Docker DNS resolver도 추가:
| |
2️⃣ 두 번째 장애물: SSL Protocol Error
증상:
| |
원인: Traefik 설정이 traefik-public 네트워크 & websecure 엔트리포인트를 사용하려 했지만, 실제 환경은:
- 네트워크:
proxy - 엔트리포인트:
web(8080)
해결:
| |
3️⃣ 세 번째 장애물: Frontend가 unhealthy 상태
증상:
| |
원인: healthcheck가 IPv6 localhost로 연결 시도 → 실패
| |
해결:
| |
Traefik은 unhealthy 컨테이너를 라우팅에서 제외하므로, 이게 해결되자 즉시 정상 작동!
🎓 배운 것들
Docker Networking 101
멀티 네트워크 전략:
proxy: 외부 노출용 (Traefik 연결)teach-internal: 내부 통신용 (DB, Backend)
네트워크 순서 중요:
1 2 3networks: - proxy # 먼저 선언된 게 primary - teach-internal
Caddy + Traefik 조합
사용자 (HTTPS)
↓
Caddy WAF (80/443) - 자동 Let's Encrypt 발급
↓
Traefik (8080) - Docker label 기반 라우팅
↓
Container
핵심 포인트:
- Caddy:
/config/caddy/Caddyfile(마운트 경로 확인 필수!) - Traefik:
traefik.docker.network라벨로 명시적 네트워크 지정
Healthcheck 함정
| |
Alpine Linux에서 localhost는 IPv6를 먼저 시도하는데, nginx가 IPv6를 비활성화한 경우 실패!
📊 최종 아키텍처
Internet
↓ HTTPS (443)
┌─────────────────────┐
│ Caddy WAF │ - Let's Encrypt 자동 발급
│ (Coraza CRS) │ - WAF 룰 적용
└──────────┬──────────┘
↓ HTTP (8080)
┌─────────────────────┐
│ Traefik │ - Docker label 기반 라우팅
│ (Reverse Proxy) │ - proxy 네트워크
└──────────┬──────────┘
↓
┌──────────┴──────────┐
│ teach-frontend │ ← nginx (React SPA)
│ (proxy + │
│ teach-internal) │
└──────────┬──────────┘
↓ /api/*
┌─────────────────────┐
│ teach-backend │ ← FastAPI (8000)
│ (proxy + │
│ teach-internal) │
└──────────┬──────────┘
↓
┌─────────────────────┐
│ teach-db │ ← PostgreSQL 16
│ (teach-internal) │
└─────────────────────┘
🚀 결과
소요 시간: 약 1시간 (문제 진단 + 해결)
해결 순서:
- nginx DNS resolver 추가 (5분)
- Traefik 네트워크 & 엔트리포인트 수정 (10분)
- Caddy 설정 추가 & reload (5분)
- Frontend healthcheck 수정 (10분)
- Traefik 재시작 & 최종 확인 (5분)
최종 상태:
- ✅ HTTPS 정상 작동 (Let’s Encrypt 자동 발급)
- ✅ 모든 컨테이너 healthy
- ✅ Traefik 라우팅 정상
- ✅ WAF 보호 활성화
💡 Tips
1. Caddy 설정 reload 시 주의사항
| |
2. Traefik 디버깅
| |
3. healthcheck 빠른 테스트
| |
🤔 회고
좋았던 점:
- TDD로 Backend를 먼저 완성해둬서 문제가 인프라에만 집중됨
- 에러 로그가 명확해서 문제 진단이 빨랐음
- Docker Compose 멀티 네트워크 전략 이해도 상승
아쉬웠던 점:
- 환경마다 다른 Traefik 설정 (websecure vs web)
- localhost IPv6 함정을 미리 예상하지 못함
- Caddy 마운트 경로 확인 누락
다음엔 개선할 점:
- 프로젝트 템플릿에 healthcheck 예제 추가
- Traefik 설정 문서화 (네트워크/엔트리포인트 명시)
- Caddy 설정 경로 환경변수로 통일
📚 관련 글
한줄 요약: Docker 네트워크와 healthcheck는 작은 실수가 큰 장애를 만든다. 로그를 믿고, 문서를 확인하고, 차근차근 해결하면 된다! 🚀