Docker+Nginx+Node.js实战教程:从零搭建高可用的前后端分离项目

更多云服务器知识,尽在hostol.com

你是否已经掌握了Nginx的“交通指挥术”?是否也学会了Docker的“集装箱打包法”?是否对Node.js的后端开发跃跃欲试?

很好。今天,我们将把这三大“王牌军”——Nginx、Docker、Node.js——整合在一起,从一张白纸开始,亲手构建一个在现代Web世界中最经典、最实用、也最能体现你专业水准的“作战队形”——一个高可用的、前后端分离的项目

这篇文章会很长,很深入,细节会很多。但请跟紧我的步伐。完成这次演习后,你将不再是一个只会使用单个工具的“士兵”,你将晋升为一个懂得如何排兵布阵、构建稳健体系的“战场指挥官”。


“作战蓝图”:我们要建造一个什么样的“餐厅”?

在开工前,我们先来理清我们的“作战蓝图”。我们要搭建的是一个前后端分离的应用。让我们用一个“开餐厅”的比喻来理解它:

  • 后端 (Backend): 这是我们的“厨房”。我们将用Node.js来打造这个厨房,它负责处理所有核心的“烹饪”工作(业务逻辑、数据处理)。
  • 前端 (Frontend): 这是我们的“餐厅大堂”。我们将用一个纯静态的HTML+JS页面来构建,它只负责“展示菜品”和“接待顾客”(界面UI和用户交互)。
  • Nginx: 这是我们餐厅的“大堂经理兼总调度”。他负责两件大事:
    1. 将顾客直接引导到“餐厅大堂”(处理前端静态页面的访问)。
    2. 将顾客点的“菜单”(API请求),准确无误地传递给“厨房”(后端服务),并将做好的“菜品”(数据)端回给顾客。
  • Docker: 这是我们用来建造和管理“厨房”和“大堂”的“标准化施工队”。我们将把前端和后端,分别打包成独立的、标准化的“集装箱”,确保它们能在任何地方,以同样的方式完美运行。
  • 高可用 (High Availability): 这是本次演习的“进阶科目”。我们不只建一个“厨房”,我们要建两个一模一样的厨房!如果其中一个厨房突然“煤气泄漏”(服务器宕机),我们的“大堂经理”Nginx,会自动、无缝地,将所有菜单都转交给另一个健康的厨房。顾客甚至都不知道刚才后厨发生了一场危机。

蓝图清晰了?很好,指挥官,请下达开工指令!

第一阶段:建造“厨房”——我们的Node.js API后端

我们先来构建负责核心业务的“厨房”。

  1. 创建项目目录并准备“食材” (package.json) 在你的本地电脑上,创建一个项目文件夹,比如my-project,然后在里面创建一个backend文件夹。

Bash

mkdir -p my-project/backend
cd my-project/backend

backend目录里,创建一个package.json文件,告诉Node.js我们的项目需要什么“配料”(依赖):

JSON

{
  "name": "backend",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.1"
  }
}

2编写“菜谱” (server.js) 创建一个server.js文件,这是我们厨房的核心“主厨”,负责烹饪一道简单的“招牌菜”:

JavaScript

const express = require('express');
const app = express();
const PORT = 3000;
const HOSTNAME = require('os').hostname(); // 获取主机名,用于区分不同容器

app.get('/api/status', (req, res) => {
  res.json({
    message: 'Hello from the Backend!',
    serverHost: HOSTNAME // 返回当前是哪个“厨房”在为您服务
  });
});

app.listen(PORT, '0.0.0.0', () => {
  console.log(`Backend server is running on host ${HOSTNAME}, port ${PORT}`);
});

这个API很简单,当访问/api/status时,它会返回一条信息,并告诉我们是哪个“厨房”(容器)处理了这次请求。

3为“厨房”撰写“建造说明书” (Dockerfile)backend目录下,创建Dockerfile

Dockerfile

# 使用一个轻量级的Node.js环境作为“厨房地基”
FROM node:18-alpine

# 在厨房里,创建一个叫/app的操作台
WORKDIR /app

# 把“食材清单”复制进去
COPY package*.json ./

# 根据清单,自动“采购食材”
RUN npm install

# 把所有“菜谱”都复制进去
COPY . .

# 声明这个厨房的“出餐口”在3000
EXPOSE 3000

# 规定厨房的开工命令
CMD [ "npm", "start" ]

  1. 这份说明书,详细地记录了如何一步步地,将我们的Node.js应用,打包成一个标准化的Docker“厨房集装箱”。

第二阶段:装修“餐厅大堂”——我们的静态前端

厨房建好了,我们来装修门面。

  1. 创建前端目录和“菜单” (index.html)

my-project下,创建frontend文件夹,并在里面创建一个index.html文件。

Bash

cd ../frontend
nano index.html

写入以下内容:

HTML

<!DOCTYPE html>
<html>
<head>
    <title>前后端分离项目</title>
    <style>
        body { font-family: sans-serif; text-align: center; margin-top: 50px; }
        button { font-size: 1.2em; padding: 10px 20px; }
        #status { margin-top: 20px; font-weight: bold; }
    </style>
