原标题:Jenkins | 实战干货:基于 SSH Agent 容器打造支持多版本 Node.js 的 CI/CD 工作节点
解锁 Jenkins Agent 节点,构建基于 NVM 实现 Node.js 多版本环境切换的工作节点
前言简述
随着内部业务系统逐步进行微服务改造,以及实现前端页面和后端接口分离,前端项目也逐渐从单体架构转变为微服务架构。在微服务架构中,当前公司前端项目使用了 VUE 框架依赖于 Node.js 运行环境。并且在不同的前端项目中使用到不同版本的 Node.js ,在持续集成和交互(CI/CD)中可能需要在一个 Jenkins 构建环境中安装多个版本的 Node.js 环境,以满足不同前端项目的编译构建需求。
本文,作者将从自身企业实践案例为切入点,基于 jenkins/ssh-agent:jdk11 容器镜像,通过在容器中安装 nvm 管理工具,进而实现基于 nvm 在 Jenkins 工作节点中安装多个版本的 Node.js 环境,并在构建 Vue 项目时,根据项目需求选择合适的 Node.js 版本进行编译构建。
温馨提示:
Node.js 是一个开源的跨平台 JavaScript 执行环境,由 Linux 基金会的协作项目计划促成,其官网地址 [https://nodejs.org/]
Node Version Manager(NVM)是一个用于在机器上管理多个 Node.js 版本的工具,安装和更新说明参考地址 [https://nvm.nodejs.cn/docs/#installing-and-updating]
温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_dc1dx8voqcu3.html
实践之路
为了方便各位看友学习理解,作者会首先介绍如何在 Docker 容器中安装 NVM,最后在介绍通过 Dockerflie 编写基于 Jenkins 的 ssh-agent 镜像安装 NVM ,并根据业务需求进行选择安装 Node.js 版本以及软件源、缓存配置,在后续 CI/CD 中作为构建节点使用。
如何在 Docker 容器镜像中安装 NVM?
我们将介绍三种在 Docker 容器中安装 NVM 的方式:
- • 1.我们可以从 Docker Hub 上选择几个现成的 NVM 镜像,✅ 优点是简单快捷,适合快速测试或部署,❌ 缺点是镜像可能版本较旧,不够灵活,并且存在安全风险问题,毕竟你也不知道他在构建时有无夹杂私货,总结一点个人测试项目无所谓,公司项目还是老实构建吧。
- • 2.通过命令手动安装:适用于在已有的或新建的 Docker 容器中安装。
如果我们需要在一个已有的或新建的容器中安装 NVM,可以通过命令手动安装。Java(编程语言)
以 Ubuntu 容器为例,先启动一个交互式容器:
$ docker run -it --name ubuntu-demo ubuntu /bin/bash -c "echo 'Hello World'; /bin/bash"
接着安装 curl:
root@81o99fbb81e1:/# apt update && apt install curl -y
然后使用 curl 下载并安装 NVM, 该脚本将nvm存储库克隆到 ~/.nvm,并尝试将下面代码段中的源代码行添加到正确的配置文件(~/.bashrc、~/.bash_profile、~/.zshrc或~/.profile)
root@81o99fbb81e1:/# curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
weiyigeek.top-手动安装nvm图
安装完成后,我们需要手动加载 NVM:
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
然后即可安装指定版本的 Node.js,指定国内镜像仓库安装 vue 项目常用工具脚手架。
# 安装 LTS 版本
root@81o99fbb81e1:/# nvm install 24.15.0
# 安装 nrm rimraf yarn@1.22.22 vite 包
root@81o99fbb81e1:/# npm install --registry=https://registry.npmmirror.com/ -g nrm rimraf yarn@1.22.22 vite
# 切换为淘宝镜像源
root@81o99fbb81e1:/# nrm use taobao
验证安装结果:
root@81o99fbb81e1:/# nvm current
v24.15.0
⚠️ 踩坑提醒:每次新打开一个 shell 时都需要重新 source 加载 nvm.sh ,否则无法识别 nvm 命令。
- • 3.通过 Dockerfile 构建镜像,适用于需要自动化安装流程的场景(PS: 我们后续 Jenkins ssh-agent nodejs 环境工作节点便基于此方法)✅ 优点:安全、可复用、可定制、适合 CI/CD 流程,❌ 缺点:首次配置稍复杂,构建时间略长。
如果我们希望将安装过程自动化(适用于 CI/CD 作业),可以在交互式和非交互式容器中运行,最推荐的方式是使用 Dockerfile 构建自定义镜像。
FROM ubuntu:latest
ARG NODE_VERSION=24.16.0
# install curl
RUN apt update && apt install curl -y
# install nvm
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
# set env
ENV NVM_DIR=/root/.nvm
# install node
RUN bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION"
# set ENTRYPOINT for reloading nvm-environment
ENTRYPOINT ["bash", "-c", "source $NVM_DIR/nvm.sh && exec "$@"", "--"]
# set cmd to bash
CMD ["/bin/bash"]
此示例默认安装的是 nodejs 版本 24.16.0 长期版本。你也可以使用 docker build args 轻松覆盖版本,例如:安装最新的 v26.2.0 版本
docker build -t ubuntu-nvm:0.40.3 --build-arg NODE_VERSION=26.2.0 .
运行构建好的容器,另外,也可以再次手动安装其他版本的
$ docker run --rm -it nvm
root@c012e7d835d9:/# nvm ls
-> v26.2.0
nvm install 24.16.0
查看已安装版本
root@c012e7d835d9:/# nvm ls
-> v26.2.0
24.16.0
如何在 ssh-agent 镜像中安装 NVM 及 多版本 node.js 切换?
描述:在 CI/CD 或者直接运行 node.js 项目通常是需要自行构建镜像,但是为了方便各位看友学习理解,作者也将介绍如何在 Jenkins 官方提供的 ssh-agent 容器中安装 NVM 及多版本 Node.js 环境,主要用于构建前端项目生成静态资源。
当前作者实践的 Jenkins 版本为 2.387.3 需使用 jdk11 版本的 Agent 环境,这里官方提供的 ssh-agent 镜像为 jenkins/ssh-agent:jdk11,在之前的知识星球中的《Jenkins | Agent 简单介绍及基于 SSH 协议使用Docker容器化实现分布式构建节点》文章,作者讲解了如何使用 ssh-agent 镜像创建一个容器化的Jenkins Agent 工作节点。
- • 步骤 01:编写 Dockerfile 构建镜像
FROM jenkins/ssh-agent:jdk11
ARG NODE_VERSION=24.15.0
# 安装 curl、 nvm 自定义安装目录
RUN apt update && apt install curl -y && mkdir -vp /home/jenkins/cache/ /opt/nvm && echo"storage" > /home/jenkins/cache/storage && chown -R jenkins:jenkins /home/jenkins/cache/ && curl -o- https://gh-proxy.org/https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | NVM_DIR=/opt/nvm bash
# 设置环境变量
ENV NVM_DIR=/opt/nvm
ENV PATH="/opt/nvm/versions/node/v24.15.0/bin:${PATH}"
# 安装指定版本的 Node.js 版本,并安装常用工具包
RUNchown -R jenkins:jenkins /opt/nvm && echo'export NVM_DIR="/opt/nvm"' >> /home/jenkins/.bashrc && echo'[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"' >> /home/jenkins/.bashrc && echo'[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"' >> /home/jenkins/.bashrc && bash -c "source $NVM_DIR/nvm.sh && nvm install ${NODE_VERSION} && npm install --registry=https://registry.npmmirror.com/ -g nrm rimraf yarn vite && nrm use taobao && apt clean all && rm -rf /tmp/*"
温馨提示:若需要安装其他版本的 Node.js,只需在构建时通过 --build-arg 参数指定即可;此外,对于 yarn 有特定版本需求的可拼接 @版本号,例如 yarn@1.22.22。
- • 步骤 02:构建镜像并推送至私有仓库(可选),私有仓库搭建部署请参考 [Harbor | 企业私有镜像存储仓库基础介绍与快速安装][https://articles.zsxq.com/id_ohwha1duwzox.html] 教程。
# 镜像构建
docker build -t jenkins/ssh-agent:jdk11_nvm .
# 指定版本
docker build --build-arg NODE_VERSION=26.2.0 -t jenkins/ssh-agent:jdk11_nvm .
# 镜像推送
docker tag jenkins/ssh-agent:jdk11_nvm hub.weiyigeek.top/library/jenkins/ssh-agent:jdk11_nvm
docker push hub.weiyigeek.top/library/jenkins/ssh-agent:jdk11_nvm
# 运行验证
docker run --rm -it jenkins/ssh-agent:jdk11_nvm bash
root@dacd36688106:/home/jenkins# export
declare -x NVM_BIN="/opt/nvm/versions/node/v24.15.0/bin"
declare -x NVM_CD_FLAGS=""
declare -x NVM_DIR="/opt/nvm"
declare -x NVM_INC="/opt/nvm/versions/node/v24.15.0/include/node"
declare -x OLDPWD
declare -x PATH="/opt/nvm/versions/node/v24.15.0/bin:/opt/java/openjdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
root@dacd36688106:/home/jenkins# nvm ls
-> v24.15.0
system
default -> 24.15.0 (-> v24.15.0)
iojs -> N/A (default)
unstable -> N/A (default)
node -> stable (-> v24.15.0) (default)
stable -> 24.15 (-> v24.15.0) (default)
lts/* -> lts/krypton (-> N/A)
lts/argon -> v4.9.1 (-> N/A)
lts/boron -> v6.17.1 (-> N/A)
lts/carbon -> v8.17.0 (-> N/A)
lts/dubnium -> v10.24.1 (-> N/A)
lts/erbium -> v12.22.12 (-> N/A)
lts/fermium -> v14.21.3 (-> N/A)
lts/gallium -> v16.20.2 (-> N/A)
lts/hydrogen -> v18.20.8 (-> N/A)
lts/iron -> v20.20.2 (-> N/A)
lts/jod -> v22.22.3 (-> N/A)
lts/krypton -> v24.16.0 (-> N/A)
root@dacd36688106:/home/jenkins# nvm current
v24.15.0
root@dacd36688106:/home/jenkins# node --version
v24.15.0
root@dacd36688106:/home/jenkins# npm --version
11.12.1
root@dacd36688106:/home/jenkins# npm version
{
npm: '11.12.1',
node: '24.15.0',
acorn: '8.16.0',
ada: '3.4.4',
amaro: '1.1.8',
ares: '1.34.6',
brotli: '1.2.0',
cldr: '48.0',
icu: '78.2',
llhttp: '9.3.1',
merve: '1.2.2',
modules: '137',
napi: '10',
nbytes: '0.1.3',
ncrypto: '0.0.1',
nghttp2: '1.68.0',
openssl: '3.5.5',
simdjson: '4.5.0',
simdutf: '6.4.0',
sqlite: '3.51.3',
tz: '2026a',
undici: '7.24.4',
unicode: '17.0',
uv: '1.51.0',
uvwasi: '0.0.23',
v8: '13.6.233.17-node.48',
zlib: '1.3.1-e00f703',
zstd: '1.5.7'
}
weiyigeek.top-镜像运行验证环境图
步骤 03:在 docker 环境或者 K8S 工作节点中运行此镜像,作者以 Docker 环境为例,运行如下命令:
# 创建数据卷目录,用于缓存包
mkdir -vp /data/jenkins-ssh/nodejs
docker run -d --name=jenkins-ssh-nodejs
-p 2223:22
-v /data/jenkins-ssh/nodejs:/home/jenkins
-v /var/run/docker.sock:/var/run/docker.sock
-v /usr/bin/docker:/usr/bin/docker
-v /usr/bin/kubectl:/usr/bin/kubectl
--add-host git.weiyigeek.top:192.168.12.6
-e "JENKINS_AGENT_SSH_PUBKEY=ssh-rsa AAAAB3NzaC1yc2EAAAAD*****10kjaCQA0sp4Uij6ykY0bd8= jenkins@d6b80d5b2fb6" harbor.weiyigeek.top/library/jenkins/ssh-agent-nodejs:v1.3
温馨提示:在运行镜像的宿主机中安装了 docker、kubectl 工具,主要用于 Jenkins 构建作业时,可以执行 docker、kubectl 相关命令,当然若采用多个安装了相关工具的 jenkins 工作节点则可忽略,但是对于 JENKINS_AGENT_SSH_PUBKEY 环境变量,请替换为 Jenkins 服务器中的公钥。
步骤 04:在 Jenkins 中添加 ssh 节点,如何添加请参考 [Jenkins | Agent 简单介绍及基于 SSH 协议使用Docker容器化实现分布式构建节点][https://articles.zsxq.com/id_zsmm2hm0w6bl.html] 星球文章进行配置。
主要操作流程:点击 Dashboard 》系统管理 》节点列表 ,再点击 New Node 按钮,特别注意启动方式选择 Launch agent via SSH ,以及节点标签后续在 Jenkins 流水线中会进行待用,关键配置如下图所示:
weiyigeek.top-添加工作节点图
步骤 05:添加成功后可在节点列表中查看到,在 Jenkins 流水线中使用此镜像构建作业如下所示(PS: 部分脚本式的 groovy 代码):
// [Jenkins Pipeline 流水线 开始]
pipeline {
// 关键点1:流水线运行的主机绑定,此处利用动态的K8s节点或docker运行的节点进行构建
agent {
label 'nodejs'
}
......
// 注意 从 0.5 版开始,阶段中的 container 必须位于“steps”块中。
// container('nodejs') {
script {
try {
// 关键点2:动态设置 nodejs 版本,此处以 v24.15.0 版本为例以及为 yarn 配置缓存。
sh """
ln -s /opt/nvm/versions/node/v24.15.0/bin/node /usr/bin/node; /opt/nvm/versions/node/v24.15.0/bin/yarn config set registry https://registry.npmmirror.com
&& /opt/nvm/versions/node/v24.15.0/bin/yarn config set global-folder "/home/jenkins/cache/.yarn_global"
&& /opt/nvm/versions/node/v24.15.0/bin/yarn config set cache-folder "/home/jenkins/cache/.yarn_cache"
&& /opt/nvm/versions/node/v24.15.0/bin/yarn
"""
if ( config.CI_ENVIRONMENT != "prod" ) {
echo "Test 环境"
sh '/opt/nvm/versions/node/v24.15.0/bin/vite build --mode pressuretest'
} else {
echo "Prod 环境"
sh '/opt/nvm/versions/node/v24.15.0/bin/vite build --mode production'
}
println "暂时取消vue构建"
updateGitlabCommitStatus name:'项目构建', state:'success'
} catch(Exception err) {
echo err.toString() /* hudson.AbortException: Couldn't find any revision to build. Verify the repository and branch configuration for this job. */
unstable '项目构建失败'
updateGitlabCommitStatus name:'项目构建', state:'failed'
error "[-Error] : 项目构建失败n [-Msg] : ${err.getMessage()} "/* 构建停止 */
}
}
......
步骤 06:运行流水线验证构建的镜像是否可用,如下所示:
weiyigeek.top-Jenkins流水线运行验证图
至此,作者实践了在 Docker 容器化三种安装 nvm (Node.js) 环境的方式,并实现了动态切换不同版本的 Node.js 环境思路,最后介绍实践了 jenkins 如何通过 ssh 对接我们运行的 nodejs 构建环境,希望此实践能对大家有所帮助。
152