用前端姿勢玩docker【四】基於docker快速構建webpack的開發與生產環境

牙疼哥哥發表於2020-07-16

目錄

前言

關於docker構建前端環境,相關的坑點與難點,基本上都在這兒了,很多都是個人嘗試總結的經驗,都是從小白過來的,希望能幫助大家快速解決一些問題,拋開前端環境來看,差不多點的映象基本也夠用了。反而前端對易用性的要求更高(前端開發人員可不是天天跟linux打交道),還需要考慮類unix系統與windows的差異化問題,這點會在下一篇文章中重點說明。

打賞啥的也不需要,如果可以,很感激能在github上給個小星星,github入口在 部落格最頂部

回顧

之前也說過 docker對於前端而言組重要的兩個優勢:

  1. 工作環境的快速構建
  2. 工作環境的統一
    所以利用docker的工程化工作流在想象中應該是這樣的:
    例如一個新人從0到1構建前端環境: 安裝docker => 拉取映象 => 根據環境(dev、build)的不同傳入不同的環境變數執行相應的容器 至此ok,易用性做到位之後,整一個開發環境基本相當於是在安裝軟體,這樣基本上就很香了。

難點與坑點

以下主要分享一下過程當中出現的比較坑或比較重要的點,在此不做傻瓜式的教程,也希望大家能夠多動手操作,遇到問題嘗試自我解決並開源分享。

坑點一:docker pull 基礎映象拉取緩慢

首先獲取映象有這麼以下三種方式:

  1. dockerHub: 映象最全, 但訪問速度慢,很蛋疼
  2. 區域網倉庫:需要手動搭建,公司內部大批量使用的最佳方案,但不建議個人開發者使用
  3. 通過import 引入他人匯出的壓縮包來獲取映象
    關於使用dockerhub映象緩慢的問題在第三篇文章中已經介紹過了,配置了DNS、切換了阿里,網易,中科大的國內源之後,很可能仍然很緩慢,之前的方法網上一抓一大把,效果有限,有時候拉取一個源,一下午都拉不下來,只能等晚上才會稍微好點。關於這點,經過這麼多次嘗試後(至少對於我個人所處的網路拓撲結構下),最穩定,速度最快的是使用阿里雲的個人映象加速:登入阿里雲 => 搜尋容器映象服務 => 尋找 映象加速器 => 按照文件操作即可。 至少使用第一種方法來說,這已經是我嘗試過的最快的方法了。

坑點二:alpine安裝nvm夭折

  • 這裡首先要了解alpine是一個非常小巧的linux基礎映象, alpine + busybox 的架構與傳統雙系linux發行版不同,並且手動安裝直接安裝的時node最新版本,多版本的node難以同時存在,當然也不是不能安裝nvm,只是比較麻煩。在此建議直接跑apk的命令, 個人嘗試過切換穩定源後,安裝非常迅速,即便是通過反覆安裝切換node版本也非常迅速。

坑點三:命令找不到,例如 sh: 命令未找到

在linux映象中若出現not found或者命令未找到的報錯資訊,多半是因為環境變數未找到,未在相應檔案中(~/bashrc, ~/.zshrc, ~/.profile, ~/.bash_profile)匯出環境變數, 根據不同的基礎映象與shell型別,與環境變數相關的檔案也不盡相同: 例如ubuntu的檔案為 ~/.bash_profile

  • 單次生效可直接使用 export, 但需要注意,在dockerfile中,儘量在同一個layer層中使用export,也就是在同一個RUN命令下匯出。
  • 使用source命令執行相應的配置檔案,此處需要注意 ubuntu的 sh命令是基於dash,因此source不能直接使用,需要利用 /bin/bash -c "source ~/.bash_profile"

坑點四:build失敗 網路問題,build後不是最新的

  1. 首先說build失敗的網路問題
    此項主要涉及yum、apt-get等update操作或者安裝軟體包的問題,需要保證兩點:
    • docker編譯環境 與 宿主機的 網路連線問題,這點可以通過引數 --network解決,注意 build是 --network, 而 run是 --net
      docker build -t <image>:<tag> --network=host .
    
    • 修改相應的映象源
      例如使用alpine需要修改 /etc/apk/repositories
      echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories \
      && echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories \
    
    使用ubuntu則需要修改 /etc/apt/sources.list
  2. 使用 docker build之後,發現build的映象不是最新的,與dockerfile不符
    如果確定自己run的image和tag都正確,一般出現這種情況大概率是因為快取,快取這個東西嘛,你是個前端你還不懂嗎?哈哈,清除快取即可
      docker build -t <image>:<tag> --network=host --no-cache.
    

坑點五:npm install 易失敗,安裝時間長

  • 在此有必要說一下npm,cnpm,yarn的鎖機制:
    首先需要清除版本號前面^與的區別,1.2.3匹配的是1.2.3-1.2.x ; ^1.2.3 匹配的是 1.2.3-1.x.x
  1. npm: 存在鎖機制 ,高版本(具體忘記了) 增加 package-lock.json, 可以再相同的package.json 與 package-lock.json 配合下安裝確定版本的npm包。
  2. cnpm:不存在鎖版本,目的是使用符合嚴格規範的包(按照 大.中.小 來說, 大版本的數字代表的重大的更新, 中版本的數字代表著功能迭代,但要向下相容,小版本則代表bug修復), 預設安裝大版本下最新的包。
  3. yarn: 存在鎖機制,使用yarn.lock(version欄位、resolved欄位共同起作用)
    install 時安裝時間長主要因為源不夠穩定快速,nrm是一個解決方案。
  • 其次主要涉及一些特殊的包例如: node-sass,這個包在不同的node大版本下,相容的版本是不同的:
NodeJS Minimum node-sass version Node Module
Node 14 4.14+ 83
Node 13 4.13+ 79
Node 12 4.12+ 72
Node 11 4.10+ 67
Node 10 4.9+ 64
Node 8 4.5.3+ 57

坑點六:-v 掛載卷後,容器內檔案被清空

  • 我們起初的想法是,通過本機目錄掛載容器目錄,從而實現修改原始碼的目的,但實際操作中會出現掛載後容器內 掛載卷內的檔案缺失的情況。主要原因如下:
    宿主機 /home/files/src 與 容器 /home/src 相掛載, 是以宿主機目錄 /home/files/src/ 為基準,若起初宿主機目錄為空,容器對應目錄存在檔案,則掛載後,容器內檔案會被清空。在此有個小技巧: 大部分情況下原始碼都在版本控制器中,以git為例,容器內檔案被清空後,可以通過 git status 檢視,是存在操作記錄的,所以清空操作與git並不衝突, 使用正則做部分匹配還原即可:
      git checkout -- src/**
  • 其次資料的通訊、備份、恢復等操作可以通過資料卷容器,可以回看我的第一篇文章。

坑點七:宿主機訪問docker容器內webpack-dev-server,埠對映失敗

  • 這裡首先要ping 一下127.0.0.1 保證本地的迴環地址是通的,並且TCP/IP功能正常。
  • 其次最重要的一點就是 webpack-dev-server的配置中要確保host設定為: 0.0.0.0
    devServer: { ... host: '0.0.0.0', port: '9999', }
  • 然後使用 docker run 做埠對映即可
      docker run -it -p 9999:9999 <image>:<tag>

坑點八:在宿主機訪問後,webpack 熱更新失敗

當時想達到的效果就是通過容器執行dev操作,開啟webpack-dev-server做埠對映,宿主機瀏覽器直接訪問相應網址, 由於掛載卷中的檔案與容器相連,修改後可根據熱更直接顯示在宿主機瀏覽器上,這樣就大功告成了。
但實際操作過程是這樣的:

  1. 首先開啟容器
docker run -it -v /Users/tate/Documents/work/geek/docker/ws/src:/home/webpack-multipage-cli/src -p 9999:9999 ws:1 /bin/bash
  1. 在對應目錄下執行 npm run dev 啟動開發模式
  2. 在宿主機瀏覽器使用 localhost 訪問, 成功訪問。
  3. 修改掛載的資料卷中的檔案,檢查是夠能夠熱更新。
  4. 熱更新失敗,但是重新整理後,修改內容生效。
    解決此問題需要保證以下幾點:
  • 按照坑七將devServer的host修改為 0.0.0.0
  • 將webpack的output配置按照以下修改:
// 此處的publicPath的埠號要與devServer中相同
module.exports = {
      output: {
          publicPath: `//localhost:9999/`,
          hotUpdateChunkFilename: 'js/hot-update-[name].js',
          hotUpdateMainFilename: `hot-update.json`
      }
}
* 在宿主機通過localhost或宿主機IP訪問,避免通過0.0.0.0訪問

成型

dockerfile配合shell指令碼做差異化處理

  • 解決上述問題後,可以再dockerfile中將宿主機的shell指令碼copy至映象中,在預設啟動時執行:
COPY init.sh /home/
CMD ["/bin/bash", "-c", "/home/init.sh"]
  • init.sh 檔案則根據傳入的環境變數構建不同的環境 dev or build
# !/bin/bash
source ~/.bash_profile
cd /home/webpack-multipage-cli
git checkout -- src/** page/**
echo "WEBPACK_MODE: $WEBPACK_MODE"
if [ $WEBPACK_MODE = 'dev' ]; then
      echo "running in develop mode"
      npm run dev
else
      echo "running in build mode"
      npm run build
fi
FROM pomelott/webpack-cli
WORKDIR /home/webpack-multipage-cli
COPY init.sh /home/
COPY cli-config.js /home/webpack-multipage-cli/
COPY output.js /home/webpack-multipage-cli/config/dev/
RUN chmod -R +x /home/init.sh
EXPOSE 9999
CMD ["/bin/bash", "-c", "/home/init.sh"]

實際使用

  • dev環境,掛載容器卷,埠對映開啟熱更
    注: 因不需要手動執行 npm run dev ,所以不需要手動指定/bin/bash
      docker run -it -p 9999:9999 -v /Users/tate/Documents/work/geek/docker/ws/src:/home/webpack-multipage-cli/src --env WEBPACK_MODE=dev pomelott/webpack-cli:latest
  • build 環境
    注: 根據個人需要更換掛載目錄:/Users/tate/Documents/work/geek/docker/ws/src 、/Users/tate/Documents/work/geek/docker/ws/dist
      docker run -it -v -v /Users/tate/Documents/work/geek/docker/ws/src:/home/webpack-multipage-cli/src \
       -v /Users/tate/Documents/work/geek/docker/ws/dist:/home/webpack-multipage-cli/dist --env WEBPACK_MODE=build pomelott/webpack-cli:latest

相關文章