只有物理能打败魔法

 

 

 

1

 

 

有句话,叫「只有魔法能打败魔法」。

 

但我现在觉得,「只有物理能打败魔法」。

 

 

我最近直播时发现一个问题,几个平台同时直播,视频号还好,因为老粉丝居多,都给面子,而抖音那边是平台给的流量,我发现什么都能杠起来。

 

比如我带头巾这个事,在直播间粉丝问我最多的就是,是不是秃头了,才带头巾?

 

我给他们看看我头发,发现我头发还可以,视频号就不问了。

 

但是,抖音不一样,一看你有头发,就开始更加阴阳怪气。

 

「你是不是崇洋媚外,所以戴头巾?」

 

 

我开始的策略是用魔法打败魔法,他说我崇洋媚外,我就说,我戴头巾,是我们华北农民的光荣传统,你攻击我崇洋媚外,你是不是不爱国?

 

这种魔法打败魔法的战略,开始有用。

 

 

但是后来发现,他就缠上了你,给你深度讨论头巾的问题,而你要靠辩论打赢他几乎不可能。

 

后来我学会了,我直接告诉他,我戴头巾,是因为我素质低,我是痞子。

 

一旦我这样说了以后,他反而开始往回杠了,也不能这么说吧,戴头巾的也不都是痞子。

 

你会发现,一旦你这样说了以后,喷子都开始讲逻辑了。

 

喷子一旦讲逻辑,气势整个就弱掉了。

 

 

所以我发现,你不可能用魔法打败魔法,你只能用物理打败魔法。

 

用魔法打败魔法,他就会跟你深度讨论魔法问题,你诡辩,他比你更诡辩。

 

世界的道理,就是不要绕弯,直接硬怼。

 

 

 

2

 

 

我看日本剑圣宫本武藏的传记,他一生跟其他剑道高手挑战,无一败绩。

 

他与人决斗,有一个重要的原则,就是绝不用比对手短的武器。

 

他最著名的一次决斗,是与佐佐木小次郎在岩流岛决斗,他用的武器,是一只船桨。

 

而宫本武藏本人,是一个身高大约175的日本男性,这样的身高,在当时的日本武士中,属于绝对碾压。

 

175的身高,加上从不用比对手短的武器,就是一代剑圣的剑道奥义。

 

 

这说明一个道理,男人绝对不能比别人短。

 

不是,划掉,是一寸长,一寸强。

 

成功的秘诀,不是搞那些弯弯绕,不要搞技术性花活,是能用强的时候,一定要用强。

 

 

我以前看过一本漫画书,《塔希里亚故事集》。

 

其中有一个故事,是大魔法师艾克萨罗斯,他是一个天才法师。

 

同时他也是一个兽人,而兽人被认为不可能成为魔法师。

 

 

但是,艾克萨罗斯,却成了有史以来最厉害的魔法师之一。

 

 

实际上,艾克萨罗斯,天生力大无穷,擅长裂骨技。

 

在他法力没有大成以前,无数跟他决斗的魔法师,往往还没有召唤出魔法,就被他用超强的力量掐断了脖子。

 

 

 

3

 

 

《权力的游戏》里,小指头知道瑟曦跟詹姆偷情,想以此威胁瑟曦。

 

小指头自命不凡:Knowledge is Power(知识就是力量),暗喻瑟曦没有脑子。

 

瑟曦马上让四个御林铁卫抓住他,拿剑抵住小指头的喉咙。

 

瑟曦放开他,告诉他一个真理:Power is power(力量才是力量)。

 

《权力的游戏》里小指头的一生,闪转腾挪,从一个边缘小贵族,几乎成为维斯特洛的大诸侯,但是他的一个致命伤就是,他几乎没有掌握过真正的力量,他只能在几个大家族的缝隙间纵横捭阖。

 

而瑟曦则是另一个反面,她无脑而有野心,这样的人本该在第一集死掉,但是却几乎活到最后。

 

一个原因就是,瑟曦虽然不够聪明,但她知道,要么自己掌握力量,要么依附力量。

 

小指头的闪转腾挪,相比之下,就有点螺蛳壳里做道场的意味,一生奔忙,也不过是为他人做嫁衣裳。

 

 

知识就是力量这种话,骗过了太多人。

 

其实,力量就是力量,在绝对的力量面前,知识什么也不是。

 

 

 

4

 

我昨天在一个群里说过一句话:

人生中的大部分问题,几乎都可以用「搞钱吧」来解决。

只有物理能打败魔法

世界其实很简单。

 

你要比剑,那你想方设法赢就完事了,至于是你剑比别人长还是比别人短,那不重要,因为对手已经死了。

 

你要决斗,你弄死他就行了,至于怎么弄死的,是魔法弄死还是用手掐死,不重要。

 

你可以在他墓碑上尽情赞美他。

野蛮的兽人法师,艾克萨罗斯的名言是:

 

「那些自诩天才的魔法师们,往往还在吟唱阶段的时候,就被没打算施法的兽人法师用裂骨技直接掐断了脖子。然后这些兽人法师会学习你的成果,再把天才和科学五个字讽刺地刻在你的墓碑上」。

 

 

这就是世界的真相。

赢了,远远比怎么赢更重要。

 

 

但是大多数庸人,都搞错了方向,把希望寄托于别人,或者忽然有了知识分子的自尊和矫情,必须要以某种特殊的方式胜利,必须要技巧性获胜,结果往往就是,输个一塌糊涂。

 

 

 

5

 

 

我自己喜欢做菜,其实我以前做菜,经常失败。

 

后来我发现了一个秘诀,你只要抛弃所谓厨师的自尊,就可以把大部分菜做好吃。

 

什么意思呢?

 

你觉得爆炒要糊锅了,就加一点水,把锅离开炉子;

 

你觉得刀工不好,就慢点切;

 

就这么简单。

 

尽管不能接近你看到的那些大师的水平,但是也不错。

大部分做菜失败的人,他的问题在哪里?

 

就是他作为一个新手,忽然有了高手的自尊、大师的矜持。

 

明明他水平不够,他非要说,人家大师是那样做的,我也要那样做。

 

但是人家练了多少年,他不管。

 

 

这就是辽沈战役,林彪说廖耀湘,六根不净输个干干净净。

 

大敌当前,你还想既要又要还要,你自然输个干干净净。

 

 

 

6

 

 

所谓物理打败魔法,就是要六根清净。

 

什么叫六根清净呢?

 

 

举个栗子,看过张怡宁打球吗?张怡宁打球,就是典型的「六根清净」。

 

张怡宁就是这样,专注,面无表情,似笑不笑,赢球输球几乎波澜不惊。

 

张怡宁私下里,据说她特别开朗,大大咧咧,平常衣服都堆宿舍都不收拾。

 

 

张怡宁这种人,就是「呆若木鸡」。

什么叫呆若木鸡?

 

呆若木鸡的本意,就是两个斗鸡打架,又嚣张,又张牙舞爪的,都是弱鸡。

 

那个呆呆傻傻,跟个木头鸡一样的家伙才是狠角色,天下无敌。

 

 

所谓的呆若木鸡,就是极度沉浸比赛,专注于赢。

大部分高手,也都是「张怡宁」,你需要专注重要的事,专注升职,专注搞钱,要呆若木鸡,不要情绪化。

 

 

比如职场,你做错事,上司痛批臭骂你一顿,没有关系,拿笔把他臭骂你的5分钟总结一个1234,总分总,原因、错误、分析、要点、改进措施,总结成2-3分钟、4-5百字的小文稿,等他骂完,单独汇报。

 

把他的关键用词跟你的理解,掰开揉碎,跟你的错误要点一一对应,然后提出整改意见,然后跟你的上司说,您老是我职场偶像,您看我这样对吗?想请您给指点一下。

 

指点以后,隔三差五,再次找他复盘,下一步指点。

 

打蛇随棍上,不要让他的情绪影响你的情绪,打击也好,赞扬也好,都不重要。

 

呆若木鸡,就是把事件当成机会,经营关系,提高能力,小步快跑,稳步前进,这就是「张怡宁法」。

 

 

比如我赚钱,喜欢说一句话,我把我的一切伤痛,拿来赚钱,那些别人看来痛苦的经历,对我几乎没有影响,这都是我宝贵的营养。

 

这就是,「六根清净,坐上来自己动」。

 

自己勤快点,奔着搞钱,奔着自己爽的方向一路狂奔,其他完全无视。

 

 

这几年,我越来越强调一个道理,不要意识改变,要物理改变,要动起来,思想的改变都是浅层次的,只有肉身改变了,才是真的改变。

 转自:https://mp.weixin.qq.com/s/5hER_iwE54HyFuezbovs7Q

新加坡的防疫经验其实就四个字

2021年6月24日,当新加坡抗疫跨部门工作小组的三位部长,也就是贸工部长颜金勇、财政部长黄循财和卫生部长王乙康联名在媒体发表题为《与冠病共处,如常生活》的文章时,国际社会一片哗然。

 

新加坡的防疫经验其实就四个字

 

彼时,德尔塔病株正攻占世界,大部分国家都处于焦头烂额和严防死守之中。

 

然而话音未落,裕廊渔港就爆发了一波严重的疫情,新加坡政府被迫再度收紧了管控措施。

 

那时的欧美媒体和国内媒体,嘲笑声连成一片。

 

自此之后,疫情的持续爆发似乎再没有影响过新加坡的“共存”决心。

 

仅仅一个月后的8月10日,也就是新加坡国庆日后的第一天,总理李显龙就正式宣布进入“共存预备期”,与之配套的,是一系列逐步开放经济、社交和旅游活动的政策。

 

在这之后,就算因为德尔塔病株的肆虐,新加坡的重症和死亡率在九、十月份创出了疫情以来的新高,也没有阻止这一系列政策实施的步伐。

 

而在ICU使用率逼近九成、医疗系统濒临崩溃的临界点,新加坡更是开始大胆采用了轻症患者居家隔离恢复的措施。

 

接着,就是那次令人印象深刻的电视演讲——10月9日,新加坡总理李显龙发表了题为“为什么新加坡不再清零”的演说,再一次明确了这个国家防疫政策的方向。

 

那么,是什么支撑着新加坡一步步走向“共存”的?

 

答案似乎显而易见。

 

现在回过头再去看2021年6月的那篇文章,里面就已经提到了新加坡防疫政策变迁最核心的变量——

 

接种疫苗。

 

事实上,在2021年8月咬紧牙关进入“预备期”的时候,新加坡单针疫苗接种率已经突破了80%,两剂完全接种率超过70%,其中80岁以上老年人疫苗接种率也超过70%。

 

然而,答案真的这么简单吗?

 

为了解答这一问题,我翻看了很多的资料,包括新加坡卫生部的官网https://www.moh.gov.sg/,上面除了有详尽的数据之外,还有非常多的政策指引和制定这些政策背后的科学依据和论述。

 

此外,博鳌亚洲论坛2022年年会的“科学为盾,战胜疫情”分论坛上,新加坡卫生部长王乙康的发言也让人受益匪浅。

 

最后,中金研究院在本月中发布了一份名为《平衡生命与生计的谨慎突围——新加坡的防疫之道》的研究报告,里面对于新加坡的整个防疫政策变迁有着清晰的分析与论述。

 

先不论我们学得来什么,显然,新加坡的有一些经验,是我们学不来的。

比如高额的财政刺激。仅2021年,新加坡政府就拨款近1,000亿新元抗疫(折合人民币约4700亿人民币),这一金额相当于其一年GDP的21%。你可以想象中国拨出相当于五分之一GDP的金额吗?

 

再比如新加坡经济高度依赖国际商贸,缺乏内循环可行性,所以某种程度上选择共存也是无奈之举。因此这个国家从一开始就是会绞尽脑汁的去想如何走向开放,甚至走向共存。这一点与我们,以及与很多大型发达国家,显然也有着很大的不同。

 

但在查了诸多资料之后,我的另一个感觉是,我们能从新加坡身上学的东西同样很多。

 

新加坡真正的防疫经验,其实用四个字就可以概括,而这四个字,并不是接种疫苗。

 

请容我卖个关子,文章结尾见分晓。

 

1

 

在中金的研究报告中,曾经描述了新加坡在进入共存预备期前所做的三个“转向”,这对于理解新加坡防疫重心的平稳过渡尤为关键。

 

第一个转向是社会观念的转向。

 

防疫重心要转移,首先得让老百姓明白新冠是可以被控制的、被治疗的,尤其是对于已经进行了疫苗接种的人来说。

打开新加坡政府的相关官网,以及新加坡的主流媒体网站,过去一年多以来铺天盖地的都是针对新冠病情的分析、疫苗接种的重要性以及如何做好居家隔离的信息。

 

这些内容并不是纯粹的主观表述,而是辅以了大量的有详细来源的数据和研究佐证。

 

细节是魔鬼。

 

新加坡政府除了每天向公众发布疫情数据外,还会及时公布政府对策和病床使用情况,使民众得知疫情进展、医疗资源余量和政府应对措施。

 

此外,就像新加坡卫生部长王乙康在博鳌论坛上说的那样,“当新的研究数据出炉后,如果防疫部门认为此前的抗疫政策“不恰当”,就会调整疫情应对的举措,并且向公众说明政策转变的理由。当新加坡政府向民众说明‘和病毒共存’的策略时,就要明确疫苗覆盖率高、重症监护床位的数量充足等条件已经得到满足。”

 

新加坡的案例说明,通过深入推广普及关于新冠的科学认识,以及由政府官员和专家不断地出面论述,公众对新冠的恐慌情绪是可以被慢慢缓解的。

 

公众或许在知识储备上不如专家,在情绪控制上也会出现这样那样的问题,但如果在宣导上能够循序渐进、依次展开,同时辅以足够公开透明的流程和数据,公众也是明事理、讲道理的。

 

第二个转向是监测指标的转向。

 

随着对病毒认知的增强和医疗技术的普及,新加坡政府监测的重点逐步从新冠感染人数转向了病毒传播率、重症率和死亡率。

 

这一点不但反映在每天公布的数据中,也反映在一系列的宣传和政策中。

 

例如李显龙总理在去年10月份的演讲中就明确表示:

 

“……疫苗的接种大大降低了重症率,大部分(98%)确诊病人是无症状或轻症,只有2%是重症,其中0.2%需要ICU治疗……”

 

而与此同时,新加坡政府也转变了收治政策,将医疗资源瞄准弱势及重症群体,进行分级收诊、鼓励轻症居家自愈。

 

其实我之前也一直以为,新加坡的医疗资源怎么也比中国丰富,甚至应该好于中国的一线城市。

 

然而事实上,根据世界银行和世卫组织的数据,新加坡的每千人医师数略高于中国,但每千人床位数甚至比中国的平均水平还要低50%。

新加坡的防疫经验其实就四个字

因此,新加坡人均医疗资源并不宽裕,只有将监测指标转向,同时辅以分级诊疗制度,将有限的医疗资源优先治疗各类危急重症患者,才能真正做到对老人和幼童的保护。

 

根据官方数据,在奥密克戎全面爆发的2022年3月初,新加坡ICU病床中依然有一半以上是给非新冠重症患者使用的,新冠患者使用占比仅约为15%,并未形成医疗挤兑。

 

第三个转向是检测手段的转向。

 

由于新加坡在进入共存预备期后,必然需要通过大量检测来配合隔离措施,延缓病毒传播,以及确保社会活动安全进行,因此必须要找到比核酸更加快速、便捷、高效的检测手段才可以。

 

与国内直到今年3月份才逐步尝试快速抗原检测(ART)不同,新加坡政府从2021年5月末预备开放以来,就陆续批准了20余项ART产品,供民众在检测点使用或自主购买。

 

由于抗原检测具有省时和无需专业人员的优势,但也有低风险人群假阳性比例较高的劣势,因此新加坡为抗原检测明确的制定了三个目标:诊断密接切断传播链、筛查高风险行业人员预防集聚性感染,以及筛查聚集人群避免超级传播事件。

 

而除此之外的目标,依然会通过核酸检测来完成。

 

换句话说,检测手段的转向是整个共存政策的一环,抗原和核酸是被分别应用在不同场景的,而不像国内现在这样只是叠加起来使用。

 

只有这样,抗原检测才能发挥它应有的作用。

 

2

 

由以上的三个转向我们可以看出,新加坡的防疫管控政策并非迅速、单向度的放松,而是审时度势,根据疫情现状灵活调整。

 

而且在提出共存和重启经济之前,已经做了充分和多元化的准备,以避免出现医疗挤兑。

 

当然,在所有的准备中,重中之重就是打疫苗。

 

我们能看到的是结果——根据新加坡政府提供的数据,截至2022年4月11日,超过92%的新加坡人口完成了两针疫苗接种、72%人口已接种加强针,80岁以上人口疫苗完全接种率超过94%。

 

而我们看不到的是过程——包括如何及早确定和确保疫苗的供应,以及如何软硬兼施推进疫苗接种。

 

在这个过程中,新加坡政府做了大量的工作。

 

例如早在疫情刚爆发的前几个月,也就是2020年4月,新加坡政府就组建了专家小组,对候选疫苗进行评估,最终与辉瑞、Moderna、科兴签订了疫苗预先采购协议。

 

而在同年8月辉瑞疫苗获得美国FDA批准后短短四个月内,12月14日新加坡卫生科学局就进行了临时授权,确保首批疫苗在当月运抵新加坡,从而让新加坡成为最早接种新冠疫苗的国家之一。

 

此外,新加坡的疫苗接种计划也很有讲究。

 

除了接种顺序根据轻重缓急做了区分之外,还采取了一系列通用型措施和针对性措施来确保疫苗接种率的提升。

 

例如新加坡很早就对已接种疫苗者出入公众场合、参与大型活动乃至入境等方面提供了多种便利和差异化政策,还为12岁以下儿童提供了仅为三分之一剂量的疫苗以缓解家长的担忧。

 

而在老年人群体的接种计划中,更是多管齐下,采取包括经济激励、合作劝导、上门接种以及提供多样化选择等方式,快速提升了关键人群中的疫苗接种率。

 

