택시짱의 개발 노트

django 토이 프로젝트에 docker-compose로 celery + rabbitmq + flower 적용해보기 (회고 느낌) - 2 본문

Django(장고)

django 토이 프로젝트에 docker-compose로 celery + rabbitmq + flower 적용해보기 (회고 느낌) - 2

택시짱 2022. 3. 1. 18:28

1편 - django 토이 프로젝트에 git action, code cov 적용해보기 (회고 느낌) - 1

 

오늘은 docker-compose를 이용하여 비동기 아키텍처를 구성해 보려고 합니다. (celery + rabbitmq + flower)

먼저 개념들에 대해 간단하게 알아보고 가시죠

비동기란?

비동기 방식은 현재 작업의 응답과 다음 작업의 요청이 동시에 진행되지 않아도 되는 것으로 응답에 관계없이 바로 다음 동작이 실행되는 것

celery란?

celery는 분산 메시지 전달에 기반한 비동기 작업 queue로써 별도로 실행 중인 Worker Process가 Broker로부터 Message를 전달 받아 작업을 대신 수행해 주는 라이브러리이다.

rabbitmq란?

RabbitMQ는 AMQP(Advanced Message Queueing Protocol)을 구현한 오픈소스 메세지 브로커(중개자)이다. 간단히 말해서 Rabbit MQ는 데이터를 일단 어딘가에 쌓아두고 나중에 비동기적으로 적절한 처리를 하고 싶은 경우를 위한 데이터 저장소이다.

(출처 - https://jithub.tistory.com/443)

flower란?

celery 클러스를 모니터링하고 관리하기 위한 웹 기반 도구이다. celery events를 사용하여 실시간 모니터링을 제공한다.

(출처 - https://flower.readthedocs.io/en/latest/)

 

 

일단 나는 docker를 이용하여 위의 동작과정에서 필요한 부분을 구성하려고 했다.

사실 docker에 대한 전문적인 지식이 없어 Dockerfile, docker-compose.yml 파일을 보는순간 왜 이렇게 했을까 라는 고민이 드실수 있습니다ㅠㅠ 좋은 방법 또는 방향이 있다면 댓글로 남겨주세요 

 

django Dockerfile

Django의 Dockerfile은 아래와 같습니다.

# Django의 Dockerfile

FROM python:3.7.9

RUN apt-get update

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN mkdir /code

COPY . /code
WORKDIR /code

RUN mkdir /static
RUN mkdir /staticfiles

RUN pip install --upgrade pip
RUN pip install --no-cache-dir -r requirements/requirements-dev.txt

RUN python manage.py migrate
RUN python manage.py collectstatic --noinput

 

docker-compose.yml 코드

저는 docker-compose를 이용하여 DB를 제외한 서버의 모든 구성요소를 docker container를 이용하였습니다.

# DB를 제외한 서버 구성 요소 (nginx, django, redis, rabbitmq, celery(worker, beat), flower)

version: "3"

services:
  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - .:/code
      - ./nginx:/etc/nginx/conf.d
      - ./static:/static
    depends_on:
      - django

  django:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: djnago
    command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - .:/code
    environment:
      - DEV_NAME=${DEV_NAME}
      - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE}
      - PASSWORD=${PASSWORD}
      - HOST=${HOST}
      - PORT=${PORT}
      - USER=${USER}
      - REDIS_HOST=redis
      - CELERY_BROKER_URL=amqp://guest:guest@rabbitmq
    depends_on:
      - rabbitmq
      - celery_worker
      - redis

  redis:
    container_name: redis_service
    image: redis
    ports:
      - "6379:6379"
    volumes:
      - .:/code

  rabbitmq:
    container_name: rabbitmq
    image: rabbitmq:3.7.14-management-alpine
    environment:
      - RABBITMQ_USER=guest
      - RABBITMQ_PASSWORD=guest
    ports:
      - "5672:5672"
      - "15672:15672"
    expose:
      - "15672"
    volumes:
      - .:/code

  celery_beat:
    restart: always
    build:
      context: .
      dockerfile: Dockerfile
    command: "celery -A config beat --loglevel=info"
    volumes:
      - .:/code
    environment:
      - DEV_NAME=${DEV_NAME}
      - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE}
      - PASSWORD=${PASSWORD}
      - HOST=${HOST}
      - USER=${USER}
      - PORT=${PORT}
      - CELERY_BROKER_URL=amqp://guest:guest@rabbitmq
    depends_on:
      - rabbitmq

  celery_worker:
    restart: always
    build:
      context: .
      dockerfile: Dockerfile
    command: "celery -A config worker --loglevel=info"
    volumes:
    - .:/code
    environment:
      - DEV_NAME=${DEV_NAME}
      - DJANGO_SETTINGS_MODULE=${DJANGO_SETTINGS_MODULE}
      - PASSWORD=${PASSWORD}
      - HOST=${HOST}
      - USER=${USER}
      - PORT=${PORT}
      - CELERY_BROKER_URL=amqp://guest:guest@rabbitmq
    depends_on:
      - rabbitmq

  flower:
    image: mher/flower
    environment:
      - CELERY_BROKER_URL=amqp://guest:guest@rabbitmq
      - FLOWER_PORT=5555
    ports:
      - 5555:5555

 

