[실무 기록] 온프렘 도커 배포 실전 삽질기 (네트워크, 쿠키, 헬스체크 해결편)
최근 온프렘(On-premise) 서버 환경에서 여러 마이크로서비스를 Docker Compose로 묶어 배포하며 겪은 시행착오를 정리합니다. 구성 자체는 표준적이었으나, 실제 운영 환경으로 넘어가며 네트워크 통신과 인증 유지 부분에서 예상치 못한 이슈들이 있었습니다.
1. 전체 서비스 아키텍처 구성
모든 서비스는 docker-compose.yml 하나로 관리하며, 동일한 Docker 네트워크(keural-network) 내에서 통신하도록 설계했습니다.
- Frontend: Next.js
- Backend API: Spring Boot (
api) - AI Backend: FastAPI (
fastapi) - Database: PostgreSQL
- Vector DB: Qdrant
- Inference: vLLM (GPU 가속 활용)
2. Qdrant 컨테이너의 이유 없는 Unhealthy 상태
가장 먼저 맞닥뜨린 문제는 Qdrant 컨테이너가 정상 기동되었음에도 상태가 unhealthy로 표시되는 현상이었습니다.
[문제 상황]
기존에 설정한 헬스체크 방식입니다.
healthcheck:
test: ["CMD-SHELL", "sh -c 'echo > /dev/tcp/127.0.0.1/6333'"]
interval: 30s
timeout: 10s
retries: 5
로그상으로는 Qdrant HTTP listening on 6333이 찍히며 잘 돌아가는데, Docker는 계속 실패로 간주했습니다.
[원인 분석]
qdrant/qdrant이미지는 경량화를 위해 기본 쉘로/bin/sh를 사용합니다./dev/tcp를 이용한 헬스체크 방식은 Bash 전용 기능입니다. 쉘이 지원하지 않으니 명령어가 항상 에러를 뱉었던 것입니다.
[해결 전략]
이 경우 curl이 설치되어 있다면 curl을 쓰고, 아니면 헬스체크 설정을 제거합니다. 이번 프로젝트에서는 Compose의 depends_on이 기동 순서만 보장한다는 점을 인지하고, 실제 서비스 가용성(Readiness)은 애플리케이션 레벨의 재시도 로직으로 처리하기로 했습니다.
3. Docker 네트워크 내부 DNS vs 호스트 OS
초보 개발자들이 가장 많이 헷갈려 하는 부분입니다.
- 컨테이너 내부:
curl http://qdrant:6333/healthz(성공) - 호스트 OS:
curl http://qdrant:6333/healthz(실패)
핵심 정리
Docker Compose에서 서비스명(qdrant, keural-api 등)은 Docker가 관리하는 내부 DNS에서만 해석됩니다. 호스트 OS나 외부 브라우저에서는 이 이름을 알 수 없습니다. 디버깅 시 docker exec로 컨테이너 안에 들어가서 테스트해야 하는 이유입니다.
4. 온프렘 환경의 필수 관문: SSH 포트 포워딩
보안상의 이유로 서버 포트가 막혀있는 온프렘 환경에서는 SSH 터널링이 필수입니다.
ssh -N \
-L 13000:127.0.0.1:3000 \
-L 18088:127.0.0.1:8088 \
-L 18000:127.0.0.1:8000 \
-L 21434:127.0.0.1:21434 \
user@server-ip
이렇게 터널을 뚫어놓으면 내 로컬의 localhost:13000이 서버의 3000번 포트(Next.js)로 연결됩니다.
Tip:
-N옵션을 주면 쉘 명령을 실행하지 않고 터널만 유지합니다. 아무 메시지가 안 뜨는 것이 정상적으로 연결된 상태입니다.
5. 로컬에선 되는데 도커에선 풀리는 '로그인 토큰'의 비밀
가장 삽질 시간이 길었던 쿠키 유지(Session/Token) 문제입니다.
[증상]
로컬 개발 환경에서는 로그인이 잘 유지되는데, 도커로 배포하고 SSH 터널링으로 접속하면 로그인 직후 세션이 끊깁니다.
[해결]
이는 네트워크 문제가 아니라 브라우저의 보안 정책과 쿠키 설정 문제였습니다.
- Domain 설정 오류: 쿠키의 Domain을
keural-api같은 내부 서비스명으로 설정하면 브라우저는 이를 유효하지 않은 도메인으로 판단해 무시합니다. - Secure/SameSite: HTTPS가 아닌 HTTP 환경에서
Secure=true옵션을 주면 쿠키가 저장되지 않습니다. - Credentials: 프론트엔드(Next.js) 호출 시
credentials: 'include'옵션이 누락되면 쿠키가 전송되지 않습니다.
교훈: 브라우저 입장에서 쿠키는 오직 접속 URL(Domain/Port/Protocol) 기준으로만 작동한다는 점을 명심해야 합니다.
6. 운영 안정화: 자동 기동 및 관리 스크립트
서버가 재부팅되어도 서비스가 살아나도록 설정을 마무리했습니다.
- Docker 데몬 자동 실행:
sudo systemctl enable docker - 컨테이너 자동 재시작:
restart: always(compose 파일 적용)
또한, 실무 효율을 위해 별도의 관리 스크립트를 구성했습니다.
./scripts/up.sh: 전체 배포./scripts/show_all_logs.sh: 특정 레벨(error, info) 로그 필터링 관찰
마무리하며
이번 배포를 통해 얻은 인사이트를 요약하자면:
- Docker Compose는 단순한 실행 도구가 아니라, 네트워크 구조에 대한 이해가 필수다.
- 헬스체크는 이미지의 Base OS(Shell) 환경에 따라 다르게 구성해야 한다.
- 인증/쿠키 이슈는 서버 환경보다 브라우저 관점에서 먼저 디버깅하는 것이 빠르다.
다음 포스팅에서는 Next.js ↔ Spring ↔ FastAPI 간의 데이터 흐름을 실제 패킷 단위로 분석한 내용을 다뤄보겠습니다.
'DevOps > Docker' 카테고리의 다른 글
| [Docker compose - Postgres 설정] 부팅 이슈 해결하기 (Internship : Infra) (0) | 2026.01.20 |
|---|---|
| [docker-compose]온프레미스 환경에서 Docker Compose로 서비스 구성하기 (0) | 2025.12.31 |
| [Docker] 실행 방법 (0) | 2025.12.11 |
| [docker] 개념 정리 (0) | 2025.12.10 |
| [BuildX & 서버 이전] FastAPI + PaddleOCR 서버 이전기: "exec format error"와 Docker Buildx 완벽 가이드 (0) | 2025.12.07 |