筆者搞了一年多微前端項目,一個團(tuán)隊管理十個微應(yīng)用,換成docker鏡像部署后,發(fā)布操作一下從原來的腳本直連服務(wù)器的1分鐘變成了幾十分鐘,尤其上傳每個應(yīng)用到各自的阿里云倉庫。這里就再寫個腳本一鍵打包docker鏡像并上傳阿里云。
本文只講怎么制作一個腳本幫助去減輕開發(fā)人員負(fù)擔(dān),關(guān)于docker-compose的配置見: 使用各種姿勢舒服的部署微前端項目(上:打包與上傳)
/**
* @name docker鏡像打包上傳腳本
* @author weilan
* @time 2021.02.22
*/
const fs = require('fs');
const path = require('path');
const util = require('util');
const { log } = require('../utils/log');
const exec = util.promisify(require('child_process').exec);
const sub_app_ath = path.resolve();
let sub_apps = fs.readdirSync(sub_app_ath).filter(i => /^subapp|master/.test(i));
const inquirer = require('inquirer'); // 用于命令行交互
/**
* @name 命令行交互配置項
*/
const question = [
{
type: 'confirm',
name: 'dist',
message: '是否需要打包前端靜態(tài)資源?',
},
{
type: 'confirm',
name: 'env',
message: '請選擇是否需要打包成不聯(lián)網(wǎng)的內(nèi)網(wǎng)部署',
when: function (answers) { // 當(dāng)answer為true的時候才會提問當(dāng)前問題
return answers.dist
}
},
{
type: 'checkbox',
name: 'apps',
message: '請選擇要發(fā)布的模塊',
choices: sub_apps,
validate: function (val) {
if (val.length) { // 校驗
return true;
}
return "選擇不能為空";
}
},
]
/**
* @name 根據(jù)命令交互配置結(jié)果做邏輯處理
*/
inquirer.prompt(question).then(async (answer) => {
let subApps = answer.apps;
let buildScript = answer.env ? 'yarn build --Intranet' : 'yarn build';
let needDist = answer.dist;
let now = +new Date();
// 登錄阿里云
const { error: loginError } = await exec('docker login --username=哈哈哈 --password=嘿嘿 registry.cn-zhangjiakou.aliyuncs.com');
if (loginError) {
log.red(loginError, '登錄鏡像中心失敗')
return;
}
console.log(`開始依次處理 ${JSON.stringify(subApps)} ......`);
subApps.reduce((chain, item) => {
return chain.then(() => publishIamge(item, now, needDist, buildScript))
}, Promise.resolve())
});
/**
* @name 打包鏡像并推送阿里云
* @param {String} moduleName 模塊名
* @param {String} now 當(dāng)前版本時間戳
* @param {Boolean} needDist 是否需要打包前端靜態(tài)資源
* @param {String} buildScript 前端靜態(tài)資源打包命令
*/
async function publishIamge(moduleName, now, needDist, buildScript) {
// 打包前端靜態(tài)資源
if (needDist) {
console.log('開始打包前端靜態(tài)資源' + moduleName);
const { error } = await exec(buildScript, { cwd: path.resolve(moduleName) });
if (error) {
log.red(moduleName, '前端代碼打包錯誤:', error)
return;
}
log.green(moduleName + '前端代碼打包成功')
}
// 打包鏡像
console.log(`開始打包鏡像 ${moduleName} ......`);
const { stdout: buildStdout, error: buildError } = await exec('docker-compose build ' + moduleName);
if (buildError) {
log.red(buildError, '鏡像打包錯誤')
return;
}
log.cyan(buildStdout)
log.green('鏡像打包完成,開始制作鏡像標(biāo)簽')
// 更新鏡像標(biāo)簽
const imageName = 'ibp2fe_' + moduleName;
const { error: tagError } = await exec(`docker tag ${imageName} registry.cn-zhangjiakou.aliyuncs.com/futureweb/${imageName}:${now}`);
if (tagError) {
log.red(tagError, '鏡像標(biāo)簽異常')
return;
}
log.green('鏡像版本標(biāo)簽更新完畢,開始更新last標(biāo)簽')
// 更新鏡像標(biāo)簽last版本
const { error: tagLastError } = await exec(`docker tag ${imageName} registry.cn-zhangjiakou.aliyuncs.com/futureweb/${imageName}`);
if (tagLastError) {
log.red(tagError, '鏡像last標(biāo)簽異常')
return;
}
log.green('鏡像last標(biāo)簽更新完畢,開始上傳')
const { stdout: pushStdout, error: pushError } = await exec('docker push registry.cn-zhangjiakou.aliyuncs.com/futureweb/' + imageName);
if (pushError) {
log.red(pushError, '鏡像上傳失敗')
return;
}
log.cyan(pushStdout)
log.green('鏡像上傳成功')
}
process.on('unhandledRejection', (reason, p) => {
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
// application specific logging, throwing an error, or other logic here
});
這里有個需要注意的點,因為是工具函數(shù)處理,每個模塊的路徑由node讀取,因此你的模塊名和docker-compose.yml里的服務(wù)名、容器名最好都一致;另外注意你的鏡像打包后是你docker-compose.yml外的根目錄下劃線連接你的docker-compose服務(wù)名,因此你的阿里云鏡像倉庫命名最好和這個組合后鏡像名一致;以上都是為了方便工具函數(shù)能夠通用處理各個模塊鏡像。