[Django]Docker-compose로 Django 환경 구축하기 6 - Celery
SW개발/Django

[Django]Docker-compose로 Django 환경 구축하기 6 - Celery

비동기 태스크 큐 - Celery

기본적으로 Web Server의 경우 프로세스를 동기적으로 처리하기 때문에 오래 걸리는 연산의 경우 사용자는 작업이 마무리 될 때까지 기다려야 한다.

이 때 오래 걸리는 작업들을 따로 모아 비동기로 처리하는 방식을 수행하면 사용자는 해당 작업을 기다릴 필요가 없어지게 된다.

따라서, 사용자 측면에서의 속도 개선을 위한 비동기 태스크 큐인 Celery가 주로 Django와 함께 사용하게 되는데 이를 container 하는 과정을 알아볼 것이다.

(보통, 푸시 알림과 같은 용도로 사용한다.)

 

현재 Message Broker로 사용되는 RabbitMQ 컨테이너가 존재하므로 장고 서버에서 메시지 큐로 Task를 전달하고 Celery Worker가 메시지 큐에 존재하는 작업들을 받아서 비동기로 처리하는 방식으로 구현해볼 예정이다.

 

마찬가지로 docker-compose.yml 에 설정을 추가하자.

 

docker-compose.yml 추가

# yml 파일 포맷의 version 설정
version: "3"

# docker-compose 볼륨 정의 (이 볼륨은 도커가 관리함)
volumes:
  postgres_db_dev: {}

# 띄울 컨테이너 목록
services:
  # Database container, 추후에 설명
  postgres:
    container_name : postgres_service
    # dockerhub의 postgres9.5 이미지 사용
    image: postgres:9.5
    # postgres_db_dev를 컨테이너 ~/data에 마운트
    volumes:
      - postgres_db_dev:/var/lib/postgresql/data
    # postgres 환경 변수
    environment: 
      - POSTGRES_DB=leffedb
      - POSTGRES_USER=leffe
      - POSTGRES_PASSWORD=leffe
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8\

  # Django container
  django:
    # 컨테이너 명
    container_name : django_service
    # django 라는 이름으로 로컬에 이미지를 저장
    image: django
    # docker build 옵션
    build:
      # docker build 명령어를 실행할 디렉토리 경로
      context: .
      # 지정할 Dockerfile 경로
      dockerfile: ./Dockerfile
    # 환경 변수 설정  
    environment: 
      - POSTGRES_DB=leffedb
      - POSTGRES_USER=leffe
      - POSTGRES_PASSWORD=leffe
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - RABBITMQ_USER=guest
      - RABBITMQ_PASSWORD=guest
      - RABBITMQ_HOST=rabbitmq
    # 현재 디렉토리를 컨테이너 내의 /home/에 mount
    volumes: 
      - ./:/home/
    # 작업 폴더 지정
    working_dir : /home/
    # 실행할 명령어
    command: > 
      bash -c "
      chmod +x /wait-for-it.sh
      && /wait-for-it.sh postgres:5432 -t 10
      && python3 manage.py migrate
      && python3 manage.py runserver"
    # 호스트 OS와 컨테이너의 포트를 바인딩
    ports: 
      - "8000:8000"
    # 호스트 OS에 포트를 공개하지 않고 컨테이너만 포트를 공개 (현재는 주석)
    #expose : 
    #  - "8000"
    # 컨테이너를 실행하는 종속성을 정의
    depends_on:
      - redis
      - rabbitmq
    
  # Redis
  redis:
    container_name: redis_service
    image: redis
    ports:
      - "6379:6379"
      
  # RabbitMQ
  rabbitmq:
    container_name: rabbitmq_service
    image: rabbitmq:3.7.14-management-alpine # 웹 UI management 툴 포함
    # 환경변수 설정
    environment: 
      - RABBITMQ_USER=guest
      - RABBITMQ_PASSWORD=guest
    ports:
      - "5672:5672" # rabbitMQ default port
      - "15672:15672" # UI를 위한 port
      
  # Nginx
  nginx:
    image: nginx
    container_name : nginx_service
    volumes: 
      # nginx.conf 설정을 위한 파일 -> /etc/nginx/conf.d/leffe.conf 에 mount
      - ./leffe.conf:/etc/nginx/conf.d/leffe.conf
    ports:
      - "80:80" # Nginx default port
    depends_on : 
      - django
      
  # Celery
  celery:
    container_name: celery_service
    # django 이미지를 재사용
    image: django
    environment: 
      - POSTGRES_DB=leffedb
      - POSTGRES_USER=leffe
      - POSTGRES_PASSWORD=leffe
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - RABBITMQ_USER=guest
      - RABBITMQ_PASSWORD=guest
      - RABBITMQ_HOST=rabbitmq
    volumes: 
      - ./:/home/
    working_dir: /home/
    # Celery Worker 실행
    command: > 
      bash -c "celery -A tasks worker --loglevel=info" # tasks.py, loglevel : info
    depends_on:
      - django

