Docker - 設定 Node.js



Node.js 應用程式 Docker 化是一種將應用程式及其依賴項打包到一個包含且可重現的單元中的可靠方法。這確保了跨不同環境的部署的一致性,並簡化了開發到生產的工作流程。

在本章中,我們將帶您從設定 NodeJs 專案到在 Docker 容器內執行應用程式的旅程。我們將涵蓋重要的主題,例如組織專案、建立 Dockerfile 和管理容器。在本章結束時,您將完全理解如何將 Node.js 應用程式 Docker 化,但更重要的是,能夠將這些概念應用到您的專案中。

先決條件

在將我們的 Node.js 應用程式 Docker 化之前,您應該瞭解以下一些先決條件:

  • 已安裝 Node.js 和 npm(或 yarn) - 這些對於開發 Node.js 應用程式至關重要。
  • 對 Node.js 和 Express.js 的基本瞭解 - 熟悉這些框架將有所幫助。
  • 程式碼編輯器或 IDE - 用於編寫和管理程式碼。
  • 版本控制系統(可選) - 用於管理專案的程式碼(例如,Git)。

建立倉庫

要組織您的專案,請建立一個新的 Git 倉庫:

選擇一個倉庫名稱 - 為您的專案選擇一個描述性的名稱,例如node-blog

初始化 Git 倉庫

git init

建立遠端倉庫(可選) - 如果您想協作或備份您的程式碼,請在 GitHub、GitLab 或 Bitbucket 等平臺上建立一個遠端倉庫。

目錄結構

我們將建立一個簡單的部落格應用程式。以以下方式為您的專案建立目錄結構:

node-blog/

├── package.json

├── index.js

├── routes/

│   ├── posts.js

│   └── users.js

├── models/

│   ├── posts.js

│   └── users.js

├── controllers/

│   ├── postsController.js

│   └── usersController.js

├── public/

│   └── index.html

├── Dockerfile

└── .gitignore
  • package.json - 儲存專案依賴項和元資料。
  • index.js - 主要的應用程式入口點。
  • routes - 包含不同 API 端點的路由定義。
  • models - 定義應用程式的資料模型。
  • controllers - 處理業務邏輯並與模型互動。
  • public - 儲存靜態檔案,例如 html、css 等。
  • Dockerfile - 定義 Docker 映象構建指令。
  • .gitignore - 指定要從 Git 版本控制中排除的檔案和目錄。

此結構將為您的部落格應用程式提供堅實的基礎。您可以根據專案的具體需求對其進行調整。

讓我們繼續下一節:新增路由

新增路由

讓我們建立一個基本的路由來處理我們部落格主頁的 GET 請求。

步驟 1. 安裝 Express.js

$ npm install express

步驟 2. 安裝 Bootstrap

$ npm install bootstrap

步驟 3. 建立 index.js 檔案

const express = require('express');
const app = express();

app.get('/', (req, res) => {
   res.send('Hello from your blog!');
});

app.listen(3000, () => {
   console.log('Server listening on port 3000');
});

步驟 4. 執行應用程式

node index.js
Docker Setting NodeJs 1

解釋

  • 首先,我們匯入 Express 庫並建立一個 Express 應用程式例項。
  • 然後,我們使用app.get()方法定義了一個路由來處理對根路徑('/')的 GET 請求。
  • 路由處理程式傳送一個簡單的“Hello from your blog!”訊息作為響應。
  • 最後,我們在埠 3000 上啟動伺服器。
Docker Setting NodeJs 2

完成程式碼

在深入研究程式碼之前,讓我們建立專案結構:

project-directory/

   models/

      posts.js

      users.js

   controllers/

      postsController.js

      usersController.js

   routes/

      posts.js

      users.js

   public/
    
      index.html

   index.js

models/posts.js

let posts = [];
let nextId = 1;

const createPost = (title, content, authorId) => {
   const post = { id: nextId++, title, content, authorId };
   posts.push(post);
   return post;
};

const getPosts = () => {
   return posts;
};

const getPostById = (id) => {
   return posts.find(post => post.id === parseInt(id));
};

const updatePost = (id, title, content) => {
   const postIndex = posts.findIndex(post => post.id === parseInt(id));

   if (postIndex !== -1) {
      posts[postIndex] = { ...posts[postIndex], title, content };
      return posts[postIndex];
   }
   return null;
};

const deletePost = (id) => {
   const postIndex = posts.findIndex(post => post.id === parseInt(id));

   if (postIndex !== -1) {
      return posts.splice(postIndex, 1)[0];
   }
   return null;
};

module.exports = {
   createPost,
   getPosts,
   getPostById,
   updatePost,
   deletePost,
};

models/users.js

let users = [];
let nextUserId = 1;

