2. Node.js 微服务开发环境搭建

微服务开发环境的核心诉求是“可复现”:同一份代码,在任何一台机器上都能用相同的 Node.js 版本、相同的依赖解析结果、相同的容器运行时启动。本节围绕四件事展开——Node.js 版本管理、npm 与 pnpm 工作区、Docker 与 Docker Compose 安装、项目目录结构初始化。它们不是四个孤立步骤,而是一条从“本机运行时”到“可编排服务”的搭建链路。

一、Node.js 版本管理:先锁定运行时

微服务通常由多个独立仓库或一个多包仓库组成,不同服务可能依赖不同 Node.js 主版本。若直接用系统包管理器安装单一 Node.js,升级或降级会互相干扰。推荐使用版本管理器,如 nvm(macOS/Linux)、nvm-windows 或 fnm。以 nvm 为例,安装后在项目根目录放置 .nvmrc:

# .nvmrc
20.11.1

# 使用
nvm install
nvm use
node -v   # v20.11.1

选择版本时优先考虑 LTS(长期支持)版本,例如 20.x 或 22.x。示例中选用 20.11.1 作为教学版本,实际项目应结合依赖库的 engines 字段与团队约定确定。除 .nvmrc 外,还应在 package.json 中声明引擎约束,让不满足条件的运行时尽早报错:

{
  "engines": {
    "node": ">=20.11.0 <21",
    "pnpm": ">=9.0.0"
  }
}

判断是否生效:执行 node -v 应输出与 .nvmrc 一致的主版本;若使用 pnpm,再执行 pnpm -v 确认包管理器版本。两者都对齐,运行时层才算稳定。

二、npm 与 pnpm 工作区:依赖如何被解析

npm 是 Node.js 自带包管理器,pnpm 则以内容寻址存储和硬链接著称,安装更快、磁盘占用更小,并且对多包工作区支持更严格。微服务项目常见形态是 monorepo:一个仓库内包含多个服务包和共享库。此时用 pnpm workspace 管理最合适。

在仓库根目录创建 pnpm-workspace.yaml:

packages:
  - "services/*"
  - "packages/*"

根 package.json 中声明私有并固定包管理器:

{
  "name": "microservices-root",
  "private": true,
  "packageManager": "pnpm@9.1.0",
  "scripts": {
    "dev:user": "pnpm --filter @ms/user-service dev",
    "build": "pnpm -r build"
  }
}

各服务通过 workspace:* 引用共享包,例如用户服务依赖共享日志库:

{
  "name": "@ms/user-service",
  "dependencies": {
    "@ms/shared-logger": "workspace:*",
    "fastify": "^4.26.0"
  }
}

执行 pnpm install 后,pnpm 会在根 node_modules/.pnpm 建立内容寻址存储,并在各包的 node_modules 中创建符号链接。判断工作区是否生效:运行 pnpm -r list --depth -1 应列出所有子包;运行 pnpm --filter @ms/user-service exec node -e "console.log(require.resolve('@ms/shared-logger'))" 应解析到工作区内的共享包路径,而不是远程下载的副本。

三、Docker 与 Docker Compose 安装

微服务最终要跑在容器里,本机需要 Docker Engine 与 Compose 插件。Linux 上可用官方脚本或发行版仓库安装;macOS/Windows 推荐 Docker Desktop,它已内置 Compose v2。安装后验证:

docker --version
docker compose version
docker run --rm hello-world

三条命令分别确认引擎、Compose 插件和镜像拉取运行链路正常。若 docker compose 报“不是命令”,说明只装了旧版 docker-compose,应升级到 Compose v2。安装完成后,把当前用户加入 docker 组可免 sudo(Linux):sudo usermod -aG docker $USER,重新登录后生效。

四、项目目录结构初始化

一个可直接起步的 monorepo 目录如下:

microservices/
├── pnpm-workspace.yaml
├── package.json
├── .nvmrc
├── docker-compose.yml
├── services/
│   ├── user-service/
│   │   ├── package.json
│   │   └── src/index.js
│   └── order-service/
│       ├── package.json
│       └── src/index.js
└── packages/
    └── shared-logger/
        ├── package.json
        └── src/index.js

初始化顺序建议:先建根目录与 .nvmrc,再写 pnpm-workspace.yaml 与根 package.json,然后创建 services/ 与 packages/ 下的子包,最后补 docker-compose.yml。Compose 文件先定义基础设施,例如 PostgreSQL 与 Redis,服务容器可后续再接入:

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ms
      POSTGRES_PASSWORD: ms_dev_pwd
      POSTGRES_DB: ms_dev
    ports:
      - "5432:5432"
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

执行 docker compose up -d 后,用 docker compose ps 查看容器状态,两个服务应为 running;再用 docker compose logs postgres 确认数据库已就绪。至此,运行时、包管理、容器编排、目录骨架四层就位,后续服务代码可以直接挂载进这套环境。

Node.js 微服务开发环境四层搭建链路 ① Node.js 运行时 nvm + .nvmrc 20.11.1 (LTS) engines 约束 node -v 校验 ② pnpm 工作区 pnpm-workspace.yaml services/* packages/* workspace:* 链接 pnpm -r list 校验 ③ Docker 引擎 Docker Engine Compose v2 插件 docker compose version hello-world 验证 ④ 目录骨架 services/ packages/ docker-compose.yml compose up -d docker-compose.yml 编排的基础设施与服务 postgres:16-alpine 5432:5432 ms_dev 数据库 redis:7-alpine 6379:6379 缓存 / 消息 user-service @ms/user-service 依赖 shared-logger order-service @ms/order-service shared-logger @ms/shared-logger workspace:*
版本管理、包管理、容器运行时、目录骨架四层中,任何一层版本漂移都会导致“本机能跑、换机失败”。把 .nvmrc、packageManager、docker-compose.yml 一并提交到仓库,是保证环境可复现的最低成本做法。
不要把 node_modules、.pnpm-store 或本地数据库数据目录提交到 Git;它们应由 pnpm install 与 docker compose up 重新生成。若 pnpm install 后子包仍解析到远程包,检查 pnpm-workspace.yaml 的 glob 是否覆盖了该子包路径。