</head>
<body>
    <h1>欢迎来到我的应用!</h1>
    <button onclick="fetchStatus()">点击我,从后端获取状态</button>
    <div id="status"></div>

    <script>
        function fetchStatus() {
            const statusDiv = document.getElementById('status');
            statusDiv.innerText = '正在获取...';

            fetch('/api/status') // 注意!这里直接请求相对路径
                .then(response => response.json())
                .then(data => {
                    statusDiv.innerText = `消息: ${data.message} \n (由厨房 ${data.serverHost} 提供服务)`;
                })
                .catch(error => {
                    statusDiv.innerText = '获取失败!请检查后端服务。';
                    console.error('Error:', error);
                });
        }
    </script>
</body>
</html>

这个页面很简单,只有一个按钮。点击它,它会通过JavaScript的fetch函数,去请求我们后端的/api/status接口,并把返回的结果,显示在页面上。

第三阶段:召唤“总包工头”——用docker-compose编排一切

现在,我们有了“厨房”和“大堂”的图纸,但我们需要一个“总包工头”,来把它们一起建造并连接起来。这个角色,由docker-compose来扮演。

在你的项目根目录my-project下,创建docker-compose.yml文件。

YAML

version: '3.8'

services:
  # --- 后厨部门 ---
  backend:
    build: ./backend  # 使用backend目录下的Dockerfile来建造
    deploy:
      replicas: 2     # 魔法在这里!告诉总包工头,给我一模一样地建“两间”厨房
    networks:
      - my-app-network

  # --- 前厅与总调度 ---
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"       # 将我们服务器的80端口,映射到Nginx容器的80端口
    volumes:
      - ./frontend:/usr/share/nginx/html  # 将我们的“餐厅大堂”文件,挂载到Nginx的网站目录
      - ./nginx.conf:/etc/nginx/nginx.conf # 将我们的“大堂经理工作手册”,挂载到Nginx的配置目录
    depends_on:
      - backend
    networks:
      - my-app-network

networks:
  my-app-network:
    driver: bridge

这份docker-compose.yml,就是我们的总施工蓝图。它定义了我们需要建造的两个服务(backendnginx),以及它们之间的关系。

第四阶段:撰写“大堂经理”的终极工作手册 (nginx.conf)

最后,我们需要为Nginx这位“大堂经理”,撰写一份清晰的工作手册,告诉它如何接待客人和传达菜单。

在项目根目录my-project下,创建nginx.conf文件。

Nginx

events {}

http {
    # 定义我们的“后厨小组”
    upstream backend_servers {
        # Docker Compose会自动将backend这个名字,解析到我们那两个厨房容器的IP上
        # 它会自动在这两者之间,进行轮询负载均衡
        server backend:3000;
    }

    server {
        listen 80;

        # 如果客人是直接访问首页
        location / {
            root   /usr/share/nginx/html;
            index  index.html;
            try_files $uri $uri/ =404;
        }

        # 如果客人的请求路径,是以/api/开头的(点菜)
        location /api/ {
            # 就把这个菜单,转交给“后厨小组”去处理
            proxy_pass http://backend_servers;
            proxy_set_header Host $host;
            # ... 此处省略其他几行proxy_set_header...
        }
    }
}

这份配置,完美地实现了我们“前后端分离”和“负载均衡”的目标。

发射!——启动你的“商业帝国”

好了,所有的蓝图和说明书,都已准备就绪。

现在,SSH登录到你那台已经装好了Docker和Docker Compose的腾讯云轻量服务器上。把你的整个my-project文件夹,上传到服务器。

进入项目目录,然后,念出那句最激动人心的咒语:

Bash

sudo docker-compose up --build -d

-d表示在后台运行。

Docker Compose这位“总包工头”,会开始阅读你的蓝图,自动地:

  1. 建造你的“餐厅大堂”(拉取Nginx镜像)。
  2. 建造你的两间一模一样的“厨房”(根据Dockerfile构建后端镜像,并启动两个容器)。
  3. 建立它们之间的内部“高速网络”。
  4. 启动Nginx“大堂经理”,并让他按照你的手册开始工作。

最后的检阅: 打开你的浏览器,访问你服务器的公网IP地址。你会看到那个简洁的前端页面。

现在,反复点击那个按钮!你会看到,页面上显示的“由厨房 ... 提供服务”后面的那个主机名,在不断地变化

这意味着,Nginx这位大堂经理,正在完美地,将你的每一次“点菜”请求,轮流地、平均地,分发给我们那两间健康的“厨房”!我们的“高可用”架构,成功了!

你,已是“全栈指挥官”

看看你的成果。你不再只是一个会做一道菜的“厨师”,或者一个只会迎宾的“服务员”。你成了一位能设计出整个餐厅的复杂动线、并让它高效、稳定、且具备容灾能力运转的“餐厅总设计师”(全栈工程师/DevOps)。

你亲手指挥了一场漂亮的多兵种协同作战。这,就是现代Web开发的艺术。它关乎分离,也关乎协作;关乎性能,也关乎稳定。

现在,去为你的下一个伟大创意,设计一套更宏伟、更可靠的“作战蓝图”吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值