스파클링소다 4.0 Rest App

작성자 박형춘 수정일 2024-02-16 15:34



들어가며

  • 이전 아티클에서 모델 저장소인 model assets에 등록된 모델을 실제로 서비스하는 방법을 알아보았습니다.

  • 이번 아티클에서는 모델서비스를 활욯하기 위해 restapp을 구성하는 방법을 알아보겠습니다.

  • 포탈 접속은 고객사에서 사전 신청한 도메인(url)로 접속합니다.초기 포탈 로그인 정보는 다음과 같습니다.
    User ID : superuser
    Password : superuser$01
지난 아티클에서 관리계정 또는 일반 계정을 생성하였다면 해당 계정으로 접속합니다.





Restapp

  • Rest App은 모델서비스 앞단에 위치하여 외부 Client의 rest 요청을 받아 모델서비스로 연결합니다.

  • 모델서비스를 활용하기 위해 Custom 가능한 어플리케이션으로 data 전처리/후처리 코드를 작성합니다.

  • 아키텍처는 아래와 같습니다.



Rest 프로젝트 생성

  • Rest 서비스 코드 작성을 위한 프로젝트를 생성합니다.


  • Rest 프로젝트의 정보를 입력하고 저장합니다.
    A. General : Rest 프로젝트의 기본 정보를 입력합니다.

- Rest App Name : Rest App의 이름을 지정합니다.


- Image : Rest App이 실행되는 컨테이너의 도커이미지를 선택합니다.


- Description : 자세한 설명을 입력합니다.


B. Resource : Rest App이 사용할 리소스를 지정합니다.

일반적으로 Rest app은 gpu 자원이 필요하지 않으므로 cpu 노드를 선택합니다.


C. Volume : Rest App 컨테이너와 호스트 서버 볼륨을 마운트합니다.

호스트 서버에 존재하는 파일을 Restapp 컨테이너와 공유하거나 Restapp 내부에 생성되는 Output 파일을 호스트 서버와 공유하기 위해 볼륨을 마운트할 수 있습니다.



  • 설정을 마치고 저장하면 restapp 컨테이너가 생성되며 정상적으로 Running 상태가되면 'Running'을 클릭하여 노트북 환경으로 이동합니다.



모델 서비스 설정

  • Restapp 노트북에서 실행중인 model service와 연결하기 위한 설정을 진행합니다.
    app/configs/app_config.yaml 의 내용을 수정합니다.
    - mode : 기본 값인 test를 사용합니다.

    - log_level : restapp 실행시 출력할 로그 레벨을 지정합니다.
TRACE > DEBUG > INFO > WARN > ERROR > FATAL

INFO로 셋팅하면, INFO, WARN, ERROR, FATAL은 출력됩니다.

FATAL : 아주 심각한 에러가 발생한 상태를 의미합니다.
ERROR : 어떠한 요청을 처리하는 중 문제가 발생하여 즉시 조치가 필요한 상태를 의미합니다.
WARN : 프로그램의 실행에는 문제가 없지만, 향후 시스템 에러를 유발할 가능성이 있는 상태를 의미합니다.
INFO : 어떠한 상태 변경과 같은 정보성 메시지를 의미합니다.
DEBUG : 개발시 디버그 용도로 사용하는 메시지를 의미합니다.
TRACE : 디버그 레벨이 너무 광범위한 것을 해결하기 위해서 좀 더 상세한 이벤트를 출력합니다.


- model_service : 이전 아티클에서 생성한 모델서비스의 이름을 입력합니다.

이 필드에 지정한 모델서비스를 restapp 생성시 호출합니다.


- model_name : 모델서비스에서 사용하는 모델의 이름을 입력합니다.



- model_version : 모델서비스에서 사용하는 모델의 버전을 입력합니다.

 


모델 서비스 Input/Output 코드 작성

  • 연결될 모델서비스의 인터페이스를 구성합니다.
    app/mod_service/services.py의 기본 코드를 지우고 코드를 작성합니다.



  • 아래의 예시 코드를 작성합니다.
    import numpy as np
    from app.config import cfg
    
    # SoDAFlow Model Servce
    from app.common.model_service import SodaModelService
    
    def predict(param=None):
        # configs/app_config.yaml - model connection
        model_service = SodaModelService(
            server_url=cfg.model_service,
            model_name=cfg.model_name,
            model_version=cfg.model_version
        )
    
        # input setting
        input_data = np.reshape(eval(param), (1,1,28,28)).astype(np.float32)
    
        # model input, output setting
        model_service.add_input(name='0', shape=(1,1,28,28), dtype='FP32', tensor=input_data)
        model_service.add_output(name='27')
    
        # inference
        results = model_service.predict()
    
        return { 'predict': results }




