[Django] celery, rabbitMQ, redis 도커 적용

코딩은 돈이 된다·2024년 7월 4일

쓰게된 이유

우선 우리 프로젝트 구조이다.
조금 난잡하긴 하지만 설명해보자면, rabbitMQ와 celery를 통해서 gpt에 보낸 여러 요청들을 비동기처리를 해주려고 했다.
이유는 사용자가 몰렸을 경우에 긴 시간 대기해야하는 경우가 생길 것 같아서 사용자에게 더 좋은 경험을 제공하기 위함이다.

redis는?

redis를 사용하는 이유는, 사용자의 응답과 gpt의 답변을 저장하는 임시 공간이 필요해서 사용하였다. DB에 굳이 저장해야할 이유가 없어서 redis를 사용하기로 하였다.

러닝 커브가 높긴 하지만, 필요한 기능이기에 사용했다.
무조건 사용하기보다는 본인의 서비스에 맞는 기술을 선택해서 구축하는 것이 중요하다.

구축 방법

아키텍쳐 구축에서 보이듯 docker내부에 다 넣을 것이다.(물론 좋은 환경은 아니지만, 사이드 프로젝트라서 이렇게 해보았다)

따라서 redis, rabbitMQ, celery 모두 도커로 띄울 것이라서 docker-compose 파일을 작성하고, 프로젝트를 세팅하는 과정에서 있었던 이슈나, 적용 방법 등을 적어보고자 한다.

rabbitMQ

가장 적용하기 편했다.
그냥 docker-compose파일만 작성해도 알아서 되는 것 같다.

  rabbitmq:
    image: rabbitmq:3.7-management
    container_name: rabbitmq
    ports:
      - "5672:5672"
      - "15672:15672"
    expose:
      - 5672
    environment:
      - CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672//
      - RABBITMQ_USER=guest
      - RABBITMQ_PASSWORD=guest
    depends_on:
      - 대부분 backend 컨테이너로 지정
    networks:
      - 본인이 구축하려는 networks

여기서 특이한 점이 CELERY_BROKER_URL인데,
나는 rabbitMQ와 celery를 연결하기 때문에
guest/guest 계정을 사용하여 rabbitMQ 서버에 연결하고 있다,RABBITMQ_USER와 RABBITMQ_PASSWORD는 RabbitMQ에 대한 인증 정보이다.

redis

  redis:
    image: redis:6.0
    container_name: redis
    ports:
      - "6379:6379"
    networks:
      - 본인이 구축하려는 networks

이것도 마찬가지로 도커파일 자체는 간단하다.
settings.py 파일을 수정해줘야 한다.

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://redis:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        },
    }
}

redis를 캐시로 사용하기 위한 설정이다.
만약 django_redis에 오류가 발생한다면, django의 Dockerfile에

RUN pip install django-redis

이 구문을 추가해주면, 도커가 실행되면서 알아서 설치해 줄 것이다.

celery

가장 설정할 부분이 많았다. 어렵다기보다는 잘못 이해한 부분들이 있어서 조금 오래 걸렸다.

  celery:
    container_name: celery
    env_file:
      - .env
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672//
    command: sh -c "celery -A celery 애플리케이션 이름 worker -l info"

    networks:
      - 본인이 구축하려는 networks
    depends_on:
      - rabbitmq
      - backend

여기서 왜 Dockerfile을 가져와야하는지 의문이 생겨서 찾아봤다.

이유는
celery가 Django 코드와 동일한 환경에서 실행될 수 있도록 하기 위해서이다. 쉽게 말해서 celery 작업자는 Django의 코드를 활용하여 비동기 작업을 처리해야 하기 때문에, Django 동일한 종속성을 갖추고 있어야 한다.
이때문에 Dockerfile을 같이 빌드하는 것이다.

celery는 backend와 rabbitMQ가 모두 실행된 뒤에 실행해야 하기 때문에 종속성 처리를 해주었다.

init.py

from .celery import app as celery_app

__all__ = ("celery_app",)

그냥 이렇게만 작성하면 된다.

추가로 할 작업은 루트 init.py가 있는 위치에
from .celery로 찾을 수 있도록 celery.py를 생성해주어야 한다.

celery.py

from __future__ import absolute_import, unicode_literals
import os
from celery import Celery

# 설정 파일의 경로를 환경 변수로 설정
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings")

# Celery 앱 생성
app = Celery("backend")

# Celery 설정 로드
app.config_from_object("django.conf:settings", namespace="CELERY")

# 장고 애플리케이션을 찾아서 Celery 작업을 등록
app.autodiscover_tasks()

주석으로 다 달아놔서 그렇게 큰 어려움은 없을 것이다.

settings.py

CELERY_BROKER_URL = "amqp://guest:guest@rabbitmq:5672/"
CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"

여기도 그냥 보면 이해가 된다.
나는 rabbinMQ를 브로커로 쓰기 때문에 BROKER_URL로 설정해주었다.
여기서 CELERY_RESULT_BACKEND라는 설정이 있는데,
말 그대로 celery의 결과를 저장하는 공간이다.

나는 gpt의 응답을 저장해둘 곳이 필요해서 redis를 썼는데, 혹시 result backend로 설정하지 않으면 아예 응답 자체가 서버쪽에 안넘어오나 했는데, 그건 아니어서 그냥 받은 응답을 구분해서 redis에 넣는 방식으로 사용했다.
result backend 옵션은 필수가 아니기 때문에 잘 판단해서 넣으면 될 것 같다.

마지막으로 requirements.txt 파일에

celery==5.3.1

한 줄 적어줘서 모두가 같은 버전으로 동작할 수 있도록 지정해주었다.

마치며

처음 써보는 스택이어서, 설정하기에 막막했지만, 막상 구축은 그리 어렵지 않았다. 앞으로 개발을 진행하면서 해당 기술을 활용하여 코드를 작성해야하는데, 아예 안써본 기술이다보니 계속 배우면서 진행해야할 것 같다.

특히 gpt api를 활용하여 작업하는 건 감도 안온다..
rabbitMQ와 celery를 거치며 작업을 해야 해서 많은 공부가 필요할 것 같다.

0개의 댓글