关于前端:npm-私库从搭建到数据迁移最后容灾备份的一些解决方案

9次阅读

共计 7287 个字符,预计需要花费 19 分钟才能阅读完成。

这是第 80 篇不掺水的原创,想获取更多原创好文,请搜寻公众号关注咱们吧~ 本文首发于政采云前端博客:npm 私库从搭建到数据迁徙最初容灾备份的一些解决方案

前言

依照国际惯例,注释开始之前,咱们先简略介绍下目前市面上的 npm 私库开源框架。

  • Verdaccio

    Verdaccio 是 sinopia 开源框架的一个分支。它提供了本人的小数据库,以及代理其余注册核心的能力(例如。npmjs.org 网站),配置以及部署绝对简略,一步到 ” 胃 ”。如果公司的私包比拟少的话或者你想偷懒,能够考虑一下。

  • Cnpmjs.org

赫赫有名的 cnpm,想必各位早就感触到了它的速度之“快”,没错,它的 register 服务就是淘宝镜像。次要是基于 Koa、MySQL 和简略存储服务的企业专用 npm 注册和 web 服务,其中最弱小的性能就是它的同步模块机制(定时同步所有源 registry 的模块、只同步曾经存在于数据库的模块、只同步 popular 模块)。

  • Nexus

后端开发的小伙伴应该比拟相熟。Nexus2 次要是用于 maven/gralde 仓库的对立治理,而 Nexus3 则增加了 npm 插件,能够对 npm 提供反对,其中 npm 仓库有三种类型,别离是 hosted(公有仓库)、proxy(代理仓库)、group(组合仓库)。

总体来讲,抛开 Nexus,尽管 Cnpmjs.org 在部署过程以及总体设计计划上绝对于 Verdaccio 简单的多,然而它提供更高的拓展性,定制性,能够反对多种业务应用场景。接下来,咱们别离从 Cnpmjs.org 容器化部署、数据迁徙、OSS 容灾备份等内容,层层开展。

Cnpmjs.org 容器化部署

目前,公司的利用部署根本都是容器化部署,外部搭建了 ipaas 平台,利用流程化部署以及一键公布。而 Cnpmjs.org 也附带了 Dockerfile 以及 docker-compose.yml 文件,所以,这里大抵解说下怎么用 docker 部署吧。

  • 首先让咱们看看 Dockerfile 文件
FROM node:12
MAINTAINER zian yuanzhian@cai-inc.com

# Working enviroment
ENV \
    CNPM_DIR="/var/app/cnpmjs.org" \
    CNPM_DATA_DIR="/var/data/cnpm_data" 

# shell 格局
# 在 docker build 时运行
RUN mkdir -p ${CNPM_DIR}

# 指定工作目录:用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在
WORKDIR ${CNPM_DIR}

# 复制指令:从上下文目录中复制目录或文件到容器里指定的门路
COPY package.json ${CNPM_DIR}

RUN npm set registry https://registry.npm.taobao.org

RUN npm install --production

COPY .  ${CNPM_DIR}
COPY docs/dockerize/config.js  ${CNPM_DIR}/config/

# 申明端口(7001 为 register 服务、7002 为 web 服务)EXPOSE 7001/tcp 7002/tcp

# 匿名数据卷:在启动容器时遗记挂载数据卷,会主动挂载到匿名卷。VOLUME ["/var/data/cnpm_data"]

RUN chmod +x ${CNPM_DIR}/docker-entrypoint_prod.sh

# Entrypoint 
# exec 格局
# 在 docker run 时运行
# dockerfile 存在多个 CMD 命令,仅最初一个失效
# CMD ["node", "dispatch.js"]
CMD ["npm", "run", "prod"]

这里把 CMD 命令批改为["npm", "run", "prod"],因为减少了一层不同环境的 shell 脚本,目前全局变量全都寄存在这里。

示例:docker-entrypoint_env.sh

export DB='db_cnpmjs'
export DB_USRNAME='root'
export DB_PASSWORD='123456'
export DB_HOST='127.0.0.1'

export BINDING_HOST='0.0.0.0'

DEBUG=cnpm* node dispatch.js 
  • 再批改下 docker-compose.yml 文件,这里把 mysql-db 这个服务删掉了,起因是可通过 /docs/dockerize/config.js 下的配置文件去连贯公司测试环境的 mysql 数据库,则不须要构建生成 mysql-db 镜像
version: '3' # docker 版本
services: # 配置的容器列表
  web: # 自定义,服务名称
    build: # 基于 dockerfile 构建镜像(可减少 args)
      context: .
      dockerfile: Dockerfile ## 依赖的 Dockerfile 文件
    image: cnpmjs.org # 镜像名称或 id
    volumes:
      - cnpm-files-volume:/var/data/cnpm_data
    ports:
      - "7001:7001"
      - "7002:7002" 

<font color=”red”> 留神点:1、全局配置文件门路:/docs/dockerize/config.js;2、bindingHost 为 0.0.0.0。</font>

  • 最初,在控制台敲下docker-compose up -d,即以守护过程模式模式启动利用,而后关上浏览器入http://127.0.0.1:7002,就会看到 web 页面。执行 npm config set registry http://127.0.0.1:7001 可设置为搭建的私库的镜像源地址,这里举荐应用 nrm,可自在切换 npm 源。

展现站点如下图:

<font color=”red”> 留神点:1、当你扭转本地代码之后,先执行 docker-compose build 构建新的镜像,而后执行 docker-compose up -d 取代运行中的容器。</font>

数据迁徙

因为公司之前用的 Verdaccio 搭建的私库,要切换应用新的 npm 私库,意味着要把之前公布过的私包全副迁徙过去。大略统计了下,有 400 多个 package,总共有 7000 多个版本,依照失常逻辑,做数据迁徙首先会从数据库下手,然而 Verdaccio 并不依赖数据库。刚开始没有一点脉络,大略看了下 Cnpmjs.org 的源码,理解到当咱们 publish 模块时,它是怎么把 npm 模块 的元数据存储到数据库,上面咱们一步步来揭开她的面纱。

通过路由文件(/routes/registry.js)咱们很容易找到/controllers/registry/package/save.js,这个文件便是咱们想要的。

外围代码:

var pkg = this.request.body; // 这里拿到 npm 模块元数据,即 package.json 文件通过 libnpmpublish 模块解决过的 json 数据
var username = this.user.name; // 以后用户名
var name = this.params.name || this.params[0]; // npm 模块名
var filename = Object.keys(pkg._attachments || {})[0]; // npm 模块的压缩后的文件名
var version = Object.keys(pkg.versions || {})[0]; // npm 模块的最新版本
// upload attachment

// base64 解码,获取模块文件二进制数据。从 libnpmpublish 模块理解到 tardata.toString('base64'),即 npm 模块文件流转 base64 字符串
var tarballBuffer = Buffer.from(attachment.data, 'base64'); 
// 默认应用 fs-cnpm,将 npm 模块文件保留到本地,默认保留门路:path.join(process.env.HOME, '.cnpmjs.org', 'nfs')
var uploadResult = yield nfs.uploadBuffer(tarballBuffer, options);

var versionPackage = pkg.versions[version];
var dist = {
  shasum: shasum,
  size: attachment.length
};

// if nfs upload return a key, record it
if (uploadResult.url) {dist.tarball = uploadResult.url;} else if (uploadResult.key) {
  dist.key = uploadResult.key;
  dist.tarball = uploadResult.key;
}
var mod = {
  name: name,
  version: version,
  author: username,
  package: versionPackage
};

mod.package.dist = dist;

// 模块数据保留到数据库
var addResult = yield packageService.saveModule(mod);

即只有咱们可能拿到 npm 模块的元数据(即 package.json 被解决过的 json 数据),就能把模块文件上传到文件系统或者 OSS 服务,同时数据落库。Verdaccio 有两个 api 能够拿到其私库 npm 模块全量数据和以后 npm 模块的 json 数据,门路别离是/-/verdaccio/packages/-/verdaccio/sidebar/$PKG$,其中有 scope 的模块的申请门路是/-/verdaccio/sidebar/$SCOPE$/$PKG$

思路曾经很明确了,开始动起来吧!新增 save_zcy.js 文件,基于原来的 /controllers/registry/package/save.js 稍加革新下。

外围代码:

// 申请近程文件,并返回二进制流
const handleFiles = function (url) {return new Promise((resolve, reject) => {
    try {
      http.get(url, res => {res.setEncoding('binary') // 二进制
        let files = ''res.on('data', chunk => { // 加载到内存
          files += chunk
        }).on('end', () => { // 加载完
          resolve(files)
        })
      }) 
    } catch (error) {reject(error)
    }
  })
};

// 获取近程模块文件的二进制数据
yield handleFiles(dist.tarball).then(res => {
  // 利用 Buffer 转为对象
  const tardata = Buffer.from(res, 'binary')
  pkg._attachments = {};
  pkg._attachments[filename] = {
    'content_type': 'application/octet-stream',
    'data': tardata.toString('base64'), // 从缓冲区读取数据,应用 base64 编码并转换成字符串
    'length': tardata.length,
  };
}, error => {
  this.status = 400;
  this.body = {
    error,
    reason: error,
  };
  return;
});

接下来咱们把控制器 save_zcy.js 接入到 registry 服务的 app 路由上。

// 新增 fetchPackageZcy、savePackageZcy 控制器
app.get('/:name/:version', syncByInstall, fetchPackageZcy, savePackageZcy, getOneVersion);
app.get('/:name', syncByInstall, fetchPackageZcy, savePackageZcy, listAllVersions);

