使用pkg打包node.js项目(egg框架)为可执行包
<p>问题:</p><p> 公司有个工具型项目使用node.js 开发,需要部署到客户的服务器中,遇到的问题:</p>
<p> 1、客户的服务器没有外网。环境配置,依赖安装等都比较麻烦,只能手工上传,最好能一个文件直接搞定;</p>
<p> 2、直接包源码部署到客户的机器中,存在源码泄露的风险。</p>
<p>方案:</p>
<p> 使用pkg npm包可以很好的解决我们以上的问题。</p>
<p> 先参考前人的经验 : Egg.js线上部署——利用pkg打包Egg.js工程 </p>
<p>pkg原理:</p>
<p> pkg打包工具主要会按平台(支持window、mac、linux)分别打包。</p>
<p> pkg中会包含node的可执行文件,还会包含你要打包进去的代码。代码通过一个虚拟的文件系统把所有的代码和资源文件都挂载到 /snapshot/${被打包项目的文件夹名} 下面(pkg hack了 fs 的很多方法,拦截文件操作,如果发现读的文件路径是在挂载目录下就特殊处理,返回打包进去的文件信息,如果不在挂载目录下,则按node默认逻辑进行)</p>
<p> pkg 会根据被打包项目的package.json 中的licence声明判断是否要对源码进行编译,npm上安装的依赖包基本都是开源的,会以源码的形式打包;而用户的源码如果没有声明开源的协议,则会把js文件编译为v8字节码进行保存(在项目中也就没法通过fs读取到源码,会给出抛错,即使是破解安装包也只能拿到v8字节码)</p>
<p>实现:</p>
<p>1、安装(也可以考虑只安装到项目内,通过npm script 调用打包) </p>
<div class="cnblogs_code">
<pre>npm <span style="color: rgba(0, 0, 255, 1)">install</span> pkg -g</pre>
</div>
<p> </p>
<p>2、在egg配置文件中把涉及到写文件的路径都移到包外(pkg的虚拟文件系统只是用来应对读的行为,所有写相关都得移出包外) </p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 通过process.cwd()获取当前执行文件执行的路径</span>
config.rundir = process.cwd() + '/run';<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 配置执行时临时文件的路径</span>
config.logger =<span style="color: rgba(0, 0, 0, 1)"> {
dir: path.join(process.cwd(), </span>'logs', logDir),<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">配置普通日志文件地址</span>
<span style="color: rgba(0, 0, 0, 1)">};
config.customLogger </span>=<span style="color: rgba(0, 0, 0, 1)"> {
scheduleLogger: {
file: path.join(process.cwd(), </span>'logs', logDir, 'egg-schedule.log'),<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">配置定时任务日志的地址</span>
<span style="color: rgba(0, 0, 0, 1)"> },
};
config.static </span>= { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 必须把public移出项目,否则在pkg的包中egg的static中间件会有对public操作(确保文件夹),会有抛错</span>
prefix: '/'<span style="color: rgba(0, 0, 0, 1)">,
dir: process.cwd() </span>+ '/public',<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">配置静态文件的地址</span>
};</pre>
</div>
<p> 3、修改package.json文件</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
...
</span>"bin": "pkg-entry.js", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行包的入口文件,可执行包启动的时候默认会调用该文件</span>
"pkg": {<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 以下主要是声明那些文件需要被打包(pkg会解析require中的静态路径,但在egg.js中很多文件都是通过框架引用的,无法依赖解析)</span>
"scripts": [ <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里是声明需要打包的js文件,这里的声明的js文件都会被编译为v8字节码(建议主动声明,不要依赖pkg自动引入)</span>
"./app/**/*.js"<span style="color: rgba(0, 0, 0, 1)">,
</span>"./config/**/*.js"<span style="color: rgba(0, 0, 0, 1)">,
</span>"./normalJs/**/*.js", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 不只是egg的文件,只要要用到就要声明打包</span>
"./app.js"<span style="color: rgba(0, 0, 0, 1)">,
</span>"./agent.js"<span style="color: rgba(0, 0, 0, 1)">
],
</span>"assets": [ <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里是声明需要打包的静态文件(即使有js文件也不进行编译)。</span>
"./lib/**/*",<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> lib中是我打算开放的一些egg组件,所有不需要编译</span>
"./app/public/**/*",<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果要把前端静态文件打包进来,就直接声明(但是在egg中static中间件会有抛错,需要hack egg或者 hack pkg)</span>
"./node_modules/**/*"<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> npm安装的所有依赖包全部打包进来,不要依赖自动引入,很容易导致部分文件没打包,出现各种意料外的错误</span>
<span style="color: rgba(0, 0, 0, 1)"> ]
},
}</span></pre>
</div>
<p> </p>
<p> 4、增加入口文件pkg-entry.js(名字保持和package.json中一致)</p>
<div class="cnblogs_code">
<pre>const fs = require('fs'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果是egg的ts项目,由于egg-script会给ts项目通过-r引入sourcemap的注入文件,但是pkg的spawn不支持,所以把项目标识为飞ts</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 如果不是ts项目忽略一下两行</span>
const pkgInfo = require('./package'<span style="color: rgba(0, 0, 0, 1)">);
pkgInfo.egg.typescript </span>= <span style="color: rgba(0, 0, 255, 1)">false</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 防止egg-script识别为 typescript 自动添加soucemap支持(--require 在pkg的spawn中不支持)</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">我自己的工具包的配置文件是直接打包到安装包里面的,这样就不方便修改配置了。于是把提供给运维配置的配置都适用dotenv来配置,以下引入dotenv的预执行脚本</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">也可以考虑把配置文件放到包外,不过因为包内执行包外js,会增加被攻击的风险</span>
require('dotenv/config'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 由于egg-script是默认以当前执行proccess.cwd() 路径为默认项目的,打包后需要每次输入 /snapshot/${项目文件夹名} 作为指定目录</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> 所以,以下为修改参数,自动嵌入“/snapshot/${项目文件夹名}”</span>
const baseDir = '/snapshot/' + fs.readdirSync('/snapshot');
console.log(</span>'baseDir:'<span style="color: rgba(0, 0, 0, 1)">, baseDir);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当 start 的时候,自动嵌入bashDir为 /snapshot/${项目文件夹名}</span>
const startIndex = process.argv.indexOf('start'<span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (startIndex > -1<span style="color: rgba(0, 0, 0, 1)">) {
process.argv </span>=<span style="color: rgba(0, 0, 0, 1)"> [].concat(
process.argv.slice(</span>0, startIndex + 1<span style="color: rgba(0, 0, 0, 1)">),
baseDir,
process.argv.slice(startIndex </span>+ 1<span style="color: rgba(0, 0, 0, 1)">),
);
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 然后直接调起egg-scripts执行</span>
require('./node_modules/egg-scripts/bin/egg-scripts.js');</pre>
</div>
<p> 5、<span style="font-family: "Courier New"; font-size: 12px"> 执行打包</span></p>
<div class="cnblogs_code">
<pre>## --<span style="color: rgba(0, 0, 0, 1)">targets 用于制定平台和node版本,不指定时默认为3个平台以package.json中的node版本配置为准
## </span>--out-<span style="color: rgba(0, 0, 0, 1)">path 指定执行包输出文件夹,默认为当前文件夹
## </span>--<span style="color: rgba(0, 0, 0, 1)">debug 用于调试,可了解哪些文件被打包
pkg .</span>--targets node8-linux-x64 --out-path /usr/dist--debug</pre>
</div>
<p>6、运行</p>
<div class="cnblogs_code">
<pre>> <span style="color: rgba(0, 0, 255, 1)">chmod</span> a+x ./<span style="color: rgba(0, 0, 0, 1)">appName #给可执行包增加执行权限
</span>> ./appName start # 启动项目,除项目路径其它参数都和egg-scripts一致 可以用--<span style="color: rgba(0, 0, 0, 1)">title指定egg服务名
</span>>./appName stop # 关闭项目(会关闭当前服务器的所有egg服务,如果有多个,最好用--title来指定要关闭的项目)</pre>
</div>
<p> </p>
<p> </p>
<p>ps:</p>
<p> 如果需要动态配置或者包中使用了c++编译的node依赖包,可参考该文章 Egg.js线上部署——利用pkg打包Egg.js工程 。<br><br></p><br><br>
来源:https://www.cnblogs.com/chianquan/p/11781211.html
頁:
[1]