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://127.0.0.1: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://127.0.0.1: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。
广告