Linux: 综合实战——从零搭建 Web 开发服务器

这是本课程的最终项目。你将综合运用全部 25 课学到的技能——从一台裸机开始,搭建一台完整可用的 Web 开发服务器。

📋 前置知识:需要先掌握以下内容

1. 你将学到


2. 小明的"明天上线"挑战

(1) 痛点:新服务器,明天就要上线

小明的公司买了一台云服务器,老板说:"明天上线。"小明面对一台只有 IP 和 root 密码的裸机,之前学的所有知识都需要综合运用了。

(2) 从裸机到可用的 10 步流程

小明制定了计划:

BASH

1. SSH 登录          → 6. 部署 Node.js 应用
2. 用户创建 + SSH 加固 → 7. 配置 HTTPS 证书
3. 防火墙配置        → 8. 配置 PM2 进程管理
4. 安装 Nginx        → 9. 自动备份
5. 配置反向代理      → 10. 健康检查 + 告警

(3) 收益:3 小时内交付

小明从下午 2 点开始,按计划逐步执行,下午 5 点完成了全部部署。老板测试后回复:"很好,可以上线。"


3. 知识点讲解

(1) 服务器初始化清单

BASH
□ SSH 登录(root 密码)
□ 更新系统(apt update && apt upgrade)
□ 创建普通用户(useradd -m -s /bin/bash)
□ 配置 sudo(usermod -aG sudo)
□ SSH 密钥认证
□ 禁用 root + 密码登录
□ 防火墙(ufw: 22/80/443)
□ fail2ban 防护
□ 自动安全更新
⚠️ 注意:禁用密码登录前,务必确认 SSH 密钥已正确配置并测试成功。否则禁用密码后你将无法登录服务器。推荐顺序:1)配置密钥;2)用密钥成功登录;3)确认无误后再禁用密码登录。始终保留一个已登录的 SSH 会话作为安全网。

(2) LEMP 栈(Linux + Nginx + MySQL + PHP/Node.js/Python)

BASH
# Nginx
sudo apt install nginx
sudo systemctl enable --now nginx

# 如果使用 MySQL
sudo apt install mysql-server
sudo mysql_secure_installation

# 应用部署方式对比
方式 适合 优点 缺点
systemd service 任何语言 系统原生、成熟稳定 配置稍繁琐
PM2 Node.js 零配置、自带监控 只支持 Node.js
Gunicorn/uWSGI Python 性能好、WSGI 标准 需要 Nginx 配合
Docker Compose 复杂应用 环境一致、易扩展 需要 Docker 基础

▶ 示例:部署 Node.js 应用到 Nginx

BASH
#!/bin/bash
# 部署 Node.js 应用到生产环境

# 1. 创建应用目录
sudo mkdir -p /opt/webapp
sudo chown -R alice:alice /opt/webapp

# 2. 拷贝应用文件(假设已开发好)
git clone https://github.com/example/webapp.git /opt/webapp

# 3. 安装依赖和构建
cd /opt/webapp
npm install
npm run build

# 4. 配置 PM2
npm install -g pm2
pm2 start server.js --name webapp -i max
pm2 save
pm2 startup

# 5. 配置 Nginx 反向代理
sudo tee /etc/nginx/sites-available/webapp << 'EOF'
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /static/ {
        alias /opt/webapp/public/;
        expires 30d;
    }
}
EOF

sudo ln -sf /etc/nginx/sites-available/webapp /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

输出:

TEXT 📖 仅展示
npm WARN deprecated package@1.0.0
added 245 packages in 12s
Building production bundle...
[PM2] Process webapp started
┌────┬──────────┬─────────┬─────────┐
│ id │ name     │ status  │ cpu     │
├────┼──────────┼─────────┼─────────┤
│ 0  │ webapp   │ online  │ 0%      │
└────┴──────────┴─────────┴─────────┘
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

▶ 示例:HTTPS(Let's Encrypt + certbot)

💡 提示:certbot 默认会自动添加 systemd timer 或 cron 任务来续期证书。配置完成后,务必用 sudo certbot renew --dry-run 测试续期流程是否正常。同时建议在日历上设置提醒,在证书到期前 30 天(约第 60 天)手动确认续期是否成功,避免因配置变动导致证书过期。

BASH
# 安装 certbot
sudo apt install certbot python3-certbot-nginx -y

# 获取证书(会自动修改 Nginx 配置)
sudo certbot --nginx -d example.com -d www.example.com

# 查看证书
sudo certbot certificates

# 验证自动续期
sudo certbot renew --dry-run

# 证书会自动续期(certbot 会添加 systemd timer)
sudo systemctl list-timers | grep certbot

输出:

TEXT 📖 仅展示
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for example.com and www.example.com
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem

Found the following certs:
  Certificate Name: example.com
    Expiry Date: 2026-10-05 10:23:45+00:00 (VALID: 89 days)

Congratulations, all simulated renewals succeeded:
  /etc/letsencrypt/live/example.com/fullchain.pem

certbot.timer  certbot.service  Sat 2026-07-08 00:00:00 UTC  13h left

▶ 示例:健康检查脚本

BASH
#!/bin/bash
# health-check.sh - 多服务健康检查

check_service() {
    local name="$1"
    local url="$2"
    local expect="${3:-200}"
    
    local status=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null)
    
    if [ "$status" = "$expect" ]; then
        echo "[✓] $name: $status (正常)"
        return 0
    else
        echo "[✗] $name: $status (异常)"
        return 1
    fi
}

echo "=== 系统健康检查 ==="
echo "时间: $(date)"
echo ""

# 系统资源
echo "CPU: $(top -bn1 | grep "Cpu(s)" | awk '{print $2}')%"
echo "内存: $(free -h | grep Mem | awk '{print $3 "/" $2}')"
echo "磁盘: $(df -h / | tail -1 | awk '{print $3 "/" $2 " (" $5 ")"}')"
echo ""

# Web 服务
check_service "Nginx" "http://localhost/nginx_status" 200
check_service "WebApp" "http://localhost:3000/health" 200
check_service "HTTPS" "https://example.com/" 200

echo ""
echo "=== 检查完成 ==="

输出:

TEXT 📖 仅展示
=== 系统健康检查 ===
时间: Tue Jul  7 10:30:00 UTC 2026

CPU: 2.3%
内存: 1.2Gi/3.9Gi
磁盘: 12G/50G (24%)

[✓] Nginx: 200 (正常)
[✓] WebApp: 200 (正常)
[✓] HTTPS: 200 (正常)

=== 检查完成 ===

▶ 示例:完整的部署脚本

⚠️ 注意:脚本中的 curl -fsSL URL | bash - 模式将远程脚本直接在本地执行,存在供应链攻击风险——如果源站被入侵或 DNS 被劫持,你可能执行的是恶意代码。生产环境中,更安全的做法是先下载脚本审查内容,确认安全后再执行:curl -fsSL URL -o setup.sh && less setup.sh && bash setup.sh

BASH
#!/bin/bash
# setup-server.sh - 从零搭建 Web 开发服务器
# 用法: sudo ./setup-server.sh example.com

set -euo pipefail

DOMAIN="${1:-example.com}"
APP_DIR="/opt/webapp"
APP_PORT=3000
ADMIN_USER="admin"

echo "========================================"
echo "    从零搭建 Web 开发服务器"
echo "    域名: $DOMAIN"
echo "========================================"

# 1. 系统更新
echo ""
echo ">>> 1/10 更新系统..."
apt update && apt upgrade -y

# 2. 创建普通用户
echo ""
echo ">>> 2/10 创建管理用户..."
if ! id "$ADMIN_USER" &>/dev/null; then
    useradd -m -s /bin/bash "$ADMIN_USER"
    usermod -aG sudo "$ADMIN_USER"
    echo "请为 $ADMIN_USER 设置密码:"
    passwd "$ADMIN_USER"
    
    # 注意:请手动将你的 SSH 公钥添加到 /home/$ADMIN_USER/.ssh/authorized_keys
    mkdir -p /home/$ADMIN_USER/.ssh
    cp /root/.ssh/authorized_keys /home/$ADMIN_USER/.ssh/ 2>/dev/null || true
    chown -R $ADMIN_USER:$ADMIN_USER /home/$ADMIN_USER/.ssh
    chmod 700 /home/$ADMIN_USER/.ssh
    chmod 600 /home/$ADMIN_USER/.ssh/authorized_keys
fi

# 3. SSH 加固
echo ""
echo ">>> 3/10 SSH 加固..."
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sed -i 's/^PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
sed -i 's/^#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd

# 4. 防火墙
echo ""
echo ">>> 4/10 配置防火墙..."
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

# 5. fail2ban
echo ""
echo ">>> 5/10 安装 fail2ban..."
apt install -y fail2ban
cat > /etc/fail2ban/jail.d/sshd.conf << EOF
[sshd]
enabled = true
maxretry = 3
bantime = 86400
findtime = 600
EOF
systemctl enable --now fail2ban

# 6. 安装 Nginx
echo ""
echo ">>> 6/10 安装 Nginx..."
apt install -y nginx
systemctl enable --now nginx

# 7. 安装 Node.js
echo ""
echo ">>> 7/10 安装 Node.js..."
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
npm install -g pm2

# 8. 配置 Nginx 反向代理
echo ""
echo ">>> 8/10 配置 Nginx..."
cat > /etc/nginx/sites-available/$DOMAIN << EOF
server {
    listen 80;
    server_name $DOMAIN www.$DOMAIN;

    location / {
        proxy_pass http://127.0.0.1:$APP_PORT;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host \$host;
        proxy_cache_bypass \$http_upgrade;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }
}
EOF

ln -sf /etc/nginx/sites-available/$DOMAIN /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx

# 9. HTTPS 证书
echo ""
echo ">>> 9/10 配置 HTTPS..."
apt install -y certbot python3-certbot-nginx
certbot --nginx --non-interactive --agree-tos \
    --email admin@$DOMAIN \
    -d $DOMAIN -d www.$DOMAIN \
    || echo "⚠️ certbot 失败,请手动运行: sudo certbot --nginx -d $DOMAIN"

# 10. 健康检查脚本
echo ""
echo ">>> 10/10 配置健康检查..."
mkdir -p /opt/scripts

cat > /opt/scripts/health-check.sh << 'EOF'
#!/bin/bash
WEBHOOK="https://hooks.slack.com/services/YOUR/WEBHOOK"

for svc in nginx sshd fail2ban; do
    if ! systemctl is-active --quiet "$svc"; then
        echo "[$(date)] ❌ $svc 异常" >> /var/log/health.log
    fi
done

DISK=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
[ "$DISK" -gt 85 ] && echo "[$(date)] ⚠️ 磁盘 ${DISK}%" >> /var/log/health.log
EOF

chmod +x /opt/scripts/health-check.sh

# 加入 cron
(crontab -l 2>/dev/null; echo "*/10 * * * * /opt/scripts/health-check.sh > /dev/null 2>&1") | crontab -

echo ""
echo "========================================"
echo "✅ 服务器搭建完成!"
echo ""
echo "部署信息:"
echo "  管理用户: $ADMIN_USER"
echo "  Nginx: http://$DOMAIN"
echo "  HTTPS: https://$DOMAIN(如果 certbot 成功)"
echo "  Node.js: $(node --version)"
echo "  PM2: $(pm2 --version)"
echo ""
echo "后续步骤:"
echo "  1. 将应用代码部署到 $APP_DIR"
echo "  2. 启动应用: pm2 start $APP_DIR/server.js --name webapp"
echo "  3. 从 $ADMIN_USER 用户登录(禁止 root 直接登录)"
echo "========================================"

输出:

TEXT 📖 仅展示
========================================
    从零搭建 Web 开发服务器
    域名: example.com
========================================

>>> 1/10 更新系统...
...
========================================
✅ 服务器搭建完成!
管理用户: admin
Nginx: http://example.com
Node.js: v20.11.0
========================================

▶ 综合示例:完整的部署验收

BASH
#!/bin/bash
# verify-deploy.sh - 部署完成后验证

echo "========================================"
echo "    部署验证"
echo "    时间: $(date)"
echo "========================================"
echo ""

errors=0

check() {
    local desc="$1"
    shift
    if "$@" &>/dev/null; then
        echo "  [✓] $desc"
    else
        echo "  [✗] $desc"
        errors=$((errors + 1))
    fi
}

echo "1. 系统服务"
echo "-----------"
check "Nginx 运行" systemctl is-active nginx
check "SSH 运行" systemctl is-active sshd
check "fail2ban 运行" systemctl is-active fail2ban
echo ""

echo "2. 网络"
echo "--------"
check "端口 80 监听" ss -tlnp | grep -q ":80 "
check "端口 443 监听" ss -tlnp | grep -q ":443 "
check "ufw 已启用" ufw status | grep -q active
echo ""

echo "3. HTTP 服务"
echo "-------------"
check "本地 Nginx 响应" curl -s -o /dev/null -w "%{http_code}" http://localhost | grep -q 200
check "应用进程运行" pm2 list 2>/dev/null | grep -q "online" || pgrep -f "node server" > /dev/null
echo ""

echo "4. 安全"
echo "--------"
check "root SSH 已禁用" grep "^PermitRootLogin" /etc/ssh/sshd_config | grep -q "no"
check "密码登录已禁用" grep "^PasswordAuthentication" /etc/ssh/sshd_config | grep -q "no"
echo ""

echo "5. 资源"
echo "--------"
echo "  CPU: $(top -bn1 | grep 'Cpu(s)' | awk '{print $2"%"}' )"
echo "  内存: $(free -h | grep Mem | awk '{print $3"/"$2}')"
echo "  磁盘: $(df -h / | tail -1 | awk '{print $3"/"$2"("$5")"}')"
echo ""

echo "========================================"
if [ $errors -eq 0 ]; then
    echo "✅ 全部验证通过!"
else
    echo "❌ $errors 项检查失败,请排查"
fi
echo "========================================"

输出:

TEXT 📖 仅展示
========================================
    部署验证
    时间: Tue Jul  7 10:45:00 UTC 2026
========================================

1. 系统服务
-----------
  [✓] Nginx 运行
  [✓] SSH 运行
  [✓] fail2ban 运行

2. 网络
--------
  [✓] 端口 80 监听
  [✓] 端口 443 监听
  [✓] ufw 已启用

3. HTTP 服务
-------------
  [✓] 本地 Nginx 响应
  [✓] 应用进程运行

4. 安全
--------
  [✓] root SSH 已禁用
  [✓] 密码登录已禁用

5. 资源
--------
  CPU: 2.3%
  内存: 1.2Gi/3.9Gi
  磁盘: 12G/50G(24%)

========================================
✅ 全部验证通过!
========================================

❓ 常见问题

Q: 服务器部署第一件事应该做什么? A: \1

Q: Nginx 反向代理和应用的关系是什么? A: Nginx 是"大门",应用是"里面的人"。用户访问 Nginx 的 80/443 端口 → Nginx 决定把请求转发到哪个后端应用。Nginx 处理静态文件、SSL 终结、负载均衡,应用只专注业务逻辑。

Q: HTTPS 证书需要定期续期吗? A: Let's Encrypt 证书有效期 90 天。certbot 会自动添加 systemd timer 定期续期。验证:systemctl list-timers | grep certbot。手动续期:sudo certbot renew

Q: 服务器监控看哪些指标? A: 核心指标:CPU 使用率、内存用量、磁盘空间(>80% 告警)、磁盘 IO、网络流量、服务是否运行、SSL 证书是否过期。

Q: 部署完成后怎么验证安全? A: 运行安全扫描工具:1)sudo ufw status 检查端口。2)ss -tlnp 检查服务监听地址。3)grep "Failed password" /var/log/auth.log | wc -l 检查攻击量。4)用 https://www.ssllabs.com/ssltest/ 测试 SSL 配置。


📖 小节


📝 作业

  1. 基础题(难度⭐):用虚拟机或云服务器,从零执行 setup-server.sh 完整脚本,部署一个简单的 Node.js 或 Python Web 应用到服务器,通过 Nginx 反向代理访问
  2. 进阶题(难度⭐⭐):配置 Let's Encrypt HTTPS 证书(可用 --dry-run 测试),并编写和部署健康检查脚本,配置告警通知(邮件或 webhook)
  3. 挑战题(难度⭐⭐⭐⭐⭐):配置完整的备份策略(cron + rsync 远程备份)并验证备份可恢复,然后编写自动化部署验证脚本,检查所有服务状态、安全配置和性能指标,输出格式化验收报告
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