задний план
Традиционный процесс ручного развертывания внешнего кода выглядит следующим образом:
Традиционное ручное развертывание должно пройти через:
- 1. Упаковать, запустить локально
npm run build
Пакет для создания папки dist. - 2. Подключаемся к серверу по SSH и переключаем путь к соответствующему каталогу в сети.
- 3. Загрузите код в веб-каталог, обычно через xshell или xftp.
Традиционное ручное развертывание имеет следующие недостатки:
- 1. Каждый раз вам нужно открывать программное обеспечение xshell, чтобы установить соединение с сервером.
- 2. Когда вы отвечаете за несколько проектов и каждый проект имеет тестовую среду и онлайн-среду, легко вызвать ошибки развертывания.
(Раньше я столкнулся с очень трагической встречей, потому что я отвечал за четыре проекта и восемь сред одновременно. Я мог модифицировать несколько проектов одновременно за один день, и у меня кружилась голова. Я развернул код тестовой среды к онлайн-среде, так и хотелось плакать без слез)
Полностью автоматизированное развертывание может быть реализовано с помощью Jenkins. Jenkins может автоматически упаковывать код в веб-каталог в соответствии с событиями gitlab push или merge. Вы можете обратиться к:
Jenkins+Docker автоматизирует развертывание проектов vue
Использование jenkins для развертывания очень удобно, но есть и недостатки, такие как хлопотная установка и настройка, упаковка и занятие ресурсов сервера.
Поскольку наш сервер работает под высокой нагрузкой круглый год, возникла ситуация, когда jenkeins упаковали сервер так, чтобы он сломался, поэтому мы можем только заставить блоггеров принять легкое решение для развертывания для достижения автоматического развертывания (конечно, технические решения вытесняются , хаха).
1. Программное исследование
считать:
Можете ли вы запустить что-то вродеnpm run deploy
Скрипт для упаковки и развертывания нашего кода непосредственно в веб-каталоге на сервере?
После некоторых исследований: нашелnode-ssh
,archiver
может удовлетворить наши потребности.
1.1.node-ssh
node-ssh — это облегченный пакет npm, основанный на ssh2, который в основном используется для ssh для подключения к серверу, загрузки файлов и выполнения команд.
руководство пользователя:
const node_ssh = require('node-ssh')
const ssh = new node_ssh()
Используемые API:
- 1.ssh.connect: подключиться к серверу
ssh.connect({
host: 'localhost',
username: 'steel',
privateKey: '/home/steel/.ssh/id_rsa'
})
- 2.ssh.putFile: загрузить файл
ssh.putFile('/home/steel/Lab/localPath', '/home/steel/Lab/remotePath').then(function() {
console.log("The File thing is done")
}, function(error) {
console.log("Something's wrong")
console.log(error)
})
- 3.ssh.execCommand: выполнять команды удаленного сервера
ssh.execCommand('hh_client --json', { cwd:'/var/www' }).then(function(result) {
console.log('STDOUT: ' + result.stdout)
console.log('STDERR: ' + result.stderr)
})
1.2.archiver
архиватор — это пакет npm для создания архивов, в основном используемый для упаковки и создания zip, rar и т. д.
руководство пользователя:
const archiver = require('archiver');
// 设置压缩类型及级别
const archive = archiver('zip', {
zlib: { level: 9 },
}).on('error', err => {
throw err;
});
// 创建文件输出流
const output = fs.createWriteStream(__dirname + '/dist.zip');
// 通过管道方法将输出流存档到文件
archive.pipe(output);
// 从subdir子目录追加内容并重命名
archive.directory('subdir/', 'new-subdir');
// 完成打包归档
archive.finalize();
1.3. План развертывания
Схема развертывания разработана следующим образом:
Процесс выглядит следующим образом:
- 1. Прочтите файл конфигурации, включая хост сервера, порт, веб-каталог и локальный каталог и другую информацию.
- 2. Местная упаковка,
npm run build
Создать дистрибутив - 3. Запаковать в zip, с помощью архиватора запаковать dist в dist.zip
- 4. Подключиться к серверу,
node-ssh
чтение конфигурации сервера подключения - 5. Загрузите zip, используйте
ssh.putFile
загрузить dist.zip - 6. Разархивируйте zip, используя
ssh.execCommand
разархивировать dist.zip - 7. Удалите локальный dist.zip, используйте
fs.unlink
удалить локальный dist.zip
Конкретный код:
// deploy.js
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const node_ssh = require('node-ssh');
const archiver = require('archiver');
const { successLog, errorLog, underlineLog } = require('../utils/index');
const projectDir = process.cwd();
let ssh = new node_ssh(); // 生成ssh实例
// 部署流程入口
function deploy(config) {
const { script } = config;
try {
console.log(`\n(1)${script}`);
childProcess.execSync(`${script}`);
successLog(' 打包成功');
startZip(config);
} catch (err) {
errorLog(err);
process.exit(1);
}
}
// 开始打包
function startZip(config) {
let { distPath, host } = config;
distPath = path.resolve(projectDir, distPath);
console.log('(2)打包成zip');
const archive = archiver('zip', {
zlib: { level: 9 },
}).on('error', err => {
throw err;
});
const output = fs.createWriteStream(`${projectDir}/dist.zip`).on('close', err => {
if (err) {
console.log(' 关闭archiver异常:', err);
return;
}
successLog(' zip打包成功');
console.log(`(3)连接${underlineLog(host)}`);
uploadFile(config);
});
archive.pipe(output);
archive.directory(distPath, '/');
archive.finalize();
}
// 上传文件
function uploadFile(config) {
const { host, port, username, password, privateKey, passphrase, } = config;
const sshConfig = {
host,
port,
username,
password,
privateKey,
passphrase
};
ssh.connect(sshConfig)
.then(() => {
successLog(` SSH连接成功`);
console.log(`(4)上传zip至目录${underlineLog(config.webDir)}`);
ssh.putFile(`${projectDir}/dist.zip`, `${config.webDir}/dist.zip`)
.then(() => {
successLog(` zip包上传成功`);
console.log('(5)解压zip包');
statrRemoteShell(config);
})
.catch(err => {
errorLog(' 文件传输异常', err);
process.exit(0);
});
})
.catch(err => {
errorLog(' 连接失败', err);
process.exit(0);
});
}
// 执行Linux命令
function runCommand(command, webDir) {
return new Promise((resolve, reject) => {
ssh.execCommand(command, { cwd: webDir })
.then(result => {
resolve();
// if (result.stdout) {
// successLog(result.stdout);
// }
if (result.stderr) {
errorLog(result.stderr);
process.exit(1);
}
})
.catch(err => {
reject(err);
});
});
}
// 开始执行远程命令
function statrRemoteShell(config) {
const { webDir } = config;
const commands = [`cd ${webDir}`, 'pwd', 'unzip -o dist.zip && rm -f dist.zip'];
const promises = [];
for (let i = 0; i < commands.length; i += 1) {
promises.push(runCommand(commands[i], webDir));
}
Promise.all(promises)
.then(() => {
successLog(' 解压成功');
console.log('(6)开始删除本地dist.zip');
deleteLocalZip(config);
})
.catch(err => {
errorLog(' 文件解压失败', err);
process.exit(0);
});
}
// 删除本地dist.zip包
function deleteLocalZip(config) {
const { projectName, name } = config;
fs.unlink(`${projectDir}/dist.zip`, err => {
if (err) {
errorLog(' 本地dist.zip删除失败', err);
}
successLog(' 本地dist.zip删除成功\n');
successLog(`\n 恭喜您,${underlineLog(projectName)}项目${underlineLog(name)}部署成功了^_^\n`);
process.exit(0);
});
}
module.exports = deploy;
2. Практика строительных лесов
проблема:
Вышеупомянутое решение уже может завершить автоматическое развертывание проекта, но если к автоматическому развертыванию необходимо подключить новый проект, очень сложно скопировать весь файл?
Таким образом, автоматизированное развертывание можно превратить в скаффолд.fe-deploy-cli
, который поддерживает создание шаблонов конфигурации развертывания и развертывание сценариев, которые можно развернуть в соответствующей среде с помощью всего одной команды.
Пакеты npm, связанные со скаффолдингом:
- командир: полное решение для интерфейса командной строки node.js
- download-git-repo: загрузка кода репозитория git
- ora: показать эффекты загрузки
- inquirer: инструмент для взаимодействия пользователей с командами
- child_process: встроенный модуль npm для выполнения
package.json
скрипт упаковки в
2.1. Инициализация
Для инициализации необходимо создать новое хранилище конфигурации развертывания git на github, выполнитьdeploy init
пройти черезdownload-git-repo
Вытащите шаблон конфигурации из git.
// init.js
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const download = require('download-git-repo');
const ora = require('ora');
const { successLog, infoLog, errorLog } = require('../utils/index');
let tmp = 'deploy';
const deployPath = path.join(process.cwd(), './deploy');
const deployConfigPath = `${deployPath}/deploy.config.js`;
const deployGit = 'dadaiwei/fe-deploy-cli-template';
// 检查部署目录及部署配置文件是否存在
const checkDeployExists = () => {
if (fs.existsSync(deployPath) && fs.existsSync(deployConfigPath)) {
infoLog('deploy目录下的deploy.config.js配置文件已经存在,请勿重新下载');
process.exit(1);
return;
}
downloadAndGenerate(deployGit);
};
// 下载部署脚本配置
const downloadAndGenerate = templateUrl => {
const spinner = ora('开始生成部署模板');
spinner.start();
download(templateUrl, tmp, { clone: false }, err => {
if (err) {
console.log();
errorLog(err);
process.exit(1);
}
spinner.stop();
successLog('模板下载成功,模板位置:deploy/deploy.config.js');
infoLog('请配置deploy目录下的deploy.config.js配置文件');
process.exit(0);
});
};
module.exports = () => {
checkDeployExists();
};
2.2 Настройка конфигурации
путем измененияdeploy.config.js
, установите конфигурацию dev (тестовая среда) и prod (онлайн-среда).
// deploy.config.js
module.exports = {
privateKey: '', // 本地私钥地址,位置一般在C:/Users/xxx/.ssh/id_rsa,非必填,有私钥则配置
passphrase: '', // 本地私钥密码,非必填,有私钥则配置
projectName: '', // 项目名称
dev: { // 测试环境
name: '测试环境',
script: "npm run build", // 测试环境打包脚本
host: '', // 测试服务器地址
port: 22, // ssh port,一般默认22
username: '', // 登录服务器用户名
password: '', // 登录服务器密码
distPath: 'dist', // 本地打包dist目录
webDir: '', // // 测试环境服务器地址
},
prod: { // 线上环境
name: '线上环境',
script: "npm run build", // 线上环境打包脚本
host: '', // 线上服务器地址
port: 22, // ssh port,一般默认22
username: '', // 登录服务器用户名
password: '', // 登录服务器密码
distPath: 'dist', // 本地打包dist目录
webDir: '' // 线上环境web目录
}
// 再还有多余的环境按照这个格式写即可
}
2.3 Регистрация команд развертывания
Команда развертывания реестра взята изdeploy.config.js
Прочитайте конфигурацию dev и prod в , а затем передайтеprogram.command
Зарегистрируйте команду dev и prod, запуститеdeploy dev
илиdeploy prod
То есть войти в процесс развертывания в разделе 1.3.
// 部署流程
function deploy() {
// 检测部署配置是否合理
const deployConfigs = checkDeployConfig(deployConfigPath);
if (!deployConfigs) {
process.exit(1);
}
// 注册部署命令,注册后支持deploy dev和deploy prod
deployConfigs.forEach(config => {
const { command, projectName, name } = config;
program
.command(`${command}`)
.description(`${underlineLog(projectName)}项目${underlineLog(name)}部署`)
.action(() => {
inquirer.prompt([
{
type: 'confirm',
message: `${underlineLog(projectName)}项目是否部署到${underlineLog(name)}?`,
name: 'sure'
}
]).then(answers => {
const { sure } = answers;
if (!sure) {
process.exit(1);
}
if (sure) {
const deploy = require('../lib/deploy');
deploy(config);
}
});
});
});
}
3. Руководство пользователя
Предварительное условие: вы можете подключиться к серверу через ssh.
Применимые объекты: Небольшие группы или отдельные проекты, которые все еще используют ручное развертывание и рассчитывают быстро реализовать упрощенное развертывание.В конце концов, у крупных компаний, таких как Alibaba, есть полная платформа для внешнего развертывания.
руководство пользователя:GitHub.com/sack for/share…
3.1. Установка
npm i fe-deploy-cli -g
Проверяем версию, установка прошла успешно
3.2. Инициализируйте шаблон развертывания
deploy init
Генерируется в рамках текущего проектаdeploy.config.js
3.3. Изменить конфигурацию развертывания
Файл конфигурации развертывания находится в папке развертывания.deploy.config.js
,
обычно содержатdev
(тестовая среда) иprod
(Онлайн-среда) Две конфигурации, и есть избыточные конфигурации среды в аналогичной форме, только одна среда может удалить другую избыточную конфигурацию (например, толькоprod
Онлайн-среда, пожалуйста, удалитеdev
конфигурация тестовой среды).
Для получения конкретной информации о конфигурации см. комментарии к файлу конфигурации:
module.exports = {
privateKey: '', // 本地私钥地址,位置一般在C:/Users/xxx/.ssh/id_rsa,非必填,有私钥则配置
passphrase: '', // 本地私钥密码,非必填,有私钥则配置
projectName: 'hivue', // 项目名称
dev: { // 测试环境
name: '测试环境',
script: "npm run build-dev", // 测试环境打包脚本
host: '10.240.176.99', // 测试服务器地址
port: 22, // ssh port,一般默认22
username: 'root', // 登录服务器用户名
password: '123456', // 登录服务器密码
distPath: 'dist', // 本地打包dist目录
webDir: '/var/www/html/dev/hivue', // // 测试环境服务器地址
},
prod: { // 线上环境
name: '线上环境',
script: "npm run build", // 线上环境打包脚本
host: '10.240.176.99', // 线上服务器地址
port: 22, // ssh port,一般默认22
username: 'root', // 登录服务器用户名
password: '123456', // 登录服务器密码
distPath: 'dist', // 本地打包dist目录
webDir: '/var/www/html/prod/hivue' // 线上环境web目录
}
// 再还有多余的环境按照这个格式写即可
}
3.4 Просмотр команды развертывания (этот шаг можно пропустить)
Настроеноdeploy.config.js
,бегать
deploy --help
Посмотреть команды развертывания
3.5 Развертывание тестовой среды
Развертывание тестовой среды используетdev
Конфигурация
deploy dev
Сначала идет подтверждение, а затем начинается процесс развертывания.После того, как скрипт автоматически завершит 6-шаговую операцию, поздравляем, развертывание прошло успешно! ! !
3.5. Развертывание онлайн-среды
В развертывании онлайн-среды используютсяprod
Конфигурация
deploy prod
Процесс развертывания аналогичен развертыванию тестовой среды:
4. Оптимизация (Дополнительно)
Вышеупомянутое реализовало автоматическое развертывание скаффолдинга и развернуло основной код.deploy.js
использоватьasync await
заменятьPromise
Оптимизировано.
// deploy.js
const path = require('path');
const fs = require('fs');
const childProcess = require('child_process');
const ora = require('ora');
const node_ssh = require('node-ssh');
const archiver = require('archiver');
const { successLog, errorLog, underlineLog } = require('../utils/index');
const projectDir = process.cwd();
let ssh = new node_ssh(); // 生成ssh实例
// 部署流程入口
async function deploy(config) {
const { script, webDir, distPath, projectName, name } = config;
try {
execBuild(script);
await startZip(distPath);
await connectSSH(config);
await uploadFile(webDir);
await unzipFile(webDir);
await deleteLocalZip();
successLog(`\n 恭喜您,${underlineLog(projectName)}项目${underlineLog(name)}部署成功了^_^\n`);
process.exit(0);
} catch (err) {
errorLog(` 部署失败 ${err}`);
process.exit(1);
}
}
// 第一步,执行打包脚本
function execBuild(script) {
try {
console.log(`\n(1)${script}`);
const spinner = ora('正在打包中');
spinner.start();
console.log();
childProcess.execSync(script, { cwd: projectDir });
spinner.stop();
successLog(' 打包成功');
} catch (err) {
errorLog(err);
process.exit(1);
}
}
// 第二部,打包zip
function startZip(distPath) {
return new Promise((resolve, reject) => {
distPath = path.resolve(projectDir, distPath);
console.log('(2)打包成zip');
const archive = archiver('zip', {
zlib: { level: 9 },
}).on('error', err => {
throw err;
});
const output = fs.createWriteStream(`${projectDir}/dist.zip`);
output.on('close', err => {
if (err) {
errorLog(` 关闭archiver异常 ${err}`);
reject(err);
process.exit(1);
}
successLog(' zip打包成功');
resolve();
});
archive.pipe(output);
archive.directory(distPath, '/');
archive.finalize();
});
}
// 第三步,连接SSH
async function connectSSH(config) {
const { host, port, username, password, privateKey, passphrase, distPath } = config;
const sshConfig = {
host,
port,
username,
password,
privateKey,
passphrase
};
try {
console.log(`(3)连接${underlineLog(host)}`);
await ssh.connect(sshConfig);
successLog(' SSH连接成功');
} catch (err) {
errorLog(` 连接失败 ${err}`);
process.exit(1);
}
}
// 第四部,上传zip包
async function uploadFile(webDir) {
try {
console.log(`(4)上传zip至目录${underlineLog(webDir)}`);
await ssh.putFile(`${projectDir}/dist.zip`, `${webDir}/dist.zip`);
successLog(' zip包上传成功');
} catch (err) {
errorLog(` zip包上传失败 ${err}`);
process.exit(1);
}
}
// 运行命令
async function runCommand(command, webDir) {
await ssh.execCommand(command, { cwd: webDir });
}
// 第五步,解压zip包
async function unzipFile(webDir) {
try {
console.log('(5)开始解压zip包');
await runCommand(`cd ${webDir}`, webDir);
await runCommand('unzip -o dist.zip && rm -f dist.zip', webDir);
successLog(' zip包解压成功');
} catch (err) {
errorLog(` zip包解压失败 ${err}`);
process.exit(1);
}
}
// 第六步,删除本地dist.zip包
async function deleteLocalZip() {
return new Promise((resolve, reject) => {
console.log('(6)开始删除本地zip包');
fs.unlink(`${projectDir}/dist.zip`, err => {
if (err) {
errorLog(` 本地zip包删除失败 ${err}`, err);
reject(err);
process.exit(1);
}
successLog(' 本地dist.zip删除成功\n');
resolve();
});
});
}
module.exports = deploy;
Инициализация скаффолда, параметры конфигурации, команды развертывания при регистрации и рекомендации по использованию соответствуют предыдущим версиям.
Эпилог
Выше приведена небольшая практика блоггеров по поводу front-end облегченного развертывания скаффолдинга.Если вы чувствуете, что что-то наработали, то можете обратить на это внимание, поставить лайк, скачать и пользоваться. Кодировать слова непросто, большое спасибо.
гитхаб-адрес:GitHub.com/sack for/share…(добро пожаловать, звезда, спасибо, спасибо)