ALO Server Mode (--server) 종합 가이드
이 문서는 ALO 프레임워크의 --server 옵션이 어떻게 동작하는지 소스 코드 수준에서 설명하고, 주요 API 기능과 데이터 처리 방식, 그리고 Titanic 예제를 통해 실제 사용 과정을 안내합니다.
1. --server 모드 개요
alo --server 명령어는 ALO를 단발성 CLI 툴이 아닌, 외부의 HTTP API 요청을 받아 추론(Inference)을 실시간으로 수행하는 API 서버로 실행시키는 기능입니다. 이 모드를 사용하면 ALO의 강력한 MLOps 파이프라인을 외부 시스템이나 어플리케이션과 쉽게 연동할 수 있습니다.
2. 코드 레벨 동작 원리 분석
--server 옵션이 실행되면, 여러 파이썬 파일이 상호작용하여 API 서버를 구동합니다. 주요 흐름은 다음과 같습니다.
| 단계 | 파일 | 역할 |
|---|---|---|
| 1 | alo/main.py | 사용자의 --server 명령어를 인식하고, 프레임워크의 실행 모드를 'server'로 설정합니다. |
| 2 | alo/alo.py | 설정된 'server' 모드에 따라 Server 클래스의 인스턴스를 생성합니다. |
| 3 | alo/api/api_server.py | Server 클래스는 uvicorn과 FastAPI를 사용하여 실제 웹 서버를 구동하고, 외부 요청을 받을 수 있는 API 엔드포인트(e.g., /api/v1/run)를 생성합니다. |
| 4 | alo/alo.py | API 요청이 들어오면, Server 클래스의 handle_api_request 메소드가 요청 데이터를 받아 기존 ALO의 추론 파이프라인(exec_stage)을 실행합니다. |
| 5 | alo/api/api_server.py | 파이프라인 실행이 완료되면, 그 결과를 JSON 형태로 API 호출자에게 응답합니다. |
상세 코드 흐름
-
명령어 파싱 (
alo/main.py) 사용자가alo --server를 입력하면,argparse가 이를 인식하고settings.computing값을'server'로 설정합니다.# alo/main.py
def __run(args):
# ...
if args.server:
settings.computing = 'server' # --server 플래그가 있으면 computing 모드를 'server'로 설정
# ...
alo = Alo()
alo.run() -
Server클래스 선택 (alo/alo.py)Alo()팩토리 함수는settings.computing값에 따라Server클래스 인스턴스를 생성하여 반환합니다.# alo/alo.py
alo_mode = {
# ...
'server': Server, # 'server' 모드는 Server 클래스를 사용
}
def Alo():
# ...
compute_mode = settings.computing
alo_class = alo_mode.get(compute_mode)
return alo_class() -
API 서버 실행 (
alo/alo.py,alo/api/api_server.py)Server객체의solve()메소드가 호출되어 FastAPI 기반의 웹 서버를 시작합니다. 이때 API 요청을 처리할 함수로self.handle_api_request가 지정됩니다.# alo/alo.py
class Server(Computing):
def solve(self):
# ... (포트 설정) ...
run_server(host=api_host, port=api_port, run_function=self.handle_api_request)run_server함수는 uvicorn을 통해 API 서버를 구동하고,/api/v1/run엔드포인트를 생성합니다.# alo/api/api_server.py
def create_app(run_function):
app = FastAPI(title="ALO API")
@app.post("/api/v1/run")
async def api_run(request: AloRequest):
args = argparse.Namespace(**request.model_dump())
run_function(args) # Server.handle_api_request 호출
return {"status": "success", ...}
return app
3. 주요 API 기능 및 데이터 처리
ALO 서버는 단순히 추론을 실행하는 것 외에도 데이터 및 모델 업로드를 위한 다양한 API 엔드포인트를 제공합니다.
주요 API 엔드포인트 (alo/api/api_server.py)
-
POST /api/v1/run: ALO의 추론 또는 학습 파이프라인 실행을 요청합니다. 요청 본문에{ "mode": "inference" }와 같이 실행할 모드를 지정할 수 있습니다. -
POST /api/v1/data/upload: 이미지, 어노테이션(Bounding Box), 클래스 정보 등을 포함하는 복합적인 데이터를 업로드합니다. 데이터는 JSON 형식으로 직렬화되어 전달됩니다. -
POST /api/v1/models/upload/tar: 학습된 모델 파일(.tar.gz)과 관련 메타데이터, 설정을 함께 업로드합니다. 모델을 서버에 등록하고 관리하는 데 사용됩니다. -
GET /api/v1/health: 서버가 정상적으로 동작하는지 확인하는 간단한 상태 체크 엔드포인트입니다."status": "healthy"를 반환합니다.
Base64 이미지 처리 과정 (alo/api/file_handlers.py)
API를 통해 이미지를 전송할 때, 바이너리 데이터를 안전하게 전달하기 위해 Base64로 인코딩하는 것이 일반적입니다. ALO 서버는 이렇게 전달받은 Base64 문자열을 다시 이미지로 변환하여 처리합니다.
-
Base64 디코딩:
deserialize_data함수는 API 요청에 포함된 Base64 문자열(data.data)을base64.b64decode()를 사용해 원본 바이트 데이터로 변환합니다. -
이미지 변환: 디코딩된 바이트 데이터를 메모리 상에서 이미지 파일처럼 다루기 위해
io.BytesIO로 감싼 후,Pillow라이브러리의Image.open()을 통해 이미지 객체로 엽니다. -
NumPy 배열화: 열린 이미지 객체는
np.array()를 통해 픽셀 데이터를 담은 NumPy 배열로 최종 변환됩니다. 이 배열은 모델의 입력으로 사용되거나 파일로 저장될 수 있습니다.
# alo/api/file_handlers.py - deserialize_data 함수
def deserialize_data(data: SerializedData) -> tuple[Any, str]:
# ...
if data.type == "byte_array":
# 1. Base64 문자열을 디코딩
decoded_data = base64.b64decode(data.data)
# 2. 바이트 데이터를 이미지로 열고 3. NumPy 배열로 변환
result = np.array(Image.open(io.BytesIO(decoded_data)))
return result, "numpy"
# ...
이렇게 변환된 이미지 데이터는 save_image_data 함수를 통해 서버의 파일 시스템에 .jpeg 또는 .npy 파일로 저장되어 추론 파이프라인에서 사용됩니다.
4. ALO API 주요 엔드포인트 입출력 정리
1. ALO 파이프라인 실행
엔드포인트: POST /api/v1/run
설명: ALO의 학습 또는 추론 파이프라인 실행을 원격으로 요청합니다.
입력 (Request Body)
형식: AloRequest 모델 (JSON)
주요 필드:
name(string, 선택): 솔루션의 이름입니다.mode(string, 선택): 실행할 모드를 지정합니다. "inference" 또는 "train"을 사용할 수 있으며, 기본값은 "inference" 입니다.computing(string, 선택): 컴퓨팅 환경을 지정합니다. 기본값은 "local" 입니다.log_level(string, 선택): 로그 레벨을 지정합니다. 기본값은 "DEBUG" 입니다.
요청 예시:
{
"mode": "inference"
}
출력 (Response Body)
형식: JSON
주요 필드:
status(string): 처리 상태를 나타냅니다. (예: "success")message(string): 처리 결과에 대한 메시지를 포함합니다.inference_result(object, 추론 시): 추론 결과 요약(summary) 등 상세 정보를 포함합니다.context_id(string, 추론 시): 해당 요청을 처리한 고유 실행 ID입니다.
2. 모델 업로드 (.tar)
엔드포인트: POST /api/v1/models/upload/tar
설명: 학습이 완료된 모델을 .tar.gz 압축 파일 형태로 업로드하여 서버에 등록합니다.
입력 (Multipart Form Data)
model_file(File): 업로드할 .tar.gz 모델 파일입니다. (필수)metadata(string, 선택): 모델에 대한 추가 정보가 담긴 JSON 형식의 문자열입니다. ModelMetadata 모델을 따릅니다. (name,version,framework,description등)config(string, 선택): 추가 설정값이 담긴 JSON 형식의 문자열입니다. 기본값은{}입니다.
출력 (Response Body)
형식: UploadResponse 모델 (JSON)
주요 필드:
status(string): 처리 상태 (예: "success")request_id(string): 이 업로드 요청의 고유 IDmodel_id(string): 서버에 등록된 모델의 고유 IDresult(object): 업로드 결과에 대한 상세 정보 (UploadResult모델)name(string): 모델 이름version(string): 모델 버전framework(string): 사용된 프레임워크 (예: pytorch, tensorflow)storage_url(string): 모델이 저장된 서버 내 경로metadata(object): 업로드 관련 메타데이터 (UploadMetadata모델)upload_time(string): 업로드 시간 (ISO 형식)file_size_bytes(integer): 파일 크기
3. 복합 데이터 업로드 (이미지, BBox 등)
엔드포인트: POST /api/v1/data/upload
설명: 이미지, 바운딩 박스(annotation), 클래스 정보 등을 포함하는 복합 데이터를 서버에 업로드합니다. 이미지 데이터는 주로 Base64로 인코딩된 문자열로 전달됩니다.
입력 (Request Body)
형식: DetectionInput 모델 (JSON)
주요 필드:
input_data(object): 실제 입력 데이터를 담는 객체 (InputData모델)image(object, 선택): 직렬화된 이미지 데이터 (SerializedData모델)type: 데이터 타입 (예: "byte_array")shape: 이미지의 형태 (예: [1080, 1920, 3])dtype: 데이터의 타입 (예: "uint8")data: Base64로 인코딩된 이미지 데이터 문자열
annotation_bbox(object, 선택): 직렬화된 바운딩 박스 데이터 (SerializedData모델)config(object): 실행 설정을 담는 객체 (Config모델)metadata(object, 선택): 디바이스 ID, 타임스탬프 등 추가 메타데이터 (Metadata모델)
출력 (Response Body)
형식: JSON
주요 필드:
status(string): 처리 상태 (예: "success")request_id(string): 요청의 고유 IDdata_id(string): 생성된 데이터의 고유 IDupload_status(string): 업로드 상태 (예: "completed")result(object): 저장된 데이터 정보storage_url(string): 데이터가 저장된 서버 내 경로image_info(object): 이미지의 shape, dtype 등 정보files(object): 서버에 저장된 각 데이터 파일의 상대 경로
4. 서버 상태 확인
엔드포인트: GET /api/v1/health
설명: API 서버가 정상적으로 동작하고 있는지 확인합니다.
입력: 없음
출력 (Response Body)
형식: JSON
내용:
{
"status": "healthy"
}