코인 가상 거래 만들어보기! (3) celery & redis / dockerize

HEYDAY7·2021년 5월 20일
0

celery와 redis를 통해 주기적으로 task를 실행시켜보려하고, 전체 프로젝트를 dockerize 하려고 한다.

시작!

구현하려고 했던 것은, 매 초마다 빗썸 api에 request를 보내서 coin 가격을 받아오고, 이를 꾸준히 적재해나가는 것이었다. 우선적으로 response야 request를 보내기만 하면 오니 상관 없고, 해결해야했던 부분은 어떻게! 주기적으로 task를 보내고, 그렇게 받아온 response를 통해 데이터를 하나하나 쌓아 나갈 것인가였다.

선택한 방식은 celery와 redis를 이용하는 것이다. 이 celery와 redis에 대한 공부는 따로 글을 작성해뒀다.

또한 docker을 이용하여 환경 설정을 새로 시작했다. docker를 연습하는 이유도 있고, celery와 redis를 어렵지 않게 셋팅할 수 있을 것 같아 선택했다.

Coin app 생성

두번째 django-app으로 Coin app을 만들어 주고, tasks.py 파일을 내부에 만들어준다. 아직 coin에 대해서 코드를 작성할 것은 없지만!, tasks.py의 위치를 미리 잡아주고자 새로운 django app을 만들어두는 느낌이다.

우선 pacakge를 다운받아주자. 잊지말자! pipenv shell을 통해 가상환경을 키고서 다운 받아야 한다!

pipenv install 'celery[redis]'
pipenv install celery
pipenv install redis
pipenv install requests 

가장 아래에 requests는 빗섬 api에 request를 보내야 하므로 필요해서 다운받는다.

celery 설정

celery를 사용하기 위해서 설정을 해주자. 설정은 back/back 내부에서 이뤄진다. 즉 project의 settings.py가 존재하는 디렉토리에서 작업할 것이다

이 글을 주로 참고했다.

back/back/celery.py

위에 말했던 위치에 celery.py를 생성하고 아래와 같이 코드를 작성한다.

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

os.environ.setdefault('DJNAGO_SETTINGS_MODULE', 'back.settings')

app = Celery('back') # project명이 들어간다
app.config_from_object('django.conf:settings', name='CELERY') # 해당 줄 덕분에 settings에서 CELERY로 시작하는 setting을 가져온다
app.autodiscover_tasks()

@app.task(bind=True)
def debug_task(self):
    print('Request: {0!r}'.format(self.request))
    
# 2초마다 해당 task를 반복하도록 설정한다!
app.conf.beat_schedule = {
    'get_coin_data': {
    	'task': 'coin.tasks.get_coin_data',
        'schedule': 2.0,
    'kwargs' : {'coin':'BTC'}
    }
} 

back/back/init.py

기존에 존재하는 파일일 것이다. 이를 수정해주자. celery를 적용시켜주는 부분이다.

### back/__init__.py
from __future__ import absolute_import, unicode_literals
from .celery import app as celery_app

__all__ = ('celery_app',)

task 설정 , back/coin/tasks.py

from __future__ import absolute_import, unicode_literals
from celery import shared_task
import requests

@shared_task
def get_coin_data(coin):
    url = f'https://api.bithumb.com/public/ticker/{coin}_KRW'
    response = requests.get(url)
    return response.json()

일단은 단순하게 request를 보내 코인 가격에 관한 결과를 response로 받아오게만 해두었다. 나중에 저장할 모델까지 만들고서 이를 수정하겠다!

back/back/settings.py

coin 앱을 새로 만들면서 미처 settings.py에 코드를 추가해주지 않았을 것을 가정하여 이를 추가하는 코드도 적는다.

INSTALLED_APPS = [
    ...,
    'coin',
]

...

CELERY_BROKER_URL = 'redis://redis:6379'
CELERY_RESULT_BACKEND = 'redis://redis:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Asia/Seoul'

아래에 부분이 Celery에 대한 적용이다. 적으면서도 완벽한 이해를 하지는 못했지만 대략적으로 celery Broker로 redis를 쓰기 때문에, redis 서버를 연결해주고!, 데이터 형태를 json으로 받겠다는 설정이다.

Dockerize

