[部署翻车系列] Gitlab CI Runner npm build Cannot find module

这是一场生产环境部署过程的翻车现场分析。

翻车现场

这是Gitlab Shared Runner CI报告的错误,npm run build的时候扑街了。Docker image base on image: node:10

背景交待

我这次改动是准备把query-string更新为>6.5版本(之前是4.3.4,看到这么大的升级我是有些忐忑的),这个版本有个新feature是支持arrayFormat: 'comma'。项目使用的react-script(^3.0.1)全家桶版本内置的是query-string@4.3.4, 我是通过yarn why来得知的:

yarn why query-string

yarn why v1.17.3
[1/4] 🤔  Why do we have the module "query-string"...?
[2/4] 🚚  Initialising dependency graph...
[3/4] 🔍  Finding dependency...
[4/4] 🚡  Calculating file sizes...
=> Found "query-string@4.3.4"
info Reasons this module exists
   - "react-scripts#mini-css-extract-plugin#normalize-url" depends on it
   - Hoisted from "react-scripts#mini-css-extract-plugin#normalize-url#query-string"
info Disk size without dependencies: "40KB"
info Disk size with unique dependencies: "72KB"
info Disk size with transitive dependencies: "72KB"
info Number of shared dependencies: 2
✨  Done in 0.81s.

这个bug诡异的地方是,你yarn install或者yarn.lock重新生成之后,每次Cannot found module都是不一样的。

解决方案 tl;dr

long-term fix (这是我第二次部署时的解决方案)

直接把node_modules去掉,然后每个job重新install,这样会牺牲build的速度。然后其实node_modules不应该去cache,我们应该cache yarn 或者 npm的 cache,这也是官方推荐的做法。所以说,其实我之前的cache node_modules并没有起到作用!!!!

我在本地环境用docker尝试去复现这个bug,然后还是复现不了。所以我重新看了gitlab-ci.yml 发现 node_modules会被缓存,不同的job直接会直接share这个folder。

直觉告诉我,node_modules在没有清空的清空下yarn install应该会让有些package变得不一致。而且node_modules是跟平台相关的,特别是有些package是native addon要是要用到node-gyp编译。

所以我让每个CI/CD的job都重新yarn install这样就保证每次node_modules都是最新和一致的。

不稳定的fix(这是原来的做法)

基于重启一下试试的原则: 我删除了yarn.lock还有rm -rf node_modules然后在本地系统yarn install,提交代码即可跑过。

过程

本地Reproduce一下?

好吧,我本地既然重现不了这个问题。node.js的package是深似海的,真不知道哪个package依赖了OS相关的东西,而且我本地的macOS和Docker环境差异这么大..

Image result for node.js package hell
Dependency Hell

Google一下

我的代码是没有引用gensync的,因为我们有eslint,如果引用了没有安装的module,它会直接爆error。

如上图所示,Cannot find module 'gensync'错误。我看到这个和babel-preset-react-app有关系我就直接搜索了关键字: babel-preset-react-app gensync

然而找到了一个比较吻合的stackoverflow 问题,然而并有人回答。

装一下gensync?

gensync现在最新是1.0.0-beta.1,说实话我有点怕npm那些beta的东西,还有说好的follow semver怎么patch version更新有breacking changes呢?所以这个方法我直接跳过了。

更新一下babel?

因为项目使用了react-script全家桶,内置了很多package,这样贸然更新babel可能会直接导致版本冲突。

更新一下react-script?

现在react-script最新版本是3.4.1,和项目使用的差了4个minor版本。为了早点下班,真不想去处理那些兼容性的问题。况且,整个前端项目没有cypress E2E 测试。手工测试就要我加班了。

降低一下query-string?

好的,我就这样做了。这个package的changelog很不友好,要在GitHub release page翻页很多次。我直接找了中间不大不小的版本开始,最后找到了最小直接arrayFormat: 'comma'是6.5。然而CI pipeline 还是failed了,问题依旧是 Cannot find module 'gensync' 只是触发的地方不同了。

这个gensync module的问题更神秘了,然而为了下班我并不想怎么鸟它。

Search一下Gihub的issue?

找了babel还有query-string的issue都没有相关的。此时我心里已经在骂娘,一个破query-stringpackage就是处理一下query url的,依赖这么多东西干嘛?

最后

最后我还是采用重启一下试试的方法。当然我可以深入挖掘一下问题所在,然后在github report 一下bug啥的。但是我并没有这样做,因为我要下班做别的东西。如果你知道你可以留言告诉我。。。

问自己

问:不是有多环境CI/CD么?为什么部署到testing环境的时候,CI没有failed?

:因为master branch有新feature的代码,还不能直接merge到production branch。此时又有urgent的hot-fix,所以直接改了production branch。然后master branch没有及时地pull production branch的hot-fix。(我们这里是master push会deploy到testing env,一打tag会部署production branch到production env)

《UNIX编程艺术》里面的人 (持续更新中…)

向优秀的人学习是一种高效的学习方式,所以我收集了些《UNIX编程艺术》提及过的人物。UNIX不仅仅是一个操作系统,他更是一种哲学(你可以说是宗教,毕竟还是有不少信徒的)。然而这些都是由千千万万的贡献者,还有特别是以下的人物打造的。

Ken Thompson 肯·汤普逊 (1943 ~ )

Ken Thompson and Dennis Ritchie--1973.jpg
左边的那个大胡子。这个照片我记得也是某本经典编程书的封面。

生于1943年,设计和实现了UNIX,创造了B语言(C语言的前生)。2006年加入google,共同设计了Golang。

(我边查资料边打字,感觉手都有点抖。。。我感觉只要把这些名字写在我的博客,就能沾光的样子。。。)

一个小八卦,陈皓的博客能找到KEN THOMPSON登入UNIX时使用的密码。

