在Node.js 我們可以通過以下代碼捕獲 uncaughtException 錯誤:

捕獲 uncaughtException 后,Node.js 的進(jìn)程就不會退出,但是當(dāng) Node.js 拋出uncaughtException 異常時就會丟失當(dāng)前環(huán)境的堆棧,導(dǎo)致 Node.js 不能正常進(jìn)行內(nèi)存回收。
也就是說,每一次、 uncaughtException 都有可能導(dǎo)致內(nèi)存泄露。既然如此,退而求其次,我們可以在滿足前兩個條件的情況下退出進(jìn)程以便重啟服務(wù)。
當(dāng)然還可以利用 domain 模塊做更細(xì)致的異常處理,這里就不做介紹了。
3. 如何編寫 Dockerfile
3.1 基礎(chǔ)鏡像選擇
我們先選用 Node.js 官方推薦的 node:argon 官方 LTS 版本最新鏡像,鏡像大小為 656.9 MB (解壓后大小,下文提到的鏡像大小沒有特殊說明的均指解壓后的大小)
我們事先寫好了兩個文件 package.json , app.js :


下面開始編寫 Dockerfile,由于直接從 Dockerhub 拉取鏡像速度較慢,我們選用時速云的docker官方鏡像docker_library/node(https://hub.tenxcloud.com/repos/docker_library/node),這些官方鏡像都是與 Dockerhub 實時同步的:

執(zhí)行以下命令進(jìn)行構(gòu)建:
docker build -t zhangpc/docker_web_app:argon .
最終得到的鏡像大小是 660.3 MB ,體積略大,Docker 容器的優(yōu)勢是輕量和可移植,所以承載它的操作系統(tǒng)即基礎(chǔ)鏡像也應(yīng)該迎合這個特性,于是我想到了 Alpine Linux ,一個面向安全的,輕量的 Linux 發(fā)行版,基于 musllibc 和 busybox 。
下面我們使用 alpine:edge 作為基礎(chǔ)鏡像,鏡像大小為 4.799 MB :

執(zhí)行以下命令進(jìn)行構(gòu)建:
docker build -t zhangpc/docker_web_app:alpine .
最終得到的鏡像大小是 31.51 MB ,足足縮小了20倍,運(yùn)行兩個鏡像均測試通過。
3.2 還有優(yōu)化的空間嗎?
首先,大小上還是可以優(yōu)化的,我們知道 Dockerfile 的每條指令都會將結(jié)果提交為新的鏡像,下一條指令將會基于上一步指令的鏡像的基礎(chǔ)上構(gòu)建。
所以如果我們要想清除構(gòu)建過程中產(chǎn)生的緩存,就得保證產(chǎn)生緩存的命令和清除緩存的命令在同一條 Dockerfile 指令中,因此修改 Dockerfile 如下:

執(zhí)行以下命令進(jìn)行構(gòu)建:
docker build -t zhangpc/docker_web_app:alpine .
最終得到的鏡像大小是 21.47 MB ,縮小了10M。
其次,我們發(fā)現(xiàn)在構(gòu)建過程中有一些依賴是基本不變的,例如安裝 Node.js 以及項目依賴,我們可以把這些不變的依賴集成在基礎(chǔ)鏡像中,這樣可以大幅提升構(gòu)建速度,基本上是秒級構(gòu)建。
當(dāng)然也可以把這些基本不變的指令集中在 Dockerfile 的前面部分,并保持前面部分不變,這樣就可以利用緩存提升構(gòu)建速度。
最后,如果使用了 Express 框架,在構(gòu)建生產(chǎn)環(huán)境鏡像時可以設(shè)置 NODE_ENV 環(huán)境變量為 production ,可以大幅提升應(yīng)用的性能,還有其他諸多好處,下面會有介紹。
小結(jié)

我們構(gòu)建的三個鏡像大小對比見上圖,鏡像的大小越小,發(fā)布的時候越快捷,而且可以提高安全性,因為更少的代碼和程序在容器中意味著更小的攻擊面。
使用 node:argon 作為基礎(chǔ)鏡像構(gòu)建出的鏡像(tag 為 argon)壓縮后的大小大概為 254 MB ,也不是很大。
如果對 Alpine Linux 心存顧慮的童鞋可以選用 Node.js 官方推薦的 node:argon 作為基礎(chǔ)鏡像構(gòu)建微服務(wù)。
4.微服務(wù)部署及 devops 集成
部署微服務(wù)時有一個原則:一個容器中只放一個服務(wù),可以使用stack 編排把各個微服務(wù)組合成一個完整的應(yīng)用: