AI/Natural language processing

[NLP] docker container 내 Reranker 모델 로드 에러 해결

hjjummy 2025. 10. 21. 14:38

이번 글에서는 내가 직접 겪은 RAG 서비스의 리랭커(Reranker) 400 오류 문제를 처음부터 끝까지 해결한 과정을 공유하려 한다. 

이 문제는 단순히 “모델이 안 뜬다” 라는 단순이슈가 아니라, 컨테이너 내부의 Hugging Face 캐시 구조, 도커 볼륨 마운트 설정, 모델 중복 캐시까지 엮여 있었던 복합적 이슈였다.

이 이슈는 답변 성능 테스트를 진행하던 도중 갑자기 발생하였고, 처음보는 오류였어서 해결하는데 어려웠어서 

이번에 해당 오류 원인 규명부터 해결한 과정을 정리해 같은 문제를 겪을 때 이 글만 보고 쉽게 해결 할 수 있도록 하려고 한다!

 

결론부터 말하자면, 원인은 컨테이너의 “리랭커 모델이 들어 있는 폴더가 사실상 비어 있었던 것”이었고,
해결은 “호스트의 정상 모델 캐시를 볼륨마운트를 이용해 컨테이너와 공유”하는 방식으로 깔끔히 끝났다.

 

1. 도커 컨테이너 내부에서 확인 → 2. 호스트의 Hugging Face 캐시 확인 → 3. docker-compose.yml 수정 → 4. 도커 재기동

1. 발생한 에러

 

OSError: Can't load the model for 'dragonkue/bge-reranker-v2-m3-ko'.

If you were trying to load it from 'https://huggingface.co/models', make sure you don't have a local directory with the same name. Otherwise, make sure 'dragonkue/bge-reranker-v2-m3-ko' is the correct path to a directory containing a file named pytorch_model.bin, tf_model.h5, model.ckpt or flax_model.msgpack.

 

→ transformers 내부에서 모델 파일을 찾지 못해 발생한 1차 예외이다.
→ 즉, pytorch_model.bin이나 config.json 같은 핵심 파일이 없거나, 경로가 잘못된 경우.

 

RuntimeError: Reranker 모델 로딩 실패: Can't load the model for 'dragonkue/bge-reranker-v2-m3-ko'
→ 최종 오류명령어(애플리케이션 레벨에서 잡힌 RuntimeError)

 

 

 

 

문제 상황을 요약하자면 리랭킹 서비스가 HTTP 400을 반환하였다. 

 

리랭커가 문서 검색 결과를 점수화하지 못하고, 그냥 “원본 결과”를 쓰는 상황이었다.
검색 품질이 즉시 저하되는 치명적인 오류였다.

처음에는 네트워크 문제나 API 엔드포인트 불일치로 의심했지만, 로그를 아무리 확인해도 HTTP 요청 자체는 성공했으나, 모델 로드 과정에서 실패하는 패턴이었다.

 

서버 내 

/home/agilesoda/.cache/huggingface/hub/models--dragonkue--bge-reranker-v2-m3-ko
폴더가 약 2.2GB 용량으로 정상적으로 잘 올라가 있는것도 확인했는데 이상이 없었다.

2. 도커 컨테이너 안으로 직접 들어가 보기

의심이 생겨 도커 컨테이너 안으로 들어가 확인을 시작했다.

# 컨테이너 접속
docker exec -it <컨테이너이름> bash

그다음 Hugging Face 캐시가 저장되는 경로로 이동했다.

# 컨테이너 내부 HF 캐시 확인
cd ~/.cache/huggingface/hub
ls -al
du -h --max-depth=1
# 문제 모델 폴더 진입
cd models--dragonkue--bge-reranker-v2-m3-ko
ls
# .no_exist/ blobs/ refs/ snapshots/ 등만 있고,
# 실제 blobs 안에 대형 가중치 파일이 거의 없는 상태

# 스냅샷 해시 확인
cd snapshots && ls
# 2aca5884ecac490192af9ebd86836d9073d826cd (예시)

 

결과를 보고 깜짝 놀랐다.

36K ./models--dragonkue--bge-reranker-v2-m3-ko
48K .

 

리랭킹 모델이 고작 36KB.......??


리랭커는 원래 수백 MB, 심지어 GB 단위의 파일을 가진 모델인데, 이건 거의 텅 빈 폴더였다.

폴더를 더 깊이 들어가보니 snapshots/, refs/, .no_exist/ 디렉터리만 있고 정작 핵심인 blobs/ 폴더에는 아무것도 없었다.

