파이썬 & 오픈소스 개발 Tip과 강좌

이곳은 파이썬과 여러 오픈소스 기반 프레임워크 관련 Tip과 강좌 게시판 입니다. 관련 개발을 진행하면서 알아내신 Tip이나 강좌와 새로운 소식을 적어 주시면 다른 공부하는 분들에게 큰 도움이 됩니다. 감사합니다. SQLER.com은 개발자와 IT전문가의 지식 나눔을 실천하기 위해 노력하고 있습니다.

 

최근 여러 대규모 머신러닝 프로젝트에서는 기존 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 Python 무료 강좌 - 기초, 중급, 머신러닝(2021년 1월 업데이트) 코난(김대우) 2021.01.01 549
109 embedded SQL - GlueSQL, rust 기반 오픈소스 프로젝트 file 코난(김대우) 2021.10.22 8
» 대규모 머신러닝 프로젝트 Serving에 사용되는 Python WAS, ASGI - uvicorn file 코난(김대우) 2021.10.06 41
107 Apache Arrow 리뷰 file 코난(김대우) 2021.10.05 29
106 autopep8을 이용한 python 린트(linting) 구현 코난(김대우) 2021.09.15 38
105 Python에서 환경변수(environment variable) 처리 코난(김대우) 2021.09.15 31
104 Python에서 random 문자열이나 숫자 가져오는 방법 코난(김대우) 2021.09.15 35
103 Python 중급 강좌 - 9. 비동기 작업(Asynchronous operations): asyncio 코난(김대우) 2021.01.03 326
102 Python 중급 강좌 - 8. 외부 리소스 관리(Managing external resources): with 코난(김대우) 2021.01.03 180
101 Python 중급 강좌 - 7. 파일작업(File read/write) file 코난(김대우) 2021.01.03 206
100 Python 중급 강좌 - 6. 파일시스템(File system) 관리 코난(김대우) 2021.01.03 216
99 Python 중급 강좌 - 5. 다중상속(Mixins - multiple inheritance) 코난(김대우) 2021.01.03 116
98 Python 중급 강좌 - 4. 상속(Inheritance) 코난(김대우) 2021.01.03 63
97 Python 중급 강좌 - 3. 클래스(Class) 코난(김대우) 2021.01.03 77
96 Python 중급 강좌 - 2. 람다(Lamda) file 코난(김대우) 2021.01.03 103
95 Python 중급 강좌 - 1. Python 스타일 가이드: 서식(Formatting)과 린팅(Linting) file 코난(김대우) 2021.01.02 141
94 Python 초급 강좌 목차 - 19. 코드에서 중요한 키(패드워드) 관리 - dotenv 코난(김대우) 2021.01.02 98
93 Python 초급 강좌 목차 - 18. 데코레이터(Decorators) 코난(김대우) 2021.01.02 72
92 Python 초급 강좌 목차 - 17. JSON 데이터 처리 file 코난(김대우) 2021.01.02 165
91 Python 초급 강좌 목차 - 16. 외부 웹서비스 API 호출 file 코난(김대우) 2021.01.02 184
90 Python 초급 강좌 목차 - 15. 패키지(Package): import, pip 코난(김대우) 2021.01.02 67





XE Login