前言
前段时间学习了docker
相关的内容,docker
可以实现数据隔离、跨平台等,加快项目后续部署。
在了解docker部署流程后,学习了CI/CD
的概念,通过对gitlab
及docker
的 简单配置即可实现持续集成,大幅提高生产效率。
本人基于gitlab
及docker
配置前端部署大致流程:
服务器监听远端git分支是否出现新提交 --> 服务器拉取对应分支代码 --> 基于
node
镜像实现依赖下载和源码编译 --> 基于nginx
镜像及编译文件build前端镜像 --> 基于docker-compose.yml
运行相应容器
联系之前实现的前端自动化部署工具
(详情请见:从零开始 Node实现前端自动化部署),
准备对其升级并支持docker
部署。
构思
考虑兼容传统部署方式基础上,支持本地编译后代码的上传部署和源码上传、远端编译部署两种方式。
考虑部分简单项目可能无需使用docker-compose
进行容器编排,直接使用docker run
指令即可。
因此考虑支持以下方式进行项目部署:
部署方式 | legacy | docker | docker-compose |
---|---|---|---|
本地打包编译dist | ✔ | ✔ | ✔ |
源码远端打包编译 | ❌ | ✔ | ✔ |
由此考虑用户使用流程如下:
选择项目-->选择部署方式-->选择上传源码or编译后代码-->远端自动部署
分为以下两种实现思路:
- 源码上传至云端,云端借助
Dockerfile
基于node
镜像实现依赖下载、打包编译,基于nginx
镜像及编译文件build前端镜像,最后运行容器。 - 项目本地编译后,打包上传至云端,直接基于
nginx
镜像及编译文件build前端镜像,最后运行容器。
升级前的准备
为更好理解该项目实现,可提前准备以下内容:
- 熟悉docker部署流程及
Dockerfile
、docker-compose.yml
语法规则 - 成功安装docker、docker-compose等基础环境的远端服务器
- node基础知识
升级
逻辑梳理 模块划分
首先对原项目进行逻辑梳理与模块划分,明确各模块的功能,最终文件目录如图:
image文件压缩 升级支持过滤列表
由于此次需要实现对源码的打包上传,需要实现对node_modeles
相关文件的过滤。考虑根据配置文件中exclude
字段配置过滤列表实现打包过滤功能。
在进行压缩前,读取目标文件目录targetDir
的子文件名称,排除存在过滤列表excludeFiles
中的文件,返回过滤后的文件名数组。
在压缩过程中,旧版本使用archive.directory()
实现对整个文件目录的压缩,现改为基于文件数组依次进行打包,使用fs.statSync(filePath).isDirectory()
判断子文件是否为文件夹,使用archive.file()
、archive.directory()
进行对应打包处理。
compress.js
源码:
const fs = require('fs')
const archiver = require('archiver')
const join = require('path').join
function compress (targetDir, localFile, excludeFiles, homeDirName = 'web/') {
return new Promise((resolve, reject)=>{
// filter exclude files
const filterDir = filterExcludeFiles(targetDir, excludeFiles)
console.log('正在压缩文件...')
let output = fs.createWriteStream(localFile) // create file stream write
const archive = archiver('zip', {
zlib: { level: 9 } // set compress level
})
output.on('close', () => {
console.log('压缩完成!共计 ' + (archive.pointer() / 1024 /1024).toFixed(3) + 'MB')
resolve('Compression complete')
}).on('error', (err) => {
console.error('压缩失败', err)
reject('Compression failed')
})
archive.on('error', (err) => {
throw err
})
archive.pipe(output) // save file by pipe
// append file and dir
filterDir.forEach(file => {
const filePath = join(targetDir, file)
const stat = fs.statSync(filePath)
if (stat.isDirectory()) {
archive.directory(filePath, homeDirName + file)
} else {
archive.file(filePath, { name: file, prefix: homeDirName })
}
})
archive.finalize() // make sure file stream write completely
})
}
// filter exclude files
function filterExcludeFiles (targetDir, excludeFiles = []) {
return fs.readdirSync(targetDir).filter(file => {
return (!excludeFiles.includes(file))
})
}
module.exports = compress
升级终端颜色
基于colors
实现终端命令的颜色区分显示,使用如下:
const colors = require('colors')
colors.setTheme({
silly: 'rainbow',
input: 'grey',
verbose: 'cyan',
prompt: 'grey',
data: 'grey',
help: 'cyan',
debug: 'blue',
info: 'blue',
error: 'red',
warn: 'yellow',
success: 'green'
})
console.log('粗体文字'.bold)
console.log('出现错误'.error)
tip: 部署方式等选择依然基于inquirer
实现。
新增Dockerfile docker-compose.yml
这里以支持源码编译的Dockerfile
为例,介绍前端镜像构建过程:
- 引用
node:lts-alpine3.12
(基础版node镜像,文件较小)
- 切换为阿里源(若依赖下载较慢,可切换为阿里源)
- 指定工作目录
/tmp/cache
(容器内) - 添加当前同级目录中
package.json
(package-lock.json存在时请添加) - 执行依赖安装
- 拷贝当前目录文件至工作目录
- 执行编译指令
- 引用
socialengine/nginx-spa:latest
(包含SPA相关配置的nginx镜像,监听地址为/app/index.html) - 拷贝 node镜像
/tmp/cache/dist
至 nginx镜像/app
目录下(编译后文件夹可自定义)
Dockerfile
FROM node:lts-alpine3.12 as build
# 若依赖下载较慢,可切换为阿里源
RUN npm config set registry https://registry.npm.taobao.org
WORKDIR /tmp/cache
ADD package.json .
# 存在package-lock.json时启用
ADD package-lock.json .
RUN npm install
ADD . .
# 编译指令可自定义
RUN npm run build
FROM socialengine/nginx-spa:latest as nginx
# 编译后文件夹可自定义
COPY --from=build /tmp/cache/dist /app
docker-compose.yml
较为简单,指定容器名、镜像名、重启方式、映射端口(宿主机端口:容器端口)等信息。
ps: 远端安装最新版本docker-compose
,这里使用3.8的语法,低版本可使用2.x
语法
docker-compose.yml
version: "3.8"
services:
web:
# 容器名、镜像名请保持与配置文件一致
container_name: spa_web
restart: always
image: spa/web:dev
ports:
- 8900:80
docker部署流程
在主程序中,根据用户选择进行部署判断,主要分为以下流程
- docker、docker-compose安装检查
- 上传
Dockerfile
或docker-compose.yml
- 构建docker镜像
- 启动容器前检查是否存在同名容器,存在则停止并删除同名容器
- 根据配置使用
docker run
或docker-compose.yml
启动容器 - 展示当前运行中容器状态
- 提示完成部署
tips:
- 若服务器网速较慢,可提前安装
node
和nginx
的docker镜像,以便加快后续部署速度- docker pull node:lts-alpine3.12
- docker pull socialengine/nginx-spa:latest
spp.js
docker部分代码:
// docker流程
// docker 部署流程 docker env check --> upload Dockerfile --> build image
const dockerFilePath = deployDir + releaseDir
await runCommand(ssh, `docker -v`, '/')
await uploadFile(ssh, getAbsolutePath(BUILD__MODE === 'dist' ? docker_file : docker_file__build), dockerFilePath + '/Dockerfile') // upload Dockerfile
console.log('5- 开始构建docker镜像...请耐心等待'.bold)
await runCommand(ssh, `docker build -t ${ image } .`, dockerFilePath)
console.log('6- 准备启动docker容器...请耐心等待')
if (DEPLOY__MODE === 'docker') {
if ((await runCommand(ssh, `docker ps -f name=${ container_name }`)).indexOf('\n') !== -1) {
console.log('存在同名容器,正在删除同名容器...')
await runCommand(ssh, `docker stop ${ container_name }`, '')
await runCommand(ssh, `docker rm ${ container_name }`, '')
}
await runCommand(ssh, `docker run --name ${container_name} -p ${ports} -d ${image}`, dockerFilePath)
} else {
// docker-compose 部署流程 upload docker-compose --> run docker-compose --> show container
await runCommand(ssh, `docker-compose -v`, '/')
await uploadFile(ssh, getAbsolutePath(docker_compose), dockerFilePath + '/docker-compose.yml') // upload docker-compose
if ((await runCommand(ssh, `docker ps -f name=${ container_name }`)).indexOf('\n') !== -1) {
console.log('存在同名容器,正在删除同名容器...')
await runCommand(ssh, `docker stop ${ container_name }`, '')
await runCommand(ssh, `docker rm ${ container_name }`, '')
}
await runCommand(ssh, 'docker-compose up -d', dockerFilePath)
}
// 显示当前运行中容器
console.log('7- 当前运行中的容器...'.bold)
await runCommand(ssh, 'docker ps', dockerFilePath)
console.log(`恭喜!${ name }部署成功`.success)
至此完成该项目的主要升级,更多细节请参考my-auto-deploy。
使用
这里选取两种情况进行展示:
- docker + source build
- docker-compose + dist
这里以react
的在线壁纸为例,服务器已开放8800、8900端口。
- 拉取代码至本地,安装依赖,执行本地构建,目录如图:
- 该项目构建后产生
build
文件夹且不存在package-lock.json
文件,因此修改配置文件
和Dockerfile
,如图
配置文件
imageDockerfile
image- 运行部署程序,选择相应信息开始部署,直至部署完成,如图
选择部署
image部署成功(容器信息和预期结果一致)
image- 访问对应地址,验证部署成功,如图
- 修改
docker-compose.yml
端口为8900后,再次进行部署,如图
docker-compose.yml
image选择部署
image- 由于存在同名容器,部署过程中会停止并删除同名容器,之后使用最新的镜像启动容器,如图
- 访问对应地址,验证部署成功,如图
最后
🎉该项目已开源至 github
欢迎下载使用 后续会完善更多功能 🎉
源码及项目说明
Tip: 喜欢的话别忘记 star
哦😘,有疑问🧐欢迎提出 pr
和 issues
,积极交流。
其他文章:
网友评论