控制器 fetchPackageZcy 作用是申请下面的 api(/-/verdaccio/sidebar/$SCOPE$/$PKG$ 或 /-/verdaccio/sidebar/$PKG$)来拉取对应模块的 json 数据。

Ok,接下来咱们写一个定时工作,每隔一段时间执行 npm install [name],这样原来私库的 npm 包都可能 install 并进入到下面的控制器逻辑,功败垂成!

OSS 容灾备份

首先,简略阐明下为什么要做 OSS 容灾备份,有以下几点。

  • 如果服务器上磁盘损坏,易失落文件,有肯定的危险
  • 若服务器磁盘爆满,可主动降级上传模块文件到 OSS

基于以上几点,咱们整顿了下容灾备份计划:

  • package publish

<img src=”https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/06b600e8297a434087f43bcad08c7f6d~tplv-k3u1fbpfcp-zoom-1.image” style=”zoom:33%;” />

即公布模块文件时本地存储,同时上传到 oss 作为备份,用到的插件别离是 fs-cnpmoss-cnpm

  • package install

<img src=”https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df3ff3801cf74755a0c09be7d2a19029~tplv-k3u1fbpfcp-zoom-1.image” style=”zoom:33%;” />

即下载模块文件时,先判断是否是私包(即是包名否有带 scope),如果不是私包代理到上游 registry,若是私包先判断服务器本地是否有该私包文件,如果不存在先去 oss 下载到本地 nfs 目录下,如果存在则间接从 nfs 目录找到模块文件,而后读取并写到 downloads 目录下,最初调用 fs.createReadStream 办法流读取该文件。

isEnsureFileExists 即判断模块文件本地是否存在,代码如下:

const mkdirp = require('mkdirp');
const fs = require('fs');

function ensureFileExists(filepath) {return function (callback) {fs.access(filepath, fs.constants.F_OK, callback);
  };
}

留神,在 oss 下载模块文件到 nfs 之前,肯定要先创立模块文件目录,办法如下:

const mkdirp = require('mkdirp');

function ensureDirExists(filepath) {return function (callback) {mkdirp(path.dirname(filepath), callback);
  };
}

邮件告诉

Cnpmjs.org 原本就带有邮件告诉的性能,但只利用谬误日志上报。因为咱们的私包大部分都是业务组件、工具等,有时候公布正式版本的业务组件须要告诉到业务组件的应用方。目前,咱们采纳 maintainers 来保护,蕴含模块的维护者及使用者。

示例:

"maintainers": [
  {
    "name": "yuanzhian",
    "email": "yuanzhian@cai-inc.com"
  }
]

邮箱配置如下:

mail: {
  enable: true,
  appname: 'cnpmjs.org',
  from: process.env.EMAIL_HOST,
  host: 'smtp.mxhichina.com',
  service: 'qiye.aliyun', // 应用了内置传输发送邮件, 查看反对列表:https://nodemailer.com/smtp/well-known/
  port: 465, // SMTP 端口
  secureConnection: true, // 应用了 SSL
  auth: {
     user: process.env.EMAIL_HOST,
     pass: process.env.EMAIL_PSD, // 
   }
 }

写在文末

将来,咱们还能够在 Cnpmjs.org 上做很多定制化开发,比方接入公司外部权限零碎、web 页面重构、对接业务组件在线文档等等。如果你正好也须要搭建 npm 公有库,心愿这篇文章对你有所帮忙。

举荐浏览

分分钟教会你搭建企业级的 npm 公有仓库

编写高质量可保护的代码:组件的形象与粒度

招贤纳士

政采云前端团队(ZooTeam),一个年老富裕激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 40 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员形成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、性能体验、云端利用、数据分析及可视化等方向进行技术摸索和实战,推动并落地了一系列的外部技术产品,继续摸索前端技术体系的新边界。

如果你想扭转始终被事折腾,心愿开始能折腾事;如果你想扭转始终被告诫须要多些想法,却无从破局;如果你想扭转你有能力去做成那个后果,却不须要你;如果你想扭转你想做成的事须要一个团队去撑持,但没你带人的地位;如果你想扭转既定的节奏,将会是“5 年工作工夫 3 年工作教训”;如果你想扭转原本悟性不错,但总是有那一层窗户纸的含糊… 如果你置信置信的力量,置信平凡人能成就不凡事,置信能遇到更好的本人。如果你心愿参加到随着业务腾飞的过程,亲手推动一个有着深刻的业务了解、欠缺的技术体系、技术发明价值、影响力外溢的前端团队的成长历程,我感觉咱们该聊聊。任何工夫,等着你写点什么,发给 ZooTeam@cai-inc.com

正文完
 0