使用 Docker 構建前端應用
4 人贊了文章
隨著容器化技術的大行其道,Docker 在前端領域中也有著越來越廣泛的應用。本文主要介紹了容器化技術給前端工程帶來的變化,演示了如何使用 Docker 構建不同種類的前端應用。
什麼是 Docker
Docker 最初是 dotCloud 公司創始人 Solomon Hykes 在法國期間發起的一個公司內部項目,它是基於 dotCloud 公司多年雲服務技術的一次革新,並於 2013 年 3 月以 Apache 2.0 授權協議開源,主要項目代碼在 GitHub 上進行維護。Docker 項目後來還加入了 Linux 基金會,並成立推動開放容器聯盟(OCI)。
Docker 使用 Google 公司的 Go 語言 開發實現,基於 Linux 內核的 cgroup,namespace,以及 AUFS 類的 Union FS 等技術,對進程進行封裝隔離,屬於操作系統層面的虛擬化技術。由於隔離的進程獨立於宿主和其它隔離的進程,因此稱其為容器。最初實現是基於 LXC,從 0.7 版本後開始去除 LXC,轉而使用自行開發的 libcontainer,從 1.11 開始,進一步演進使用 runC 和 containerd。Docker 在容器的基礎上,進行了進一步的封裝,從文件系統、網路互聯到進程隔離等等,極大的簡化了容器的創建和維護。使得 Docker 技術比虛擬機技術更為輕便、快捷。
前端為什麼要用 Docker
這裡只討論使用 Docker 給前端帶來的優勢,偏運維相關的比如啟動速度快,資源利用率高等略過,有興趣的同學可以上官網看看文檔。
- 提供一致的運行環境。在任何環境下使用 Docker 構建的鏡像的運行環境都是確定的,Docker 給應用提供了一個從開發到上線均一致的環境。比如 Node.js 項目在不同版本下性能表現不一致,開發環境用的是 Node.js 6,UAT 環境用了 Node.js 10,那麼很可能介面的壓測結果不一致。
- 更輕鬆的遷移。由於 Docker 確保了運行環境的一致性,使得應用的遷移更加容易。可以很輕易將在一個平台上運行的應用,遷移到另一個平台上,而不用擔心運行環境的變化導致應用無法正常運行。比如接到任務說下周要加一個分區,或者客戶要求部署私有雲,可以很放心的說鏡像拿走,而不用擔心環境問題。
- 持續交付和部署。代碼從開發到最終在生產環境上的部署,需要經過很多中間環境,通過定製應用鏡像來實現持續集成、持續交付,非常有助於降低構建持續交付流程的複雜程度。在中小型公司可以考慮直接使用 GitLab CI 搭建持續集成環境。
- 快速部署、回滾。得益於 Docker 使用的分層存儲和鏡像技術,使得擴展鏡像變得非常簡單。可以預先把程序需要的依賴,靜態資源等在構建過程中添加到鏡像,在需要的時候啟動該容器實現快速部署、回滾、止血。比如當出現線上事故需要回滾時,傳統做法是觸發某些自動化工具去拉代碼裝依賴打包最後部署,一旦某個環節出了問題,譬如網路被牆了導致依賴拉不下來,構建失敗等等,小事故可能會演變為 P0 事故。
使用 Docker 構建 Web 前端項目
Web 前端項目的部署上線一般會經歷 babel 編譯,webpack 構建等過程,最終將打包後的靜態資源放在靜態資源伺服器上。
第一步,創建項目。這個過程和使用的框架並沒有太多關係,這裡就拿 React 來演示,使用 create-react-app
創建項目,進入目錄後使用 npm run build
命令構建出部署所需要的靜態資源
npx create-react-app my-appcd my-appnpm run build
第二步,選擇合適的靜態資源伺服器。這裡選用 Nginx
,這是一款輕量級的 HTTP 伺服器,具有很多非常優越的特性:輕量、高性能、並發能力強,用來部署靜態頁面很便捷
在根目錄創建 nginx.conf
,按需進行配置
- 現代前端框架幾乎都使用了 HTML5 push/pop history API 來完全控制 Web 應用程序的歷史記錄,在 Nginx 中需要配置 try_files 指令
- 前後端分離後,在 Nginx 中需要配置反向代理解決前端跨域問題
- ...
server { listen 80 default_server; server_name _; location / { root /usr/share/nginx/html; index index.html ; try_files $uri $uri/ /index.html; } # location ~ /api/ { # proxy_connect_timeout 2s; # proxy_read_timeout 600s; # proxy_send_timeout 600s; # proxy_pass http://gateway:8080; # proxy_set_header Host $host:80; # proxy_set_header X-Real-IP $remote_addr; # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # client_max_body_size 1000m; # }}
第三步,定製鏡像。在項目的根目錄創建 Dockerfile
文件來定製我們的鏡像
- 使用
FROM
指令指定基礎鏡像,官方已經給我們準備好了 Nginx 的鏡像 - 使用
LABEL
指令為構建的鏡像設置作者信息 - 使用
ADD
指令將 build 文件夾下的所有文件拷貝至 Nginx 的根目錄 - 使用
ADD
指令添加上一步準備好的 Nginx 配置文件 - 使用
EXPOSE
指令聲明運行時容器提供的服務埠,暴露 80 埠
第四步,構建鏡像。使用 docker build
命令進行鏡像構建
$ docker build -t himstone/test .Sending build context to Docker daemon 118.1 MBStep 1/5 : FROM nginx:latest ---> 3c5a05123222Step 2/5 : LABEL maintainer "himstone.yang@gmail.com" ---> Using cache ---> fe13a72edfa9Step 3/5 : ADD ./build/ /usr/share/nginx/html/ ---> Using cache ---> 7725efecebbeStep 4/5 : ADD nginx.conf /etc/nginx/ ---> Using cache ---> 402dd5589c05Step 5/5 : EXPOSE 80 ---> Using cache ---> ba0ce6d40a8fSuccessfully built ba0ce6d40a8f
第五步,測試並上傳鏡像。
使用 docker run
命令啟動容器並將本地的 8080 埠 映射到容器的 80 埠
$ docker run -d -p 8080:80 himstone/test 72162395ba4241201e1b3ca08ed38f4da768051d14b3962bc2a1d77261c4df24
打開瀏覽器,訪問 localhost:8080。出現如下頁面表示工作正常,測試通過。
使用 docker push
上傳鏡像。這裡只是演示,直接上傳到了 Docker Hub 上,推薦使用 Harbor
搭建私有鏡像倉庫
$ docker login -u himstone -p yourPasswordLogin Succeeded$ docker push himstone/testThe push refers to a repository [docker.io/himstone/test]e4d08e577219: Pushed69c46c39d897: Pushed08422f31c8c5: Pushed4d3d2ca78cd4: Pushed9c46f426bcb7: Pushedlatest: digest: sha256:3136cad04911e71d40afb0f481f56c6ac286ac8afe50b5578efe8e617a0f4e32 size: 1365
使用 Docker 構建 Node.js 前端項目
前端應用中還包括使用 Node.js 開發的後端服務,常用於承擔一些Api 中間層、BFF層的角色,甚至是完整的核心服務。
第一步,創建項目。可選的框架有很多,比如 express,koa 等,在這裡拿阿里開源的 Egg.js
做演示,使用 egg-init
初始化項目,再將 package.json
里 scripts-start 里的 --daemon 去掉,在 Docker 內建議前台運行
egg-init egg-example --type=simplecd egg-examplenpm i
第二步,定製鏡像。在項目的根目錄創建 Dockerfile
文件來定製我們的鏡像
- 使用
FROM
指令指定基礎鏡像,這裡使用官方提供的node:8
- 使用
LABEL
指令為構建的鏡像設置作者信息 - 使用
COPY
指令將根目錄下的所有文件拷貝至鏡像內 - 使用
RUN
指令執行npm install
安裝依賴 - 使用
EXPOSE
指令聲明運行時容器提供的服務埠,暴露 7001 埠 - 使用
CMD
指令設置容器啟動命令為npm start
第三步,構建鏡像。使用 docker build
命令進行鏡像構建
$ docker build -t himstone/test-egg .Sending build context to Docker daemon 123.5 MBStep 1/6 : FROM node:8 ---> c5e9a81034a9Step 2/6 : LABEL maintainer "himstone.yang@gmail.com" ---> Using cache ---> f68494fbcd6eStep 3/6 : COPY . . ---> 685e9ad05928Removing intermediate container 3c0e94e7453eStep 4/6 : RUN npm install ---> Running in 94c5e4d37c64npm WARN co-mocha@1.2.2 requires a peer of mocha@>=1.18 <6 but none is installed. You must install peer dependencies yourself.up to date in 6.249s ---> 8768307d6ae9Removing intermediate container 94c5e4d37c64Step 5/6 : EXPOSE 3000 ---> Running in b554fc4777ab ---> 55d1a6cff311Removing intermediate container b554fc4777abStep 6/6 : CMD npm start ---> Running in 31378b658364 ---> c7fdb400b841Removing intermediate container 31378b658364Successfully built c7fdb400b841
第四步,測試並上傳鏡像
使用 docker run
命令啟動容器並將本地的 8080 埠映射到容器的 7001 埠
$ docker run -d -p 8080:7001 himstone/test-egg 72162395ba4241201e1b3ca08ed38f4da768051d14b3962bc2a1d77261c4df24
打開瀏覽器,訪問 localhost:8080。出現如下的頁面表示工作正常,測試通過。
使用 docker push
上傳鏡像。
$ docker push himstone/test-eggThe push refers to a repository [docker.io/himstone/test-egg]2c4ef22a1c6b: Pushed7e45aa7e5ac9: Pushedd9fdc5af195e: Mounted from himstone/test-ssr245ce6af2e7b: Mounted from himstone/test-ssr373fc5310302: Mounted from himstone/test-ssr25494c62cf78: Mounted from himstone/test-ssrd714f65bc280: Mounted from himstone/test-ssrfd6060e25706: Mounted from himstone/test-ssrd7ed640784f1: Mounted from himstone/test-ssr1618a71a1198: Mounted from himstone/test-ssrlatest: digest: sha256:ce0a10b07ab2f91904d60f2b35754995c177e7dd8e3d0397e84e2453d79a3384 size: 2426
使用 Docker 構建 SSR 服務端渲染的前端項目
上文有介紹如何使用 Docker 構建普通的Web項目,即使用前端渲染,在遭遇 SEO、首屏性能等問題時,往往會考慮使用服務端渲染技術,兩者在構建上的差異主要體現在後者不需要靜態資源伺服器,直接由 Node.js 直出 HTML。因為和構建 Node.js 前端項目的過程中有許多相似處,這裡只做簡要介紹,完整的 demo 代碼可參考文末的鏈接。
第一步,創建項目。這裡就拿 Next.js 來演示,參考 官方文檔 創建項目
第二步,定製鏡像。在項目的根目錄創建 Dockerfile 文件來定製我們的鏡像
- 使用
COPY
指令將根目錄下的所有文件拷貝至鏡像內 - 使用
RUN
指令執行npm install
安裝依賴 - 使用
RUN
指令執行npm run build
打包靜態資源 - 使用
EXPOSE
指令聲明運行時容器提供的服務埠,暴露 3000 埠 - 使用
CMD
指令設置容器啟動命令為npm start
第三步,構建鏡像。使用 docker build
命令進行鏡像構建
第四步,測試並上傳鏡像。
出現如下的頁面表示工作正常。
小結
通過上述的三個例子,演示了如何使用 Docker 構建不同類型的前端應用。Docker 為前端應用提供了一致的運行環境,帶來了快速部署、回滾等特性。除此之外,還需要考慮容器化應用的部署,容器的編排、調度等問題,可以使用 Kubernetes
等容器管理平台。
當然 Docker 還可以應用到持續集成中,GitLab CI
為 runner 提供了許多執行器,其中就包括了 Docker Executor。如下圖所示,Docker Executor 對比其他擁有許多優勢,比如每次構建提供一致的運行環境,零配置支持並發構建,適配複雜的構建環境等。
所有的 demo 源代碼已上傳至 Github,需要的可以參考
himStone/build-frontend-app-with-docker推薦閱讀: