0. 오류 발생
FastAPI 를 사용해 서버를 만들고, AI 모델링을 통해 생성한 pickle 파일을 서버에 업로드하는 과정에서 오류가 발생했다.
💡pickle 파일이란?
https://restart-web-base.tistory.com/20
[Python] 피클(pickle) 파일
0. pickle파이썬 객체 구조의 직렬화와 역직렬화를 위한 바이너리 프로토콜(Binary Protocols)을 구현하는 모듈객체 자체를 원래 형태 그대로 저장하고 불러올 수 있게 해주는 라이브러리pickle — Python
restart-web-base.tistory.com
- 1차
- 2차
- 3차
- 9차...?
❗실행을 멈추지 않으면 계속해서 오류가 반복된다.❗
- 지속적으로 Process SpawnProcess-n: 이라는 오류가 발생했고, 서버가 실행되지 않았다.
- 파이썬의 고질적인 문제인 multiprocessing과 관련된 오류라고 예측할 수 있었으나, 정확한 원인을 알 수 없었다.
- 오류의 원인을 정확하게 이해하고 수정해야겠다고 생각했고, 정확한 오류 원인과 해결방법을 찾아서 정리해보게 되었다.
1. 오류 원인
원인1. FastAPI의 자동 multiprocess와 python의 충돌
- pickle 파일을 업로드하는 과정에서 FastAPI에서 자동적으로 spawn모드가 실행되고 새로운 프로세스가 생성된다. 그러면서 FastAPI에서 multiprocessing을 사용하게 되고 python과 충돌하게 된다.
- Linux 환경이 아닌, Windows 나 Mac 환경에서 uvicorn을 실행할 때 이 문제가 자주 발생한다.
💡spawn 모드란?
자식 프로세스가 run() 메서드를 실행할 때 필요한 자원만 상속 받는 것. 부모 프로세스의 불필요한 파일 기술자, 핸들은 상속 받지 않는다. 따라서 fork, forkserver에 비해서 속도가 느리다.
👇파이썬의 multiprocessing의 자세한 내용은 하단 링크 참조
https://docs.python.org/3/library/multiprocessing.html
multiprocessing — Process-based parallelism
Source code: Lib/multiprocessing/ Availability: not Android, not iOS, not WASI. This module is not supported on mobile platforms or WebAssembly platforms. Introduction: multiprocessing is a package...
docs.python.org
원인2. dill 라이브러리 미사용
- pickle 파일을 FastAPI로 로드할 때, dill 라이브러리를 사용하지 않으면, 일부 객체에서 직렬화 문제가 발생할 수 있다.
원인3. --reload 옵션
- uvicorn main:app --reload 명령어로 실행할 경우, --reload 옵션이 multiprocessing 방법으로 실행되고, 파이썬과 충돌할 수 있다.
2. 해결 방법
방법1. if __name__ == "__main__" 블록 추가
💡if name == "__main__" 이란?
https://restart-web-base.tistory.com/21
[Python] __name__ 부터 if __name__ == “__main__” : 까지 완벽 정리
0. 익숙하지만 애매한 if __name__ == “__main__” :다양한 파이썬 코드들을 살펴보면 if __name__ == “__main__” : 이렇게 생긴 조건문에 많은 함수들이 들어있는 것을 자주 본 적이 있을 것이다.본인도
restart-web-base.tistory.com
=> 이를 통해서 FastAPI 실행 시, multiprocessing이 자동으로 실행되어서 충돌하는 것을 방지할 수 있다.
- if __name-- == "__main__" 으로 서버 실행을 묶어준다
import pickle
from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post("/upload/")
async def upload_pkl(file: UploadFile = File(...)):
with open("uploaded_model.pkl", "wb") as f:
f.write(await file.read())
with open("uploaded_model.pkl", "rb") as f:
model = pickle.load(f) # 여기에서 오류 발생 가능
return {"message": "File uploaded successfully"}
# 충돌 방지
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
- 이렇게 작성 후 원래 방법대로 서버 실행
uvicorn main(파일이름):app --reload
방법2. dill 을 사용해서 문제 해결
- 일부 객체는 pickle 이 아닌, dill을 사용해야 직렬화가 제대로 이루어진다.
- dill 설치 방법
pip install dill
- dill 을 이용한 피클 파일 로드
import dill as pickle # pickle 대신 dill 사용
from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post("/upload/")
async def upload_pkl(file: UploadFile = File(...)):
with open("uploaded_model.pkl", "wb") as f:
f.write(await file.read())
with open("uploaded_model.pkl", "rb") as f:
model = pickle.load(f) # dill을 사용하여 해결
return {"message": "File uploaded successfully"}
방법3. uvicorn 실행 방식 변경
- --reload 옵션을 사용할 경우 spawn 모드가 활성화 될 수 있다.
- --reload 플래그 : 서버 실행중 코드가 변경되었을 때, 변경된 부분을 감지하고 반영해서 서버를 다시 로드해준다.
⇒ --reload 없이 실행해준다.
uvicorn main:app --host 0.0.0.0 --port 8000
⇒ multiprocessing 을 비활성화하고 실행한다.
uvicorn main:app --workers 1
3. 결론
- FastAPI 서버에서 서버 파일과 같은 위치의 로컬파일을 업로드할 때 주로 이런 문제가 발생한다.
- 이 방법 외에도 파일을 업로드하는 방식은 다양하기 때문에 각각 업로드하는 파일의 종류마다 더 적합한 방식을 사용해봐야 한다.
- 계속해서 문제가 발생할 경우에 위 3개의 해결 방안을 적용해 보면 해결할 수 있을 것이다!