이전에 작성했던 것들과는 약간은 다른 양상을 띄고있다. 특히 Celery의 경우 파이썬에서 실행하는 것이기 때문에 앞서 작성했던 Dockerfile(Django) 이미지를 재활용하여 동일 이미지로 컨테이너 2개(django, celery)를 띄울 것이다.

우선, django의 image 옵션을 django로 정해줌으로써 Dockerfile이 빌드된 후의 결과를 로컬에 django라는 이미지 이름으로 저장을 시킨다. 이 이미지를 celery 컨테이너의 image 옵션에 적어줌으로써 해당 이미지를 재 사용할 수 있게 했다.

environment, volumes는 django 서비스와 동일하게 설정해주었고, depends_on 옵션에 django를 주어 django 컨테이너가 먼저 실행되도록 의존성을 설정해두었다.

command 부분의 tasks는 프로젝트의 루트 디렉토리의 tasks.py 모듈을 뜻하며 여기에 Celery 인스턴스를 생성하고 수행할 작업들을 넣어주면 된다.

 

도커 컨테이너의 특징이자 지켜야 할 원칙중의 하나는 모든 컨테이너는 포그라운드로 실행되어야 한다. 백그라운드로 컨테이너를 띄울 경우 컨테이너는 해당 명령을 수행 후에 종료되어 버린다. 따라서 Celery Worker를 띄울때 역시 포그라운드로 실행시키도록 설정해두었다.

또다른 원칙중의 하나는 컨테이너는 언제나 소멸 가능한 상태를 유지해야 한다. 이러한 이유들 때문에 django 컨테이너 안에서 Celery Worker를 실행시키지 않고 새로운 컨테이너를 생성하게 되었다.

 

tasks.py 작성

from django.conf import settings
from celery import Celery

brokers = [f'amqp://{settings.RABBITMQ_USER}:{settings.RABBITMQ_PASSWORD}@{host}//' 
		for host in settings.RABBITMQ_HOSTS]
app = Celery('puddlr', broker=brokers, 
		broker_transport_options={'confirm_publish': True})
        
@app.task
def add(x, y):
	return x + y

Celery의 Broker를 RabbitMQ로 사용할 것이므로 RabbitMQ의 url을 브로커로 설정해준다. 이 url과 함께 Celery 인스턴스를 생성시킨다.

이제 위에 적힌 함수처럼 Task함수를 정의하면서 작업을 생성할 수 있다.

 

아주 기본적인 Celery의 사용을 다룬 것이기 때문에 좀 더 심도있는 작업은 Celery 공식 문서나 타 자료들을 참고하면서 각자의 상황에 맞추어 task들을 작성해나가면 될 것이다.

 

지금까지 Django 프로젝트에 다양한 Stack들을 붙여 Dockerizing 하는 과정을 알아보았습니다. 환경을 구축하는 것에 대한 포스팅은 여기서 마무리 되었습니다. 제가 실제로 현업에서 진행 중인 프로젝트에서 도커를 도입하는 과정에 있어서 어려웠던 점, 느꼈던 점들을 공유해보는 시간을 가져볼까 합니다. 

 

긴 글 읽어주셔서 감사하고 다음 포스팅에서는 도커를 활용함에 있어서 Tip 들과 느낀점들로 다시 찾아오겠습니다.

공부하면서 정리한 내용이니 틀린 부분이 있을 수 있습니다. 댓글로 남겨주시면 감사하겠습니다.

 

728x90