多个Docker容器和芹菜

时间:2017-08-24 13:33:11

标签: docker celery

我们现在有以下项目结构:

  1. 处理来自客户端的传入请求的Web服务器。
  2. 分析模块,为用户提供一些建议。
  3. 我们决定保持这些模块完全独立,并将它们移动到不同的docker容器中。当来自用户的查询到达Web服务器时,它会向分析模块发送另一个查询以获取建议。

    为了使建议保持一致,我们需要定期进行一些背景计算,例如,当新用户在我们的系统中注册时。此外,一些后台任务完全与Web服务器逻辑相关联。为此,我们决定使用分布式任务队列,例如Celery。

    有以下可能的任务创建和执行方案:

    1. 在网络服务器上排队的任务,在网络服务器上执行(例如,处理上传的图像)
    2. 在网络服务器上排队的任务,在分析模块执行(例如,计算新用户的推荐)
    3. 任务在分析模块中排队并在那里执行(例如,定期更新)
    4. 到目前为止,我看到在这里使用Celery有三种奇怪的可能性:

      予。芹菜在单独的容器中,并做一切

      1. 将Celery移至单独的泊坞窗容器中。
      2. 提供来自Web服务器和分析的所有必要软件包以执行任务。
      3. 与其他容器共享任务代码(或在Web服务器和分析中声明虚拟任务)
      4. 这样,我们就失去了隔离,因为Celery容器和其他容器共享了这些功能。

        II。芹菜在单独的容器中并且做得少得多

        相同,但现在任务只是对Web服务器和分析模块的请求,这些请求在那里异步处理,结果在任务内部轮询,直到准备就绪。

        通过这种方式,我们从拥有经纪人中获益,但所有繁重的计算都是从芹菜工人那里获得的。

        III。每个容器中分开芹菜

        1. 在Web服务器和分析模块中运行Celery。
        2. 将虚拟任务声明(分析任务)添加到Web服务器。
        3. 添加2个任务队列,一个用于Web服务器,一个用于分析。
        4. 这样,在Web服务器上安排的任务可以在分析模块中执行。但是,仍然需要跨容器共享任务代码或使用虚拟任务,此外,还需要在每个容器中运行芹菜工作者。

          执行此操作的最佳方法是什么,或者逻辑应该完全更改,例如,将所有内容移到一个容器中?

1 个答案:

答案 0 :(得分:18)

首先,让我们澄清芹菜库(您使用pip installsetup.py)和芹菜工作者之间的区别 - 这是实现任务的实际过程来自经纪人并处理它们。当然,您可能希望拥有多个 worker / processes(例如,将不同的任务分离给不同的工作人员)。

假设您有两项任务:calculate_recommendations_taskperiodic_update_task,并且您希望在另一名工作人员recommendation_workerperiodic_worker上运行这些任务。 另一个进程是celery beat,它每隔x小时将periodic_update_task排入代理。

此外,假设您使用bottle实现了简单的Web服务器。

我假设您想使用芹菜经纪人&还有docker的后端,我会选择芹菜的推荐用法 - RabbitMQ作为经纪人,Redis作为后端。

所以现在我们有6个容器,我将它们写在docker-compose.yml

version: '2'
services:
  rabbit:
    image: rabbitmq:3-management
    ports:
      - "15672:15672"
      - "5672:5672"
    environment:
      - RABBITMQ_DEFAULT_VHOST=vhost
      - RABBITMQ_DEFAULT_USER=guest
      - RABBITMQ_DEFAULT_PASS=guest
  redis:
    image: library/redis
    command: redis-server /usr/local/etc/redis/redis.conf
    expose:
      - "6379"
    ports:
      - "6379:6379"
  recommendation_worker:
    image: recommendation_image
    command: celery worker -A recommendation.celeryapp:app -l info -Q recommendation_worker -c 1 -n recommendation_worker@%h -Ofair
  periodic_worker:
    image: recommendation_image
    command: celery worker -A recommendation.celeryapp:app -l info -Q periodic_worker -c 1 -n periodic_worker@%h -Ofair
  beat:
    image: recommendation_image
    command: <not sure>
  web:
    image: web_image
    command: python web_server.py

构建recommendation_imageweb_image的dockerfiles应安装芹菜。只有recommendation_image应该有任务代码,因为工作人员将处理这些任务:

<强> RecommendationDockerfile:

FROM python:2.7-wheezy
RUN pip install celery
COPY tasks_src_code..

WebDockerfile:

FROM python:2.7-wheezy
RUN pip install celery
RUN pip install bottle 
COPY web_src_code..

其他图片(rabbitmq:3-management&amp; library/redis可从docker hub获取,当您运行docker-compose up时,它们会自动提取。

现在就是这样:在您的Web服务器中,您可以通过字符串名称触发celery任务,并通过task-id拉取结果(不共享代码) web_server.py

import bottle
from celery import Celery
rabbit_path = 'amqp://guest:guest@rabbit:5672/vhost'
celeryapp = Celery('recommendation', broker=rabbit_path)
celeryapp.config_from_object('config.celeryconfig')

@app.route('/trigger_task', method='POST')
def trigger_task():
    r = celeryapp.send_task('calculate_recommendations_task', args=(1, 2, 3))
    return r.id

@app.route('/trigger_task_res', method='GET')
def trigger_task_res():
    task_id = request.query['task_id']
    result = celery.result.AsyncResult(task_id, app=celeryapp)
    if result.ready():
        return result.get()
    return result.state

上一个文件 config.celeryconfig.py

CELERY_ROUTES = {
    'calculate_recommendations_task': {
        'exchange': 'recommendation_worker',
        'exchange_type': 'direct',
        'routing_key': 'recommendation_worker'
    }
}
CELERY_ACCEPT_CONTENT = ['pickle', 'json', 'msgpack', 'yaml']