이번 작업에서는 Spring → FastAPI → OCR Server 구조에서 파일을 직접 전달하지 않고 공유 스토리지(NFS) 를 통해 처리하도록 아키텍처를 변경했습니다.
기존에는 FastAPI에서 파일을 /tmp에 다시 저장한 뒤 OCR 서버로 넘기는 구조였는데, 서버가 분리되고 트래픽이 늘어나면서 중복 I/O, 디스크 낭비, 관리 복잡도 문제가 커졌기에
결론적으로 서버 간 공유 마운트(NFS) 를 적용했고, 그 과정에서 발생한 실제 에러와 해결 과정을 정리하려 합니다.
- 파일은 Spring에서 한 번만 저장
- FastAPI / OCR Server는 경로만 전달받아 읽기
- 서버 간 파일 전송 제거
NFS 패키지 설치
sudo apt update
sudo apt install -y nfs-kernel-server
우선 설정할 때 주의했던 부분들은 UID 설정과 접근 권한 설정입니다.
- UID / GID 통일
- 파일 접근 권한(ro / rw) 명확히 분리
이걸 제대로 안 맞추면 마운트는 되는데 권한으로 인한 실패, 컨테이너에서는 보이는데 실제 파일 접근 실패, Permission denied, Read-only file system 같은 에러가 계속 발생합니다.
NFS는 권한을 이름이 아니라 UID/GID 숫자 기준으로 판단합니다. 그래서 서버마다 UID가 다르면 같은 유저여도 다른 유저로 인식됩니다.
들어가기전, NFS 를 설정할 모든 서버 파일의 UID 를 설정해줍니다.
# 모든 서버에서 동일하게 실행
sudo groupadd -g 2101 {username}
sudo useradd -u 2101 -g 2101 -m {username}

이 작업을 NFS 서버 / Spring 서버 / FastAPI 서버 / OCR 서버 전부에서 동일하게 맞췄습니다.
NFS Export 설정 (/etc/exports)
UID 설정이 끝났다면, 이제 NFS 서버(파일 원본 서버) 에서 어떤 디렉토리를, 어떤 서버에게, 어떤 권한으로 공유할지 설정합니다.
공유 디렉토리 구조
서버에서 실제 파일이 저장되는 경로를 미리 지정해줍니다.
/srv/file/uploads
└── system
└── {uuid}
└── *.pdf
이 디렉토리를 FastAPI / OCR Server에서 읽기 전용(ro) 으로 마운트할 예정입니다.
/etc/exports 설정
NFS 서버에서 /etc/exports 파일을 수정합니다.
sudo vi /etc/exports
예시 설정
/srv/file/uploads \
{server IP}(ro,sync,no_subtree_check,root_squash) \
{server IP}(ro,sync,no_subtree_check,root_squash) \
{server IP}(rw,sync,no_subtree_check,root_squash)
- ro / rw
- FastAPI / OCR 서버 → 읽기 전용
- Spring 서버 → 쓰기 가능
- sync
- 안정성 우선 (운영 환경 필수)
- root_squash
- root 권한으로 접근하더라도 실제 파일은 일반 유저 권한으로 처리
export 반영
sudo exportfs -ra
sudo systemctl restart nfs-server
sudo exportfs -v
정상이라면 서버별로 ro, rw 권한이 명확히 보입니다.
클라이언트 서버에서 NFS 마운트
이제 FastAPI / OCR 서버에서 공유 디렉토리를 마운트합니다.
마운트 디렉토리 생성
sudo mkdir -p /srv/file/uploads
마운트 실행
sudo mount -t nfs -o ro,nfsvers=4 192.168.0.30:/srv/keural/uploads /srv/keural/uploads
확인
mount | grep keural
실제 파일 접근 확인 (중요)
FastAPI / OCR 서버에서 직접 파일이 보이는지 확인해야 합니다.
ls -al /srv/file/uploads/system/chat
여기서 중요하게 처리했던건 NFS 를 설정할 때, ro / rw 분리 설계 와 서비스를 띄울 때 user id 값을 제대로 지정해주는 거였습니다.
서버에 띄워진 도커 같은 경우, docker compose 로 올릴 때 추가적으로 volumes 옵션을 달아줘야 되는데, 이때
앞서 root 에서 접근을 허용한게 아닌, 따로 UID 설정을 해두었기에 service 의 user를 설정해둬야 됩니다.