這是山月關於高級前端進階暨前端工程系列文章的第 M 篇文章 (M 隨便打的,畢竟也不知道能寫多少篇),關於前 M-1 篇文章,能夠從個人 github repo shfshanyue/blog 中找到,若是點進去的話能夠捎帶~點個贊~,若是沒有點進去的話,那就給這篇文章點個贊。javascript
本篇文章地址在 前端工程化系列,歡迎訂閱。html
我在 github 上新建了一個倉庫 每日一題,天天一道面試題,歡迎交流。前端
Docker 變得愈來愈流行,它能夠輕便靈活地隔離環境,進行擴容,運維管理。對於業務開發者而言,隨着持續集成的發展,對代碼質量及快速迭代的要求也愈來愈高。java
對於前端而言,在 CI 環境中使用也更容易集成開發,測試與部署。好比能夠爲流水線(Pipeline)設置 Lint/Test/Security/Audit/Deploy/Artifact 等任務,更好地把控項目質量。node
如今不管是前端,後端仍是運維,都很強調 devops
的理念,接下來我將會寫一系列關於 devops
在前端中應用的文章。你能夠在個人博客 https://github.com/shfshanyue... 中或者個人公衆號 【全棧成長之路】中訂閱更多文章。react
這裏將介紹如何使用 Docker 部署前端應用,千里之行,始於足下。始於足下的意思就是,先讓它可以跑起來。webpack
首先,簡單介紹一下一個典型的前端應用部署流程nginx
npm install
, 安裝依賴npm run build
,編譯,打包,生成靜態資源介紹完部署流程後,簡單寫一個 Dockerfilegit
FROM node:10-alpine # 表明生產環境 ENV PROJECT_ENV production # 許多 package 會根據此環境變量,作出不一樣的行爲 # 另外,在 webpack 中打包也會根據此環境變量作出優化,可是 create-react-app 在打包時會寫死該環境變量 ENV NODE_ENV production WORKDIR /code ADD . /code RUN npm install && npm run build && npm install -g http-server EXPOSE 80 CMD http-server ./public -p 80
如今這個前端服務已經跑起來了,接下來你能夠完成部署的其它階段了。github
通常狀況下,如下就成了運維的工做了,不過,拓展本身的知識邊界老是沒錯的。其它階段介紹以下
nginx
或者 traefik
作反向代理。在我內部集羣中使用了 traefik
,詳見 traefik 簡易入門 kubernetes
或者 docker compose
作容器編排。在我內部集羣中使用了 compose
,詳見 docker compose 簡易入門 gitlab ci
,drone ci
或者 github actions
等作 CI/CD 自動部署。在我內部集羣中使用了 github actions
,詳見 github actions 簡易入門 這時鏡像存在兩個問題,致使每次部署時間過長,不利於產品的快速交付,沒有快速交付,也就沒有敏捷開發 (Agile)
咱們注意到,相對於項目的源文件來說,package.json
是相對穩定的。若是沒有新的安裝包須要下載,則再次構建鏡像時,無需從新構建依賴。則能夠在 npm install 上節省一半的時間。
對於 ADD
來說,若是須要添加的文件內容的 checksum
沒有發生變化,則能夠利用緩存。把 package.json/package-lock.json
與源文件分隔開寫入鏡像是一個很好的選擇。目前,若是沒有新的安裝包更新的話,能夠節省一半時間
FROM node:10-alpine ENV PROJECT_ENV production ENV NODE_ENV production # http-server 不變更也能夠利用緩存 RUN npm install -g http-server WORKDIR /code # 首次添加此兩個文件,充分利用緩存 ADD package.json package-lock.json /code RUN npm install --production ADD . /code RUN npm run build EXPOSE 80 CMD http-server ./public -p 80
關於利用緩存有更多細節,須要特別注意一下。如 RUN git clone <repo>
,若是命令字符串沒有更新,則將使用緩存,當命令是非冪等性時,這將有可能致使問題。
關於緩存及可能致使的問題,能夠參考個人文章 Dockerfile 最佳實踐
FROM node:10-alpine ENV PROJECT_ENV production ENV NODE_ENV production # http-server 不變更也能夠利用緩存 RUN npm install -g http-server WORKDIR /code # 首次添加此兩個文件,充分利用緩存 ADD package.json package-lock.json /code RUN npm ci ADD . /code RUN npm run build EXPOSE 80 CMD http-server ./public -p 80
在 CI 環境下主要作了一點改動:使用 npm ci
代替 npm i
,經實驗,npm ci
能夠減小將近一半的的依賴安裝時間。
$ npm install added 1154 packages in 60s $ npm ci added 1154 packages in 35s
另外,當 package.json
與 package-lock.json
版本不匹配時,npm ci
將會報出異常,提早檢測出不安全信息,及早發現問題,及早解決問題。
得益於緩存,如今鏡像構建時間已經快了很多。可是,此時鏡像的體積依舊過於龐大,這也將會致使部署時間的加長。緣由以下
考慮下每次 CI/CD 部署的流程
顯而易見,鏡像體積過大會在前兩步上傳及下載時形成傳輸效率低下,增長每次部署的延時。
即便,構建服務器與生產服務器在同一節點下,沒有延時的問題 (基本沒可能)。減小鏡像體積也可以節省磁盤空間。
關於鏡像體積的過大,徹底是由於node_modules 臭名昭著的體積:
但最後咱們只須要構建生成的靜態資源,對於源文件以及 node_modules
下文件,佔用體積過大且沒必要要,形成浪費。
此時能夠利用 Docker 的多階段構建,僅來提取編譯後文件,即打包生成的靜態資源,對 Dockerfile 作一改進
FROM node:10-alpine as builder ENV PROJECT_ENV production ENV NODE_ENV production # http-server 不變更也能夠利用緩存 WORKDIR /code ADD package.json package-lock.json /code RUN npm ci ADD . /code RUN npm run build # 選擇更小體積的基礎鏡像 FROM nginx:10-alpine COPY --from=builder /code/public /usr/share/nginx/html
此時,鏡像體積從 1G+ 變成了 50M+。若此時的部署僅僅是在測試環境或者多分支環境下爲了方便測試,那就大功告成,完美解決問題了。
分析一下 50M+ 的鏡像體積,nginx:10-alpine
的鏡像是16M,剩下的40M是靜態資源。生產環境的靜態資源每每會在獨立域名上維護,並使用 CDN 進行加速。
若是把靜態資源給上傳到文件存儲服務,即OSS,並使用 CDN 對 OSS 進行加速,則沒有必要打入鏡像了。而在生產環境下也有對靜態資源上 CDN 的強烈需求。
此時鏡像大小會控制在 20M 如下。雖然極大地減少了鏡像體積,可是它會增長複雜度與增長鏡像構建時間(如上傳到OSS),對於測試環境或者分支環境不必使用 OSS。
關於靜態資源,能夠分類成兩部分:
/build
,此類文件在項目中使用 require/import 引用,會被 webpack 打包並加 hash 值,並經過 publicPath 修改資源地址。能夠把此類文件上傳至 oss,並加上永久緩存,不須要打入鏡像/static
,此類文件在項目中直接引用根路徑,直接打入鏡像,若是上傳至 OSS 可能增長複雜度 (批量修改 publicPath)此時經過一個腳本命令 npm run uploadOss
,來把靜態資源上傳至 OSS。更新後的 Dockerfile 以下
FROM node:10-alpine as builder ENV PROJECT_ENV production ENV NODE_ENV production # http-server 不變更也能夠利用緩存 WORKDIR /code ADD package.json package-lock.json /code RUN npm ci ADD . /code # npm run uploadOss 是把靜態資源上傳至 oss 上的腳本文件 RUN npm run build && npm run uploadOss # 選擇更小體積的基礎鏡像 FROM nginx:10-alpine COPY --from=builder code/public/index.html code/public/favicon.ico /usr/share/nginx/html/ COPY --from=builder code/public/static /usr/share/nginx/html/static
通過本篇文章總結,在前端中構建鏡像須要注意如下幾點
alpine
的鏡像,減少鏡像體積。node
的版本號,儘量也鎖定 alpine
的版本號,如 node:10.19-alpine3.11
。(我示例代碼中未如此詳細地指出)NODE_ENV
及 PROJECT_ENV
,如在測試環境下進行構建掃碼添加個人機器人微信,將會自動(自動拉人程序正在研發中)把你拉入前端高級進階學習羣
推薦一個關於大廠招聘的公衆號【互聯網大廠招聘】,做者將在公衆號裏持續推送各個大廠的招聘職位及要求,並與大廠面試官以及招聘負責人直通,感興趣的能夠直接與負責人交流。
另外,做者也將持續推送優質的大廠面試經驗,各類大廠獨家面試題以及優秀文章分享,不限於前端,後端,運維和系統設計。
我在 github 上新建了一個倉庫 每日一題,天天一道面試題,歡迎交流。