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

