目錄
- 前言
- 原文開始
- 開始編寫Dockerfile
- 在本地測試下
- Dockerfile 生產環境優化
- 使用Alpine node鏡像
- 添加 NODE_ENV 環境變量
- 使用npm ci 而不是npm install
- 使用User指令
- 使用多階段構建
- 最終的Dockerfile
前言
眾所周知,Node.js項目在部署的時候,無論是在虛擬機部署,還是使用docker
進行部署,無非都是要先npm install
,然后把整個node_modules
丟上去,最后啟動服務
但是有些開發依賴在生產環境部署的時候是不需要的。如果能每次打包只打包生產依賴,那就極大的減少node_modules
大小
所以當時我在優化公司的nestjs項目時,苦苦尋找解決方案,就找到了這一篇文章,英文版原本在這里 www.tomray.dev/nestjs-dock…
原文開始
這是一篇手把手的教程,教你如何在制作nestjs
鏡像時,能夠編寫出一個優化生產依賴的Dockerfile
有了這個Dockerfile
,無論是在本地開發環境,還是在容器環境都能很輕松完成部署
P.S 如果你想直接復制最終的Dockerfile
,請直接跳到文章末尾
開始編寫Dockerfile
每個鏡像都可以視為一個單獨的軟件包,你可以通過編寫Dockerfile
告訴docker
如何來打包鏡像
讓我們開始編寫吧,首先,先創建一個空的文件
touch Dockerfile
然后把我們的指令添加到Dockerfile
里面,并且注釋每一步是干什么
# 基礎鏡像 FROM node:18 # 創建一個應用目錄 WORKDIR /usr/src/app # 這個星號通配符意思是復制package.json和package-lock.json,復制到當前應用目錄 COPY package*.json ./ # 安裝應用依賴 RUN npm install # 安裝完畢后復制當前目錄所有文件到鏡像目錄里面 COPY . . # 執行npm run build 后生成dist目錄 RUN npm run build # 使用打包后的鏡像 CMD ["node","dist/main.js"]
同樣的,創建.gitignore
文件,我們可以把那些不需要經過docker
打包的文件給忽略掉
touch .dockerignore
把一下文件給排除忽略掉
Dockerfile .dockerignore node_modules npm-debug .log dist
在本地測試下
如果你在本地安裝了docker
,可以在本地進行打包測試,讓我們來瞧瞧是否如預期中那樣打包鏡像
在命令行中執行以下命令,當然,你也可以把nest-app-demo
換成你想要的鏡像名,需要注意的是,不要忘記后面的.
號!
docker build -t nest-app-demo .
接著你可以在你本機執行以下命令,查看是否已經成功打包了鏡像
docker images
噢,感謝上帝,已經成功打包成鏡像了,可以看到我們的命名nest-app-demo
就像只肥碩的土撥鼠靜靜的躺在鏡像列表里面
docker images REPOSITORY TAG IMAGE ID CREATED SIZE nest-app-demo latest 004f7f222139 31 seconds ago 1.24GB
緊接著讓我們來把鏡像給跑起來,映射到本機80
端口,如果端口被占用可以使用其他端口
docker run -p 80:3000 nest-app-demo
這時候你就在瀏覽器中輸入http://localhost
進行訪問,可以看到容器正常啟動。 如果你想刪除那些正在運行的容器,可以使用以下命令進行刪除
docker rm -f $(docker ps -aq)
Dockerfile 生產環境優化
好了,現在我們對鏡像包進行壓縮了,因為可以看到,目前鏡像大小是1.24G,噢,上帝,真是太大了!
讓我們來看看之前編寫的Dockerfile
,看如何對它進行優化
使用Alpine node鏡像
強烈推薦使用node:18-alpine
而不是node:18
,使用alpine
的鏡像可以直接把鏡像體積從1.24g減少到466MB!
添加 NODE_ENV 環境變量
很多依賴包會根據當前的NODE_ENV
環境變量而進行判斷是否優化壓縮,所以我們可以在Dockerfile
里面把環境變量加進去,設置為production
ENV NODE_ENV production
順便提一句,如果你不知道如何在Nestjs里面通過配置文件進行環境變量設置的話,可以看下這篇入門文章www.tomray.dev/nestjs-conf…
使用npm ci 而不是npm install
npm 比較推薦使用npm ci
而不是npm install
來打包鏡像,至于原因可以點擊這里查看docs.npmjs.com/cli/v8/comm…
"
npm ci
與npm install
很相似,除了當它用于自動化時,如測試平臺,持續集成和部署————或者任何你想確保能有一個干凈的依賴安裝環境"
正好符合我們現在的情況,所以我們要使用npm ci
來替換npm install
RUN npm ci
使用User指令
默認情況下,Dockerfile
會使用root
權限來構建你的鏡像,這會存在一定的安全風險,在這里,我們已經擁有一個叫node
的用戶,我們可以直接使用它
USER node
當你在使用COPY
指令時,添加標志以確保用戶能夠擁有正確的權限也是一種好做法,比如可以使用--chown=node:node
COPY --chown=node:node package*.json ./
使用多階段構建
在Dockerfile
中,你可以定義多階段構建,這是一種通過多個鏡像構建出最優鏡像的方式,可以使得最后生成的鏡像最小化
################### # BUILD FOR LOCAL DEVELOPMENT ################### FROM node:18-alpine As development # ...開發環境構建說明 ################### # BUILD FOR PRODUCTION ################### # 生產環境基礎鏡像 FROM node:18-alpine As build # ... 這里是構建說明 ################### # PRODUCTION ################### # 生產環境基礎鏡像 FROM node:18-alpine As production # ... 你的生產環境構建說明
上面是多階段構建的3個階段:
development
這是用于本地環境構建鏡像時的階段build
這是用于構建生產鏡像的階段production
復制構建完畢后的文件并且啟動服務
如果你不需要在本地環境使用docker
啟動你的Nestjs應用,可以把前兩個階段合二為一
上述多階段設置的好處在于,這樣你就有了一個可以在本地開發中使用的Dockerfile
(與docker-compose
組合在一起)。同時創建一個用于生產的優化Docker
鏡像。
如果你對使用Docker Compose
的多階段Dockerfile
進行本地開發(熱加載)有興趣,可以點擊看這篇文章www.tomray.dev/nestjs-dock…
最終的Dockerfile
通過上述使用的方案進行優化后,最終的Dockerfile
如下,他可以幫助我們構建出最優的鏡像
################### # BUILD FOR LOCAL DEVELOPMENT ################### FROM node:18-alpine As development # 創建應用目錄 WORKDIR /usr/src/app # 復制依賴清單到容器鏡像里. # 這個星號通配符意思是復制package.json和package-lock.json,復制到當前應用目錄. # 首先復制這個選項可以防止在每次代碼更改時重新運行npm install. COPY --chown=node:node package*.json ./ # 使用npm ci來安裝依賴而不是npm install RUN npm ci # 復制安裝后的依賴包到當前目錄下 COPY --chown=node:node . . # 使用指定的用戶而不是root權限用戶 USER node ################### # BUILD FOR PRODUCTION ################### FROM node:18-alpine As build WORKDIR /usr/src/app COPY --chown=node:node package*.json ./ # 我們需要通過Nest CLI 來執行npm run build,這是個開發依賴,然后把安裝后依賴全部復制到指定目錄 COPY --chown=node:node --from=development /usr/src/app/node_modules ./node_modules COPY --chown=node:node . . # 執行打包命令 RUN npm run build # 設置生產環境變量 ENV NODE_ENV production # 運行' npm ci '會刪除現有的node_modules目錄,并傳入——only=production確保只安裝了生產依賴項。這確保node_modules目錄盡可能優化 RUN npm ci --only=production && npm cache clean --force USER node ################### # PRODUCTION ################### FROM node:18-alpine As production # 將生產依賴和打包后的文件復制到指定目錄下 COPY --chown=node:node --from=build /usr/src/app/node_modules ./node_modules COPY --chown=node:node --from=build /usr/src/app/dist ./dist # 啟動服務 CMD [ "node", "dist/main.js" ]
可以看到,最后打包的鏡像只有189MB大小
REPOSITORY TAG IMAGE ID CREATED SIZE nest-cloud-run latest 004f7f222139 31 seconds ago 189MB