REST API 및 Data 전처리/후처리 코드 작성

  • data 전처리/후처리 코드를 작성합니다.
    app/mod_service/controller.py에 기본 코드를 지우고 코드를 작성합니다


  • 아래의 예시 코드를 작성합니다. (예시 코드에서는 전처리 코드를 생략합니다.
import numpy as np
import json

from flask import Blueprint, request, jsonify
from app.mod_service import services
from app.common.utils import as_json

# Define the blueprint: 'train', set its url prefix: app.url/test
mod_service = Blueprint('service', __name__, url_prefix='/api')

# Set the route and accepted methods
@mod_service.route('/predict/', methods=['GET', 'POST'])
@as_json
def predict():
    try:
        output = None

        if request.method == 'GET':
            if len(request.args.to_dict()) == 0:
                return "no parameter(get)"
            else:
                # get type parameter
                param = request.args["image_data"] 
        else:
            param = request.get_json()
            
            if param == None:
                return "no parameter(post)"
            
            else:
                # post type(json) parameter
                param = param['image_data']               

        # call inference(services.py/predict)
        output = services.predict(param)

        # data post-processing.
        post_data = output['predict'][0]['27']
        # json to array
        result = json.loads(post_data)
        
        # return
        return np.argmax(result)

    except Exception:
        result = dict()
        result['success'] = False
        result['log'] = traceback.format_exc()

        return result




REST API 코드 점검

  • 작성한 restapp 코드가 정상적으로 동작하는지 테스트합니다.
    test_client.sh에 REST API 호출 url을 작성합니다.



  • 아래의 코드를 추가합니다.
# label 9
#curl -X GET http://localhost:8088/api/predict/?'image_data'='0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,145,195,255,254,203,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,164,243,253,236,220,253,253,245,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,199,254,223,96,29,13,79,249,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,224,253,195,40,0,0,0,0,207,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,198,254,203,34,0,0,0,0,0,208,254,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,81,251,248,56,0,0,0,0,0,43,249,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,237,253,113,0,0,0,0,0,0,55,253,244,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,253,46,0,0,0,0,34,119,222,253,128,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,170,254,245,95,162,161,195,229,254,254,239,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,216,253,253,254,253,253,236,254,253,213,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,25,129,230,230,145,96,38,254,253,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,93,254,219,13,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,185,254,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,106,247,241,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,157,253,184,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,68,240,253,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,164,254,195,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,114,251,248,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,203,253,230,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,186,253,162,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'

# label 4
curl -X POST http://localhost:8088/api/predict/ -H "Content-Type: application/json" \
    -d '{"image_data":"0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,192,134,32,0,0,0,0,0,0,0,0,15,77,5,0,0,0,0,0,0,0,0,0,0,0,0,17,235,250,169,0,0,0,0,0,0,0,0,15,220,241,37,0,0,0,0,0,0,0,0,0,0,0,20,189,253,147,0,0,0,0,0,0,0,0,0,139,253,100,0,0,0,0,0,0,0,0,0,0,0,0,70,253,253,21,0,0,0,0,0,0,0,0,43,254,173,13,0,0,0,0,0,0,0,0,0,0,0,22,153,253,96,0,0,0,0,0,0,0,0,43,231,254,92,0,0,0,0,0,0,0,0,0,0,0,0,163,255,204,11,0,0,0,0,0,0,0,0,104,254,158,0,0,0,0,0,0,0,0,0,0,0,0,0,162,253,178,5,0,0,0,0,0,0,9,131,237,253,0,0,0,0,0,0,0,0,0,0,0,0,0,0,162,253,253,191,175,70,70,70,70,133,197,253,253,169,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,228,253,253,254,253,253,253,253,254,253,253,219,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,65,137,254,232,137,137,137,44,253,253,161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,34,254,206,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,160,253,69,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,85,254,241,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,158,254,165,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,231,244,50,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,104,254,232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,208,253,157,0,13,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,208,253,154,91,204,161,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,208,253,254,253,154,29,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,190,128,23,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"}'



  • 작성한 rest api 호출을 하기 위해 local_rest_server를 실행합니다.
python test_run.py

터미널은 Ctrl + Shift + ` 단축키로 열수 있습니다.



  • 새로운 터미널을 열고 test_client.sh 스크립트를 실행합니다.
    sh test_client.sh
  • 모델서비스에 요청 후 결과 값인 '4'를 정상적으로 반환하였으며 local_server 로그에도 http 상태코드 '200'으로 정상 처리 되었음을 확인할 수 있습니다.





REST API 코드 gitlab에 저장

  • 노트북에서 테스트한 코드를 restapp을 실행할 때 적용하기 위해 gitlab에 저장합니다.
git add . 
git commit -m 'ned-test-app'
git push




REST 서비스 실행

  • 실제 REST 서비스를 실행합니다.



마무리

  • 마무리 내용

아티클이 유용했나요?

훌륭합니다!

피드백을 제공해 주셔서 감사합니다.

도움이 되지 못해 죄송합니다!

피드백을 제공해 주셔서 감사합니다.

아티클을 개선할 수 있는 방법을 알려주세요!

최소 하나의 이유를 선택하세요
CAPTCHA 확인이 필요합니다.

피드백 전송

소중한 의견을 수렴하여 아티클을 개선하도록 노력하겠습니다.

02-558-8300