[이슈]
celery beat를 이용하여 cron을 대처하기 위해
celery beat를 붙였다.

잘 작동하였다.

 

특히 beat의 스케줄은 crontab으로 관리하였는데, 
이슈는 crontab에 등록해놓은 시간이...  제시간에 실행되지 않고, 9시간 뒤의 task를 땡겨와서 실행하고 있었다... 하...

import datetime


app = Celery('****')
app.conf.timezone = "Asia/Seoul"
app.conf.enable_utc = False

app.now = datetime.datetime.now

app.conf.beat_schedule = {
    'alliance_order_daily_upload_worker':{
        'task': 'celery_batch.tasks.alliance_order_daily_upload',
        'schedule': crontab(minute=0, hour=1),
        'options': {'queue': 'slow_tasks'},
        'args': ()
    },
}

 

위와 같이 설정하였고, 기대는 새벽 1시에 beat가 돌기를 기대하였다.

하지만 기대와 다르게 업무 시간이었던 오후 4시에 crontab이 task를 queue에 넣었다.
즉 9시간 뒤의 task를 queue에 넣은 것이다.

도대체 뭐가 꼬인 걸까?
분명 UTC와 한국 시간의 차이가 9시간 이고

python shell에서 
생성된 app의 
app.now()를 체크해보면 한국 시간으로 체크되고 분명 한국 시간과 같은데, 
또 다시 9시간 뒤에 것을 떙겨와서 queue에 쌓는다라...

 

너무 이상한 점이 많았다.
beat에 등록된 crontab 시간을 한국 시간이 아니라 UTC 시간으로 보았다면, UTC 1시는 한국 시간 10시니깐 
10시에 시작을 했어야 했는데, 새벽 1시에 등록된 task가 한국 시간 오후 4시에 시작이 되었다. 

그럼 crontab에 등록된 시간이 UTC인가??
아니다 한국 시간이 맞다 ㅠㅠㅠㅠ..

In [1]: from danoshop.celery import *


In [2]: app.now()
Out[2]: datetime.datetime(2020, 5, 14, 16, 50, 38, 574737, tzinfo=<DstTzInfo 'Asia/Seoul' KST+9:00:00 STD>)

이렇게 나온다.

[Spoqa 기술 블로그 | 파이썬의 시간대에 대해 알아보기(datetime.timezone)](https://spoqa.github.io/2019/02/15/python-timezone.html)

위의 문서를 참고해보면

import datetime
from pytz import timezone, utc

KST = timezone('Asia/Seoul')

now = datetime.datetime.utcnow()
# UTC 기준 naive datetime : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879)

utc.localize(now)
# UTC 기준 aware datetime : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879, tzinfo=<UTC>)

KST.localize(now)
# UTC 시각, 시간대만 KST : datetime.datetime(2019, 2, 15, 4, 18, 28, 805879, tzinfo=<DstTzInfo 'Asia/Seoul' KST+9:00:00 STD>)

utc.localize(now).astimezone(KST)
# KST 기준 aware datetime : datetime.datetime(2019, 2, 15, 13, 18, 28, 805879, tzinfo=<DstTzInfo 'Asia/Seoul' KST+9:00:00 STD>)

 

와 같이 나와있는데 결국 위의 shell에서 나온 것에 대한 해석은 해당 시간을 3번째 케이스로 보기에는 시간대가 한국과 같아 제일 아래로 보는게 맞다고 판단하였다.

 

그럼 app.now의 시간은 지금 시간은 맞아.... 지금 시각이야.
crontab에 등록된 것도 지금 시각이야...

근데 9시간 뒤의 것을 먼저 실행한다...??

어떤 원인인지 정확히 파악은 못했다.
app.now의 시각은 분명히 한국 시간인데, 이것을 beat에서는 어느중간 UTC 시간으로 바꾸고, 9시간 뒤의 crontab을 가지고 온다고 하니 이야기가 맞아 떨어졌다.

 

해결

해결책 1. 

  • 현재 app의 시간을 utc시간으로 바꾸어 준다.

  • 따로 crontab의 시간을 바꾸지 않아도 된다.

# app.conf.timezone = "Asia/Seoul" : CELERY_TIMEZONE에 있어서 주석처리

# app.conf.enable_utc = False 어디에 쓰는 걸까...? CELERY_ENABLE_UTC = False 에 있다.

app.now = datetime.datetime.utcnow

  • 단 djcelery_periodictask; db의 last_run_at time이 UTC 기준으로 들어가게 된다. 즉 한국 시간보다 9시간 뒤의 시간이 저장된다.

 

해결책 2

  • app.now의 값은 그대로 놔두고 crontab의 시간을 9시간씩 더해서 해준다.

  • 그러면 너무 쉽지 않으니깐.. 만들어진 함수를 쓰자

def convert_cron_hour(hour):

    now = datetime.datetime.now()

    utc_datetime = datetime.datetime(

        now.year, now.month, now.day, hour, 0, 0, 0, tzinfo=pytz.timezone("UTC")

    )

    local_datetime = utc_datetime.astimezone(pytz.timezone('Asia/Seoul'))

    return local_datetime.hour





app.conf.beat_schedule = {

    'alliance_order_daily_upload_worker':{

        'task': 'celery_batch.tasks.alliance_order_daily_upload',

        'schedule': crontab(minute=0, hour=convert_cront_hour(1)),

        'options': {'queue': 'slow_tasks'},

        'args': ()

    },



  • 위와 같이 해주면 알아서 9시간 뒤로 cronschdule를 저장해준다. 

 

하... 원인을 좀 더 자세히 파악해보고 싶은데,
영혼까지 털털 털려서 ㅠㅠ 일단은 추후에 좀 더 알아봐야겠다

 

 

 

 

 

 

 

 

 

+ Recent posts