docker-compose 실행!

celery worker, beat의 구성은 django의 구성과 거의 흡사한데 

django에서 비동기 작업을 queue로 던지면 django의 구성요소와 동일한 녀석이 worker로 동작하여 작업을 처리하면 되지 않을까 라는 생각을 가져서 django의 구성과 동일하게 하였습니다.

 

위의 docker-compose.yml파일을 실행 시키면 아래와 같이 각각의 container가 생성 되는것을 확인할 수 있습니다.

CONTAINER ID   IMAGE
a165a0f5c931   nginx:latest
2ef40f0419e7   django_shopping_mall_api_project_django
1ce11a3247ea   django_shopping_mall_api_project_celery_worker
1f2fd3bad67e   django_shopping_mall_api_project_celery_beat
211a245decc6   rabbitmq:3.7.14-management-alpine
34f5bda1c66d   mher/flower
5c3e185db6c2   redis

 

비동기 처리 테스트 코드

이제 비동기 처리를 할수 있는 인프라가 구축이 되었고, 간단하게 테스트를 해보겠습니다.

100까지 반복문을 돌면서 rabbitmq에 queue가 쌓이는것을 확인해보기 위해 time.sleep(1)을 주었습니다.

간단하게 views.py를 만들고 Postman으로 비동기 함수를 호출할 수 있게 했습니다.

# tasks.py
@app.task(bind=True)
def do_calc_total(self):
    total = 0
    for i in range(100):
        time.sleep(1)
        total += i
    return total
    
# views.py
@decorators.api_view(['GET'])
def celery_test_view(request):
    do_calc_total.delay()
    return Response()

 

비동기 작업 시작전 상태 확인!

API를 호출하기 전에 rabbitmq, flower에서 현재 작업상태를 확인할 수 있습니다.

rabbitmq에는 queue가 비어 있는 것을 확인할 수 있고

queue가 비어있음

flower에는 현재 동작하고 있는 woker가 없는것을 확인할 수 있습니다.

동작하고 있는 worker가 없다.

 

API를 이용하여 비동기 작업 실행!

현재 worker의 갯수는 4개로 설정을 해놓아 4개의 worker가 active인것을 확인할 수 있다.

이미 5개나 완료된것을 볼수있다.

worker가 4개라 일을 4명밖에 못한다 ㅠ

위에서 worker의 갯수는 4개 queue에 쌓인 작업들을 소비하지 못해

ready에 job이 쌓여 있는것을 확인할 수 있다.

일꾼이 4명이라 아직 85개의 작업이 대기하고있다 ㅠㅠ

 

마무리

ec2위에 덕지덕지 install하며 비동기 처리를 해본적이 있는데 docker로는 처음 해봤는데 확실히 무지성으로 install하고 따라치면서 했던것 보다는 docker-compose.yml을 작성하면서 흐름을 파악할 수 있었던거 같았다.

docker-compose는 다른분이 보셨을때 부족한점이 많을것으로 생각되지만.. 구축했다는 것으로 만족..

 

서버 개발을 하며 비동기 처리를 왜쓰고 어디서 쓰는지를 조금씩 배워가고있다. 예를들면 인증번호 발송, 마케팅 email발송, 월말 또는 분기별 데이터 취합 등등..

 

그리고 현재 worker가 4개로 한정이 되어 있는데 대기하고 있는 job queue가 많으면 aws의 autoscaling과 같이 유동적으로 worker의 갯수를 늘리는 방법이 있는지 찾아보면 좋을것 같다.

예를들면 대기하고 있는 job queue가 10개 이상이면 worker의 갯수를 5개로 늘리고, 30개 이상이면 7개, 50개 이상이면 10개 등등...

docker container를 이용한다면 쿠버네티스를 써야되나?? 그냥 container를 늘리면되나? 

반응형
Comments