本书引用的观点:

UNIX就如同其他工程领域的民间传统一样是自下而上,而不是自上而下。(画外音:我理解的是实用主义和经验的产物。毕竟人类很难去设计一个自己想象不到的东西。有点摸着石头过河的感觉。)

Rob Pike 罗勃·派克(1956 ~ )

“罗勃·派克”的图片搜索结果
看看衣服上的The Go gopher

生于1956,曾在贝尔实验室工作,UNIX小组成员,共同和Ken Thompson(UNIX之父)开发了UTF-8。目前在谷歌工作,参与Golang的研发。据说他还是Golang之父。

他的个人网站: http://www.herpolhode.com/rob/

邮箱竟然是: r(at)google.com (谷歌把很多软件奠基人都挖走了。。。)

本书引用了他的观点,有如下:

  • 在你没对代码进行估量,特别是没找到最耗时的那部分之前,别去优化速度。
  • 花哨的算法比简单的算法更容易出bug、更难实现。尽量使用简单的算法配合简单的数据结构。
  • 编程的核心是数据结构,而不是算法。

John Lions (1937 ~ 1998)

这位前辈是澳大利亚人,他被计算机历史记住,其中一个原因为他是这本书的作者《Lions’ Commentary on UNIX 6th Edition, with Source Code》。这本书也简称做Lions Book,是Dennis Ritchie作序的哦。

在那个年代,AT&T授权Unix给高校使用,之后迅速在高校圈走红,甚至传到了澳大利亚。这个授权应该是被严格的版权控制的。这本书因为侵权的原因,在那个年代是不能在美国合法出版的,所以只能在美国本土“偷偷“流通。

Unix黑客们手头没有这本书都不好意思说自己是懂Unix的。当时高校拿到的Unix源码拷贝是没有内核文档,所有有好心人做一份文档显得弥足珍贵,而且更加加速的Unix的传播。我快速扫了一些这份文档(点我下载),只有155页,有源码解析,还有各种系统工作机制的讲解。真心感觉不错,看了下目录,我感觉很多高校计算机教材都是有借鉴这本书的。

Unix的衍生版本版权和商业化过程经过了很多坎坷,甚者让Unix错失了PC时代的黄金发展时期(被微软抢占了先机)。

为了纪念John Lions,现在新南威尔士大学计算机学院大门还有一个John Lions Garden。

我很八卦地在Google map找了一下,John Lions的子女Liz Lions竟然还评论了。三周前,现在是2020MAR29。

Larry Wall 拉里·沃尔 (1954 ~ )

拉里·沃尔- 维基百科,自由的百科全书
他的插手姿态让我想起了我的同学。

他大学主修化学音乐而且还学习了医学预科,研究生去了UC Berkeley。他知名与设计开发了patch,还有perl编程语言。大师真的是触类旁通呀。这不禁让我想起了Paul Graham(Y Combinator创始人,著名《黑客与画家》作者,他是极客也是画家)。感觉要学点艺术才能成为优秀的程序员呀。

历史就在我的macOS里

Larry Wall在UC Berkeley的年份应该是70年代后期,Ken Thompson 刚好也在UC Berkeley教书,Unix BSD family就是在这个时候衍生出来的。当时贝尔实验室都还是属于AT&T,AT&T低价甚者免费授权给美国各大高校试用Unix。UC Berkeley的geeks就开始改造Unix,而且给Unix开发很多程序工具。比如Larry Wall做的patch。

第一次使用italki

闲聊

一天和同事闲聊得知,她在italki预约不同国家的英语老师给自己的孩子练习口语,让小朋友早一点适应不同国家的英语口语。

我感觉这种教育方式挺有趣的,而且成本并不高。为了让孩子对学英文更有趣,她让italki的老师只聊孩子感兴趣的话题,其中一个是孩子喜欢玩的游戏,Minecraft。

行动

在和同事闲聊的过程中,我马上Google了italki。

整个网站风格很简洁扁平化,主题突出,色彩不多,靠阴影突出重点,没有多余的动画。非常容易上手,这应该是一个慢慢打磨了很久的产品。我查了下历史,发现原来2007年就已经成立。

italk主页,整个网站用起来很舒服。

据说之前是可以自由地找语伴配对,现在变为了纯付费的平台。

我在知乎找了些对这个网站评价,发现都是好几年前的。很多人在吐槽配对的语伴怎么不靠谱,还有些人有其他的目的。因为如果有自由配对的话,其实属于陌生人社交,而且自己看视频。这就可以让有些用户打着学外语的名义,做其他的事情。

现在变为付费的平台,筛选了教师端和学生端。马上大家的目的比起前单纯了。

上课

得知了这个网站当晚,我就预约了一个trial课程,5美金30分钟,一位波兰的英语老师。好吧,第一次上课我就是找个眼缘好点的老师,能和我说话就好。

说实话,主动预约和一个陌生人聊天,还是要有点勇气的,特别是说英文。说英文本来就不是我自信的点,很多观点用英文说会大打折扣。这段时间读了《小狗钱钱》伴有励志的故事后,我对很多摇摆的事,我更主动去做了。比如求职,比如录VLOG等等。做一些有点挑战的事,其实你会马上能感觉到生活更有趣,也没这么空虚。

所以,查来查去,挖掘什么背后的八卦产品逻辑,还不如直接去使用。体验一个产品带来的最核心的东西。

图为上课过程的截图,老师记录了一些我口语要纠正的问题

下一步的行动

  • 定期预约老师保持练习英文的节奏
  • 预约不同地区的英语老师,理解不同文化背景

WordPress

感觉wordpress也在不断进步。特别是文章的编辑界面挺不错的。现在感觉都走这种简洁风,和Notion,Medium类似。