就像今年4月12日李显龙在行政官员任命与擢升仪式的演讲中说的,

 

“……我们必须敬畏病毒,但不能被它吓坏。我们必须尽可能正常地过生活,采取个人防护措施,遵守限制措施。有了疫苗,对我们大部分人来说,新冠现在是可治的、不严重的。

 

而在接种疫苗计划下,当局不仅要定下接种覆盖率的目标,也必须向民众提供透明的数据和信息,包括疫苗安全性信息等一系列内容。”

 

3

 

其实,新加坡政府并非没有意识到“与病毒共存”之下,对于老年、儿童等脆弱群体会有怎样的影响。

 

从上海这次疫情的重症和死亡病例来看,就算在有疫苗护体的情况下,老年人和基础病患者依然是危险的。

 

但新加坡的做法是——对症下药。

 

首先,政府在各种场合明确告知公众新冠对高危人群(60岁以上未接种疫苗者及所有80岁以上人口)的危险性,老吾老以及人之老,督促全民一定要遵守防疫措施。

 

其次,在所有的感染者指引中,都明确的将“高危人群”单独列示,从检测手段到处理方式再到后续评估,对于高危人群都有着和其他人群不同的应对机制。

 

新加坡的防疫经验其实就四个字

 

再次,就算最后评估出来需要居家隔离,但对于这部分老年患者,政府和社会组织也会一同在物资保障、心理安抚等方面提供额外的支持。

 

如果老年人是和其他家庭成员同住,那么政府规定,只要将患者隔离在单独房间内,其余家庭成员就可以在每日抗原自测为阴性的情况下正常出门,不影响工作和生活。

 

这样一来,家庭成员也就能更好地为老年人提供陪护和支持。

 

而如果是独居老年人,则会有社区义工体系将为其派送食品和杂货。一些专门的义工组织也为独居老人提供远程友伴服务,通过视频通话等方式为独居老人提供情感和心理支持。

 

从新加坡的案例来看,对于易感人群和高危人群的关心并不是只停留在口号和纸面上,而是切实落地在各种政策以及具体执行层面。

 

既不一刀切,也不大而化之,科学和精准,才是保护老年人和高危人群的真正武器。

 

4

 

说了新加坡防疫政策这么多的优点,并不是说这个国家就没有做错的地方,或者说政策从一开始就如此的严谨和有序。

 

李显龙总理自己也在不同场合表示过,“没人知道具体该怎么做,能做的只有摸着石头过河”。

 

包括他在讲话中的一些表达,可能也会让部分人难以接受:

 

“对于新加坡来说,我们不得不改变方向。这也要求说服民众,现在有必要接受每天几千例的病例。我们会尽最大的努力,但病例和死亡难以避免,后者主要是老人。这就是生活,也是流感、肺炎以及其他疾病每年夺去成千上万老年人生命的方式。我们不得不面对现实。”

 

——新加坡总理,李显龙

此外,在疫情初期,新加坡也曾有过因为佛系应对而导致疫情爆发的时候。在疫情的中期,也曾付出过沉重的防疫代价和经济成本。而在最新的开放过程中,则不得不容忍更高的新冠感染人数。

 

就像新加坡卫生部长王乙康在博鳌论坛上的表态,

 

“有些国家认为新冠病毒就是一种流感,但实际上,新冠的危害比流感大得多。在新加坡,唯有当绝大多数人口都完成了疫苗接种之后,新加坡政府对于从“清零策略”转向“不再追求清零”時才增加了一些信心。这个决定的背后,也虑及了新加坡经济遭受的影响。

 

那么新加坡真正的防疫经验究竟是什么呢?

 

答案揭晓。在我看来,其实就是四个大字——

 

实事求是。

 

从观念,到措施,从和民众一次次透明而坦诚的沟通,到循序渐进且有据可循的政策组合。

 

在疫情蔓延的前两年,我国防疫政策在应对原始株乃至德尔塔病株取得了巨大成功。因为彼时,果断的措施和强有力的执行力是最关键的。

但当传染性更强、毒性却较弱的奥密克戎株开始肆虐的时候,政策的弹性、柔性、变通性,以及更加实事求是的防疫措施,正在变得更加重要。

 

关于这一点,我们应该,也肯定学得来。不是吗?

“实事求是,是马克思主义的根本观点,是中国共产党人认识世界、改造世界的根本要求,是我们党的基本思想方法、工作方法、领导方法。”

 

——庆祝中国共产党成立100周年大会


转自:https://mp.weixin.qq.com/s/lazYuzBObznX_DfPtMkLMA

桃李面包的渡劫之路

本文系基于公开资料撰写,仅作为信息交流之用,不构成任何投资建议。
最近看了不少桃李面包(SH:603866)的资料,包括年报,季报,券商研报,网络上的一些评论和公开信息,对这家公司有了一定的认知,自己也做了很多思考和调研。相信关注的人都这么觉得:桃李面包值得看,至于是否值得买,很难说。
 
一,先理一下基本信息和观点:
 
桃李面包所处的烘焙零售市场中的面包品类,无论市场体量还是增量空间,都还是值得期待的,这是最大的行业基本面。
 
桃李主要做短保面包,采用“中央工厂+批发”模式,来自沈阳的公司,经过二十多年的发展,上市也已近七年,业务快速扩张,已经基本覆盖全国。
 
以上事实说明两点:
 
1、公司的经营实力和商业模式非常厉害,能把既普通又竞争激烈的面包做到全国,做到“桃李满天下”。
2、既然已经做到覆盖全国,那么成长应该已经不是公司的主要看点了。
 
用数据可以更直观地应证这两点:在过去的10年里,公司的营收始终保持正增长,除了2020和2021年是个位数增长,之前都保持了两位数。除了2021年利润增速为负,之前也一直都是平均20%左右的增速。是标准的白马消费股的表现。
 
见图1历年营收与同比增速,图2历年净利与同比增速
 
桃李面包的渡劫之路
桃李面包的渡劫之路
 
成长见顶降速,应该是市场对桃李的一个普遍共识,见顶或许未必,但降速是一定的。
 
对任何上市公司来说,成长的终点都是盈利能力,兑现价值的能力。不再成长也很正常,只要你有本事大把赚钱,从成长公司转型成价值公司,就没有任何问题。
 
所以说,桃李面对的最主要问题就是规模化之后的盈利能力是否能够兑现。这直接决定了公司的价值。
 
但在讨论兑现盈利能力之前,还是要先分析一下桃李的基本面和业务特点。
 
二,短保面包,“中央工厂+批发”,业务前景。
 
(一)  面包按保质期长短一般是这样划分的:
 
短保(3-6天)、中保(30-45天)、长保(6个月)。
 
短保主要有:桃李、宾堡、曼可顿、手工现做的面包房(面包新语,85度C等等一大批品牌)。中保的代表品牌是港荣,长保的有达利园、盼盼、好丽友。
 
对消费者来说,随着生活水平提高,在大部分场景下都是优先选择短保的。如果单纯追求新鲜健康和口味,首选肯定是手工面包房,但手工面包房经营成本很高,价格不会便宜。于是有了桃李宾堡等中央工厂类型的短保面包的市场空间,其竞争点在于性价比:价格低,又兼顾新鲜与健康。
 
(二)国内面包行业主要有三种经营模式:
 
1“中央工厂+批发”:企业在各个城市设立中央工厂,以工厂为圆心,通过集中物流配送,将产品批发给本地及周边城区的商场、超市或经销商,生产自动化程度较高,规模效应显著。桃李是典型。
 
2“连锁门店”、将面包店开设在城镇人口密集的商业区和居民区,通过可视化操作和专业化的现场加工,最大限度地满足顾客在产品质量、口感和新鲜程度等方面的要求。
 
3“中央工厂+线上销售”。没有实体门店,更为轻资产,以低价、促销策略吸引新用户。
 
对企业来说,采用“中央工厂+批发”是可以实现销售最大化从而收益最大化的一种快速扩张模式,减少环节,使食品安全风险更可控。
 
(三)业务前景
 
粗看之下,桃李的生产基地已经遍布全国(20个生产基地投入使用,6个在建),也实现了全国市场铺货,市场空间确实到头了。
 
生产基地情况见图3
桃李面包的渡劫之路
 
但细看会发现实际上还是有很大潜力可以挖的。
 
桃李39家区域子公司(包括已注销的2家),2021年报显示有14家是亏损的,主要集中在南方,比如上海,江苏,浙江,深圳,全都是亏损。这些都是巨大的消费市场,如果说大部分长江以南的中国消费者都不接受中央工厂的短保面包,那显然不是这么回事。比如江苏、武汉、海南是因为投产时间短产能还没上来。浙江、泉州、福州、厦门的亏损按常规思路应该是因为暂时没有对应的产能基地,但对应的浙江和泉州生产基地已经在建设中。这些只要多给一点都可以改善。
 
桃李的全国拼图还没有完全完成,尤其是南方几大区域。如果能再给点时间,是有机会更进一步的。但话说回来这个更进一步也不是那么容易(从60分进步到80容易,从80分进步到100分可就难得多了),从数据看,恰恰是这几个区域的业务,去年毛利率下降是最大的。主营业务按地区分见图4
桃李面包的渡劫之路
 