요약하면, 컨테이너는 “폴더는 있으나 가중치는 없는 빈 캐시”를 참조하고 있었고, 이로 인해 모델 로딩이 실패하면서 400 응답을 유발하고 있었다.

 

3. 원인 추정 – 왜 이런 빈 깡통이 생겼을까?

이 문제는 꽤 자주 발생하는 유형이다. 보통 원인은 다음 중 하나다.

  1. 도커 빌드 중 모델 다운로드가 중단되어 불완전한 캐시가 남음 --> 다시 도커 컴포즈 삭제하고 재시도 하였으나 해결X
  2. 컨테이너 권한 문제로 인해 모델이 정상적으로 저장되지 않음 -> 서버에서 다른분이 리랭크 모델을 내렸나?
  3. 같은 모델을 대소문자 다르게 호출해서 중복 캐시가 생성됨 -> 그치만 나는 파일 변경한적이 X, 기존에는 잘 돌아갔음
    (예: BGE-m3-ko와 bge-m3-ko)

실제로 내 환경에서도 다음과 같은 구조가 있었다.

/home/agilesoda/.cache/huggingface/hub/
├── models--dragonkue--bge-m3-ko/
├── models--dragonkue--BGE-m3-ko/
├── models--dragonkue--bge-reranker-v2-m3-ko/

모두 같은 계열의 모델이지만, 대소문자 차이 때문에 각각 따로 2.2GB씩 차지하고 있었다.
이제 와서 생각하면, 리랭커 컨테이너가 잘못된 버전의 캐시(비어 있는 버전)를 참조하고 있었던 것일 수도 있으나 이전에는 잘 돌아갔던게 갑자기 오류가 난다는게 이해가 되진 않지만 아마 다른분이 서버 내에서 리랭크 모델을 다시 내렸다가 올렸고, 그래서 잘못된 파일을 호출하고 있었던게 아닌가..? 라고 추측했다/


4. 컨테이너의 Hugging Face 캐시 구조 간단히 살펴보기

문제를 이해하려면 캐시 구조를 알아야 한다.

~/.cache/huggingface/hub/models--{org}--{repo}/
│
├── snapshots/   # 특정 커밋/리비전의 파일 스냅샷 폴더
├── blobs/       # 실제 모델 대용량 가중치 파일 저장소 
├── refs/        # 최신 스냅샷 참조 즉, 최신 리비전에 대한 포인터
└── .no_exist/   # 다운로드 실패 시 생성되는 빈 폴더

 

 

여기서 정상 상태라면 blobs/에 수백 MB~수 GB의 파일들이 존재하며, snapshots/의 각 파일이 blobs/를 참조하는 형태로 구성된다. 폴더만 있고 blobs가 거의 없으면, 모델 로딩 시 파일을 찾지 못해 오류가 난다.

 

 

딱 내 경우가 그랬다.


5. 해결 방법 – 호스트서 캐시를 볼륨마운트로 컨테이너에 연결하기

문제의 핵심은 간단했다.
“컨테이너 안의 캐시는 비어 있고, 내 서버에는 정상 모델이 있다.” 

따라서 컨테이너가 호스트의 HF 캐시를 그대로 참조하도록 볼륨 마운트를 추가하였다. 이렇게 하면 컨테이너 안에서 별도로 모델을 다시 받지 않고도, 호스트에서 이미 검증된 가중치를 즉시 재사용할 수 있다.

 

docker-compose.yml 수정

services:
  jeju-rag-app:
    volumes:
      - .:/app
      - ./logs:/app/logs
      - /home/agilesoda/.cache/huggingface/hub:/root/.cache/huggingface/hub

마지막 줄이 핵심이다.
이 한 줄로 호스트의 Hugging Face 캐시 디렉터리를 컨테이너의 /root/.cache/huggingface/hub(동일 캐시 경로)에 바인드 마운트했다. 간단하게 연결했다고 생각하면 된다.

이로써, 컨테이너 내부에서 모델을 로드할 때, “자신의 빈 폴더”가 아니라 “호스트의 완전한 모델 캐시”를 그대로 사용하게 된다.


6. 깨진 캐시 정리와 재다운로드

나의 경우 호스트(내 서버)에는 HF에서 내려받은 완전한 모델 파일(가중치, 설정, 토크나이저 등) 모델이 있어서 캐시를 볼륨마운트로 컨테이너에 연결하는 것 만으로도 해결이 되었지만

 

혹시 호스트에도 해당 모델 파일이 없거나 깨진 캐시가 있는 경우에는 아래 방법으로 해결해야 한다.

 

