ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 왜 Django는 여러개의 프로세스로 실행하는거지? (feat. libuv, ASGI)
    카테고리 없음 2022. 3. 27. 21:25

    안녕하세요! Jay 입니다!

    오늘은 제가 업무에서 Django를 주로 쓰다가 FastAPI, Nest.js를 접하면서 생겼던 궁금증에 대해서 정리해보려고 합니다!

     

    Django 개발을 하는 분들은 다 아시겠지만 runserver로 서비스를 실행시켜서 production 환경에서 사용하지 않습니다! Django 공식문서에도 나와있지만 runserver로 production 환경에서 서빙할 경우 보안에 문제가 있을 수 있다고 합니다. 그래서 Nginx 같은 웹서버와 wsgi를 함께 사용하라고 합니다!

     

    더불어 서비스를 배포할 때 python wsgigunicorn, uwsgi 등 을 사용하면서 여러 개의 워커(worker)들을 띄워서 클라이언트 요청들을 처리를 합니다.

     

    여태까지는 그냥 아 python이 싱글 스레드(Single-Thread)이니까 여러 개의 프로세스를 띄워서 각각 일을 나눠서 처리하려고 그렇게 사용하는 거라고만 이해하고 있었습니다.

     

    python은 GIL(Global Interpretor Lock) 때문에 멀티스레드(Multi-Thread)를 지원하지 않습니다.

    (단, cpu-bounded에 한해서입니다. IO-bounded 작업은 GIL에 영향을 받지 않고 멀티스레드로 작동합니다) 

     

    주로 Django를 쓰다가 최근에 FastAPI, Nest.js를 사용하면서 궁금한 점이 생겼습니다.

     

    "왜? Nest.js는 프로세스 1개로 백엔드 애플리케이션을 서빙하지?"
    "어? FastAPI는 비동기로 처리 가능한데, production 환경에서는
    gunicorn으로 여러 worker 띄워서 사용하네?"

     

    등의 궁금증이 생겼고, 동료들과 점심을 먹으면서 이야기하다가 글로 한번 정리해보면 좋겠다는 생각을 했습니다. 지금 와서 한번 정리하고 글로 쓰려니까 정말 별거 아니지만 제대로 정리하려면 꽤 많은 양이더라고요;; ㅎㅎ

     

    그래서 이야기하고자 하는 내용의 포인트만 정리해서 말씀드리겠습니다.

     

    1. WSGI 와 Django


    was 요약

    - WSGI(Web Server Gateway Interface): 이름 그대로 웹 서버(Web Server)와 python application이 서로 통신하기 위한 인터페이스입니다. Django의 경우 WSGI 중 uWSGI, gunicorn 등 과 함께 production 환경에서 서빙을 합니다.

     

    여기서 중요하건 WSGI는 request/response 형식으로 동기(Synchronous)로 동작한다는 것입니다. 그렇기 때문에 worker 개수를 늘서 여러 개의 프로세스로 서빙을 하는 방식인 거죠! (병렬)

     

    여러 개의 프로세스로 서빙하지 않는다면 하나의 request에서 처리하는 로직이 오래 걸린다면 그만큼 병목현상이 일어나서 다른 request를 처리하지 못하겠죠? production 환경에서 이런 일이 일어난다면....ㅎㅎ (생각만 해도 끔찍하네요... 바로 장애입니다)

     

     

    2. nest.js는 왜 하나의 프로세스만으로 서빙?


    하나의 프로세스만 실행되어 있는 모습

    nest.js 는 node.js 기반 웹 프레임워크(Web Framework)입니다. 회사에서 nest.js로 만들어진 애플리케이션이 서빙되는 서버에 들어가서 프로세스를 보니 하나만 실행되고 있었습니다.

    어? Django랑은 좀 다르네?

    라는 생각과 이유에 대해 찾아봤습니다!

    node.js는 싱글 스레드(Single-Thread)이다! 하지만 멀티스레드(Multi-Thread)로 작업을 처리한다!

     

    응?! 싱글 스레드인데 멀티스레드로 처리한다고? 무슨 말일 까요? ㅎㅎ node.js의 구조를 한번 살펴보겠습니다.

    node.js 구조

    node.js의 구조가 이렇게 생겼다고 합니다. node.js를 구성하는 요소가 v8엔진, libuv가 있는데요.

     

    v8엔진은 자바스크립트(javascript) 코드를 읽어서 실행하는 프로그램이며  자바스크립트 코드를 마이크로프로세서에서 이해할 수 있는 low-level(기계어)로 변환해준다고 합니다. libuvevent-loop기반의 비동기 I/O를 지원하는 멀티 플랫폼 라이브러리입니다.

     

    우리가 주목해야 할 것은 libuv입니다. node.js는 싱글 스레드가 맞습니다. 하지만, 처리하는 작업들이 blocking 되지 않도록 libuv의 이벤트 루프(event-loop)와 스레드 풀(thread-pool)을 사용합니다.

     

    node.js 프로세스 동작 과정

    위 이미지를 보면 node.js는 이렇게 실행된다고 합니다.

    1. 프로그램 init
    2. "Top-level" 코드(콜백 함수 안에 있지 않은 모든 코드) 실행 
    3. 모듈 require
    4. 콜백 이벤트 등록
    5. 이벤트 루프 실행

     

    오 그림을 보니 좀 더 이해가 가는 것 같습니다.  이벤트 루프가 돌다가 오래 걸리는 작업들은 스레드 풀로 보내서 처리하고 callback으로 결과를 이벤트 루프로 전달하는 형태입니다.

    (스레드 풀의 스레드 개수는 default가 4개입니다.)

     

    스레드 개수를  무조건 많이 하면 한 번에 많이 처리할 수 있을 것 같지만, 결국 cpu core 수만큼 처리할 수 있기 때문에 cpu core개수에 맞게 설정한다고 합니다.

    스레드풀에 cpu core가 할당되는 모습

    cpu가 쿼드코어(quad-core) 일 경우 위와 같은 형태로 스레드에 core가 할당될 것입니다. 다만 cpu가 적거나 많을 때 각 스레드에 core가 어떻게 할당되는지 정확히는 잘 모르겠네요. 아마 별도의 CPU 스케줄러가 cpu core의 상태를 체킹 하면서 할당을 해줄 것 같습니다.

     

    결론은 node.js 환경에서는 libuv라는 라이브러리가 이벤트 루프, 스레드 풀을 통해 멀티 스레딩으로 작업들을 처리하기 때문에 싱글 스레드인 node.js에서 non-blocking 비동기 처리가 가능한 것입니다. 이것이 python wsgi와 큰 차이점인 것 같습니다!

     

    -T 명령어로 쓰레드를 확인하니 이렇게 여러개가 있는걸 확인

    ps -T 혹은 ps -eLf 명령어를 치면 여러개의 스레드가 있는걸 볼 수 있습니다. 다만 이 쓰레드가 모두 libuv의 스레드 풀은 아니고 v8에서 사용하는 스레드, 이벤트 루프에서 사용하는 스레드 등이 함께 있다고 합니다. (https://github.com/nodejs/help/issues/1571)

     

    (프로세스, 스레드, 코어 등에 대한 용어 정리는 아래 블로그에서 참고 바랍니다!)

    https://dmzld.tistory.com/18

     

    [OS] CPU, Processor, Core, Process, Thread 그리고 관계 정리

    1. HW 1) CPU : Central Processing Unit, 중앙처리장치 간단하게 컴퓨터의 뇌로써 '사고'를 담당 기억, 연산, 제어를 담당 cf) MPU, MCU - MPU : Micro Processing Unit CPU의 한 종류로써, 전자부품과 반도체칩..

    dmzld.tistory.com

     

     

    3. 그럼 FastAPI는 비동기로 처리된다는데? FastAPI도 하나의 프로세스로 사용해도 되나?


    결론부터 말씀드리자면 '아닙니다'. 적어도 production 환경에서는요! 

     

    FastAPIuvicorn이라는 ASGI(Asynchronous Server Gateway Interface)와 함께 사용합니다. ASGI는 쉽게 말해 비동기 WSGI라고 보시면 됩니다. Send/Receive 이벤트로 비동기 처리를 한다고 합니다.

     

    비동기가 가능하기 때문에 대용량 트래픽이 들어와도 WSGI처럼 동기적으로 실행돼 blocking이 되지 않고, non-blocking으로 작업을 처리 가능합니다.

     

    uvicorn은  libuv 기반으로 개발된 uvloop를 사용합니다. 이 uvloop는 asyncio의 이벤트 루프를 대체한다고 합니다.

    (자세한 내용은 따로 다뤄야 할 것 같습니다)

     

    uvloop가 libuv기반으로 개발된 라이브러리면
    똑같이 하나의 프로세스로만 사용해도 되는 거 아니야?

     

    라고 생각하실 수 있는데, 실제로는 python의 GIL 때문에 cpu-bounded의 작업을 멀티스레드로 처리할 수 없습니다!

     

    결국 GIL로 인하여 cpu 성능을 모두 활용하지 못하기 때문에  gunicorn등과 함께 사용하여 여러 개의 worker를 생성해서 서빙을 합니다! 그렇다고 python의 ASGI가 의미가 없는 것은 아닙니다. 비동기적으로 작업을 처리하기 때문에 동기적으로 처리하는 WSGI 보다는 훨씬 빠르죠!

     

    속도에 대한 테스트는 여기서 볼 수 있습니다.

     

     

    4. 정리


    1. python wsgi는 동기적이라 오래 걸리는 작업에 병목이 생길 수 있다. 그래서 다량의 네트워크 요청을 처리하기 위해 다수의 프로세스(worker)로 서빙한다.
    2. node.js는 싱글 스레드이지만, libuv에서 이벤트 루프와 멀티스레드로 작업을 처리한다. 그래서 non-blocking 으로 작업을 처리한다.
    3. ASGI를 통해 비동기적으로 다량의 네트워크 요청을 non-blocking으로 처리 가능하지만, python의 GIL로 인해 모든 cpu core의 성능을 사용할 수 없기 때문에 gunicorn 등과 함께 여러 개의 worker로 서빙한다.

     

    다소 의식의 흐름대로 정리했고, 중간중간 알아야 할 하드웨어적인 지식들도 있었습니다! 한 번에 모든 내용을 다루기엔 힘든 점도 있어서 조금 빈약했던 것도 같습니다 ㅎㅎ

     

    개인적으로 이번 글을 포스팅하면서 왜 CS 기본지식이 왜 중요한지 알게 되었습니다! 이 포스팅을 쓰기 전까지는 cpu, core, process에 대한 정의가 명확하지 않았고 제가 잘 못 이해하고 있는 부분들도 있더라고요 ㅎㅎ

     

    글에 잘못된 부분들이나 정정해야 하는 부분들이 있다면 댓글로 첨언 부탁드리겠습니다!

    그럼 오늘도 좋은 하루 되시고 즐거운 코딩 하시길 바랍니다!!

     

    참고 사이트

    운영체제

     

    [운영체제] 프로세스

    프로세스 : 실행중인 프로그램프로세스의 현재 상태는 프로그램 카운터값과 프로세서 레지스터의 내용으로 나타냄프로세스는 스택, 힙, 데이터,프로그램 카운터를 포함한다텍스트 섹션 : 실행

    velog.io

    쓰레드, 싱글스레드

     

    TIL | #4 쓰레드(Thread), 싱글과 멀티 쓰레드

    2021-02-16(화)

    velog.io

    node.js는 싱글스레드?

     

    [Node.js] Node.js는 싱글 스레드?

    어렵군

    velog.io

    libuv

     

    Asynchronous Engine — (1) libuv

    libuv Introduction & Inside (code reading) — libuv 내부 코드 분석

    blue-hope.medium.com

    What is node.js?

     

    What is Node.JS and When to use it? A comprehensive guide with examples

    What is Node.js? Node.js is an open-source, Javascript runtime environment that lets you effortlessly develop scalable web applications. Let’s explore Node.js together.

    www.simform.com

    멀티코어와 스레드

     

    3.Multi-Core와 Thread

    안녕하세요~ 이번글은 Multi-Core, Thread, Context switching이라는 개념을 설명을 드리고 CPU 스펙을 평가하기 위한 기본 이론지식을 마무리하도록 할거에요~ CPU는 메모리에서 명령어를 처리하는 역할을

    89douner.tistory.com

     

    댓글

운동하는 개발자 JAY-JI