当前位置: 首页 > news >正文

《拿下奇怪的前端报错》:nvm不可用报错`GLIBC_2.27‘‘GLIBCXX_3.4.20‘not Found?+ 使用docker构建多个前端项目实践

有些前端的小伙伴可能会好奇,nvm是什么?这里接简单介绍下,它是一个Nodejs版本管理工具。为什么需要它呢?当然是需要多个Nodejs版本的时候,那什么时候需要多个Nodejs版本?那肯定是在有点年头的公司了,例如vue2的很多依赖都是在Nodejs14年代的,如果你升级到Node20,那很可能会报错。如果你用vite创建新项目,那也肯定是没法在Node14下运行。(如果你问为啥要Nodejs,欢迎留言,或者看一部它的纪录片哈)

迭代过快虽然能够享受到各种新科技,但对于很多公司来说,去适配nodejs版本也是不太值得投入的成本。ε=(´ο`*)))唉,想想前端就累呀

报错信息具体内容

# node -v
node: /lib64/libm.so.6: version `GLIBC_2.27' not found (required by node)
node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by node)
node: /lib64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by node)
node: /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found (required by node)
node: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by node)
node: /lib64/libc.so.6: version `GLIBC_2.25' not found (required by node)

nodejs都不可用,更别说构建了,如果构建不出东西,在这个依赖框架的年代,就相当于白干啊!突然怀念jquery的时代了,(#^.^#)

触发场景

  1.  有老的vue项目,需要nodejs14 (这个必须的,18+就构建不了!)
  2. 有新的vue3项目,需要nodejs20+ (vite需要的,ε=(´ο`*)))唉,自己开发是挺爽,但没想到CI这里出问题了哈)
  3. 必须要jenkins上部署和构建(jenkins跑在linux上,之前我被windows server版docker搞的头发掉了一大撮)
  4. 构建物必须是一个docker镜像,这几个项目放在一个镜像(就是要一次构建多个项目)

好像上面是这个时代比较通用的了,于是便很自然的安装了nvm,构建不同项目之前,用nvm切换下。

问题分析

但是,当我构建vue3项目是,就遇到上面的报错了。呃,搜索一通,么得什么头绪,我发现切回去node16就可以了,啊,竟然是版本问题,后面发现是宿主机的centos版本太低了,我的天!我没想到一个前端竟然遇到这个问题,也算是长姿势了!

于是这时候就有个解决办法了,那就是让运维的部署点高版本的centos吧!提了个需求,但不知道何时才能实现,等等等

解决办法

看到这里,问题大概就是找到了,也可以解决了:那就是升级centos版本

解决问题思路扩展

在docker容器内构建项目

但是我从第一性原理开始想,为啥我需要安装这个Nodejs呢?既然产出物是前端代码,部署又不需要这个nodejs(只需要一个nginx镜像+那些构建物),构建的时候能不能使用Docker构建,只要用不同的镜像去构建,不就可以了?说干就干。

下面是Dockerfile构建脚本: 阶段1构建,注意不同的项目修改自己node:版本 (这里用到了docker的多阶段构建,想了解的可以自己去研究,或许后期我会抽空介绍它的好处)

# 构建阶段
FROM node:20.16-alpine as builder# 设置工作目录
WORKDIR /app# 复制package.json和package-lock.json(如果存在)
COPY package*.json ./# 安装项目依赖
RUN npm install# 复制项目文件到工作目录
COPY . .# 构建应用
RUN npm run build

这个构建的产出物在镜像内的/app/dist,如果是直接docker部署,可以使用第二个stage直接复制到新的镜像中,但此时为考虑到某些场景需要提取出这个目录,就需要一个辅助的nodejs方法:

1. 构建

2. 启动容器

3. 复制到目标目录

启动临时容器复制到宿主机目录

一个全功能辅助的nodejs脚本

下面这个方法是一个build.cjs脚本内容,构建的时候,定义好需要构建的模块,然后node build.cjs就可以了,这里不展开。

function buildRunCopy(imageName, dockerfileDir, containerDist, outputDir) {// 生成容器名称const containerName = `container-${imageName}`;// 构建 Dockerfile 路径和输出目录的绝对路径const dockerfilePath = path.resolve(dockerfileDir, "Dockerfile");const hostDir = path.resolve(outputDir);try {// 创建宿主机目录(如果不存在的话)if (!fs.existsSync(hostDir)) {fs.mkdirSync(hostDir, { recursive: true });}// 构建 Docker 镜像console.log("Building Docker image...");execSync(`docker build -t ${imageName} -f ${dockerfilePath} ${dockerfileDir}`,{stdio: "inherit",cwd: dockerfileDir,});console.log("Docker image built successfully.");// 运行 Docker 容器console.log("Running Docker container...");execSync(`docker run --name ${containerName} -d ${imageName}`);console.log("Docker container started successfully.");// 复制容器内的目录到宿主机目录execSync(`docker cp ${containerName}:/${containerDist} ${hostDir}`);console.log("Files copied successfully.");} catch (error) {console.error(`Error: ${error.message}`);throw error;} finally {// 停止并移除 Docker 容器try {console.log("Removing Docker container...");execSync(`docker rm -f ${containerName}`);console.log("Docker container removed successfully.");} catch (removeError) {console.error(`Error removing Docker container: ${removeError.message}`);}}
}

脚本函数用法举例

下面就是一个例子:将项目某个test-app下面的项目进行构建,并将其构建物从/app/dist复制到原项目下面的dist目录

// 构建 build.cjs const basePath = path.join(projectsBasePath, "test-app");const tmpDistFolder = path.join(basePath, "dist");console.info(`清理构建临时目录`);fs.rmSync(tmpDistFolder, { recursive: true, force: true });buildRunCopy("test-app", basePath, "/app/dist", basePath);

然后就可以实现使用docker构建项目了,宿主机的nodejs只需要跑这个脚本,构建过程用到的nodejs是来自于容器内的,想用啥版本直接修改Dockerfile文件就可了

看完记得活动下脖子,这算是我今天的前3.5个小时的总结了...

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 计算机网络:概述 --- 体系结构
  • ML 系列:机器学习和深度学习的深层次总结(08)—欠拟合、过拟合,正确拟合
  • QT中添加资源文件(一看就会)
  • 开源实时多模态AI聊天机器人Moshi,语音对话延迟低至200毫秒!
  • MySQL面试题——第一篇
  • 信息学奥赛:青少年编程的高光舞台,通向未来科技的敲门砖
  • text2sql(NL2Sql)综述《The Dawn of Natural Language to SQL: Are We Fully Ready?》
  • 【游戏引擎】C++自制游戏引擎 Lunar Game Engine
  • spring与springmvc整合
  • Stable Diffusion 使用详解(13)--- 3D纹理增强
  • 【Qt笔记】QTabWidget控件详解
  • 【我的 PWN 学习手札】House of Botcake —— tcache key 绕过
  • 量化交易----数据透视表----融资融券优惠代码
  • 前端三大框架 Vue、React 和 Angular 的市场占比分析
  • 【AI】简单了解AIGC与ChatGPT
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • avalon2.2的VM生成过程
  • el-input获取焦点 input输入框为空时高亮 el-input值非法时
  • ES6之路之模块详解
  • magento2项目上线注意事项
  • Work@Alibaba 阿里巴巴的企业应用构建之路
  • 初识 webpack
  • 前端技术周刊 2019-02-11 Serverless
  • ​软考-高级-系统架构设计师教程(清华第2版)【第9章 软件可靠性基础知识(P320~344)-思维导图】​
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • "无招胜有招"nbsp;史上最全的互…
  • # 学号 2017-2018-20172309 《程序设计与数据结构》实验三报告
  • #C++ 智能指针 std::unique_ptr 、std::shared_ptr 和 std::weak_ptr
  • (11)MSP430F5529 定时器B
  • (35)远程识别(又称无人机识别)(二)
  • (9)YOLO-Pose:使用对象关键点相似性损失增强多人姿态估计的增强版YOLO
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (数据结构)顺序表的定义
  • (已解决)什么是vue导航守卫
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .config、Kconfig、***_defconfig之间的关系和工作原理
  • .libPaths()设置包加载目录
  • .net 调用海康SDK以及常见的坑解释
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • .net经典笔试题
  • .NET序列化 serializable,反序列化
  • .net最好用的JSON类Newtonsoft.Json获取多级数据SelectToken
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • @ComponentScan比较
  • [2015][note]基于薄向列液晶层的可调谐THz fishnet超材料快速开关——
  • [AIGC] 深入浅出 Python中的`enumerate`函数
  • [Android] Android ActivityManager
  • [AndroidStudio]_[初级]_[修改虚拟设备镜像文件的存放位置]
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法
  • [BUUCTF NewStarCTF 2023 公开赛道] week4 crypto/pwn
  • [C++] new和delete
  • [C++]unordered系列关联式容器
  • [Day 36] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
  • [dfs] 图案计数