2022-06-23

前端 docker 優雅打包切分環境

團隊最近要使用 GitLab CI/CD 功能了,DevOps 介紹完之後馬上就意識到有個很久的問題必須要解決,之前很菜的時候以及開發流程還沒有很完整的時候還可以手動來決定正式環境還是測試環境 docker 再打包就好,現在需要透過 Dockerfile 產生編譯結果以及透過文件指令來區分兩者的差別。

目標:

  1. 需要修改的項目越少越好。
  2. 最好的狀況是透過設定好的文件和指令互相作用產生不同的編譯結果。

主要步驟:

  1. 在 Dockerfile 產生編譯結果
  2. 透過打包指令產生正式環境或測試環境

菜雞流程

  1. 手動執行 npm run build 或 npm run dev
  2. 打包成 image docker build -t tag_name .
  3. Dockerfile 單純只複製 dist 內容
FROM node:lts-alpine RUN npm install -g http-server RUN mkdir dist COPY ./dist ./dist EXPOSE 8080 CMD [ "http-server", "dist" ]

這些動作我原本都是透過編輯 sh 檔來完成,不想要正式環境更新就把指令註解起來,兩個都不想更新就通通註解起來, 最後我只要下指令sh maker.sh 系統就會做完所有事。

在 Dockerfile 產生編譯結果

很多文章分享的大概會是這樣子的 Dockerfile 內容

FROM node:lts-alpine RUN npm install -g http-server WORKDIR /app COPY package*.json ./ RUN npm install --production COPY . . RUN npm run build EXPOSE 80 CMD [ "http-server", "dist" ]

如果以官方範例模板來說這段其實沒問題,因為檔案夠小還包得住,但是當專案已經安裝了很多套件或是有點規模時就包不住了,因為 CPOY . . 指令,這個指令是把根目錄的所有東西複製到 docker 環境裡,然而 node_modules 也一起複製進去了啊啊啊。

神奇的是即便我寫了 .dockerignore 忽略 node_modules 仍然會失敗(好像和內存有關??),所以我只好把 npm run build 的動作從 Dockerfile 分離,就產生了前一段的菜雞流程。

我把這個困擾我很久的問題描述給 DevOps,他跟我說 Dockerfile 在打包的時候可以多段建構,每一段的建構環境都是乾淨的而且可以互相指向,並給我他使用的 Dockerfile 給我當參考。

修改運行成功的結果 Dockerfile :

# Builder FROM node:14.19 as builder WORKDIR /app COPY . . RUN npm install RUN npm run build # Distribution FROM node:lts-alpine RUN npm install -g http-server RUN mkdir dist COPY --from=builder /app/dist ./dist EXPOSE 8080 CMD [ "http-server", "dist" ]

第一段是在 Docker 進行編譯 npm run build,第二段是只將編譯好的結果包起來。

切分建構階段的關鍵是 FROM 以及 as 指令,我們可以將第一段建構環境使用 as 命名,讓第二段建構可以透過 --from=builder 指向到第一段環境 COPY 出 dist 內容,docker 會依照最後建構階段的內容打包成 image,並且容量也和菜雞流程包出來的容量一樣小。

官方文件說明非官方中文說明

要注意的是如果 FROM 一樣的話,那 docker 會自動認為那是同個環境,像是如果我把第二階段改成 FROM node:14.19 與第一段相同,那打包出來的容量會超大 XDDD,因為第一段執行過 npm install 產生的 node_modules 也一起包進去了。

透過打包指令產生正式環境或測試環境

由於前後分離的關係,前端程式碼大多已經是編譯好的結果,不像後端程式碼可以隨時修改環境變數產生變化,也就是所謂的靜態檔案,搜尋了很多如何讓前端程式碼根據指令來替換環境變數,第一時間看到的大多是透過 shell 指令來修改關鍵字,但是我覺得那樣太暴力了,應該有更好的方法。

理想的狀況是在 docker build 的時候修改 Dockerfile,從 npm run build 改為 npm run dev 指令,但是 Dockerfile 所提供的指令 ENV ARG 偏向修改環境變數,但環境變數已經寫好在專案中的 .env 了,所以不符合我的需求。

這問題困擾我很久,弄得我好焦慮 XD,突然間想到 不然輸入 docker build --help 看有什麼東西好了。

看到 Options 有段描述

  -f, --file string   Name of the Dockerfile (Default is 'PATH/Dockerfile')

原來 Dockerfile 不一定要叫 Dockerfile 啊 XDDD,也可以下指令讓 docker 去執行指定檔名,這樣我就可以寫好兩個 Dockerfile 然後下 docker build 指令時就可以分別使用不同的 Dockerfile 產生不同的前端環境。

我在根目錄寫了兩份 Dockerfile 分別是 Dockerfile_dev Dockerfile_pro,差別只有 npm run dev 和 npm run build

當我要產生測試環境時指令為:

docker build -t tag_name_dev -f ./Dockerfile_dev .

正式環境時指令為:

docker build -t tag_name_pro -f ./Dockerfile_pro .

這樣 docker 就可以根據我的指令產生不同編譯結果以及不同情境的 image,也達成了 目標 2. 透過設定好的文件和指令互相作用產生不同的編譯結果

檔名

上一段我把 Dockerfile 檔名改為 Dockerfile_dev 這樣在 VS Code 會失去主題,有夠醜XDD,改成 dev.Dockerfile 就會吃到主題,

但是 docker 相關的檔案被四散排列有點不好找,我再把檔名改成 .dDev.Dockerfile

docker 圖案排在一起看起來舒服多了XD,而且指令也能正常發動。

docker build -t tag_name_dev -f ./.dDev.Dockerfile .

結語

這次解決了一直以來的問題,更完善了團隊前端 docker 打包流程,現階段我所理解的前端打包部分就到這裡了,如果有新發現再回來修改~

前端 docker 優雅打包切分環境

團隊最近要使用  GitLab   CI/CD  功能了,DevOps 介紹完之後馬上就意識到有個很久的問題必須要解決,之前很菜的時候以及開發流程還沒有很完整的時候還可以手動來決定正式環境還是測試環境 docker 再打包就好,現在需要透過  Dockerfile  產生編譯結果...