多个Docker容器和芹菜

我们现在有如下的项目结构:

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

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

为了使build议保持一致,我们需要定期进行一些背景计算,例如,当新用户在我们的系统中注册时。 另外一些后台任务纯粹与networking服务器逻辑连接。 为此,我们决定使用分布式任务队列,例如Celery。

有以下可能的任务创build和执行情况:

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

到目前为止,我在这里看到了使用Celery的三种可能性:

一芹菜在单独的容器,并做了一切

  1. 将芹菜移到单独的docker集装箱。
  2. 提供来自networking服务器和分析的所有必要软件包来执行任务。
  3. 与其他容器共享任务代码(或者在Web服务器和分析中声明虚拟任务)

这样,我们松散的隔离,由芹菜容器和其他容器共享的function。

II。 芹菜在单独的容器,并做得less得多

我一样 ,但是现在的任务只是对Web服务器和分析模块的请求,它们在那里被asynchronous处理,结果在任务内部轮询,直到准备就绪。

通过这种方式,我们可以从经纪人身上获得收益,但所有繁重的计算都是从芹菜工人那里转移来的。

III。 独立芹菜在每个容器

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

这样,Web服务器上的任务就可以在分析模块中执行。 但是,仍然需要在容器上共享任务代码或使用虚拟任务,另外还需要在每个容器中运行芹菜工作人员。

什么是最好的方法来做到这一点,或逻辑应该完全改变,例如,一切都在一个容器内?

首先,阐明一下芹菜库(你用pip install或者setup.py )和芹菜工作者之间的区别 – 芹菜工人是从经纪人出发任务并处理它们的实际过程。 当然,您可能希望有多个工作人员 /stream程(例如,将不同的任务分配给不同的工作人员)。

假设你有两个任务: calculate_recommendations_taskperiodic_update_task并且你想在一个单独的worker上运行它们,比如: recommendation_workerperiodic_worker 。 另一个过程将是celery beat ,其中每隔x个小时将periodic_update_task排入经纪人。

另外,让我们说你有一个简单的Web服务器瓶实施。

我假设你也想用docker来使用celery broker&backend,我会select推荐使用芹菜–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 

这两个dockerfiles,其中build立的recommendation_imageweb_image应该安装芹菜 。 只有recommendation_image应该有任务代码,因为工作人员将要处理这些任务:

RecommendationDockerfile:

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

WebDockerfile:

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

其他的图像( rabbitmq:3-managementlibrary/redis可以从docker hub获得,当你运行docker-compose up时它们会被自动拉出)。

现在是这样的:在你的web服务器中,你可以通过string名称触发芹菜任务,并通过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']