ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Celery multiple worker를 Docker 내부에서 실행하기(with Supervisord)
    개발, 코딩/트러블슈팅 2020. 3. 26. 19:44

    안녕하세요. 오늘은 celery에서 multiple worker를 foreground에서 실행하기 위한 과정과, 왜 그런 결정을 했는지에 대해 작성해보도록 하겠습니다.

    문제 상황

    Docker container 내에서 celery worker를 실행하는데, concurrency가 적절하게 설정되어 있지 않아서 cpu가 너무 낮았다.

    celery 버전 4.4

    python 3.6

    이를 해결하기 위해 했던 것들

    Celery 4.4 공식문서에서 확인할 수 있듯이, celery를 실행할 때 concurrency가 설정되어 있지 않으면, machine의 CPU 갯수로 자동 셋팅이 된다.

    The number of worker processes/threads can be changed using the --concurrency argument and defaults to the number of CPUs available on the machine. (Celery 4.4 공식 docs)

    기존 cpu 갯수가 2개 였기 때문에 concurrency가 2였고, 따라서 이를 적절히 높여가면서 부하 테스트를 하면서 최적의 concurrency를 찾게 위해 노력하였다.

    그렇게 테스트를 하던 도중, cpu 연산이 약간 적은 특정 celery task에서는 concurrency를 대략 10보다 크게 해도 concurrency가 10일 때와 task 처리량이 같고 cpu 사용량도 늘지 않는 것을 확인할 수 있었다.

    공식 문서에서도 이를 다음과 같이 다루고 있다.

    More pool processes are usually better, but there’s a cut-off point where adding more pool processes affects performance in negative ways. There’s even some evidence to support that having multiple worker instances running, may perform better than having a single worker. For example 3 workers with 10 pool processes each. (Celery 4.4 공식 docs)

    공식 문서에서 말하는 대로, "celery multi"를 이용해 여러개의 worker와 높은 concurrency를 이용해서 테스트를 하여 기존보다 훨씬 높은 처리량과 cpu 사용량을 얻어낼 수 있었다.

    실제로 적용할 때 마주친 문제

    해당 celery worker는 docker container 내부에서 실행하고 있었다. celery에서 여러개의 worker를 한번에 실행하는 "celery multi"에 몇가지 문제가 생기게 되었다.(엄밀히 말하면 celery 자체의 문제는 아니지만 어쨌든)

    1. celery multi는 background에서만 동작하기 때문에 celery multi를 실행하고 나면 docker container가 바로 꺼진다. 그래서 container를 유지하기 위해서는 쉘 스크립트에서 while true;sleep(3) 이런식으로 켜주거나, 혹은 t개의 worker를 실행하기 위해서 t-1개의 worker를 celery multi로 실행하고 나머지 1개의 worker를 celery에서 foreground로 실행을 해주어야 한다.

    2. (참고한 링크 - StackOverflow 두번째 답변에 Risk들)어떻게어떻게 실행을 했다고 해도, 나중에 worker에 뭔가 문제가 생겼을 때 빠른 트러블슈팅을 위해서는 docker logs 명령어로 worker들이 출력하는 로그들을 빠르게 볼 수 있는 것이 유리할 것이다. tail 명령어를 통해 이를 달성할 수 있지만 background worker가 예기치 못한 사유로 죽었을 때 docker가 이를 알 수 없고 로그도 남지 않을 것이다.

    3. 이건 2번의 링크에도 있지만 테스트를 하면서 발견한 것인데, celery background worker가 동작할 때 pidfile이 생긴다. 모종의 사유로 docker stop;docker start를 하거나 docker restart를 할 때 해당 pidfile을 지워주지 않으면, "PIDfile found, celery is already running?" 에러를 뱉으면서 background worker가 다시 뜨지 않게 된다. 이를 해결하려면 도커 컨테이너 내부로 들어가(docker exec -it .. /bin/bash) 해당 pidfile을 삭제하거나, 윗글 링크에서 볼 수 있듯이 docker를 실행할 때 --rm flag를 주어서 docker stop될때 container를 지우면 될듯하다.

    이 문제들을 어떻게 효과적으로 해결할 수 있을까?

    Supervisord

    공식 홈페이지 

    Supervisor is a client/server system that allows its users to monitor and control a number of processes on UNIX-like operating systems.

    한 줄로 요약하면, UNIX like 운영체제에서 많은 프로세스들을 관리해주는 시스템이다. celery multi를 사용하지 않고, celery worker 한개를 실행하는 프로세스를 여러개 실행해서 supervisor로 관리하려고 한다.

    supervisor configuration file인 "supervisord.conf" 파일을 설정할 때 여러 keyword를 이용해서 1, 2, 3 문제를 효과적으로 해결할 수 있었다. 아래 keyword는 모두 supervisord 홈페이지에서 확인할 수 있다.

    1. numprocs

    Supervisor will start as many instances of this program as named by numprocs.

    Celery worker의 갯수만큼 numprocs를 설정해주면 된다. 환경 변수로 worker 갯수를 가져와서 셋팅을 해주었다.

     

    2-1. autorestart

    Specifies if supervisord should automatically restart a process if it exits when it is in the RUNNING state.

    "autorestart = true"

     

    2-2. stdout_logfile, stderr_logfile, stdout_logfile_maxbytes, stderr_logfile_maxbytes

    stdout_logfile=/dev/stdout

    stdout_logfile_maxbytes=0 를 설정해서 docker logs에서 확인할 수 있도록 한다.

    stderr도 마찬가지이다.

     

    3. pidfile은 설정에 있지만 docker restart를 할 때 pidfile 관련 에러가 나지 않는다.

     

    읽으면 좋을 글

    https://docs.docker.com/config/containers/multi-service_container/ - Docker 공식 docs(Run multiple services in a container)

     

    마지막으로. 코드 리뷰 해주신 친절하신 ㅁㅁ님께 큰 감사를 드립니다.

    오늘 글은 여기서 끝!

    댓글

Designed by Tistory.