const createUser = (username, email, password) => {
   const user = { id: nextUserId++, username, email, password };
   users.push(user);
   return user;
};

const getUserByUsername = (username) => {
   return users.find(user => user.username === username);
};

module.exports = {
   createUser,
   getUserByUsername,
};

controllers/postsController.js

const postsModel = require('../models/posts');

const getPosts = (req, res) => {
   const posts = postsModel.getPosts();
   res.json(posts);
};

const createPost = (req, res) => {
   const { title, content, authorId } = req.body;
   const post = postsModel.createPost(title, content, authorId);
   res.status(201).json(post);
};

const getPostById = (req, res) => {
   const { id } = req.params;
   const post = postsModel.getPostById(id);
   
   if (post) {
      res.json(post);
   } else {
      res.status(404).json({ message: 'Post not found' });
   }
};

const updatePost = (req, res) => {
   const { id } = req.params;
   const { title, content } = req.body;
   const updatedPost = postsModel.updatePost(id, title, content);

   if (updatedPost) {
      res.json(updatedPost);
   } else {
      res.status(404).json({ message: 'Post not found' });
   }
};

const deletePost = (req, res) => {
   const { id } = req.params;
   const deletedPost = postsModel.deletePost(id);

   if (deletedPost) {
      res.json({ message: 'Post deleted' });
   } else {
      res.status(404).json({ message: 'Post not found' });
   }
};

module.exports = {
   getPosts,
   createPost,
   getPostById,
   updatePost,
   deletePost,
};

controllers/usersController.js

const usersModel = require('../models/users');

const createUser = (req, res) => {
   const { username, email, password } = req.body;
   const user = usersModel.createUser(username, email, password);
   res.status(201).json(user);
};

const getUserByUsername = (req, res) => {
   const { username } = req.params;
   const user = usersModel.getUserByUsername(username);

   if (user) {
      res.json(user);
   } else {
      res.status(404).json({ message: 'User not found' });
   }
};

module.exports = {
   createUser,
   getUserByUsername,
};

routes/posts.js

const express = require('express');
const router = express.Router();
const postsController = require('../controllers/postsController');

router.get('/', postsController.getPosts);
router.post('/', postsController.createPost);
router.get('/:id', postsController.getPostById);
router.put('/:id', postsController.updatePost);
router.delete('/:id', postsController.deletePost);
module.exports = router;

routes/users.js

const express = require('express');
const router = express.Router();
const usersController = require('../controllers/usersController');

router.post('/', usersController.createUser);
router.get('/:username', usersController.getUserByUsername);

module.exports = router;

index.js

const express = require('express');
const path = require('path');
const posts = require('./routes/posts');
const users = require('./routes/users');

const app = express();
app.use(express.json());

// Import Bootstrap CSS
app.use('/css', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/css')));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/posts', posts);
app.use('/users', users);

app.get('/', (req, res) => {
   res.sendFile(path.join(__dirname, 'public', 'index.html'));
});

const port = process.env.PORT || 3000;
app.listen(port, () => {
   console.log(`Server listening on port ${port}`);
});

public/index.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>My Blog</title>
   <link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
   <div class="container">
      <div class="row">
         <div class="col-md-6">
            <form action="/users" method="POST">
               <div class="mb-3">
                  <label for="username" class="form-label">Username</label>
                  <input type="text" class="form-control" id="username" name="username" placeholder="Username">
               </div>
               <div class="mb-3">
                  <label for="email" class="form-label">Email</label>
                  <input type="email" class="form-control" id="email" name="email" placeholder="Email">
               </div>
               <div class="mb-3">
                  <label for="password" class="form-label">Password</label>
                  <input type="password" class="form-control" id="password" name="password" placeholder="Password">
               </div>
               <button type="submit" class="btn btn-primary">Create User</button>
            </form>
         </div>
         <div class="col-md-6">
            <form action="/posts" method="POST">
               <div class="mb-3">
                  <label for="title" class="form-label">Title</label>
                  <input type="text" class="form-control" id="title" name="title" placeholder="Title">
               </div>
               <div class="mb-3">
                  <label for="content" class="form-label">Content</label>
                  <textarea class="form-control" id="content" name="content" rows="3"></textarea>
               </div>
               <button type="submit" class="btn btn-primary">Create Post</button>
            </form>
         </div>
      </div>
   </div>
   <script src="/js/bootstrap.bundle.min.js"></script>
</body>
</html>

程式碼解釋

模型

  • posts.js - 定義帖子的記憶體儲存,包括 CRUD 操作(建立、讀取、更新、刪除)。
  • users.js - 定義使用者的記憶體儲存,包括使用者建立和按使用者名稱檢索。

控制器

  • postsController.js - 處理與帖子相關的 HTTP 請求,與posts模型互動。
  • usersController.js - 處理與使用者相關的 HTTP 請求,與users模型互動。

