atian25 / blog

天猪部落阁 http://atian25.github.io
1.59k stars 107 forks source link

科普文:为什么不能在服务器上 npm install ? #30

Open atian25 opened 6 years ago

atian25 commented 6 years ago

背景

Node.js 很简单,容易上手。但也因此缺乏不少规范,使用者水平参差不齐。

最近经常看到的一个问题是:很多新手,在部署的时候,是直接在服务器上 npm install ,这是非常不推荐的。

存在的问题

无法确定唯一性

因为安装是有较大的网络耗时的,所以你甚至无法保证集群情况下,两台服务器上npm install 下来的包是一模一样的。

如果某个库刚好更新了,并且它有 BUG,然后你就是百思不得其解:一定概率出某个问题。排查起来简直想死。

当然,很多人为了解决这个问题,就选择「锁版本」这个方案。

鉴于 「锁版本」这个方案属于 「屎色自行车问题」 ,这里不想讨论,我们的观点参见:「知乎专栏 - 死马:为什么我不使用 shrinkwrap(lock)」

上线耗时久,无法快速回滚

上线后,发现线上故障,要快速回滚止血的时候,就懵逼了:

  1. 要等待依赖安装,万一网络有个抖动啥的,妥妥的 P4 故障变为 P0 故障,年终奖没了。
  2. 万一问题是底层依赖导致的,回滚也没用。
  3. npm cache 解决不了问题,如机器扩容的时候。

推荐方案

development lifecycle

其中,关键点是:在构建期就把依赖打包进去。

优点:

  1. 解压即可立刻启动,无需等待网络耗时。
  2. 能保证肯定是可以运行的,因为此时依赖都是确定好的,且经过 CI 单测保障的。
  3. 可以快速回滚,止血。

缺点:

  1. 包体积大(但其实存储不值钱。。。)

流程图

如何实施

那有同学就要问了:我是小公司,不像你们有这些基建可以服务,怎么办?

其实成本真的很低:

广告区

atian25 commented 6 years ago

最后那张图,是在语雀上直接用 PlantUML 画的。

PlantUML 的优势是:

一般来说,你不需要太关注语法和布局,很自然的画即可。

当然,它提供了不少特性,可以让你画的更好看点。

如下就是那张图的源码:

@startuml

autonumber
hide footbox
title **Node 应用研发部署流程**

actor "开发者" as user
participant "代码仓库(GitLab)" as gitlab
participant "持续集成系统" as ci
participant "运维发布系统" as sys #orange
participant "服务器" as server

== 研发流程 ==
user -> gitlab: 提交代码
activate user
deactivate user
activate gitlab
gitlab -> ci: 单元测试
deactivate gitlab
activate ci
note right of ci: tar 或 docker
ci -> ci: 打包
ci -> sys: 推送
note right of sys: 存储到 oss
activate sys #orange
deactivate ci
deactivate sys

...

== 上线/回滚流程 ==
user -> sys: 选择要上线的版本
activate sys #orange
activate user
deactivate user
sys -> server: 推送
activate server
note right of server: **无需--安装依赖--,快速且确定**
server -> server: 解压
server -> server: 启动
server --> sys

@enduml

看起来好像有点复杂,其实只是因为我有强迫症,喜欢用 activate/deactivate 来改变生命周期。 可以简化为:

@startuml

autonumber
hide footbox
title **Node 应用研发部署流程**

actor "开发者" as user
participant "代码仓库(GitLab)" as gitlab
participant "持续集成系统" as ci
participant "运维发布系统" as sys #orange
participant "服务器" as server

activate user
activate gitlab
activate ci
activate sys #orange
activate server

== 研发流程 ==
user -> gitlab: 提交代码
gitlab -> ci: 单元测试
note right of ci: tar 或 docker
ci -> ci: 打包
ci -> sys: 推送
note right of sys: 存储到 oss
...

== 上线/回滚流程 ==

user -> sys: 选择要上线的版本
sys -> server: 推送
note right of server: **无需--安装依赖--,快速且确定**
server -> server: 解压
server -> server: 启动
server --> sys

@enduml

image

IEfucker commented 6 years ago

具体操作没看懂,是说把依赖包也git提交吗,回滚的版本也是通过git控制?

atian25 commented 6 years ago

@IEfucker 跟 git 没啥关系,最后那张图不是画的很清楚了么,在 CI 上打包,推送到 oss 或 dockerlab,然后发布系统这边记录对应的地址即可。

YardWill commented 5 years ago

其实docker是一个更好的解决方案,一旦打包完之后就可以直接push上去,服务器只需要pull就好了,还能配合k8s做动态扩容。

atian25 commented 5 years ago

正文一开始就声明了吧,docker 是终极解决方案。本方案是分享给那些没有条件的小团队使用。

Gerhut commented 5 years ago

跑单元测试时候的依赖版本和推送到发布系统的依赖版本需要保持一致吗?

atian25 commented 5 years ago

跑单元测试时候的依赖版本和推送到发布系统的依赖版本需要保持一致吗?

必然啊,跑完单元测试后,直接打包推送发布系统了。

PS:这里会有一个小 GAP 需要权衡,devdep 怎么办,是 prune 了重新 install --prod,还是直接打包。

Gerhut commented 5 years ago

OK 我把打包错理解成 docker build (install + pack) 了

对于你的问题,npm prune --production 可解

wotermelon commented 3 years ago

那你保证发布机器和生产机器系统以及其他因素都一样吗? 否则怎么保证一些node-gyp rebuild的依赖可以在这些机器上通用?

问题不在于依赖应不应该在服务器上安装,而在于如何优雅地启动服务,包括健康检查、流量切换

atian25 commented 3 years ago

镜像啊

genffy commented 10 months ago

https://yuque.com/yuque/help/editor-puml#comment-82306 404 了,对应的应该是 https://www.yuque.com/yuque/gpvawt/editor-puml