奇怪的Docker + Celery Bug

时间:2016-10-19 19:41:05

标签: python docker celery

我正在尝试在docker容器中运行芹菜,并且由于某种原因它永远不会更新。每当我在tasks.py中添加新函数或更新现有函数时,即使我重新启动容器,它也永远不会注册芹菜。

这是我的dockerfile

# start with a base image
FROM python:3.4-slim

ENV REDIS_IP 1.1.1.111
ENV REDIS_PORT 6379
ENV REDIS_DB 0

# install dependencies
RUN apt-get update && apt-get install -y \
apt-utils \
nginx \
supervisor \
python3-pip \
&& rm -rf /var/lib/apt/lists/*

RUN echo "America/New_York" > /etc/timezone; dpkg-reconfigure -f noninteractive tzdata

# update working directories
ADD ./app /app
ADD ./config /config
ADD requirements.txt /

# install dependencies
RUN pip install --upgrade pip
RUN pip3 install -r requirements.txt

# setup config
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
RUN rm /etc/nginx/sites-enabled/default

RUN ln -s /config/nginx.conf /etc/nginx/sites-enabled/
RUN ln -s /config/supervisor.conf /etc/supervisor/conf.d/

EXPOSE 80
CMD ["supervisord", "-n"]

然后是我的supervisor.conf

[program:app]
command = uwsgi --ini /config/app.ini
autostart=true
autorestart=true

[program:nginx]
command = service nginx restart
autostart=true
autorestart=true

[program:celery]
directory = /app
command = celery -A tasks.celery worker -P eventlet -c 1000
autostart=true
autorestart=true

我的tasks.py

import os
from celery import Celery
from app import app as flask_app

def make_celery(app):
    celery = Celery(app.import_name, backend='redis://{0}:{1}/{2}'.format(os.environ['REDIS_IP'],os.environ['REDIS_PORT'],os.environ['REDIS_DB']),
                    broker='redis://{0}:{1}/{2}'.format(os.environ['REDIS_IP'],os.environ['REDIS_PORT'],os.environ['REDIS_DB']))
    celery.conf.update(
        CELERY_ENABLE_UTC=True,
        CELERY_TIMEZONE='America/New_York'
    )
    TaskBase = celery.Task
    class ContextTask(TaskBase):
        abstract = True
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return TaskBase.__call__(self, *args, **kwargs)
    celery.Task = ContextTask
    return celery

celery = make_celery(flask_app)

@celery.task()
def add_together(a, b):
    return a+ b

@celery.task()
def multiply(a,b)
    return a*b

由于某种原因:

  • 我有21名工人注册,multiply从未注册,
  • 当我对add_together进行更改时,即使重新启动容器也不会注册。

enter image description here

我正在用:

启动我的容器
docker build --rm -t myapp .
docker run -d -p 88:80 -v $(pwd)/app:/app --name=myapp myapp

并重启:

docker restart myapp

我也试过

docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)

然后重新重建应用程序。什么都没有帮助。任何想法都会非常感激。

3 个答案:

答案 0 :(得分:1)

我认为这可能是问题所在:

@celery.task()  # <-- you shouldn't call this decorator directly
def add_together(a, b):
    return a+ b

尝试将其更改为:

@celery.task
def add_together(a, b):
    return a+ b

原因:只需查看source code of decorator task

def task(self, *args, **opts):
    """Creates new task class from any callable."""

    # ... handling named options

    if len(args) == 1:
        if callable(args[0]):
            return inner_create_task_cls(**opts)(*args)
        raise TypeError('argument 1 to @task() must be a callable')
    if args:
        raise TypeError(
            '@task() takes exactly 1 argument ({0} given)'.format(
                sum([len(args), len(opts)])))
    return inner_create_task_cls(**opts)

它接受的唯一未命名的参数是要装饰的函数。否则会引发TypeError并被supervisord吞并,因为您没有配置the loglevel to debug

答案 1 :(得分:1)

我无法在我的设置上重现您的问题。我在Celery文档中创建了一个简单的Flask应用程序。

您可以尝试一些命令来仔细检查您的设置吗?

在myapp容器中打开一个shell(它必须已经在运行):

docker exec -t -i myapp /bin/bash

然后:

cd /app
celery -A tasks.celery status
celery -A tasks.celery inspect registered

新任务是否显示?

我认为你可能有其他celery实例连接到同一个redis服务器,这就是你有21个实例的原因。但我猜。

您也可以尝试使用独立的redis容器。

docker run --name myredis -d redis

在调试模式下执行celery,使用:

docker run --rm -t -i -v $(pwd)/app:/app -e REDIS_IP=myredis -u nobody -w /app --link myredis myapp celery -A tasks.celery worker -P eventlet -c 1000 -l debug

现在有任务吗?它应该只列出芹菜启动横幅消息。

我认为您的图片没有问题,但您可以仔细检查一下:

docker exec myapp /bin/bash -c "cat /app/tasks.py"

我不认为这是问题,因为您将/ app复制到图像中,当您运行容器时,您使用本地目录再次映射/ app。您是从构建容器的同一目录运行容器吗?

-v $(pwd)/ app:/ app将使用当前目录./app覆盖容器中的/ app。你真的需要这个吗?如果没有-v部分,你会得到相同的结果吗?

我希望找出错误是有帮助的。

答案 2 :(得分:1)

我制作了代码的工作回购。它存在here

我改变的事情:

  • 结肠错字(查看您的multiply def)
  • 不调用装饰器
  • 常规代码清理
  • 使用单个redis URI
  • 我的测试中的某些目录导航

我没有专注于监督部分。