本文来探讨一下nodejs系统和应用的部署和管理。主要包括nodejs系统本身,nodejs应用和应用所使用或者依赖的库和工具(也称为npm)等几个部分。
nodejs程序文件结构
首先,我们来看看nodejs程序,是如何以系列文件和目录的形式,存在于一个操作系统的文件系统当中的,就是所谓的nodejs程序文件结构。为了方便起见,我们只讨论nodejs在Linux系统下的情况。在默认情况下,安装完成之后,nodejs相关文件和程序,会分布在以下位置:
- /usr/bin: 包括node、npm、npx等程序文件
- /usr/include/node: 头文件,用于扩展的开发和编译
- /usr/lib/node_modules: 全局库文件和模块
- /usr/share/{doc/node|man/man.1/node1.}: 文档和帮助信息
这里面,最重要的主程序,就是一个单一可执行文件 /usr/bin/node,大小约为96M。而我们熟悉的nodejs官方配套的npm软件包管理命令,不是一个可执行文件,而是一个软连接符号,链接到lib/node_nodules/npm模块目录中的npm-cli.js程序文件。比如我们列表bin目录看到的那样:
shell
$ ls node-v20.10.0-linux-x64/bin -l
total 93976
lrwxrwxrwx 1 yanjh yanjh 45 Nov 22 19:42 corepack -> ../lib/node_modules/corepack/dist/corepack.js
-rwxr-xr-x 1 yanjh yanjh 96227728 Nov 22 19:42 node
lrwxrwxrwx 1 yanjh yanjh 38 Nov 22 19:42 npm -> ../lib/node_modules/npm/bin/npm-cli.js
lrwxrwxrwx 1 yanjh yanjh 38 Nov 22 19:42 npx -> ../lib/node_modules/npm/bin/npx-cli.js
作为跨平台的应用,nodejs也可以按照到Windows或者MAC系统之下,它们也会有类似的目录结构,细节可能有所不同,但基本逻辑应该是相同的,由于在生产环境中,这种部署方式比较少见,我们就不深入讨论了。
了解和熟悉这些内容,可以让我们更好的理解nodejs的安装和部署过程,后续可以更好支持进行手动安装或者故障排查等操作。
nodejs版本和系统安装
在不同的操作系统上,nodejs系统的安装和配置方式略有差异,但基本上都比较简单。比如Windows系统,提供了MSI安装文件,只需要下载执行即可。Linux系统的情况稍微复杂一点。我们以debian系统为例,其安装命令代码如下:
shell
sudo -i
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - &&\
apt-get install -y nodejs
node -v
npm -v
debian安装需要先切换到管理员角色;然后下载和执行一个在线的安装脚本,这个脚本将检查安装环境,并进行相关的设置(软件库配置、公钥等等); 并更新软件库;最后作为一般的软件进行安装,软件包名为"nodejs"。最后使用node和npm命令来检查是否安装正确和确认当前使用的版本。
遗憾的是,虽然nodejs支持很多类型的操作系统,但对于不同的操作系统,它的安装方式都略有差异,没有一个通用或者统一的方式,详见相关的安装和技术文件。
nodejs系统手动离线安装
在笔者的工作环境中,有很多是不方便直接连接互联网来进行支撑软件的安装的。作为一个基础支撑软件,就需要研究和实现nodejs的离线安装。我们以linux系统为例,简单的说明一下这个过程。
1 下载nodejs的二进制版本
可以到nodejs下载的主页面,看到并下载预编译好的二进制程序包:
这里的linux预编译版本,只有x64和arm两个选择,我们选择并点击Linux Binaries(x64),这个链接是一个.xz文件,也可以直接使用wget下载,如:
下载后,也是一个.xz文件。整个二进制软件包,压缩的大小仅为不到30M。
2 传输到部署系统
我们可以通过其他任何方式,比如U盘,或者内网的ftp,将这个文件复制到要安装的操作系统之上。一般而言,对于主流的操作系统发行版本,都不会有兼容性的问题。
3 解压缩
这里需要注意的是,这个文件的压缩方式是xz,对应可以使用tar命令将其解压:
tar -xvf node-v20.10.0-linux-x64.tar.xz
ls node-v20.10.0-linux-x64
bin CHANGELOG.md include lib LICENSE README.md share
如果不能正常解压缩,可能需要安装xz工具,或者将其在工作电脑上解压后,以文件夹的方式复制到部署系统上。正常解压后,可以看到软件的目录结构,包括bin、include、lib、share等文件夹。
4 复制文件夹
我们可以将这个目录结构,复制到合适的位置:
sudo cp -r node-v20.10.0-linux-x64/{bin,include,lib,share} /usr/
作为系统基本的应用程序安装,这里需要管理员权限。
5 检查安装
node是一个可执行程序,配套的npm是一个js程序,它们都在/usr/bin文件夹中,可以使用以下命令来检查和执行:
node -v
npm -v
如果看到正确的输出版本,就表明nodejs已经基本安装完成了。
NPM
nodejs作为一个比较成熟的Web应用开发平台,一个重要的特点和优势就是在长时间的发展过程当中,逐步建立了一个丰富而强大的生态系统,其重要表现就是拥有一个以npm技术为核心的软件包管理工具。
特点和优势
狭义而言,NPM(Node Package Manager)是 Node.js 的包管理程序,它用于基于互联网来在线管理和分发JavaScript代码和软件包。和传统的程序库和管理方式相比,NPM提供了更加简单易用而强大的特点和功能,包括:
- 内容和形态
npm提供的不仅仅是扩展的程序库,实际上它提供的软件包的功能和形态非常丰富,不仅仅是扩展库,可能还包括各种框架、工具等等。包括了几乎所有的技术类型、方向和形态,可谓五花八门,博大精深,应有尽有。据一个不完整的统计,在2022年9月,npm软件仓库中大约容纳了210万个软件包项目,应该世界上单一编程语言最大的代码仓库。
下面是一些应用比较广泛的包:
- 命令行工具
可以使用简单的命令行操作,来搜索、管理、安装、更新、卸载软件包。npm还提供了多种类型的执行脚本的支持。来帮助在开发过程中的执行、调试等工作。
- 简单配置
每个Node.js项目都可以通过一个package.json配置文件来进行npm的管理,在软件包和依赖方面,它包含了项目的元数据、依赖项列表、脚本命令等。这个文件可以由NPM创建和管理,也可以手动编辑和配置,然后使用npm命令来实现变更。这个文件就是标准的JSON格式,配置的项目和操作的过程都非常简单直观,易于使用。
- 在线的全球软件包仓库
NPM建立并提供了一个全球性的软件包仓库(NPM Registry),全世界的开发者都可以方便的从这个仓库中查找、下载、集成软件包到自己的应用当中,也可以编写、发布自己的软件包或者功能模块到这个库当中来帮助别人。大家相互支持发展合作,促进整个nodejs开源社区和生态的发展和合作。
- 依赖管理
NPM可以帮助自动的管理项目所需的依赖项,一般开发者会在项目中引入一些主要的扩展库和模块,并且在package.json 文件中进行记录。然后使用npm install命令,npm会自动的搜索、下载和配置这些扩展模块所依赖的软件包项目,不需要开发者关心和配置额外的依赖项目。
依赖黑洞
可能是由于npm提供的依赖管理机制太过于方便,随着软件库的日益庞大,它们之间的依赖关系也越来越复杂,有被滥用和无法控制的趋势。下面这张图,就在讽刺npm的复杂和臃肿。
所以,开发者在选择npm的时候,也尽量优先选择比较简洁,使用广泛,依赖关系比较简单的软件包。
安装和配置
npm和其仓库的官方站点在: www.npmjs.com/
nodejs的安装过程,就包括了npm的安装,可以直接基于命令进行使用。但由于一些网络技术方面的原因,在中国直接使用NPM的体验并不是很好,主要表现在软件包的下载速度比较慢,连接也不是很可靠,因为它们的默认的主站和服务器都在国外。针对这种情况,国内的互联网业者在国内建立了一些镜像来改善这个问题,其中主要和广泛使用的就是淘宝提供的镜像源。需要注意,这个源在2023年使用了新的域名,所以下面的配置方式就是基于这个新域名的。
使用这个镜像,需要一些配置工作,有两个方法。一个是安装一个cnpm的工具来替换npm命令;另一个是修改npm软件仓库的默认设置,指向镜像仓库;笔者一般使用后者,操作过程和命令如下:
shell
// 查看当前源
npm config get registry
// 修改使用镜像源
npm config set registry https://registry.npmmirror.com/
// 还原成为默认官方源
npm config set registry https://registry.npmjs.org/
// 如果要安装 cnpm
npm install -g cnpm --registry=https://registry.npmmirror.com/
// 使用 cnpm
cnpm install fastify async
常用命令
npm安装完成之后,一般是作为全局命令来进行工作的。我们可以通过 npm help 来看一下它提供的功能:
shell
yanjh@WK-YANJH-AMD:~$ npm help
npm <command>
Usage:
npm install install all the dependencies in your project
npm install <foo> add the <foo> dependency to your project
npm test run this project's tests
npm run <foo> run the script named <foo>
npm <command> -h quick help on <command>
npm -l display usage info for all commands
npm help <term> search for help on <term>
npm help npm more involved overview
All commands:
access, adduser, audit, bugs, cache, ci, completion,
config, dedupe, deprecate, diff, dist-tag, docs, doctor,
edit, exec, explain, explore, find-dupes, fund, get, help,
help-search, hook, init, install, install-ci-test,
install-test, link, ll, login, logout, ls, org, outdated,
owner, pack, ping, pkg, prefix, profile, prune, publish,
query, rebuild, repo, restart, root, run-script, sbom,
search, set, shrinkwrap, star, stars, start, stop, team,
test, token, uninstall, unpublish, unstar, update, version,
view, whoami
Specify configs in the ini-formatted file:
/home/yanjh/.npmrc
or on the command line via: npm <command> --key=value
More configuration info: npm help config
Configuration fields: npm help 7 config
npm@10.2.5 /usr/lib/node_modules/npm
可以看到,npm提供了丰富而强大的功能。但我们日常使用,只是其中很小的一部分,比如下面的一些命令和使用场景包括:
- 安装全局包
一般用于安装一些全局可以使用工具,如pm2等。
sudo npm i pm2 -g
这里i是install的简写。-g是全局安装命令标签。
简单而言,全局安装需要管理员权限(sudo),会将软件包安装到全局模块文件夹中(/usr/lib/node_modules);而普通安装,只影响当前项目的内容,比如它会将软件包安装到项目文件夹的node_modules文件夹中。两者没有直接的逻辑关系。
如果要想在本地项目中,使用全局的npm包,可以使用npm link命令,它会项目中创建一个全局包的软链接。这种情况通常用于在开发和测试环境中共享一些常用的软件包。
- 项目初始化
结合使用init命令,npm可以为当前文件夹建立nodejs项目配置信息,从而创建nodejs开发项目。
shell
$ npm init -y
Wrote to /home/yanjh/ntest/package.json:
...
可以看到,这个init命令,实际上就是在当前文件夹中,创建一个package.json文件,这个就是用来控制当前nodejs程序项目的配置文件。这里的-y标签,就是yes,无需交互使用默认配置进行操作,当然,这些默认创建的内容,随后都可以使用命令或者手动进行编辑。
关于package.json文件,这部分的内容其实比较关键和重要,笔者在后续有一个独立的章节深入详细讨论。
- 安装新软件包(并保存到package.json)
可以使用install子命令,在当前项目中安装所需要的软件包。这个命令可以直接指定npm的名称,执行时npm会在全球在线仓库中搜索、下载并在本地项目中安装这个软件包。这个过程是在线并且自动执行的。
npm i async --save
默认情况下,npm会将软件包,安装到当前项目的node_modules文件夹中,但不会修改package.json中相关的配置。而--save选项可以在package.json中增加这个软件包的信息。这样,如果项目进行了移植,下次就可以使用install自动进行安装。
- 基于package.json配置安装软件包
npm i
i,就是install的简写。这个命令,会搜索当前package.json配置文件中(以前已经配置并且使用的)软件包的信息,进行自动化的安装,就和指定软件包名称一样,但可以批量的进行操作。这种方式通常在项目移植或者迁移过程中使用。
- 更新npm版本
npm自身也是一个在不断升级中的软件,可以使用基本相同的方式对npm自己进行升级。基本的操作命令如下:
sudo npm i npm -g
npm模块离线安装
除了nodejs系统本身之外,我们的nodejs应用开发和运行,可能会依赖一些第三方库或者软件包。在正常的情况下,它们可能会使用前面提到的方式,使用npm命令来进行安装,但在离线的情况下,是没有在线的npm仓库可以使用的。
这时笔者摸索出的一种解决方案是:
用和生产环境相同的操作系统和软件环境基本相同的一个系统,部署一个可以正常使用npm环境,在正常安装npm软件包之后,将node_modules文件夹,整体复制到生产环境的相同的位置,一般就是项目文件的node_modules文件夹。
这里要注意的几个问题:
- 测试环境和生产环境的操作系统和支撑软件尽量使用相同的配置,包括但不限于操作系统类型、版本、内核版本,用户名词、目录结构、node版本、其他支撑软件的版本和配置(如数据库客户端、OpenSSL..)等等。
- 如果遇到需要进行编译操作的npm软件,还需要特别注意编译器和编译环境的一致
另外还有一种方式是有些npm会提供安装包文件,或者可以手动将已有模块打包,然后可以使用npm指定安装包文件来直接安装,例如:
npm pack
npm i -g my_module-0.0.1.tgz
PM2
由于本文的篇幅和内容限制,这部分内容独立成章,详见其他相关章节。
小结
本文主要探讨了nodejs环境的安装、配置和管理方面的内容。如nodejs程序文件结构,npm包管理的机制和操作,package.json文件,以及使用pm2进行nodejs应用管理等等。