更多云服务器知识,尽在hostol.com
你是否已经掌握了Nginx的“交通指挥术”?是否也学会了Docker的“集装箱打包法”?是否对Node.js的后端开发跃跃欲试?
很好。今天,我们将把这三大“王牌军”——Nginx、Docker、Node.js——整合在一起,从一张白纸开始,亲手构建一个在现代Web世界中最经典、最实用、也最能体现你专业水准的“作战队形”——一个高可用的、前后端分离的项目。
这篇文章会很长,很深入,细节会很多。但请跟紧我的步伐。完成这次演习后,你将不再是一个只会使用单个工具的“士兵”,你将晋升为一个懂得如何排兵布阵、构建稳健体系的“战场指挥官”。
“作战蓝图”:我们要建造一个什么样的“餐厅”?
在开工前,我们先来理清我们的“作战蓝图”。我们要搭建的是一个前后端分离的应用。让我们用一个“开餐厅”的比喻来理解它:
- 后端 (Backend): 这是我们的“厨房”。我们将用Node.js来打造这个厨房,它负责处理所有核心的“烹饪”工作(业务逻辑、数据处理)。
- 前端 (Frontend): 这是我们的“餐厅大堂”。我们将用一个纯静态的HTML+JS页面来构建,它只负责“展示菜品”和“接待顾客”(界面UI和用户交互)。
- Nginx: 这是我们餐厅的“大堂经理兼总调度”。他负责两件大事:
- 将顾客直接引导到“餐厅大堂”(处理前端静态页面的访问)。
- 将顾客点的“菜单”(API请求),准确无误地传递给“厨房”(后端服务),并将做好的“菜品”(数据)端回给顾客。
- Docker: 这是我们用来建造和管理“厨房”和“大堂”的“标准化施工队”。我们将把前端和后端,分别打包成独立的、标准化的“集装箱”,确保它们能在任何地方,以同样的方式完美运行。
- 高可用 (High Availability): 这是本次演习的“进阶科目”。我们不只建一个“厨房”,我们要建两个一模一样的厨房!如果其中一个厨房突然“煤气泄漏”(服务器宕机),我们的“大堂经理”Nginx,会自动、无缝地,将所有菜单都转交给另一个健康的厨房。顾客甚至都不知道刚才后厨发生了一场危机。
蓝图清晰了?很好,指挥官,请下达开工指令!
第一阶段:建造“厨房”——我们的Node.js API后端
我们先来构建负责核心业务的“厨房”。
- 创建项目目录并准备“食材” (
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" ]
- 这份说明书,详细地记录了如何一步步地,将我们的Node.js应用,打包成一个标准化的Docker“厨房集装箱”。
第二阶段:装修“餐厅大堂”——我们的静态前端
厨房建好了,我们来装修门面。
- 创建前端目录和“菜单” (
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
,就是我们的总施工蓝图。它定义了我们需要建造的两个服务(backend
和nginx
),以及它们之间的关系。
第四阶段:撰写“大堂经理”的终极工作手册 (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这位“总包工头”,会开始阅读你的蓝图,自动地:
- 建造你的“餐厅大堂”(拉取Nginx镜像)。
- 建造你的两间一模一样的“厨房”(根据Dockerfile构建后端镜像,并启动两个容器)。
- 建立它们之间的内部“高速网络”。
- 启动Nginx“大堂经理”,并让他按照你的手册开始工作。
最后的检阅: 打开你的浏览器,访问你服务器的公网IP地址。你会看到那个简洁的前端页面。
现在,反复点击那个按钮!你会看到,页面上显示的“由厨房 ... 提供服务
”后面的那个主机名,在不断地变化!
这意味着,Nginx这位大堂经理,正在完美地,将你的每一次“点菜”请求,轮流地、平均地,分发给我们那两间健康的“厨房”!我们的“高可用”架构,成功了!
你,已是“全栈指挥官”
看看你的成果。你不再只是一个会做一道菜的“厨师”,或者一个只会迎宾的“服务员”。你成了一位能设计出整个餐厅的复杂动线、并让它高效、稳定、且具备容灾能力运转的“餐厅总设计师”(全栈工程师/DevOps)。
你亲手指挥了一场漂亮的多兵种协同作战。这,就是现代Web开发的艺术。它关乎分离,也关乎协作;关乎性能,也关乎稳定。
现在,去为你的下一个伟大创意,设计一套更宏伟、更可靠的“作战蓝图”吧。