多个Docker容器和芹菜
我们现在有如下的项目结构:
- 处理来自客户端的传入请求的Web服务器。
- 分析模块,向用户提供一些build议。
我们决定保持这些模块完全独立,并将它们移动到不同的docker集装箱。 当用户查询到达Web服务器时,它会向分析模块发送另一个查询以获取build议。
为了使build议保持一致,我们需要定期进行一些背景计算,例如,当新用户在我们的系统中注册时。 另外一些后台任务纯粹与networking服务器逻辑连接。 为此,我们决定使用分布式任务队列,例如Celery。
有以下可能的任务创build和执行情况:
- 任务在networking服务器上入队,在networking服务器上执行(例如,处理上传的图像)
- 在networking服务器上排队的任务,在分析模块中执行(例如,计算新用户的build议)
- 任务在分析模块中排队并在该处执行(例如,定期更新)
到目前为止,我在这里看到了使用Celery的三种可能性:
一芹菜在单独的容器,并做了一切
- 将芹菜移到单独的docker集装箱。
- 提供来自networking服务器和分析的所有必要软件包来执行任务。
- 与其他容器共享任务代码(或者在Web服务器和分析中声明虚拟任务)
这样,我们松散的隔离,由芹菜容器和其他容器共享的function。
II。 芹菜在单独的容器,并做得less得多
和我一样 ,但是现在的任务只是对Web服务器和分析模块的请求,它们在那里被asynchronous处理,结果在任务内部轮询,直到准备就绪。
通过这种方式,我们可以从经纪人身上获得收益,但所有繁重的计算都是从芹菜工人那里转移来的。
III。 独立芹菜在每个容器
- 在networking服务器和分析模块中运行Celery。
- 向networking服务器添加虚拟任务声明(分析任务)。
- 添加2个任务队列,一个用于Web服务器,一个用于分析。
这样,Web服务器上的任务就可以在分析模块中执行。 但是,仍然需要在容器上共享任务代码或使用虚拟任务,另外还需要在每个容器中运行芹菜工作人员。
什么是最好的方法来做到这一点,或逻辑应该完全改变,例如,一切都在一个容器内?
首先,阐明一下芹菜库(你用pip install
或者setup.py
)和芹菜工作者之间的区别 – 芹菜工人是从经纪人出发任务并处理它们的实际过程。 当然,您可能希望有多个工作人员 /stream程(例如,将不同的任务分配给不同的工作人员)。
假设你有两个任务: calculate_recommendations_task
和periodic_update_task
并且你想在一个单独的worker上运行它们,比如: recommendation_worker
和periodic_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_image
和web_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-management
& library/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']