路由

  • posts.js - 定義帖子的 API 端點(GET、POST、PUT、DELETE)。
  • users.js - 定義使用者的 API 端點(POST、GET)。

index.js

  • 設定 Express 伺服器。
  • 定義 API 的基本 URL。
  • 在指定埠上啟動伺服器。

Index.html

在這裡,我們使用了 Bootstrap 和基本的 HTML 建立了一個表單,允許您建立和檢視使用者和帖子。

在本地啟動應用程式

  • 轉到專案目錄 - 開啟您的終端或命令提示符並轉到專案的根目錄。
  • 安裝依賴項 - 執行npm install以安裝package.json檔案中列出的所需依賴項。
  • 啟動開發伺服器 - 執行node index.js以啟動應用程式。

解釋

  • npm install命令將獲取並安裝應用程式正常執行所需的所有必要包。
  • 執行node index.js將執行 JavaScript 檔案index.js,它是應用程式的入口點。這將啟動 Node.js 伺服器。

測試您的應用程式

伺服器執行後,開啟 Web 瀏覽器並導航到https://:3000。您應該會看到一個指示應用程式正在執行的響應。

注意

  • 預設埠是 3000,但您可以透過修改index.js檔案中的埠號來更改它。
  • 您可以使用nodemon等工具在程式碼更改時自動重啟。
Docker Setting NodeJs 3

Docker Setting NodeJs 4

您現在可以在這裡建立使用者和帖子。

將 NodeJs 應用程式 Docker 化

讓我們在專案的根目錄中建立一個名為Dockerfile的檔案,內容如下

# Use a Node.js image as the base

FROM node:18-alpine

# Set the working directory

WORKDIR /app

# Copy package.json and package-lock.json to install dependencies

COPY package*.json ./

# Install dependencies

RUN npm install

# Copy the rest of the application code

COPY . .

# Expose the port your app will listen on

EXPOSE 3000

# Start the app

CMD ["node", "index.js"]

Dockerfile 的解釋

  • FROM node:18-alpine - 我們將使用 Node.js 18 映象作為 Docker 映象的基礎映象。
  • WORKDIR /app - 它將容器內的工作目錄設定為 /app。
  • COPY package*.json ./ - 它將 package.json 和 package-lock.json 複製到工作目錄。
  • RUN npm install - 安裝專案依賴項。
  • COPY . . - 將整個專案複製到容器。
  • EXPOSE 3000 - 為應用程式公開埠 3000。
  • CMD ["node", "index.js"] - 指定容器啟動時要執行的命令。

構建 Docker 映象

要構建 Docker 映象,您可以在終端中執行以下命令:

$ docker build -t my-node-app .
Docker Setting NodeJs 5

此命令構建 Docker 映象並將其標記為my-node-app

執行 Docker 容器

要執行 Docker 容器,請使用以下命令:

$ docker run -p 3000:3000 my-node-app
Docker Setting NodeJs 6

此命令從my-node-app映象建立一個容器,然後將容器的埠 3000 對映到主機的埠 3000,並啟動容器。

現在,您可以透過https://:3000訪問您的應用程式。

Docker Setting NodeJs 7

結論

在本章中,我們介紹了開發 Node.js 部落格應用程式並使用 Docker將其容器化的所有步驟。您現在知道如何組織您的專案、處理使用者互動、有效地打包應用程式以進行部署以及建立其他 Docker 可以從中構建的基礎映象。

儘管本教程非常基礎,但如果您計劃使此應用程式投入生產,則應新增功能、安全措施、資料庫整合等。Docker 化將使您能夠簡化所有開發、測試和部署流程,以確保您的應用程式在所有環境中的行為相同。

在 Docker 中設定 Node.js 的常見問題解答

1. 最佳化 Node.js 應用程式的 Docker 映象的最佳實踐是什麼?

Docker 映象最佳化的最佳實踐是使用最小基礎映象大小和精簡的 Node.js 執行時,僅安裝應用程式所需的依賴項,並利用構建快取。您可以使用生產就緒的 Node.js 映象並透過更好的包管理方法進一步最佳化它。

2. 如何在 Docker 化的 Node.js 應用程式中處理環境變數?

您可以使用 ENV 指令在 Dockerfile 中設定環境變數,但最佳實踐是在執行時透過 -e 標誌傳遞它。您可以使用環境變數進行配置以增強靈活性和安全性。

3. 如何除錯在 Docker 容器中執行的 Node.js 應用程式?

您可以使用 Node.js 本身、帶有遠端除錯擴充套件的 Visual Studio Code 或其他第三方工具等遠端除錯工具來除錯在 Docker 容器內執行的 Node.js 應用程式。在您的 Dockerfile 中公開除錯埠並相應地配置您的 IDE。
廣告