최근 여러 대규모 머신러닝 프로젝트에서는 기존 WSGI가 아닌 ASGI WAS를 이용해 비동기 처리를 하고 있다.

어떤 장단점이 ASGI에 있는지 알아보고, 특히 기존 응답시간이 많이 걸리는 Machine Learning의 serving 부분에서 어떤 패턴으로 적용되며, 어떻게 사용 가능한지 리뷰.

 

대규모 머신러닝 프로젝트 Serving에 사용되는 Python WAS, ASGI - uvicorn

ASGI는 Python에서 지원하는 WAS(Web Application Server)인 WSGI를 대체하는, 비동기 대용량 트래픽 처리를 위한 현대적인 서비스를 의미한다. uvicorn는 2021년 현재 ASGI를 구축할 수 있는 python 웹 어플리케이션 서버이다.

 

- Web 서버와 WAS

- WSGI와 gunicorn

- ASGI를 사용하는 이유

- ASGI - uvicorn 코드 리뷰

- ASGI interface 정리

- 다른 ASGI 서버

 

Web 서버와 WAS

웹서버의 용도는 우리가 잘 아는 것처럼, client의 HTTP요청을 받아 리턴하는 역할을 수행한다. WAS는 동적으로 결과를 리턴할 수 있도록 웹 어플리케이션을 수행하는 미들웨어이다. 주로 데이터베이스에 접근해 그 결과를 HTML로 만들어 리턴하거나, 많은 동적 콘텐트를 제공하는 역할을 수행한다.

 

WSGI와 gunicorn

WSGI는 Python으로 만든 어플리케이션이 웹서버와 통신할 수 있도록 인터페이스를 수행하며, 이 처리를 WSGI가 수행한다. Python에서는 그간 gunicorn이 이 역할을 수행했다. 이 처리는 동기(synchronous)로 처리되며 잘 동작하지만, 대규모 트래픽 처리에는 한계가 있다. node에서는 이러한 비동기 처리 기능을 잘 지원하나, Python에서는 async 패키지 라이브러리가 한동안 제공되지 않았다.

 

ASGI를 사용하는 이유

WSGI의 한계인 요청에 대해 단일 동기적으로 처리하는 루틴을 비동기로 처리해 처리량을 늘리고 높은 성능을 제공하는 WAS를 구현하기 위해서 ASGI(Asynchronous Server Gateway Interface) 명세를 구현해 나오기 시작했고, 특히 Django에서 적용되면서 빠르게 확산되었다.

 

python_django_WSGI.png

Django의 기존 WSGI 처리 패턴

 

python_django_ASGI.png

Django의 ASGI 패턴

이미지 출처: Async Tasks with Django Channels

 

 

Python의 비동기 처리를 활용해 성능적인 측면에서 많은 향상이 이루어졌고, 특히 기존 응답시간이 많이 걸리는 Machine Learning의 serving 부분에서 적용되어, 현재는 많은 대규모 ML 모델 inference에서 사용되고 있다.

 

ASGI - uvicorn 코드 리뷰

간략히 공식 repo에서 제공하는 코드를 리뷰.

WSGI와 비슷한 부분이 많고, async 부분을 제외하면 대부분 비슷한 처리가 가능할 듯.

 

기본 요청(Request)과 응답(Response)

uvicorn을 설치하려면 아래 명령을 수행한다. WSL의 ubuntu bash에서 테스트했다.

pip install uvicorn

 

이어서, 아래 기본 코드를 수행한다.

import uvicorn

async def app(scope, receive, send):
    assert scope['type'] == 'http'

    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            [b'content-type', b'text/plain'],
        ],
    })
    await send({
        'type': 'http.response.body',
        'body': b'Hello, world!',
    })


if __name__ == "__main__":
    uvicorn.run("example:app", host="127.0.0.1", port=5000, log_level="info")

py 파일로 저장하고 실행하면 uvicorn이 잘 동작한다. 

 

ASGI interface 정리

uvicorn은 세가지의 ASGI interface가 있다. 

scope: A dictionary containing information about the incoming connection.
receive: A channel on which to receive incoming messages from the server.
send: A channel on which to send outgoing messages to the server.

 

Scope

Scope에 따르는 동작을 통해 루틴을 제한하거나, 필요하다면 exception 처리를 한다.

...
assert scope['type'] == 'http'
...

 

Receive & Response

인입되는 요청을 받아 처리하는 루틴이다. 이렇게 요청을 받아 처리한 다음 body 변수에 저장 후 response의 body에 넣어 응답 처리한다.

