前言
前段時間學習了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基礎知識
升級
邏輯梳理 模塊劃分
首先對原項目進行邏輯梳理與模塊劃分,明確各模塊的功能,最終文件目錄如圖:
文件壓縮 升級支持過濾列表
由於此次需要實現對源碼的打包上傳,需要實現對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
,如圖
配置文件
Dockerfile
- 運行部署程序,選擇相應信息開始部署,直至部署完成,如圖
選擇部署
部署成功(容器信息和預期結果一致)
- 訪問對應地址,驗證部署成功,如圖
- 修改
docker-compose.yml
端口爲8900後,再次進行部署,如圖
docker-compose.yml
選擇部署
- 由於存在同名容器,部署過程中會停止並刪除同名容器,之後使用最新的鏡像啓動容器,如圖
- 訪問對應地址,驗證部署成功,如圖
最後
🎉該項目已開源至 github
歡迎下載使用 後續會完善更多功能 🎉
源碼及項目說明
Tip: 喜歡的話別忘記 star
哦😘,有疑問🧐歡迎提出 pr
和 issues
,積極交流。
其他文章: