- 原文地址:An Exhaustive Guide to Writing Dockerfiles for Node.js Web Apps
- 原文作者:Praveen Durairaj
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:lsvih
- 校對者:Raoul1996, song-han
一份為 Node.js 應用準備的 Dockerfile 指南
TL;DR
本文涵蓋了從建立簡單的 Dockerfile 到生產環境多級構建 Node.js Web 應用的例子。以下為本指南的內容摘要:
- 使用合適的基礎映象(開發環境使用 carbon,生產環境使用 alpine)。
- 在開發時使用
nodemon
進行熱載入。 - 優化 Docker 的 cache layer(快取層)—— 按照正確的順序使用命令,僅在需要時執行
npm install
。 - 使用
serve
包部署靜態檔案(比如 React、Vue、Angular 生成的 bundle)。 - 使用
alpine
進行生產環境下的多級構建,減少最終映象檔案的大小。 - #建議 — 1) 使用 COPY 代替 ADD 2) 使用
init
標識,處理 CTRL-C 等核心訊號。
如果你需要以上步驟的程式碼,請參考 GitHub repo。
內容
- 簡單的 Dockerfile 樣例與 .dockerignore 檔案
- 使用 nodemon 實現熱更新
- 優化
- 部署靜態檔案
- 生產環境中的直接構建
- 生產環境中的多級構建
讓我們先假設一個名為 node-app 的應用,一個簡單的目錄結構。在頂級目錄下,包含 Dockerfile
以及 package.json
,node app 的程式碼將存於 src
目錄下。為了簡潔起見,我們假設 server.js 定義了一個執行於 8080 埠的 node express 服務。
node-app
├── Dockerfile
├── package.json
└── src
└── server.js
複製程式碼
1. 簡單的 Dockerfile 樣例
FROM node:carbon
# 建立 app 目錄
WORKDIR /app
# 安裝 app 依賴
# 使用萬用字元確保 package.json 與 package-lock.json 複製到需要的地方。(npm 版本 5 以上) COPY package*.json ./
RUN npm install
# 如果你需要構建生產環境下的程式碼,請使用:
# RUN npm install --only=production
# 打包 app 原始碼
COPY src /app
EXPOSE 8080
CMD [ "node", "server.js" ]
複製程式碼
我們將使用最新的 LTS 版本 node:carbon
作為基礎映象。
在構建映象時,docker 會獲取所有位於 context
目錄下的檔案。為了增加 docker 構建的速度,可以在 context 目錄中新增 .dockerignore
檔案來排除不需要的檔案與目錄。
通常,你的 .dockerignore
檔案件應該如下所示:
.git
node_modules
npm-debug
複製程式碼
構建並執行此映象:
$ cd node-docker
$ docker build -t node-docker-dev .
$ docker run --rm -it -p 8080:8080 node-docker-dev
複製程式碼
你將能在 [http://localhost:8080](http://localhost:8080.)
訪問此 app。使用 Ctrl+C
組合鍵可以退出程式。
現在,假設你希望在每次修改程式碼(比如在本地部署時)時都執行以上程式碼,那麼你需要在啟停 node 服務時將程式碼原始檔掛載到容器中。
$ docker run --rm -it -p 8080:8080 -v $(pwd):/app \
node-docker-dev bash
root@id:/app# node src/server.js
複製程式碼
2. 使用 Nodemon 實現熱更新
nodemon 是一款很受歡迎的包,它在執行時會監視目錄中的檔案,當任何檔案發生了改變時,nodemon 將會自動重啟你的 node 應用。
FROM node:carbon
# 建立 app 目錄
WORKDIR /app
# 安裝 nodemon 以實現熱更新
RUN npm install -g nodemon
# 安裝 app 依賴
# 使用萬用字元確保 package.json 與 package-lock.json 複製到需要的地方。(npm 版本 5 以上)COPY package*.json ./
RUN npm install
# 打包 app 原始碼
COPY src /app
EXPOSE 8080
CMD [ "nodemon", "server.js" ]
複製程式碼
我們將構建映象並執行 nodemon,以便在 app
目錄下檔案發生變動時對程式碼進行 rebuild。
$ cd node-docker
$ docker build -t node-hot-reload-docker .
$ docker run --rm -it -p 8080:8080 -v $(pwd):/app \
node-hot-reload-docker bash
root@id:/app# nodemon src/server.js
複製程式碼
一切在 app
目錄下的更改都會觸發 rebuild,發生的變化都能在 [http://localhost:8080](http://localhost:8080.)
上實時展示。請注意,我們已經將檔案掛載到了容器中,因此 nodemon 才能正常工作。
3. 優化
在你的 Dockerfile 中,除非你需要自動解壓 tar 檔案(參考 Docker 最佳實踐),否則最好使用 COPY 來代替 ADD。
繞過 package.json
的 start
命令,而是直接將 app “燒錄”至映象檔案中。因此在 Dockerfile CMD 中不要使用:
$ CMD ["npm","start"]
複製程式碼
而應當使用:
$ CMD ["node","server.js"]
複製程式碼
來代替。這樣可以減少在容器中執行的程式數量,同時還能讓 Node.js 程式接收到 SIGTERM
與 SIGINT
等退出訊號,如若是 npm 程式則會無視這些訊號(參考 Node.js Docker 最佳實踐)。
你還可以使用 --init
標誌,用 tini 輕量集初始化系統來包裝你的 Node.js 程式,它們也能響應一些 SIGTERM
(CTRL-C
)之類的核心訊號。例如,你可以使用:
$ docker run --rm -it --init -p 8080:8080 -v $(pwd):/app \
node-docker-dev bash
複製程式碼
4. 部署靜態檔案
前文的 Dockerfile 是假設你執行了由 Node.js 構建的 API 服務。那麼下面說說如果你想要用 Node.js 部署 React.js、Vue.js、Angular.js 應用時該怎麼做。
FROM node:carbon
# 建立 app 目錄
WORKDIR /app
# 安裝 app 依賴
RUN npm -g install serve
# 使用萬用字元複製 package.json 與 package-lock.json
COPY package*.json ./
RUN npm install
# 打包 app 原始碼
COPY src /app
# 將 react、vue、angular 打包構建成靜態檔案
RUN npm run build
EXPOSE 8080
# 將 dist 目錄部署於 8080 埠
CMD ["serve", "-s", "dist", "-p", "8080"]
複製程式碼
如你所見,當你需要構建 React、Vue、Angular 製作的 UI app 時,使用 npm run build
來壓縮 JS 與 CSS 檔案,生成最終的 bundle
包,在這兒我們使用了 npm 的 [serve](https://www.npmjs.com/package/serve)
包來部署靜態檔案。
此外,可以使用一些替代方案:1) 在本地構建打包檔案,然後使用 nginx docker 來部署這些靜態檔案。2) 使用 CI/CD 工作流進行構建。
5. 生產環境中的直接構建
FROM node:carbon
# 建立 app 目錄
WORKDIR /app
# 安裝 app 依賴
# RUN npm -g install serve
# 使用萬用字元複製 package.json 與 package-lock.json
COPY package*.json ./
RUN npm install
# 打包 app 原始碼
COPY src /app
# 如需對 react/vue/angular 打包,生成靜態檔案,使用:
# RUN npm run build
EXPOSE 8080
# 如需部署靜態檔案,使用:
#CMD ["serve", "-s", "dist", "-p", "8080"]
CMD [ "node", "server.js" ]
複製程式碼
構建並執行這個一體化映象:
$ cd node-docker
$ docker build -t node-docker-prod .
$ docker run --rm -it -p 8080:8080 node-docker-prod
複製程式碼
由於底層為 Debian,構建完成後映象約為 700MB(具體數值取決於你的原始碼)。下面探討如何減小這個檔案的大小。
6. 生產環境中的多級構建
使用多級構建時,將在 Dockerfile 中使用多個 FROM
語句,但最後僅會使用最終階段構建的檔案。這樣,得到的映象將僅包含生產伺服器中所需的依賴,理想情況下檔案將非常小。
# ---- Base Node ----
FROM node:carbon AS base
# 建立 app 目錄
WORKDIR /app
# ---- Dependencies ----
FROM base AS dependencies
# 使用萬用字元複製 package.json 與 package-lock.json
COPY package*.json ./
# 安裝在‘devDependencies’中包含的依賴
RUN npm install
# ---- Copy Files/Build ----
FROM dependencies AS build
WORKDIR /app
COPY src /app
# 如需對 react/vue/angular 打包,生成靜態檔案,使用:
# RUN npm run build
# --- Release with Alpine ----
FROM node:8.9-alpine AS release
# 建立 app 目錄
WORKDIR /app
# 可選命令:
# RUN npm -g install serve
COPY --from=dependencies /app/package.json ./
# 安裝 app 依賴
RUN npm install --only=production
COPY --from=build /app ./
#CMD ["serve", "-s", "dist", "-p", "8080"]
CMD ["node", "server.js"]
複製程式碼
使用上面的方法,用 Alpine 構建的映象檔案大小約 70MB,比之前少了 10 倍。使用 alpine
版本進行構建能有效減小映象的大小。
如果你對前面的方法有任何建議,或希望看到別的用例,請告知作者。
加入 Reddit / HackerNews 討論:)
此外,你是否試過將 Node.js web 應用部署在 Hasura 上呢?這其實是將 Node.js 應用部署於 HTTPS 域名的最快的方法(僅需使用 git push)。嘗試使用 hasura.io/hub/nodejs-… 的模板快速入門吧!Hasura 中所有的專案模板都帶有 Dockerfile 與 Kubernetes 標準檔案,你可以自由進行定義。
感謝 Tanmai Gopal
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。