目錄
- 用前端姿勢玩docker【一】Docker通俗理解常用功能匯總與操作埋坑
- 用前端姿勢玩docker【二】dockerfile定制鏡像初體驗
- 用前端姿勢玩docker【三】基於nvm的前端環境構建技巧
- 用前端姿勢玩docker【四】基於docker快速構建webpack的開發與生產環境
- 用前端姿勢玩docker【五】快速構建中類Unix系統與Windows系統的差異化處理
前言
關於docker構建前端環境,相關的坑點與難點,基本上都在這兒了,很多都是個人嘗試總結的經驗,都是從小白過來的,希望能幫助大家快速解決一些問題,拋開前端環境來看,差不多點的鏡像基本也夠用了。反而前端對易用性的要求更高(前端開發人員可不是天天跟linux打交道),還需要考慮類unix系統與windows的差異化問題,這點會在下一篇文章中重點說明。
打賞啥的也不需要,如果可以,很感激能在github上給打個小星星✨: https://github.com/pomelott/webpack4-cli
回顧
之前也說過 docker對於前端而言組重要的兩個優勢:
- 工作環境的快速構建
- 工作環境的統一
所以利用docker的工程化工作流在想象中應該是這樣的:
例如一個新人從0到1構建前端環境: 安裝docker => 拉取鏡像 => 根據環境(dev、build)的不同傳入不同的環境變量運行相應的容器 至此ok,易用性做到位之后,整一個開發環境基本相當於是在安裝軟件,這樣基本上就很香了。
難點與坑點
以下主要分享一下過程當中出現的比較坑或比較重要的點,在此不做傻瓜式的教程,也希望大家能夠多動手操作,遇到問題嘗試自我解決並開源分享。
坑點一:docker pull 基礎鏡像拉取緩慢
首先獲取鏡像有這么以下三種方式:
- dockerHub: 鏡像最全, 但訪問速度慢,很蛋疼
- 局域網倉庫:需要手動搭建,公司內部大批量使用的最佳方案,但不建議個人開發者使用
- 通過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"
- 在dockerfille中通過ENV 設置環境變量可能是最方便的做法。
坑點四:build失敗 網絡問題,build后不是最新的
- 首先說build失敗的網絡問題
此項主要涉及yum、apt-get等update操作或者安裝軟件包的問題,需要保證兩點:- docker編譯環境 與 宿主機的 網絡連接問題,這點可以通過參數 --network解決,注意 build是 --network, 而 run是 --net
docker build -t <image>:<tag> --network=host .
- 修改相應的鏡像源
例如使用alpine需要修改/etc/apk/repositories
:
使用ubuntu則需要修改echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" > /etc/apk/repositories \ && echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >> /etc/apk/repositories \
/etc/apt/sources.list
- 使用 docker build之后,發現build的鏡像不是最新的,與dockerfile不符
如果確定自己run的image和tag都正確,一般出現這種情況大概率是因為緩存,緩存這個東西嘛,你是個前端你還不懂嗎?哈哈,清除緩存即可docker build -t <image>:<tag> --network=host --no-cache.
坑點五: 容器內無法ping通內網其他ip,打開VPN同樣如此.
- 具體表現可能有很多中,比如說:timeout、no route、can't access等,且不通的多為內網ip。
解決方法為修改/etc/docker/daemon.json
,增加bip,與內網分配至不通網段,例如當前使用172.17.x.x,則改為172.16.x.x
{
"experimental" : false,
"debug" : true,
"bip": "172.16.10.1/24"
}
坑點六:npm install 易失敗,安裝時間長
- 在此有必要說一下npm,cnpm,yarn的鎖機制:
首先需要清除版本號前面^與的區別,1.2.3匹配的是1.2.3-1.2.x ; ^1.2.3 匹配的是 1.2.3-1.x.x
- npm: 存在鎖機制 ,高版本(具體忘記了) 增加 package-lock.json, 可以再相同的package.json 與 package-lock.json 配合下安裝確定版本的npm包。
- cnpm:不存在鎖版本,目的是使用符合嚴格規范的包(按照 大.中.小 來說, 大版本的數字代表的重大的更新, 中版本的數字代表着功能迭代,但要向下兼容,小版本則代表bug修復), 默認安裝大版本下最新的包。
- yarn: 存在鎖機制,使用yarn.lock(version字段、resolved字段共同起作用)
install 時安裝時間長主要因為源不夠穩定快速,nrm是一個解決方案。
- 其次主要涉及一些特殊的包例如: node-sass,這個包在不同的node大版本下,兼容的版本是不同的:
- 其次還要注意可能是npm緩存有影響:
npm uninstall xxx --save
npm cache clean -f
npm install xxx
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做端口映射,宿主機瀏覽器直接訪問相應網址, 由於掛載卷中的文件與容器相連,修改后可根據熱更直接顯示在宿主機瀏覽器上,這樣就大功告成了。
但實際操作過程是這樣的:
- 首先開啟容器
docker run -it -v /Users/tate/Documents/work/geek/docker/ws/src:/home/webpack4-cli/src -p 9999:9999 ws:1 /bin/bash
- 在對應目錄下執行
npm run dev
啟動開發模式 - 在宿主機瀏覽器使用 localhost 訪問, 成功訪問。
- 修改掛載的數據卷中的文件,檢查是夠能夠熱更新。
- 熱更新失敗,但是刷新后,修改內容生效。
解決此問題需要保證以下幾點:
- 按照坑八將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訪問
坑點十 npm部分包構建不成功
- 在一些相對小巧的鏡像或者鏡像的slim版本中,不會默認安裝編譯構建c++程序的基礎包,所以一些node包就會構建失敗,例如:
# error
gulp-imagemin: Couldn't load default plugin "gifsicle"
# error
gifsicle pre-build test failed
安裝對應的編譯c++的基礎包即可, 拿ubuntu來講,需要在dockerfile中安裝以下包:
apt-get install -y g++ make automake gcc
成型
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/webpack4-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/webpack4-cli
FROM pomelott/webpack-cli
WORKDIR /home/webpack4-cli
COPY init.sh /home/
COPY cli-config.js /home/webpack4-cli/
COPY output.js /home/webpack4-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/webpack4-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/webpack4-cli/src \
-v /Users/tate/Documents/work/geek/docker/ws/dist:/home/webpack4-cli/dist --env WEBPACK_MODE=build pomelott/webpack-cli:latest