1) 먼저 비정상 폴더를 삭제한다.

# 호스트 or 컨테이너
rm -rf ~/.cache/huggingface/hub/models--dragonkue--bge-reranker-v2-m3-ko

2) 그다음 Hugging Face CLI로 올바른 리포지토리에서 모델을 재다운로드한다.

# 로그인(필요 시)
huggingface-cli login

# 올바른 리포지토리에서 다운로드
huggingface-cli download dragonkue/bge-reranker-v2-m3-ko \
  --local-dir ~/.cache/huggingface/hub/models--dragonkue--bge-reranker-v2-m3-ko \
  --local-dir-use-symlinks False

이렇게 하면내 서버인  /home/agilesoda/.cache/huggingface/hub/models--dragonkue--bge-reranker-v2-m3-ko/blobs/
안에 실제 가중치 파일들이 다운로드된다.

 

다운로드가 완료되면 이 경로 안에는 다음과 같은 구성 파일이 들어 있다.

config.json
pytorch_model.bin
tokenizer.json
tokenizer_config.json
vocab.txt (또는 merges.txt)

이 파일들이 모두 존재하면 정상 모델 캐시라고 할 수 있다.

 


7. 서비스 재시작 및 검증

모든 캐시 정리가 끝났으니, 이제 컨테이너를 재기동했다.

docker compose down
docker compose up -d

그리고 다시 내부를 확인했다.

docker exec -it jeju-rag-app-jeju-rag-app-1 bash
du -h --max-depth=1 /root/.cache/huggingface/hub

이번에는 용량이 수백 MB 이상으로 늘어 있었다.
즉, 모델이 제대로 인식된 것이다.

 

추가로, 파이썬에서 직접 로딩 테스트도 수행했다.

python - <<'PY'
from transformers import AutoTokenizer, AutoModel
m_id = "dragonkue/bge-reranker-v2-m3-ko"
tok = AutoTokenizer.from_pretrained(m_id)
model = AutoModel.from_pretrained(m_id)
print("loaded", m_id)
PY

결과:

loaded dragonkue/bge-reranker-v2-m3-ko

모델 로딩이 정상적으로 이루어졌다.


8. 오류가 사라진 로그

다시 로그를 확인하자 더 이상 400 에러는 발생하지 않았다.

[INFO] Cross-encoder 모델 로딩 완료
[INFO] Reranking 10 documents (batch=5)

서비스가 정상화된 것이다.
결국 원인은 “컨테이너 내부의 Hugging Face 캐시가 비어 있어서 모델 로드 실패”였고,
해결은 “호스트 캐시를 컨테이너에 마운트하고 올바른 모델을 재다운로드”하는 방식이었다.


 

마무리  

이번 문제를 해결하면서 다시금 느꼈다.
복잡한 RAG 파이프라인이라도, 실제 장애의 원인은 단순한 경로와 캐시 관리 문제일 때가 많다.

리랭커 400 오류는 결국 “모델 파일이 비어 있다 → 로드 실패 → HTTP 400”
이라는 단순한 흐름이었다.

하지만 이걸 찾기까지는 캐시 구조, 도커 권한, 볼륨 설정까지 전부 파고들어야 했다.

이 경험 이후로 나는 모델 캐시는 무조건 볼륨 마운트로 통일하고, 빌드/배포 전 모델 로딩 테스트를 자동화 스크립트에 포함시켰다.

이번 기록이 같은 문제를 겪는 사람에게 “어디부터 봐야 할지” 감을 잡는 데 도움이 되기를 바란다.

 

해결과정을 매우 간단하게 핵심만 요약하자면

  • /home/agilesoda/.cache/huggingface/hub/models--dragonkue--bge-reranker-v2-m3-ko
    폴더가 약 2.2GB 용량이었음.
    → 이건 모델 가중치 파일(pytorch_model.bin)이 포함되어 있다는 의미다.
    → 즉, 이미 Hugging Face에서 다운로드된 정상 캐시 모델이 호스트에 존재했다는 뜻이다.
  • 반면 컨테이너 내부 /root/.cache/huggingface/hub/...에는
    폴더 구조만 있고 용량이 36KB 정도였음.
    → 이는 실제 모델 파일 없이 “메타정보”만 들어 있는 빈 캐시였다는 뜻이다.

그래서 docker-compose.yml에 다음 라인을 추가해
호스트의 완전한 모델 캐시를 컨테이너가 직접 참조하도록 연결한 것이다.