从celery rabbitmq with docker-compose 引出对容器、依赖注入、TDD的感悟

时间:2021-07-25 05:58:14

用docker配置项目管理系统taiga的时候,不是我一个人遇到这个问题。https://github.com/douglasmiranda/docker-taiga/issues/5

问题描述:

用docker-compose启动celery_worker和rabbitmq,但是celery_worker 连不上 rabbitmq

celeryworker_1 | [2017-12-06 07:56:36,539: ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@rabbit1:5672//: [Errno -2] Name or service not known.
celeryworker_1 | Trying again in 4.00 seconds...

创建celery的地方用官网的

app = Celery(backend='amqp', broker='amqp://')

是不行的。

改成正式点的:

app = Celery(backend='amqp', broker='amqp://guest:guest@localhost:5672/')

也是不行的,不论用localhost。docker-compose 里 给 rabbitmq 设置的  hostname 都是不行的

成功尝试一:rabbitmq在容器中,celery worker 和 发起异步任务在host

遇到问题1,首先缩小问题。分步尝试,首先尝试只用容器启动rabbitmq。然后和传统方式一样,在host手工启动celery worker。

成功。

问题缩小到celery_worker容器化的问题。

稍微靠谱点的解法是

1先手工创建1个docker bridge

2 用docker命令行分别启动rabbitmq和celery worker

3 手工把celery worker 和 rabbitmq 添加进bridge。

据说可行,但是我就是要用docker-compose的啊,这样太丑陋了。

但至少说明有可能成功(不是celery本身bug之类)

成功尝试二、rabbitmq 和celeryworker都在容器中,celery用host的ip地址连接rabbitmq。

异步发起者可以在容器中,也可以在host中

docker中只运行rabbitmq,暴露5672 和15672端口,在本地host安装celery 起celery worker 是没问题的

但是如果想把celery worker也放进容器里,就出问题了。总是提示连不上amqp。

用了各种方法,最终把rabbit5672暴露,然后把HOST主机IP灌进celery,搞定

HOST_IP = '192.168.239.129' # ip of host(run docker-compose) 
app = Celery(backend = 'rpc://', broker = 'amqp://guest:guest@{0}:5672/'.format(HOST_IP))

额外的小技巧:

ubuntu查看 ip :

ip addr show

成功尝试三、rabbitmq 和celeryworker都在容器中,celery用docker-compose.yml中的service_name连接

异步发起者只能在容器中

20171207隔了一天,终于发现其实不需要HOST_IP,可以用名字连接的!

只不过名字被docker、rabbitmq给来回误导了

先说答案:celery里要用docker-compose.yml里的这个名字,即启动rabbitmq镜像的那个service的名字(而不是他的hostname,我把它注释掉了,太坑人):

从celery rabbitmq with docker-compose 引出对容器、依赖注入、TDD的感悟

从celery rabbitmq with docker-compose 引出对容器、依赖注入、TDD的感悟

这样搞的话,比用HOST_IP不好的地方在于,没法从host发起异步任务(返回不了结果)。只能从docker-compose启动容器,在里面发起异步任务。

好处是5672端口不用暴露出来了。docker-compose内网和外部完全隔离,外面感觉不到rabbitmq存在(当然我保留了暴露15672监控端口,其实也可以不暴露)

——其实postgres的问题也是一样,用Adminer连接的时候,Server处填的也是docker-composer.yml里的service名字。

http://www.cnblogs.com/xuanmanstein/p/7742647.html

结论

解决方案不细说了,放github上了

https://github.com/xuqinghan/celery-with-docker-compose#celery-with-docker-compose

心得:

1 遇到问题,要缩小、后退、分解。

 像锯木头锯不动了,要往后撤,然后稍微换换方向,角度,再用巧力,不要用蛮力:

1从部署taiga的event模块遇到celery_worker连接不上;

2退到用docker-compose运行celery官网的a+b小函数的demo 还是连不上;

3再继续后退:只用docker-compose部署rabbitmq。手工启动celery和发起异步任务,成功。

4在3成功的基础上小步前进。而此时问题已经缩小、聚焦到足够小,再尝试几种解决方案:localhost   127.0.0.1   hostname     bridge方式docker0网络

5搞定、总结。成为自己的技能点,到处复用。

2 不要有洁癖,

不追求rabbitmq不暴露任何端口,只服务于docker-compose的网络(不是docker0) ,在host里暴露端口不丢人;

在开发机上,只在调试当前工程时启动当前的docker-compose。在生产环境还是1机1个组的。不追求多工程在一个机器上运行。

不追求一次就实现全都容器化,和写代码一样,先写好单次执行的逻辑,再套外层的for循环。

——注意到了这些点,问题半天不到就搞定了;死钻牛角尖,可能N天都无解。

3 不能有0 or 100分这种想法。

不能急躁,也不能浮躁(想一次就得100分);

也不能因为想太多,把问题看成铁板1块,就畏惧困难(直接交白卷得0分)。

——做工程,都是1分、1分地得分,也是1分 1分地扣分的。 这和合同收付款、各种节点是完全不同的!不要被后者的干扰带乱了自己的工作节奏。

做工程是类似持仓、空仓的长期过程,而不是啪啪啪不断交易的各种精彩瞬间。(如果永远处在赶工、保/抢节点的状态,要么是自己工作能力不行,要么就是项目管理SB,没有轻重缓急)

4做工程的人,在工程上做任何事每次出手,都要有章有法:有高度的目的论,且目的又和普通人不同:

1 贯彻目的和意图导向的思考。在意从需求、系统用例、到组件名、类名、子过程名、直到变量名的命名,名不正则言不顺,要强迫自己和别人(Не жалей ни себя, ни врагов.“不要吝惜自己和敌人”——《斯拉夫女人的告别》白俄版)思考目的性、价值和重要性排序。并且在每一天、每一句的开发中不断加深学习、打磨、养成习惯;

2 能每时每刻聚焦特定问题,能暂时忽略其他问题和要求,有“置XX之敌于不顾”,“攻其一点,不及其余”的勇气和能耐。这是建立在对能力的自信心(“打哪个,哪个就剩不下”,所以我有资格挑着打)基础之上的纯粹和纯净的心理状态。

接触新人、业余选手、非技术人员的最大感觉就是:

在1上,想的太简单,做的太随意(用我的话说,像个孩子);

在2上,一次想得太,想得太多(0分VS100分 来回地跳变,还是像个什么都想要的孩子)。

——包括之前的自己,也是这样。还是因为功力不够+心浮气躁=棒槌。

因为和他们视角不同(甚至完全不同),所以自己首先不要生气,配合别人的任务区时可以忍着点,尽力配合;

但只要进入自己的责任区,就是要火力全开。如果气着对方也不要在意(负责这片的人是我,我也没想故意气谁,所以也无所谓)。

和战斗机一样,包线上各有优势区,但进入大喷9(Spitfire LF IX)的优势区,就不要怪Hispano 炮狠了。

技术方面的感受:容器和镜像不是万能的。不是什么都适合打包进镜像。

celery和django都相继不再在dockerhub发布官方镜像。因为这俩在用的时候,大家都是定制、引用之后才用的。

并不是这俩和docker决裂了,只是不适合打包进dockerfile罢了。

对开发人员来说,什么适合塞进dockerfile呢?配环境的步骤+配出来的一致性,隔离的、纯净的运行环境。塞配置文件的位置。

其实dockerfile有点像测试框架。而源代码和配置文件,类似mock。

所以,这符合我下意识的做法:我喜欢把源代码、和各种配置文件,数据库文件都放在外面, 用-v 或者volumns 在容器启动时挂进去,而不是写在dockerfile里(git clone 源代码,具体配置参数),直接build成image。

这种套路,非常类似于依赖注入,把repository,service塞进component的过程。

此外,这样好写测试用例啊(不依赖实际连接web和数据库)!

任何单元测试的运行,都不能依赖真实的web和数据库访问。

否则因为耗时间或者环境难配,不得不每次注释掉一部分用例,现在想想,那还测试个屁啊!

写程序的精髓在于识别和感受到  复杂性+变化性   VS  繁琐+稳定    ,然后不断分离,隔离,让不同变化速率,膨胀系数的材料,高度地解耦。

——写程序的门道无非那几条,但是知道这些道理是一回事,理解认同这些道理是另一回事, 而实际工作中能在各种场合把这些招给用出来,用活了,又是另一回事了。

和武术、修行一样,知易行难。“三岁小儿道得,八十老翁行不得”。

需要勤学苦练,不是蛮干(grasshopper里不写代码,一片一片地电池)

从各种角度、各种道路、多次实践,去领悟相同的道理。

然后就容易全面开窍和到处突破了。

之前了解、学TDD半天,但是就是用不好;现在用了一段时间docker,反而容易找到依赖注入的感觉。

自己写的程序,有哪些是框架性的,类似dockefile的;有哪些是频繁改动的,类似源代码、配置文件内容的,要层次分明,分离,然后各自获得*;

这个分离:就是意图与实现、虚VS实;部队 VS 分队,测试用例VS外部接口的mock。

关注点分离了,就各自聚焦了,也*了。分得开,责任边界清晰,才联合得起来。