문제점

Django 백엔드 서버에서 대용량 리소스를 메모리에 올려놓고 서비스를 해야 하는 상황이 생겼다.
gunicorn 기본 동작은 workers 사이에 메모리를 공유하지 않고 각각의 worker 프로세스 생성 시 따로따로 리소스를 메모리에 올려놓는 방식이다.
이걸 다르게 말하면 worker 프로세스 수만큼 대용량 리소스를 메모리에 올려놓는다는 말이고 웹서버의 메모리를 엄청나게 잡아먹는다는 말이다.
대용량 리소스를 메모리에 올려놓고 worker 프로세스 사이에서 공유했으면 좋겠다.

환경 및 상황


version: '3.7'

volumes:
  static_vol:

services:
  web:
    build:
      context: ./
      dockerfile: Dockerfile

    command: gunicorn --workers=3 recommend.wsgi:application --bind 0.0.0.0:8000
    volumes:
           - static_vol:/code/static/
           - .:/code/
    expose:
           - 8000
    env_file:
           - ./.env.prod

  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    volumes:
          - static_vol:/code/static/
          - ./media:/code/media/
    ports:
          - 80:80
    depends_on:
          - web

위와 같은 docker-compose 파일이 있다고 가정하자.
대용량 리소스의 크기는 약 3.2GB, worker 개수가 5개니깐 메모리를 약 16GB를 사용한다.
※ gunicorn --workers=5

하지만 command 제일 마지막에 --preload 라고 붙이면 귀신같이 메모리 사용률이 낮아진다
※ 사용하는 메모리 3.2GB, 그냥 1개!

왜냐? 하나만 메모리에 올리고 그걸 공유하기 때문에!
공유하는 걸 어떻게 알 수 있냐?? 해당 변수의 메모리 주소를 보면 알 수 있다.


print(hex(id(search_model)))

위와 같이 공유하고자 하는 무거운 리소스 변수가 가르치는 메모리 주소를 출력해보자
한번은 --preload를 빼고, 한번은 추가해서 진행해보자.

--preload 빼고 10번 호출!

web_1 | 0x7fe22f072590
web_1 | 0x7fe22f0af790
web_1 | 0x7fe22f073690
web_1 | 0x7fe22f074710
web_1 | 0x7fe22f073610

web_1 | 0x7fe22f074710
web_1 | 0x7fe22f0af790
web_1 | 0x7fe22f073610
web_1 | 0x7fe22f072590
web_1 | 0x7fe22f073690

위와 같이 변수의 주소값은 총 5개! 프로세스 별로 하나씩 메모리에 올려 놓고 사용하는 걸 확인 할 수 있다.

--preload 추가

web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550

web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550
web_1 | 0x7f1fc2c33550

0x7f1fc2c33550 이 주소 하나로 모든 프로세스들이 접근하고 있다.
--preload만 붙였을 뿐인데 메모리 사용량이 급격히 줄었다.

+ Recent posts