Skip to content

科普文:服务器上如何 Node 多版本共存 #31

@atian25

Description

@atian25

背景

很多公司的服务器环境没有做隔离,就是全局安装一个 Node.js Runtime,一般很少升级。

nvs / nvm 等可以用来切换版本,但无法同时共存。而且一般服务器不允许你随意升级。

因此很多同学都会很痛苦:「都 8012 年了,还是 Node 4.x 甚至 0.x 简直想死!」

时至今天,最好的办法就是 Docker。但奈何很多小公司还处于水深火热之中。

本文将介绍下我们很早前就使用的一套方案,可以完美解决非 Docker 情况下 Node 多版本共存问题。

npm scripts

首先要介绍下 npm scripts ,简单的说,就是可以在 package.json 里面定义脚本。

{
  "name": "egg-showcase",
  "scripts": {
    "start": "node index.js",
    "debug": "egg-bin debug --inspect-brk"
  },
  "devDependencies": {
    "egg-bin": "^4.7.0",
  }
}

如上,定义脚本后即可执行:

$ npm start
$ # 执行并传参,需要多一个 --
$ npm run debug -- --inspect-brk

同学们可能会比较好奇,上面的 egg-bin 是哪来的?这是 npm 的一个很重要的特性:

通过 npm run your-scripts 启动的脚本,会默认把 node_modules/.bin 加到 PATH 环境变量中。

由于,我们的依赖 egg-bin 有定义了 bin 字段 ,因此安装后会软链到 node_modules/.bin ,从而能被寻址并执行。

聪明的同学很快就会想到,如果 Node 的 Runtime 也在这个目录下,会怎么样呢?

Node Runtime

因此,问题就可以转换为:如何把 Node Runtime 打包到项目中?

答案就是在构建期打包进去,参见我们的上一篇文章 『科普文:为什么不能在服务器上 npm install ?』

🙋🙋🙋 老师!打包能否更简单一点呢?能否就像 dependencies 那样定义一下就好了呢?

没问题!方案有几种:

  • 我们很早前写了一个 nodeinstall 库来简化这个步骤:
  • Node 8 后支持了类似的特性

nodeinstall

定义:

{
  "name": "egg-showcase",
  "scripts": {
    "start": "node index.js",
    "debug": "egg-bin debug --inspect-brk",
    "echo": "node -p process.versions"
  },
  "devDependencies": {
    "egg-bin": "^4.7.0",
  },
  "engines": {
    "install-node": "^8.0.0",
    // AliNode 的话用这个
    "install-alinode": "^3.8.0"
  }
}

安装:

$ npm install nodeinstall -g

$ cd path/to/your/project
$ nodeinstall

$ # 验证
$ npm run echo

node: '8.10.0'

就这么简单,从此服务器上只要有一个任意版本的 npm 即可,各项目都可以用自己的 Node 版本,不会互相影响。

如果你是阿里员工,这一步都可以省了,因为 tnpm 内置就支持这个配置,无需单独安装 nodeinstall

但需要注意的是:

  • 必须在跟你线上服务器一样操作系统的 CI 上去打包,否则如果你是在本地 Windows 下打包的,线上却是 Linux,那对应的 Node Runtime 就不对了。
  • 记得写文档
    • 经常有同学会说,我 Node 是 8 的,但为啥不支持 async 。
    • 这时候让他执行 ./node_modules/.bin/node -p process.versions 就知道了。

Node 8 内建支持

另外,除了我们写的这个工具外,在 Node 8 里面,官方终于也支持了类似的功能。

不过它是在 postinstall 里面安装的,没有优先安装 Node ,会导致 Native Addons 依赖有问题(应该先安装 Node,再用这个版本去安装其他依赖)。现在不知道改了没。

挺期待官方完善这个特性的,这样我们的又一个轮子可以完成历史使命,退休了。

写在最后

那如果我有服务器权限,我可以随意升级 Node 版本,就不需要了吧?
我们 Node 工程化的理念是一个包可以快速部署,这样就不依赖 PE 来配置环境了。

如果应用多了,服务器就得根据应用名配置 Node 版本,这样应用升级就得分开两步操作。

当然,最优的解决方案,还是 Docker 化隔离,都是一样的思路,一份产物部署的理念。

广告区

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions