用前端姿勢玩docker【四】基於docker快速構建webpack的開發與生產環境
阿新 • • 發佈:2020-07-17
## 目錄
* [用前端姿勢玩docker【一】Docker通俗理解常用功能彙總與操作埋坑](https://www.cnblogs.com/pomelott/p/13212085.html)
* [用前端姿勢玩docker【二】dockerfile定製映象初體驗](https://www.cnblogs.com/pomelott/p/13235325.html)
* [用前端姿勢玩docker【三】基於nvm的前端環境構建技巧](https://www.cnblogs.com/pomelott/p/13296026.html)
* [用前端姿勢玩docker【四】基於docker快速構建webpack的開發與生產環境](https://www.cnblogs.com/pomelott/p/13325328.html)
* [用前端姿勢玩docker【五】快速構建中類Unix系統與Windows系統的差異化處理【待發布,請持續關注】]()
## 前言
關於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 : --network=host .
```
* 修改相應的映象源
例如使用alpine需要修改 `/etc/apk/repositories`:
```dockerfile
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 : --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並不衝突, 使用正則做部分匹配還原即可:
```bash
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 :
```
### 坑點八:在宿主機訪問後,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
```
2. 在對應目錄下執行 `npm run dev` 啟動開發模式
3. 在宿主機瀏覽器使用 localhost 訪問, 成功訪問。
4. 修改掛載的資料卷中的檔案,檢查是夠能夠熱更新。
5. 熱更新失敗,但是重新整理後,修改內容生效。
解決此問題需要保證以下幾點:
* 按照坑七將devServer的host修改為 `0.0.0.0`
* 將webpack的output配置按照以下修改:
```js
// 此處的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
```bash
# !/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
```
* 測試階段的dockerfile也做一下展示,詳細的可見github:[https://github.com/pomelott/webpack-multipage-cli](https://github.com/pomelott/webpack-multipage-cli)
```dockerfile
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:la