使用脚本一键打包并上传docker镜像的实现代码
笔者搞了一年多微前端项目,一个团队管理十个微应用,换成docker镜像部署后,发布操作一下从原来的脚本直连服务器的1分钟变成了几十分钟,尤其上传每个应用到各自的阿里云仓库。这里就再写个脚本一键打包docker镜像并上传阿里云。
本文只讲怎么制作一个脚本帮助去减轻开发人员负担,关于docker-compose的配置见:使用各种姿势舒服的部署微前端项目(上:打包与上传)
效果图
直接上代码
/**
*@namedocker镜像打包上传脚本
*@authorweilan
*@time2021.02.22
*/
constfs=require('fs');
constpath=require('path');
constutil=require('util');
const{log}=require('../utils/log');
constexec=util.promisify(require('child_process').exec);
constsub_app_ath=path.resolve();
letsub_apps=fs.readdirSync(sub_app_ath).filter(i=>/^subapp|master/.test(i));
constinquirer=require('inquirer');//用于命令行交互
/**
*@name命令行交互配置项
*/
constquestion=[
{
type:'confirm',
name:'dist',
message:'是否需要打包前端静态资源?',
},
{
type:'confirm',
name:'env',
message:'请选择是否需要打包成不联网的内网部署',
when:function(answers){//当answer为true的时候才会提问当前问题
returnanswers.dist
}
},
{
type:'checkbox',
name:'apps',
message:'请选择要发布的模块',
choices:sub_apps,
validate:function(val){
if(val.length){//校验
returntrue;
}
return"选择不能为空";
}
},
]
/**
*@name根据命令交互配置结果做逻辑处理
*/
inquirer.prompt(question).then(async(answer)=>{
letsubApps=answer.apps;
letbuildScript=answer.env?'yarnbuild--Intranet':'yarnbuild';
letneedDist=answer.dist;
letnow=+newDate();
//登录阿里云
const{error:loginError}=awaitexec('dockerlogin--username=哈哈哈--password=嘿嘿registry.cn-zhangjiakou.aliyuncs.com');
if(loginError){
log.red(loginError,'登录镜像中心失败')
return;
}
console.log(`开始依次处理${JSON.stringify(subApps)}......`);
subApps.reduce((chain,item)=>{
returnchain.then(()=>publishIamge(item,now,needDist,buildScript))
},Promise.resolve())
});
/**
*@name打包镜像并推送阿里云
*@param{String}moduleName模块名
*@param{String}now当前版本时间戳
*@param{Boolean}needDist是否需要打包前端静态资源
*@param{String}buildScript前端静态资源打包命令
*/
asyncfunctionpublishIamge(moduleName,now,needDist,buildScript){
//打包前端静态资源
if(needDist){
console.log('开始打包前端静态资源'+moduleName);
const{error}=awaitexec(buildScript,{cwd:path.resolve(moduleName)});
if(error){
log.red(moduleName,'前端代码打包错误:',error)
return;
}
log.green(moduleName+'前端代码打包成功')
}
//打包镜像
console.log(`开始打包镜像${moduleName}......`);
const{stdout:buildStdout,error:buildError}=awaitexec('docker-composebuild'+moduleName);
if(buildError){
log.red(buildError,'镜像打包错误')
return;
}
log.cyan(buildStdout)
log.green('镜像打包完成,开始制作镜像标签')
//更新镜像标签
constimageName='ibp2fe_'+moduleName;
const{error:tagError}=awaitexec(`dockertag${imageName}registry.cn-zhangjiakou.aliyuncs.com/futureweb/${imageName}:${now}`);
if(tagError){
log.red(tagError,'镜像标签异常')
return;
}
log.green('镜像版本标签更新完毕,开始更新last标签')
//更新镜像标签last版本
const{error:tagLastError}=awaitexec(`dockertag${imageName}registry.cn-zhangjiakou.aliyuncs.com/futureweb/${imageName}`);
if(tagLastError){
log.red(tagError,'镜像last标签异常')
return;
}
log.green('镜像last标签更新完毕,开始上传')
const{stdout:pushStdout,error:pushError}=awaitexec('dockerpushregistry.cn-zhangjiakou.aliyuncs.com/futureweb/'+imageName);
if(pushError){
log.red(pushError,'镜像上传失败')
return;
}
log.cyan(pushStdout)
log.green('镜像上传成功')
}
process.on('unhandledRejection',(reason,p)=>{
console.log('UnhandledRejectionat:Promise',p,'reason:',reason);
//applicationspecificlogging,throwinganerror,orotherlogichere
});
实现思路和注意事项
- 首先考虑命令行交互的方式,将需要做的配置以选择的方式让开发人员确认,最后选择要发布的模块
- 提前登录阿里云账号
- 这里可以选择并发执行所选全部模块,但是这样日志输出会无序,且电脑压力极大会暂时死机;因此这里我用依次处理的方式
- 打包所选模块的前端静态资源
- 执行docker-composebuildxxx打包所选模块镜像
这里有个需要注意的点,因为是工具函数处理,每个模块的路径由node读取,因此你的模块名和docker-compose.yml里的服务名、容器名最好都一致;另外注意你的镜像打包后是你docker-compose.yml外的根目录下划线连接你的docker-compose服务名,因此你的阿里云镜像仓库命名最好和这个组合后镜像名一致;以上都是为了方便工具函数能够通用处理各个模块镜像。
- 制作镜像标签。这里我会制作一个本次发布时间戳的版本和一个lastet版本,前面方便回退,后面方便运维部署时无需关心标签版本。也可以拉取gittag来做镜像的tag。
- 制作镜像后上传至阿里云镜像中心