...
body = f'Received {scope["method"]} request to {scope["path"]}'
...
await send({
    'type': 'http.response.body',
    'body': body.encode('utf-8'),
})
...

 

Streaming response

아래의 send패턴으로, 여러 http.response.body 메세지를 스트리밍 전달 가능하다. List인 [b'Hello', b', ', b'world!']의 각 item들을 하나씩 1초 간격으로 전달한다. 

import asyncio


async def app(scope, receive, send):
    """
    Send a slowly streaming HTTP response back to the client.
    """
    await send({
        'type': 'http.response.start',
        'status': 200,
        'headers': [
            [b'content-type', b'text/plain'],
        ]
    })
    for chunk in [b'Hello', b', ', b'world!']:
        await send({
            'type': 'http.response.body',
            'body': chunk,
            'more_body': True
        })
        await asyncio.sleep(1)
    await send({
        'type': 'http.response.body',
        'body': b'',
    })

 

다른 ASGI 서버들

uvicorn 외에도 Daphne와 Hypercorn이 있다. 아래 링크 참조.

django/daphne: Django Channels HTTP/WebSocket server (github.com)

pgjones / hypercorn · GitLab

 

참고링크

encode/uvicorn: The lightning-fast ASGI server. 🦄 (github.com)

jordaneremieff/asgi-examples: A collection of example ASGI applications (github.com)

Introduction — ASGI 3.0 documentation

 

No. Subject Author Date Views
Notice SQL강좌: 챗GPT와 함께 배우는 SQL Server 무료 강좌 목차와 소개 (2023년 9월 업데이트) 코난(김대우) 2023.08.18 24019
Notice Python 무료 강좌 - 기초, 중급, 머신러닝(2023년 6월 업데이트) 코난(김대우) 2021.01.01 12458
118 오픈소스 소통을 위한 Git 공부하기 | ep6. 내 저장소에 소스를 푸시하기 위한 Fork | 애저 듣고보는 잡학지식 코난(김대우) 2022.11.02 48
117 오픈소스 소통을 위한 Git 공부하기 | ep5. 브랜치 (Branch) 이해하기 | 애저 듣고보는 잡학지식 코난(김대우) 2022.11.01 32
116 오픈소스 소통을 위한 Git 공부하기 | ep4. 변경 단위를 만들기 위한 Commit | 애저 듣고보는 잡학지식 코난(김대우) 2022.10.31 31
115 오픈소스 소통을 위한 Git 공부하기 | ep3. 소스를 가져오기 위한 Clone | 애저 듣고보는 잡학지식 코난(김대우) 2022.10.30 30
114 오픈소스 소통을 위한 Git 공부하기 | ep2. Git를 소개합니다 | 애저 듣고보는 잡학지식 코난(김대우) 2022.10.28 38
113 오픈소스 소통을 위한 Git 공부하기 | ep1. 버전 관리의 중요성 | 애저 듣고보는 잡학지식 코난(김대우) 2022.10.27 45
112 오픈소스 소통을 위한 Git 공부하기 | ep0. 인트로 | 애저 듣고보는 잡학지식 코난(김대우) 2022.10.24 86
111 Azure VM - 오픈소스 ROS Gazebo Web 설치 및 실행 file 코난(김대우) 2022.10.19 140
110 parquet 파일의 meta 정보 추출 코난(김대우) 2021.11.16 1107
109 embedded SQL - GlueSQL, rust 기반 오픈소스 프로젝트 file 코난(김대우) 2021.10.22 435
» 대규모 머신러닝 프로젝트 Serving에 사용되는 Python WAS, ASGI - uvicorn file 코난(김대우) 2021.10.06 950
107 Apache Arrow 리뷰 file 코난(김대우) 2021.10.05 2020
106 autopep8을 이용한 python 린트(linting) 구현 코난(김대우) 2021.09.15 654
105 Python에서 환경변수(environment variable) 처리 코난(김대우) 2021.09.15 226
104 Python에서 random 문자열이나 숫자 가져오는 방법 코난(김대우) 2021.09.15 942
103 Python 중급 강좌 - 9. 비동기 작업(Asynchronous operations): asyncio 코난(김대우) 2021.01.03 714
102 Python 중급 강좌 - 8. 외부 리소스 관리(Managing external resources): with 코난(김대우) 2021.01.03 375
101 Python 중급 강좌 - 7. 파일작업(File read/write) file 코난(김대우) 2021.01.03 1378
100 Python 중급 강좌 - 6. 파일시스템(File system) 관리 코난(김대우) 2021.01.03 628
99 Python 중급 강좌 - 5. 다중상속(Mixins - multiple inheritance) 코난(김대우) 2021.01.03 457





XE Login