一份爲 Node.js 應用準備的 Dockerfile 指南

一份爲 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 repovue

內容

  1. 簡單的 Dockerfile 樣例與 .dockerignore 文件
  2. 使用 nodemon 實現熱更新
  3. 優化
  4. 部署靜態文件
  5. 生產環境中的直接構建
  6. 生產環境中的多級構建

讓咱們先假設一個名爲 node-app 的應用,一個簡單的目錄結構。在頂級目錄下,包含 Dockerfile 以及 package.json,node app 的代碼將存於 src 目錄下。爲了簡潔起見,咱們假設 server.js 定義了一個運行於 8080 端口的 node express 服務。node

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 做爲基礎鏡像。react

在構建鏡像時,docker 會獲取全部位於 context 目錄下的文件。爲了增長 docker 構建的速度,能夠在 context 目錄中添加 .dockerignore 文件來排除不須要的文件與目錄。android

一般,你的 .dockerignore 文件件應該以下所示:ios

.git
node_modules
npm-debug
複製代碼

構建並運行此鏡像:nginx

$ 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 組合鍵能夠退出程序。git

如今,假設你但願在每次修改代碼(好比在本地部署時)時都運行以上代碼,那麼你須要在啓停 node 服務時將代碼源文件掛載到容器中。github

$ 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 應用。web

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.jsonstart 命令,而是直接將 app 「燒錄」至鏡像文件中。所以在 Dockerfile CMD 中不要使用:

$ CMD ["npm","start"]
複製代碼

而應當使用:

$ CMD ["node","server.js"]
複製代碼

來代替。這樣能夠減小在容器中運行的進程數量,同時還能讓 Node.js 進程接收到 SIGTERMSIGINT 等退出信號,如如果 npm 進程則會無視這些信號(參考 Node.js Docker 最佳實踐)。

你還可使用 --init 標誌,用 tini 輕量集初始化系統來包裝你的 Node.js 進程,它們也能響應一些 SIGTERMCTRL-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


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章
相關標籤/搜索