대망의 Dockerize이다. 위에서 언급했듯이 selery와 redis를 추가하면서 여러 관리할 게 늘어났고, Docker를 통해 이를 깔끔하게 관리하기 위함이다. 그래서 database(postgresql), backend, redis, celery, celery-beat 총 5개로 구성된 container를 만들 것이다. Docker와 Docker-compose는 간략한 이해를 도울 수 있는 글도 적어뒀으니 참고하면 좋다.

Dockerfile

뭐니뭐니해도 Docker의 장점은 아이콘이 귀엽다...
넘어가고 작성을 시작해본다. 우선 Dockerfile의 위치는 Pipfile이 존재하는! 프로젝트의 가장 밖에 위치시킨다.
대강 이런 구조가 될 것이다.
Dockerfile을 작성해보자

FROM python:3.8.5 # 내가 사용중인 python 버전을 적었다.

WORKDIR /app

COPY Pipfile Pipfile.lock /app/
RUN pip3 install pipenv 
RUN pipenv install --system

#이 아래 코드는 사실 추가적인 조작이다. DB보다 backend가 먼저 켜져서 DB와 제대로 연결이 안될 수 있는데 이를 방지해주는 역할이다.
ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh / 

docker-compose.yml

헷갈릴 수 있으니 잘 따라오자

version: '3'

volumes:
  coin_postgres_db : {}
  
services:
  db:
    images: postgres
    volumes:
      - coin_postgres_db:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=coin_postgres
      - POSTGRES_USER=coin_postgres
      - POSTGRES_PASSWORD=coin_postgres
      - POSTGRES_INITDB_ARGS=--encoding=UTF-8
      
  backend:
    build:
      context: .
      dockerfile: ./Dockerfile
    stdin_open:true
    tty: true
    ports:
      - "8000:8000"
    volumes:
      - ./back:/app/
    commands:
      - bash
      - -c
      - |
        chmod +x /wait-for-it.sh
        /wait-for-it.sh db:5432 -t 10
        python manage.py runserver 0:8000
    depends_on:
      - db
      - redis
      
  redis:
    image: redis
    ports:
      - "6379:6370"
    
  celery:
    build:
      context: .
    command: celery -A back worker -l info
    volumes:
      - /back:/app/
    depends_on:
      - db
      - redis
      - backend
    
  celery-beat:
    build:
      context: .
    command: celery -A beat -l info
    volumes:
      - /back:/app/
    depends_on:
      - db
      - redis
      - backend

상세한 설명은 추후에 추가하겠다

settings.py

django에서는 기본적으로 sqlite를 쓰는것으로 되어 있다. 근데 위에서 docker-compose를 작성하면서 db를 postgres로 설정하였다. 따라서 settings.py에 있는 database 관련 셋팅을 바꿔줘야 한다.


...

DATABASE = {
    'default': {
    	'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'coin_postgres',
        'USER': 'coin_postgres',
        'PASSWORD': 'coin_postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}

이렇게 postgresql 셋팅을 완료한다.

docker 켜보기

이제는 docker를 실행해보자. docker-compose.yml이 작성되어 있는 위치에서 아래 코드를 실행해보자. docker-compose build를 하면 착착착 building이 되는 것을 볼 수 있을 것이다. build가 완료되면 docker-compose up을 실행하면 server가 켜질 것이다. 그리고 우리가 주기적인 task를 설정해뒀기 때문에 bash(terminal?)을 보면 2초마다 request로 받은 데이터가 보여지고 있을 것이다.

주의할 점 : postgres로 db를 설정하고 켰을 때 오류가 날 때가 있다. 이 때는 우리가 이전에 한번이라도 서버를 켰었다면!!! 어딘가에 db.sqlite3 파일이 있을 것이고, 그게 남아있기 때문에 db간의 충돌이 일어나는 것을 가능성이 있다. 오류가 난다면 그 파일을 깔끔하게 지우고 진행해보자

정리

이렇게까지 작성해서 이제 코드와 서버 구조가 좀 체계화 되었다. 이제 다음으로 진행할 일은 통신을 통해 받은 데이터를 저장하는것 (1번)이고, 그 이후에는 유저가 거래를 요청할 수 있도록 transaction 관련 모델을 만들어야 할 것이다!

profile
(전) Junior Android Developer (현) Backend 이직 준비생

0개의 댓글