多个Docker容器和芹菜

Tzo*_*ker 9 celery docker

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

  1. 处理来自客户端的传入请求的Web服务器.
  2. 分析模块,为用户提供一些建议.

我们决定保持这些模块完全独立,并将它们移动到不同的docker容器中.当来自用户的查询到达Web服务器时,它会向分析模块发送另一个查询以获取建议.

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

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

  1. 在Web服务器上排队的任务,在Web服务器上执行(例如,处理上传的图像)
  2. 在网络服务器上排队的任务,在分析模块执行(例如,计算新用户的建议)
  3. 任务在分析模块中排队并在那里执行(例如,定期更新)

到目前为止,我在这里看到了使用Celery的3个相当奇怪的可能性:

I.芹菜在单独的容器中,做一切

  1. 将Celery移动到单独的docker容器中.
  2. 提供来自Web服务器和分析的所有必要包以执行任务.
  3. 与其他容器共享任务代码(或在Web服务器和分析中声明虚拟任务)

这样,我们就失去了隔离,因为Celery容器和其他容器共享了这些功能.

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

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

这样,我们从拥有经纪人中受益,但所有繁重的计算都来自Celery工作人员.

III.每个容器中分开芹菜

  1. 在Web服务器和分析模块中运行Celery.
  2. 将(分析任务的)虚拟任务声明添加到Web服务器.
  3. 添加2个任务队列,一个用于Web服务器,一个用于分析.

这样,在web服务器上安排的任务可以在分析模块中执行.但是,仍然需要跨容器共享任务代码或使用虚拟任务,此外,还需要在每个容器中运行芹菜工作者.

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

Ita*_*ayB 22

首先,让我们澄清一下芹菜库(你得到的pip install或者你的setup.py)和芹菜工人之间的区别- 这是从经纪人那里将任务排队并处理它们的实际过程.当然,您可能希望拥有多个工作人员 /流程(例如,将不同的任务分配给不同的工作人员).

比方说你有两个任务:calculate_recommendations_taskperiodic_update_task你想在一个单独的工作,即运行它们recommendation_workerperiodic_worker.另一个过程是每隔x小时将celery beat其排入periodic_update_task经纪人.

另外,假设您使用瓶子实现了简单的Web服务器.

我假设您也想使用芹菜经纪人和后端与码头工人,我会选择芹菜的推荐用法 - 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
Run Code Online (Sandbox Code Playgroud)

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

RecommendationDockerfile:

FROM python:2.7-wheezy
RUN pip install celery
COPY tasks_src_code..
Run Code Online (Sandbox Code Playgroud)

WebDockerfile:

FROM python:2.7-wheezy
RUN pip install celery
RUN pip install bottle 
COPY web_src_code..
Run Code Online (Sandbox Code Playgroud)

其他图像(rabbitmq:3-managementlibrary/redis可从码头工人枢纽,当你运行,他们将被自动拉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
Run Code Online (Sandbox Code Playgroud)

最后一个文件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']
Run Code Online (Sandbox Code Playgroud)

  • 是的,没错。你说你想把服务器和工人分开..当然你也可以分开他们 (2认同)