前端docker自動化部署 前言 構思 升級前的準備 升級 使用 最後

前言

前段時間學習了docker相關的內容,docker可以實現數據隔離、跨平臺等,加快項目後續部署。

在瞭解docker部署流程後,學習了CI/CD的概念,通過對gitlabdocker的 簡單配置即可實現持續集成,大幅提高生產效率。

本人基於gitlabdocker配置前端部署大致流程:

服務器監聽遠端git分支是否出現新提交 --> 服務器拉取對應分支代碼 --> 基於node鏡像實現依賴下載和源碼編譯 --> 基於nginx鏡像及編譯文件build前端鏡像 --> 基於docker-compose.yml運行相應容器

聯繫之前實現的前端自動化部署工具
(詳情請見:從零開始 Node實現前端自動化部署),
準備對其升級並支持docker部署。

構思

考慮兼容傳統部署方式基礎上,支持本地編譯後代碼的上傳部署和源碼上傳、遠端編譯部署兩種方式。

考慮部分簡單項目可能無需使用docker-compose進行容器編排,直接使用docker run指令即可。

因此考慮支持以下方式進行項目部署:

部署方式 legacy docker docker-compose
本地打包編譯dist
源碼遠端打包編譯

由此考慮用戶使用流程如下:

選擇項目-->選擇部署方式-->選擇上傳源碼or編譯後代碼-->遠端自動部署

分爲以下兩種實現思路:

  1. 源碼上傳至雲端,雲端藉助Dockerfile基於node鏡像實現依賴下載、打包編譯,基於nginx鏡像及編譯文件build前端鏡像,最後運行容器。
  2. 項目本地編譯後,打包上傳至雲端,直接基於nginx鏡像及編譯文件build前端鏡像,最後運行容器。

升級前的準備

爲更好理解該項目實現,可提前準備以下內容:

  1. 熟悉docker部署流程及Dockerfiledocker-compose.yml語法規則
  2. 成功安裝docker、docker-compose等基礎環境的遠端服務器
  3. 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爲例,介紹前端鏡像構建過程:

  1. 引用node:lts-alpine3.12(基礎版node鏡像,文件較小)
  1. 切換爲阿里源(若依賴下載較慢,可切換爲阿里源)
  2. 指定工作目錄/tmp/cache(容器內)
  3. 添加當前同級目錄中package.json(package-lock.json存在時請添加)
  4. 執行依賴安裝
  5. 拷貝當前目錄文件至工作目錄
  6. 執行編譯指令
  7. 引用socialengine/nginx-spa:latest(包含SPA相關配置的nginx鏡像,監聽地址爲/app/index.html)
  8. 拷貝 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部署流程

在主程序中,根據用戶選擇進行部署判斷,主要分爲以下流程

  1. docker、docker-compose安裝檢查
  1. 上傳Dockerfiledocker-compose.yml
  2. 構建docker鏡像
  3. 啓動容器前檢查是否存在同名容器,存在則停止並刪除同名容器
  4. 根據配置使用docker rundocker-compose.yml啓動容器
  5. 展示當前運行中容器狀態
  6. 提示完成部署

tips:

  • 若服務器網速較慢,可提前安裝nodenginx的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端口。

  1. 拉取代碼至本地,安裝依賴,執行本地構建,目錄如圖:
  1. 該項目構建後產生build文件夾且不存在package-lock.json文件,因此修改配置文件Dockerfile,如圖

配置文件

Dockerfile

  1. 運行部署程序,選擇相應信息開始部署,直至部署完成,如圖

選擇部署

部署成功(容器信息和預期結果一致)

  1. 訪問對應地址,驗證部署成功,如圖
  1. 修改 docker-compose.yml 端口爲8900後,再次進行部署,如圖

docker-compose.yml

選擇部署

  1. 由於存在同名容器,部署過程中會停止並刪除同名容器,之後使用最新的鏡像啓動容器,如圖
  1. 訪問對應地址,驗證部署成功,如圖

最後

🎉該項目已開源至 github 歡迎下載使用 後續會完善更多功能 🎉
源碼及項目說明

Tip: 喜歡的話別忘記 star 哦😘,有疑問🧐歡迎提出 prissues ,積極交流。

其他文章:

從零開始 Node實現前端自動化部署

從零開始 React Hook實現在線壁紙網站

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章