公允的说,没做好意味着有提高的空间,但也可能意味着就是做不好。比如上海市场,桃李已经发力了但始终碰壁。如果桃李能拿下上海市场证明自己,那它的估值可以立马提升一级。
 
三,核心问题,日送模式和存货周转率
 
光看各种明面上的经营数据,是无法真正理解桃李面包的生意的。桃李商业模式的核心是日送模式。这是真正关切所在。
 
因为短保面包很容易过期,过期的面包是要收回处理的,损耗非常大,日送模式可以更精准地定量供应,减少损耗。但同时,终端数量庞大的桃李在日送上耗费的运输成本也是十分巨大的。仅考虑成本的话,采用日送多出来的运输成本甚至未必能覆盖不采用日送所增加的损耗。
 
日送模式不仅仅是为了减少过期损耗,它还可以使企业更及时准确地掌握动销情况,由此更好的去制定排产计划,营销计划,整体经营计划,提升公司对市场的把握和竞争力。而且日送模式不是那么容易做到的,能做到日送就已经体现了公司强大的管理能力。
 
快消品有一个核心指标:存货周转率,直接体现公司的效率和实力。桃李做到了在同行中的出类拔萃。说出类拔萃可能还不够准确,在申万二级休闲食品的同行中它的存货周转率是遥遥领先的第一,在整个申万一级食品饮料里它依然是第一。(因为部分公司的2021年年报还未出来,所以比较的是2020年报的数据)见图5,图6。
 
桃李面包的渡劫之路
桃李面包的渡劫之路
 
四,股价与基本面
 
气氛烘托到这了,那么问题来了:桃李怎么股价如此惨烈,从高位下来腰斩都不止,而且看上去没有完全企稳呢?不至于啊。
 
我一直认为用股价走势来应证价值逻辑是更准确更贴近真实的复盘方法。按照我的理解,桃李的看点和核心指标就是存货周转率。所以我预设了存货周转率作为观测点,认为这个很可能和股价有直接相关。
 
然后发现事实确实如此:
桃李面包的渡劫之路
桃李面包的渡劫之路
 
图7是每年一季度的存货周转率变化图,图8是股价季线图,形状很接近,而且时间点也十分对应。
 
2020年一季度的存货周转率是巅峰。同时从资产负债表也可以看到,存货在2020年3季度突然大幅增加。多年斜率向上的股价也差不多是在那前后的时间段同时见顶的。相关性非常明显。
 
存货和营收呈现一定的相关性,比如2021年的营收差不多是2016年的2倍,在5年里营收翻倍,存货也基本是翻倍,这个看似比较合理,但实际上不该如此线性,因为以桃李所达到的规模效应,存货不该和营收保持同样的增幅。
 
2020年前3季度的营收同比增长有限,存货却大幅增加,相当于阶段性证伪了桃李的增长逻辑,这才是最关键的。聪明的股价即时反转,随后令市场担忧的指标并没有得到修复,再次确认了见顶逻辑后,股价失去了支撑,开始一路向下。一段白马长周期的终结。
 
我们知道,白马周期分2种类型,第1种是股价增长远远快过公司实际的成长,所以股价会在某个时刻停下来等待公司的成长幅度跟上,股价表现是回调震荡一段时间之后还会继续向上;第2种是增长逻辑走完,股价反转向下,如果没有新的增长逻辑出现,那么股价很难再回到原点。现实中第2种远比第1种更普遍,毕竟长期持续成长的真正优质白马是很少的。
 
显然市场认为桃李是第2种。这是桃李股价杀跌的根本原因。杀的不是情绪也不是估值,而是逻辑。
 
 五,小结
 
以上是目前我所看到的桃李面包的情况。
 
因为桃李实际上还没有拼完它的拼图,现在就去评判它的规模化之后盈利兑现还有点早,渡劫之路还没走完,应该再给它机会。而且现在无论PE,PB都已经到了上市之后的最低水平,但它的基本面压根谈不上恶化,甚至连重大挫折都无从谈起,就跌成了这样,真的不至于。
 
更量化的价值衡量还需要更多一点时间看,目前没有十分明确的观点,但潜在看点还是有不少的。等我整理好之后再呈现下一篇续。
 
看的时间短,难免疏忽和错漏之处。感谢阅读,也欢迎赐教。(作者:后来居上dioyan
转自:https://mp.weixin.qq.com/s/QWMelRCG48fuYtGeYQGoqA

奥密克戎遭遇上海杠精背后的十大猜想

为什么全世界几乎都躺平了,中国还是这么倔强?奥密克戎做梦都没想到,会遇到这样子一个杠精。

 

2022314日清晨上班途中,看到张文宏医生发布的微博,貌似这也是他最后一次对于上海疫情长篇大论的发表自己的看法。

 

从此以后,噤若寒蝉。

 

跟张医生一起消失在记者发布会和公众视线的还有吴凡女士,复旦大学上海医学院副院长,上海市重大传染病和生物安全研究院院长。

 

“上海绝对不能封城,三天也不行。”这句话出自吴院长之口,以至于抖音和快手现在还在滚动播放这条三个月之前的采访视频。

 

“上海,是全国人民的上海……”。三个月河东,三个月河西。今天是2022426日,整个上海6340.5平方公里,已经实施全面封控27天。

 

吴博士接受十八路记者采访的时候,字正腔圆,掷地有声,丝毫没有苏北盐城的口音,一度我以为她是土生土长的上海本地人。每次出席新闻发布会直播,她的耳钉特别精致,忽闪忽闪。

 

当时我就是替她暗暗捏把汗的,谁给了她这么大的勇气是梁静茹吗?作为一名卫生专业科班出身勤奋好学拾阶而上的公共卫生和疾控管理领域的干部,对不起,你越界了。

 

用封控前一天陆家嘴滨江师兄弟聚餐时我师兄的原话来说,你有点膨胀了。师兄位高权不重,不过就是提醒我:拎着壶满桌敬酒就是膨胀。就你这个小身板,你是靠脑子吃饭的,又不是靠酱香型白酒咯?

 

无论如何,专业的人,做专业的事情,仅限于在专业的领域内,发出自己的声音。

 

有句话怎么说来着:其实坦诚,即说出自己内心最真实的想法,可以解决90%以上的问题。剩余的10%,如果你不想撒谎,那就用沉默来解决。

 

在贾雷德·戴蒙德编著的《枪炮、病菌与钢铁:人类社会的命运》一书中,疫情至少涉及:生物学、免疫学、生理学、病理学、诊断学、环境学、营养学、卫生学、人口学、社会学、传播学、政治学、经济学等多个学科。

 

在全球互联网的浪潮和民粹主义的趋势下,甚至还要考历史沿革、文化传统、意识形态、宗教因素差异和国际地缘政治等一系列影响因子。

 

疫情肆虐的三年多来,各路人马、精英和政客,各抒己见,大有你刚唱罢我登场的态势。其实各个学科的专业人士发表一些专业意见和观点,有争议、甚至有斗争都很正常。

 

因为大家都在盲人摸象,无一例外。

 

但有一个前提:这些观点也好、建议也好、论文也好,都必须基于自身对于科学和专业的尊重、对于道德和常识的敬畏、和对于真相和真理的追求。

 

基于上述这样一个基本前提:其实很多的想法、说法或者做法,都有合理的一面,但同时又会在时间的动态演变过程中,不断的被重组、被整合、被批评、被撕裂甚至被攻击。当然,被绑架不在我们讨论的范畴。

 

这件事情,就关乎到习总书记执政理念的两个关键词:最大公约数,命运共同体。大家伙沉住气:少用脑,多用心,耐心的琢磨一下这两个关键词,所有的答案,没错,他会自动浮出社会的水平面。

 

其实从心理学和认识行为学的视角来剖析:前期的反复、摇摆和纠结,还有大量的沉没成本和心理成本,都是最终科学、理性决策的必不可少的一个环节,一个部分。

 

国家和城市尚且如此,我们个人在处理学业、家庭、婚姻、事业的过程中更是不能例外。所以也没啥愤怒、懊恼和后悔的。

 

可以负责任的说:这件事情也不能全赖政府。现在大众舆论一边倒的黑管理当局,其实是一种噪声和羊群效应。基层部分老百姓的素质也委实堪忧,因为本来部署的无症状感染者、轻微症状的一律居家隔离的,这是一个既定的策略和战术。

 

几乎接近产生舆情事件:部分居民表示非理性的抵制和反对,呼吁有关部门把这个群体运出去。闹,使劲的闹。这种举报和搞事情的精神,或多或少有点那个那个传承基因的。

 

哀其不幸,怒其不争。结果搬起石头砸了自己的脚不算,全上海2700万民众跟着一起买单。

 

这一小撮的群体,迫使既定的防疫抗疫政策瞬间转向。完全背离前期专家达成的共识和路径。前段时间刷屏的疾控中心那位女士的公开音频可以佐证:接下来发生的一切不足为奇。据说有plan bplan c, 但均不是应对目前这种极端情况。

 

用交大同窗的话来说:危机关头,其实最考验的、最本质和最关键的,决战的胜负还是依赖于社会民众的人文素质和科学素养。人无远虑,必有近忧。

 

这些年习总书记反复强调的四个自信,高瞻远瞩,字字珠玑。但是地方政府在中观管理的层面,必须落实另外四个自信:专业自信、战略自信、决策自信和执行力自信,否则真的辜负我党和习总书记的一片丹心和期待。

 

现在目前这种情况,上海的老百姓很难受,貌似地方主政各个条线、各个区块的官员更难受。这是历史上很少有发生的,用当年李敖先生竞选台湾议员时的誓言和抱负来形容:我要让台湾的政坛官不聊生

 

可惜先生壮志未酬,驾鹤西去。

 

早在半年前,南方医科大学的郑磊教授说:试错本身就有两种可能——对或错。错了,证明这条路走不通,甚至于以前有些判断是理想化的——也是有价值的。承认就行。我们都误判过。需要求助的时候就要求助,该感谢就感谢,该改进就改进,也没什么拉不下面子的。海纳百川不是顾影自怜,众志成城也不是只有一座城。

 

郑教授直抒胸臆的时候,其实上海还在沉醉于20平米中风险区域提示”的高峰体验中。我现在就特别心疼在人民大道上班的同学和朋友,以后走在大街上怎么见人?因为小区里也有体制内的正级处级个别局级在做志愿者,压根儿就不敢暴露自己的真实身份。

 

中国历来有句古话:人在衙门好修行。怎么修行?

 

王阳明的方法论说的是:怀菩萨心肠,行霹雳手段。慎言笃行是老祖宗传下来的智慧。

 

目前这个节点我们比任何时候都需要:摸着良心说话,摸着石头过河。

 

老庄就一直提醒后人:卦不能算尽,畏天道无常;言不可至满,恐大梦一场。

 

从心理学的视角来看:前期的反复、摇摆和纠结,包括大量的沉没成本和心理成本,都是最终科学、理性决策的必不可少的一个环节,一个部分。

 

国家和城市尚且如此,我们个人在处理学业、家庭、婚姻、事业的过程中更是不能例外。所以也没啥好愤怒、指责或者后悔的。

 

执业医生或科研工作者,凭其专业性在城市公共政策制定方面的影响力是有的,而且历来是上海城市治理的一个优良传统和特色之一。

 

但是需要保持最低限度的清醒;这种影响力是很有限的。 所以我一看到某些教授名片上印着政府决策咨询专家等字眼,我就不对付,好像他们真能怎么滴似的。恩,啥职业都能飘,唯独学者不能飘。

 

学而优则仕,这个大家都知道,说的是学习好、专业好的人可以从政以达济天下。

 

但上一句其实更重要、更关键:仕而优则学。

 

所以,问题的关键就在于:决策者需要将各种专业的建议整合吸收,形成自己的想法。用教育学和心理学的术语来说就是内化

 

敲黑板,重点来了:以后要提升的绝对不是自信二字,而是自信后面的两个字:专业、战略、决策及执行。艺高人胆大,否则瞻前顾后,首鼠两端,如何心有猛虎,细嗅蔷薇?

 

我们对自己最大的误解,就是我们习惯性对于自己的认知、专业、视野、判断和经验深信不疑。当遇到和自己信念相违背的事件时,往往就会产生认知阻碍,进而形成一种内心冲突,外化出来就是固执我执

 

恰好早上老姐发来两段话:凡是你排斥的都是你应该学习的,这被称之为认知边界,而边界就是用来突破的。

 

面对认知不及我的,当包容与慈悲;横向拓展,当尊重与谦卑;对于优秀的,被吸引与去追随。 

 

古希腊哲学家爱比克泰德早在2000多年前就说过类似的桥段:对于可控的事情,要保持谨慎。对于不可控的事情,要保持乐观。人只能做能力范围内的事情。你要接受或者学会这个事实,并且以乐观的心,来应对这一切。

 

朋友圈也并非没有高人。行云流水,格物致知,好在教授的字里行间谨慎乐观。

 

事实上,人类对新冠病毒的了解是非常浅显的。

 

科学结论也随时会被反转的,如果大家记忆力好的话,三年来科学界到底被啪啪啪打脸多少回,无论国内还是国外。人民的希望反复破灭。

 

目前,仍然是这样。

 

比尔盖茨三月初的时候宣称奥密克戎是天然疫苗,今年会结束;而12日美国《科学时报》则表明即便最轻微的新冠肺炎大脑也会受到损害,英国牛津大学在《自然》杂志上的论文也验证了新冠对大脑的影响。

 

香港儿科医学会会长、港大医学院儿童及青少年科学系副教授叶柏强在表示,奥密克戎变种病毒对儿童的杀伤力大。

 

也就是说,即便你在上海亲身得了这病,感觉没事,所以得出结论是:新冠没那么严重,根本就是个谎言。这个结论本身,既是正确的也是不正确的。

 

正确的是你个人的体验是真实的,不正确的是,中国那么大,样本那么复杂,医疗资源分布那么不均,你的结论,代替不了整个中国的决策。

 

更何况,我们还不知道未来这病毒朝哪个方向变异,饶毅说了一段话,大家可以再去看看病毒变异的规律,完全不一定就是温和的。

 

415日,美国钟南山福奇撰文表示,传统的群体免疫概念可能不适用于新冠病毒,并表示这意味着相当长一段时间内,新冠病毒不会在人群中消失

 

流行病学家曾光说的很好:让子弹飞一会儿,让国外先开放,我们看看开放的结果,坚持动态清零,静观其变,当清零红利没有的时候,自然会有更优的选择。

 

我历来喜欢专治各种不服,但是这一次,我就特别欣赏这位专家也是临床教授的观点。

 

我们不妨再假设一下:除了奥密克戎,我们还有什么更重要的事情去操心,去思考,去探索,去推动,去落实?与其说这是一道选择题,不如说是一道思考题。两岸关系,国际局势,鸟乌战争,粮食危机,金融安全,政府负债,科技攻关,社会治理….哪一件事情不比奥密克戎重要性、紧迫性、战略性高出百倍?

 

这次上海滩,成为“上海瘫”,不是偶然的,也有必然性。党媒两周前就发出“国家行政有效,基层治理无效”的警告和声音。关于疫苗、核酸和抗原检测后面的阴谋论和故事更是风云变化扑朔迷离。

 

天下难治,一般都以为人民难治,殊不知真正难治的,不是人民,而是官吏。五千年的中华文明历来有种提法:明主治吏不治民。举个例子,故宫养心殿至今仍然保留着雍正手书自勉的对联:惟以一人治天下,岂为天下奉一人?

 

后来有学者考证:这句话的原始知识产权出自隋炀帝之手,这个暂且不表。问题是作为军机处的发明专利人:天下最缺乏安全感的一个人,能给芸芸众生带来安全感吗?

 

权力越大越任性,也越缺乏安全感。这点几乎是一个常识,因为有太多人觊觎这个位置了甚至知乎宇宙上有过这样一个梗:你尝试过被一只蜗牛的追杀吗?不管你在哪个地方,该蜗牛都会慢腾腾向你所在的方向靠近。

 

明主治吏不治民的治吏思想,在礼法并用,综合为治的框架体系内不断完善,这个归结于历朝历代诸子百家的努力。如今关于国家治理、社会治理乃至企业治理的提法不绝于耳,那么问题来了:究竟是治理还是治吏?这似乎不是二选一,而是一个优先劣后的范畴。

 

同样的,关于把权力关进制度的笼子里这件事情,我们就必须要先弄明白 为什么关怎么关这两个问题。从政治学和公共管理的角度来拆解,并非本人专业或所长。不过基于数字化、数据挖掘、人工智能、数字孪生等技术的视角,可能会有一些意外的心得和收获。

 

我始终认为:坏的算法利用人性,消费人性,放大人性的自然属性,而好的算法可以控制人性的弱点、个体乃至群体的非理性行为。

 

技术导向的社会治理,有的学者喜欢造字曰社会智理暂且不管,其最大的精妙之处在于:外在的约束和激励机制,本质可以对个体、群体进行刻意的训练、引导和驯化,甚至可以形成肌肉记忆。这个具有神经脑科学、认知行为学和心理学的理论基础。

 

重要的事情再说一遍:上述这个提法,具备的是自然科学理论基础,不是唾沫星子横飞的社会科学。如此安排,新兴技术能否从一个单纯推动经济发展的要素和内生变量,逐步转变为可以同时为改善个体、群体和社会内在的契约和人文精神而效力?

 

黄仁宇博士,不过据说这位学者的有些表述和提法最近比较敏感。不过先生的《万历十五年》书里有个表述我印象特别深:凡能先用法律及技术解决的问题,不要先就扯上了一个道德问题。因为道德是一切意义的根源,不能分割,也不便妥协。如果道德上的争执持久不能解决,双方的距离越来越远,则迟早必导致于战争。

 

这段话应用于当下疫情防控和社会治理状况,十分应景。

 

这几天还有个流传甚广的段子,讲的是抗疫救灾如何科学组织、如何精准处置、甚至如何五一调休(一想到这件事情我就来气),有一个网友就怯怯的提问:你们做了这么多精心的部署,人家奥密克戎知道吗?

 

我现在则想理直气壮的提问:我们的顶层设计、发展纲要、实施策略,具备这些政策传导、工具组合、信号触达、良性博弈的民众基础吗?

 

最近外部环境一直很动荡,为我们的生活增添了更多不确定性。在这样的背景下,很多人会问,“面对无常,我们究竟该如何自处,如何思考?”

 

其实,当你问出这样的问题,恭喜你!这意味着你已经伸手触碰到了:一个通往真相、通往更广阔天地的机会。

 

世界并没有变,从来就没有变过。它始终都是有好有坏,有黑暗有光明,有欢喜有悲伤的。你之所以会有很 强烈 的感觉变化,是因为,之前的你体验的现实太狭隘片面。事与愿违,其实是再正常不过的了。

 

用新生代心理学者周小宽的描述:你可以想象任何美好的愿望,并怀抱这样的愿望,你也可以去 “投射” 结局,但现实不会接受你的所有投射。愿望再强烈和坚定,你也无法把结局 “投射” 成你要的样子。

 

无常并不只是意味着不好的事情会突然发生。你沿着好的期待走下去,可能遇见幻灭和悲伤,但你沿着坏的轨迹走下去,也可能遇见惊喜。

 

很多时候,我们不是在掌控事情或者掌控关系,我们只是被 “掌控的错觉” 给掌控了。因为我们太需要可以掌控一切的错觉,以逃避未来的一切其实都不可掌控的真实。

 

之所以没有用太多的温柔去写我们如何面对无常,是因为,面对无常光靠温柔和共情,远远不够。无常有时候带来的摧毁是剧烈的,黑暗是可怖的,席卷而来,这时候最需要的是,稳定和力量。

 

也许在你身边,你会发现那种稳定而有力量的人——他们不是活在全部的光明里,他们是在黑暗中守着最后一束光的人。他们是那种面对了生活本身,承担了责任,认清了无常,不虚幻未来,却每一天都战斗在平凡生活中的普通人。

 

他们真实,强大,在今天的不容易和明天的不如意中活着,守护着黑暗中的光,坚持着自己的坚持。这才是生命和正念的奇迹。

 

对躁郁症做出了重要研究的全球顶尖权威、医界英雄杰米森,以自己三十年和躁郁症相伴的人生写出了著作《躁郁之心》。

 

在其中她写道:因为哭得更多,所以欢笑也更多;因为经历过所有的冬日,所以更能欣赏春天;因为死亡如紧身衣一般,所以更了解生命的意义;因为看到人性最善良和丑陋的部分,所以慢慢了解关心、忠诚和豁达的价值。

 

我们无法掌控人生。是的,我们甚至无法掌控我们自己,状态、情绪、潜意识。我们能做的很有限,那就在很有限中去尽情地挥洒你自己。“以最卑微的梦,致那黑夜中的呜咽和怒吼,谁说站在光里的,才算英雄。”

 

无论如何,身心和谐,言行一致,知行合一,才是我们生活的基本盘和压舱石。

 

还有点时间,我再引申一下“人定胜天”这件事情,它的常规汉语解释为:人的智慧和力量一定能够战胜自然。一点毛病都没有,很长的一段时间我们甚至提倡:与天奋斗,与地奋斗,改天换地,热情高涨,其乐无穷。

 

但我们回过神来,仔细想一想:自然界一个地震,一个洪水,一个小小的传染病毒,我们就无能为力,手足无措了。我们怎么能够胜天呢?还一定能,这是一种唯心还是唯物?再深究就涉及意识形态了,暂且不表。

 

那么这个定是什么意思呢?这个定,本质上可能是《大学》上所说的:知止而后有定,定而后能静,静而后能安,安而后能虑,虑而后能得。

 

这个定,可能就是佛法所指的,“戒定慧”的定:因戒得定,因定开慧。而按老子道德经的说法则是:人不定天亦不定,人定天亦定也。

 

讲到这里,我们大概均以或多或少的明了:“人定胜天”的本意是,人的内心安定下来,才有可能战胜自然。但也不能这么说的这么刚,貌似是才有可能与自然和平共处、和睦相处。

 

归根到底,夫唯不争,故天下莫能与之争。说的就是浩如烟海儒释道最精髓的三个关键词:慈悲,智慧和宽容。

 

行文至此,我特别要强调的一件事情是:无论何时,无论何地,无论是居庙堂之高,还是处江湖之远,宽容始终是一件双向的事:自上而下的宽容是修养,自下而上的宽容是修行。

 

酷似当下的一个热词“双向奔赴”的既视感有没有?

 

最后,祝大家安住当下,安顿好自己和家人。心有余力,力所能及,顺便照顾一下身边的人和事情。

 转自:https://mp.weixin.qq.com/s/ENv1GCr-ESZy7ojYzPQkdA

 

 

Docker-compose 八步部署Django + Uwsgi + Nginx + MySQL + Redis升级篇

Django在生产环境的部署还是比较复杂的, 令很多新手望而生畏, 幸运的是使用Docker容器化技术可以大大简化我们Django在生产环境的部署并提升我们应用的可移植性。Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux机器上。

 

前文我们介绍了如何使用docker-compose八步部署Django + Uwsgi + Nginx + MySQL + Redis (多容器组合),但该教程里有很多值得改进的地方,比如:

 

  • MySQL的数据库名,用户名和密码明文写在docker-compose.yml里,实际是可以由.env文件创建的;
  • MySQL使用的版本比较老,为5.7。如果你使用MySQL 8,那么相关配置文件需要做较大修改;
  • Nginx配置文件没有挂载,每次修改需要手动将配置文件从宿主机复制一份到容器内;
  • 容器间通信使用了–links选项,这个docker已不推荐使用。本例将使用networks实现容器间通信;
  • 原文每次web容器服务启动后,需要手动进入容器内部进行数据迁移并启动uwsgi服务;
  • 原文教程排错机制讲的较少,本文将仔细讲下如何在Docker部署时排错。

本文将是docker-compose部署Django + Uwsgi + Nginx + MySQL + Redis教程的升级版,将完善前面教程不足的地方,很多配置文件将会有非常大的参考价值,建议先收藏再阅读。

 

注意:本文侧重于Docker技术在部署Django时的实际应用,而不是Docker基础教程。对Docker命令不熟悉的读者们建议先学习下Docker及Docker-compose基础命令。

Docker-compose 八步部署Django + Uwsgi + Nginx + MySQL + Redis升级篇

什么是docker-compose及docker-compose工具的安装

Docker-compose是一个用来定义和运行复杂应用的 Docker 工具。使用 docker-compose 后不再需要使用 shell 脚本来逐一创建和启动容器,还可以通过 docker-compose.yml 文件构建和管理复杂多容器组合。

Docker-compose的下载和安装很简单,网上有很多教程,我就不再详述了。这里只记录下ubuntu系统下docker-compose的安装过程。

 # Step 1: 以ubuntu为例,下载docker-compose $ sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose # Step 2: 给予docker-compose可执行权限 $ sudo chmod +x /usr/local/bin/docker-compose # Step 3: 查看docker-compose版本 $ docker-compose --version

注意:安装docker-compose前必需先安装好docker。

Django + Uwsgi + Nginx + MySQL + Redis组合容器示意图

本例中我们将使用docker-compose编排并启动4个容器,这更接近于实际生成环境下的部署。

  1. Django + Uwsgi容器:核心应用程序,处理动态请求

  2. MySQL 容器:数据库服务

  3. Redis 容器:缓存服务

  4. Nginx容器:反向代理服务并处理静态资源请求

这四个容器的依赖关系是:Django+Uwsgi 容器依赖 Redis 容器和 MySQL 容器,Nginx 容器依赖Django+Uwsgi容器。为了方便容器间的相互访问和通信,我们使用docker-compose时可以给每个容器取个别名,这样访问容器时就可以直接使用别名访问,而不使用Docker临时给容器分配的IP了。

这四个容器的别名及通信端口如下图所示:

Docker-compose 八步部署Django + Uwsgi + Nginx + MySQL + Redis升级篇

Docker-compose部署Django项目布局树形图

我们新建了一个compose文件夹,专门存放用于构建其它容器镜像的Dockerfile及配置文件。compose文件夹与django项目的根目录myproject同级。这样做的好处是不同的django项目可以共享compose文件夹。

myproject_docker # 项目根目录├── compose # 存放各项容器服务的Dockerfile和配置文件│   ├── mysql│   │   ├── conf│   │   │   └── my.cnf # MySQL配置文件│   │   └── init│   │       └── init.sql # MySQL启动脚本│   ├── nginx│   │   ├── Dockerfile # 构建Nginx镜像所的Dockerfile│   │   ├── log # 挂载保存nginx容器内日志log目录│   │   ├── nginx.conf # Nginx配置文件│   │   └── ssl # 如果需要配置https需要用到│   ├── redis│   │   └── redis.conf # redis配置文件│   └── uwsgi # 挂载保存django+uwsgi容器内uwsgi日志├── docker-compose.yml # 核心编排文件└── myproject # 常规Django项目目录    ├── Dockerfile # 构建Django+Uwsgi镜像的Dockerfile    ├── apps # 存放Django项目的各个apps    ├── manage.py    ├── myproject # Django项目配置文件    │   ├── asgi.py    │   ├── __init__.py    │   ├── settings.py    │   ├── urls.py    │   └── wsgi.py    ├── pip.conf # 非必需。pypi源设置成国内,加速pip安装    ├── requirements.txt # Django项目依赖文件    ├── .env # 环境变量文件    ├── start.sh # 启动Django+Uwsgi容器后要执行的脚本    ├── media # 用户上传的媒体资源,如果没有需手动创建    ├── static #搜集项目的静态文件夹,如果没有需手动创建    └── uwsgi.ini # uwsgi配置文件

下面我们开始正式部署。

第一步:编写docker-compose.yml文件

修改过的docker-compose.yml的核心内容如下。我们定义了4个数据卷,用于挂载各个容器内动态生成的数据,比如MySQL的存储数据,redis生成的快照、django+uwsgi容器中收集的静态文件以及用户上传的媒体资源。这样即使删除容器,容器内产生的数据也不会丢失。

我们还定义了3个网络,分别为nginx_network(用于nginx和web容器间的通信),db_network(用于db和web容器间的通信)和redis_network(用于redis和web容器间的通信)。

整个编排里包含4项容器服务,别名分别为redis, db, nginxweb,接下来我们将依次看看各个容器的Dockerfile和配置文件。

version: "3"
volumes: # 自定义数据卷  db_vol: #定义数据卷同步存放容器内mysql数据  redis_vol: #定义数据卷同步存放redis数据  media_vol: #定义数据卷同步存放web项目用户上传到media文件夹的数据  static_vol: #定义数据卷同步存放web项目static文件夹的数据
networks: # 自定义网络(默认桥接), 不使用links通信  nginx_network:    driver: bridge  db_network:    driver: bridge  redis_network:     driver: bridge
services:  redis:    image: redis:latest    command: redis-server /etc/redis/redis.conf # 容器启动后启动redis服务器    networks:      - redis_network    volumes:      - redis_vol:/data # 通过挂载给redis数据备份      - ./compose/redis/redis.conf:/etc/redis/redis.conf # 挂载redis配置文件    ports:      - "6379:6379"    restart: always # always表容器运行发生错误时一直重启
  db:    image: mysql    env_file:        - ./myproject/.env # 使用了环境变量文件    networks:        - db_network    volumes:      - db_vol:/var/lib/mysql:rw # 挂载数据库数据, 可读可写      - ./compose/mysql/conf/my.cnf:/etc/mysql/my.cnf # 挂载配置文件      - ./compose/mysql/init:/docker-entrypoint-initdb.d/ # 挂载数据初始化sql脚本    ports:      - "3306:3306" # 与配置文件保持一致    restart: always
  web:    build: ./myproject    expose:      - "8000"    volumes:      - ./myproject:/var/www/html/myproject # 挂载项目代码      - static_vol:/var/www/html/myproject/static # 以数据卷挂载容器内static文件      - media_vol:/var/www/html/myproject/media # 以数据卷挂载容器内用户上传媒体文件      - ./compose/uwsgi:/tmp # 挂载uwsgi日志    networks:      - nginx_network      - db_network        - redis_network     depends_on:      - db      - redis    restart: always    tty: true    stdin_open: true
  nginx:    build: ./compose/nginx    ports:      - "80:80"      - "443:443"    expose:      - "80"    volumes:      - ./compose/nginx/nginx.conf:/etc/nginx/conf.d/nginx.conf # 挂载nginx配置文件      - ./compose/nginx/ssl:/usr/share/nginx/ssl # 挂载ssl证书目录      - ./compose/nginx/log:/var/log/nginx # 挂载日志      - static_vol:/usr/share/nginx/html/static # 挂载静态文件      - media_vol:/usr/share/nginx/html/media # 挂载用户上传媒体文件    networks:      - nginx_network    depends_on:      - web    restart: always

第二步:编写Web (Django+Uwsgi)镜像和容器所需文件

构建Web镜像(Django+Uwsgi)的所使用的Dockerfile如下所示:

# 建立 python 3.9环境FROM python:3.9
# 安装netcatRUN apt-get update && apt install -y netcat
# 镜像作者大江狗MAINTAINER DJG
# 设置 python 环境变量ENV PYTHONDONTWRITEBYTECODE 1ENV PYTHONUNBUFFERED 1
# 可选:设置镜像源为国内COPY pip.conf /root/.pip/pip.conf
# 容器内创建 myproject 文件夹ENV APP_HOME=/var/www/html/myprojectRUN mkdir -p $APP_HOMEWORKDIR $APP_HOME
# 将当前目录加入到工作目录中(. 表示当前目录)ADD . $APP_HOME
# 更新pip版本RUN /usr/local/bin/python -m pip install --upgrade pip
# 安装项目依赖RUN pip install -r requirements/production.txt
# 移除r in windowsRUN sed -i 's/r//' ./start.sh
# 给start.sh可执行权限RUN chmod +x ./start.sh
# 数据迁移,并使用uwsgi启动服务ENTRYPOINT /bin/bash ./start.sh

本Django项目所依赖的requirements.txt内容如下所示:

# djangodjango==3.2# uwsgiuwsgi==2.0.18# mysqlmysqlclient==1.4.6# redisdjango-redis==4.12.1redis==3.5.3# for imagesPillow==8.2.0 

start.sh脚本文件内容如下所示。最重要的是最后一句,使用uwsgi.ini配置文件启动Django服务。

#!/bin/bash# 从第一行到最后一行分别表示:# 1. 等待MySQL服务启动后再进行数据迁移。nc即netcat缩写# 2. 收集静态文件到根目录static文件夹,# 3. 生成数据库可执行文件,# 4. 根据数据库可执行文件来修改数据库# 5. 用 uwsgi启动 django 服务# 6. tail空命令防止web容器执行脚本后退出while ! nc -z db 3306 ; do    echo "Waiting for the MySQL Server"    sleep 3done
python manage.py collectstatic --noinput&&python manage.py makemigrations&&python manage.py migrate&&uwsgi --ini /var/www/html/myproject/uwsgi.ini&&tail -f /dev/null
exec "$@"

uwsgi.ini配置文件如下所示: 

[uwsgi]
project=myprojectuid=www-datagid=www-database=/var/www/html
chdir=%(base)/%(project)module=%(project).wsgi:applicationmaster=Trueprocesses=2
socket=0.0.0.0:8000chown-socket=%(uid):www-datachmod-socket=664
vacuum=Truemax-requests=5000
pidfile=/tmp/%(project)-master.piddaemonize=/tmp/%(project)-uwsgi.log
#设置一个请求的超时时间(秒),如果一个请求超过了这个时间,则请求被丢弃harakiri = 60post buffering = 8192buffer-size= 65535#当一个请求被harakiri杀掉会,会输出一条日志harakiri-verbose = true
#开启内存使用情况报告memory-report = true
#设置平滑的重启(直到处理完接收到的请求)的长等待时间(秒)reload-mercy = 10
#设置工作进程使用虚拟内存超过N MB就回收重启reload-on-as= 1024

第三步:编写Nginx镜像和容器所需文件

构建Nginx镜像所使用的Dockerfile如下所示:

# nginx镜像compose/nginx/Dockerfile
FROM nginx:latest
# 删除原有配置文件,创建静态资源文件夹和ssl证书保存文件夹RUN rm /etc/nginx/conf.d/default.conf && mkdir -p /usr/share/nginx/html/static && mkdir -p /usr/share/nginx/html/media && mkdir -p /usr/share/nginx/ssl
# 设置Media文件夹用户和用户组为Linux默认www-data, 并给予可读和可执行权限,# 否则用户上传的图片无法正确显示。RUN chown -R www-data:www-data /usr/share/nginx/html/media && chmod -R 775 /usr/share/nginx/html/media
# 添加配置文件ADD ./nginx.conf /etc/nginx/conf.d/
# 关闭守护模式CMD ["nginx", "-g", "daemon off;"]

Nginx的配置文件如下所示

# nginx配置文件# compose/nginx/nginx.conf
upstream django {    ip_hash;    server web:8000; # Docker-compose web服务端口}
# 配置http请求,80端口server {    listen 80; # 监听80端口    server_name 127.0.0.1; # 可以是nginx容器所在ip地址或127.0.0.1,不能写宿主机外网ip地址
    charset utf-8;    client_max_body_size 10M; # 限制用户上传文件大小
    access_log /var/log/nginx/access.log main;    error_log /var/log/nginx/error.log warn;
    location /static {        alias /usr/share/nginx/html/static; # 静态资源路径    }
    location /media {        alias /usr/share/nginx/html/media; # 媒体资源,用户上传文件路径    }
    location / {        include /etc/nginx/uwsgi_params;        uwsgi_pass django;        uwsgi_read_timeout 600;        uwsgi_connect_timeout 600;        uwsgi_send_timeout 600;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;        proxy_redirect off;        proxy_set_header X-Real-IP  $remote_addr;       # proxy_pass http://django;  # 使用uwsgi通信,而不是http,所以不使用proxy_pass。    }}

第四步:编写Db (MySQL)容器配置文件

启动MySQL容器我们直接使用官方镜像即可,不过我们需要给MySQL增加配置文件。

# compose/mysql/conf/my.cnf[mysqld]user=mysqldefault-storage-engine=INNODBcharacter-set-server=utf8secure-file-priv=NULL # mysql 8 新增这行配置default-authentication-plugin=mysql_native_password  # mysql 8 新增这行配置
port            = 3306 # 端口与docker-compose里映射端口保持一致#bind-address= localhost #一定要注释掉,mysql所在容器和django所在容器不同IP
basedir         = /usrdatadir         = /var/lib/mysqltmpdir          = /tmppid-file        = /var/run/mysqld/mysqld.pidsocket          = /var/run/mysqld/mysqld.sockskip-name-resolve  # 这个参数是禁止域名解析的,远程访问推荐开启skip_name_resolve。
[client]port = 3306default-character-set=utf8
[mysql]no-auto-rehashdefault-character-set=utf8

我们还需设置MySQL服务启动时需要执行的脚本命令, 注意这里的用户名和password必需和docker-compose.yml里与MySQL相关的环境变量(.env)保持一致。

# compose/mysql/init/init.sqlAlter user 'dbuser'@'%' IDENTIFIED WITH mysql_native_password BY 'password';GRANT ALL PRIVILEGES ON myproject.* TO 'dbuser'@'%';FLUSH PRIVILEGES;

.env文件内容如下所示:

MYSQL_ROOT_PASSWORD=123456MYSQL_USER=dbuserMYSQL_DATABASE=myprojectMYSQL_PASSWORD=password

第五步:编写Redis 容器配置文件

启动redis容器我们直接使用官方镜像即可,不过我们需要给redis增加配置文件。大部分情况下采用默认配置就好了,这里我们只做出了如下几条核心改动:

 # compose/redis/redis.conf # Redis 5配置文件下载地址 # https://raw.githubusercontent.com/antirez/redis/5.0/redis.conf
 # 请注释掉下面一行,变成#bind 127.0.0.1,这样其它机器或容器也可访问 bind 127.0.0.1
 # 取消下行注释,给redis设置登录密码。这个密码django settings.py会用到。 requirepass yourpassword

第六步:修改Django项目settings.py

在你准备好docker-compose.yml并编排好各容器的Dockerfile及配置文件后,请先不要急于使用Docker-compose命令构建镜像和启动容器。这时还有一件非常重要的事情要做,那就是修改Django的settings.py, 提供mysql和redis服务的配置信息。最重要的几项配置如下所示:

 # 生产环境设置 Debug = False Debug = False
 # 设置ALLOWED HOSTS ALLOWED_HOSTS = ['your_server_IP', 'your_domain_name']
 # 设置STATIC ROOT 和 STATIC URL STATIC_ROOT = os.path.join(BASE_DIR, 'static') STATIC_URL = "/static/"
 # 设置MEDIA ROOT 和 MEDIA URL MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_URL = "/media/"
 # 设置数据库。这里用户名和密码必需和docker-compose.yml里mysql环境变量保持一致 DATABASES = {     'default': {         'ENGINE': 'django.db.backends.mysql',         'NAME': 'myproject', # 数据库名         'USER':'dbuser', # 你设置的用户名 - 非root用户         'PASSWORD':'password', # # 换成你自己密码         'HOST': 'db', # 注意:这里使用的是db别名,docker会自动解析成ip         'PORT':'3306', # 端口     } }
 # 设置redis缓存。这里密码为redis.conf里设置的密码 CACHES = {     "default": {         "BACKEND": "django_redis.cache.RedisCache",         "LOCATION": "redis://redis:6379/1", #这里直接使用redis别名作为host ip地址         "OPTIONS": {             "CLIENT_CLASS": "django_redis.client.DefaultClient",             "PASSWORD": "yourpassword", # 换成你自己密码         },     } }

第七步:使用docker-compose 构建镜像并启动容器组服务

现在我们可以使用docker-compose命名构建镜像并启动容器组了。

 # 进入docker-compose.yml所在文件夹,输入以下命令构建镜像 sudo docker-compose build # 启动容器组服务 sudo docker-compose up

如果一切顺利,此时你应该可以看到四个容器都已经成功运行了。

Docker-compose 八步部署Django + Uwsgi + Nginx + MySQL + Redis升级篇此时打开你的浏览器,输入你服务器的ip地址或域名指向地址,你就应该可以看到网站已经上线啦。

第八步:排错

初学者使用Docker或Docker-compose部署会出现各种各样的错误,本文教你如何排错。

Nginx容器排错

容器已启动运行,网站打不开,最有用的是查看Nginx的错误日志error.log。由于我们对容器内Nginx的log进行了挂载,你在宿主机的/compose/nginx/log目录里即可查看相关日志。

 # 进入nginx日志目录,一个access.log, 一个error.log cd compose/nginx/log # 查看日志文件 sudo cat error.log

绝大部分网站打不开,Nginx日志显示nginx: connect() failed (111: Connection refused) while connecting to upstreamNginx 502 gateway的错误都不是因为nginx自身的原因,而是Web容器中Django程序有问题或则uwsgi配置文件有问题。

在进入Web容器排错前,你首先要检查下Nginx转发请求的方式(proxy_pass和uwsgi_pass)以及转发端口与uwsgi里面的监听方式以及端口是否一致。

uWSGI和Nginx之间有3种通信方式unix socket,TCP socket和http如果Nginx以proxy_pass方式转发请求,uwsgi需要使用http协议进行通信。如果Nginx以uwsgi_pass转发请求,uwsgi建议配置socket进行通信。

更多关于Nginx和Uwsgi的配置介绍见个人博客:

  • https://pythondjango.cn/python/tools/6-uwsgi-configuration/

  • https://pythondjango.cn/python/tools/5-nginx-configuration/

Web容器排错

Web容器也就是Django+UWSGI所在的容器,是最容易出现错误的容器。如果Nginx配置没问题,你应该进入web容器查看运行脚本命令时有没有报错,并检查uwsgi的运行日志。uwsgi的日志非常有用,它会记录Django程序运行时发生了哪些错误或异常。一旦发生了错误,uwsgi的进程虽然不会停止,但也无法正常工作,自然也就不能处理nginx转发的动态请求从而出现nginx报错了。 

 # 查看web容器日志 $ docker-compose logs web # 进入web容器执行启动命令,查看有无报错 $ docker-compose exec web /bin/bash start.sh # 或则进入web柔情其,逐一执行python manage.py命令 $ docker-compose exec web /bin/bash  # 进入web容器,查看uwsgi是否正常启动 $ ps aux | grep uwsgi  # 进入uwsgi日志所在目录,查看Django项目是否有报错 cd /tmp

另外一个常发生的错误是 docker-compose生成的web容器执行脚本命令后立刻退出(exited with code 0), 这时的解决方案是在docker-compose.yml中包含以下2行,  另外脚本命令里加入tail -f /dev/null是容器服务持续运行。

 stdin_open: true tty: true

有时web容器会出现不能连接到数据库的报错,这时需要检查settings.py中的数据库配置信息是否正确(比如host为db),并检查web容器和db容器是否通过db_network正常通信(比如进入db容器查看数据表是否已经生成)。在进行数据库迁移时web容器还会出现if table exists or failed to open the referenced table ‘users_user’,  inconsistent migration history的错误, 可以删除migrations目录下文件并进入MySQL容器删除django_migrations数据表即可。

数据库db容器排错

我们还需要经常进入数据库容器查看数据表是否已生成并删除一些数据,这时可以使用如下命令:

 $ docker-compose exec db /bin/bash # 登录 mysql -u usernmae -p; # 选择数据库 USE dbname; # 显示数据表 SHOW tables; # 清空数据表 DELETE from tablenames; # 删除数据表,特别是Django migrationstable DROP TABLE tablenames;

小结

本文详细地介绍了如何使用docker-compose工具分七步在生成环境下部署Django + Uwsgi + Nginx + MySQL + Redis。过程看似很复杂,但很多Dockerfile,项目布局及docker-compose.yml都是可以复用的。花时间学习并练习本章内容是非常值得的,一但你学会了,基本上可以10分钟内完成一个正式Django项目的部署,而且可以保证在任何一台Linux机器上顺利地运行。本文最后的排错文章更会助你一臂之力。

注意:整个部署过程如果遇到问题,可以给我们留言或发信息,可以节省你的时间。

相关阅读

Docker部署Django由浅入深系列(下): 八步部署Django+Uwsgi+Nginx+MySQL+Redis

如何在阿里云Ubuntu服务器通过uWSGI和Nginx部署Django项目教程-大江狗原创出品

转自:https://mp.weixin.qq.com/s?__biz=MjM5OTMyODA4Nw==&mid=2247486903&idx=1&sn=5d2172f66c0357ffaefac7c783c80867&chksm=a73c6d8f904be4999d7844a7f70f9d3aa8cc460cd18089f13924711a3261cd4c69d1c7b6933e&scene=132#wechat_redirect