3.3 实战:搭建个人网站

预计阅读时间:13 分钟

📖 目录

学习目标

学完本章后,你将能够:

  • 完成从购买云服务器到网站上线的完整流程
  • 配置 SSH 密钥登录和系统安全加固
  • 使用 Nginx 部署静态网站并配置域名和 HTTPS
  • 用 Docker Compose 编排全栈网站环境
  • 建立网站监控与备份体系

核心知识

  • 云服务器(VPS)——在云平台上创建的虚拟服务器,可从本地通过 SSH 远程连接
  • SSH 密钥对——公钥(放服务器)和私钥(本地保存)组成的认证方式,比密码更安全
  • UFW(Uncomplicated Firewall)——iptables 的前端工具,简化防火墙规则配置
  • Let's Encrypt——免费、自动化的 CA(证书颁发机构),通过 certbot 工具自动申请和续期 TLS 证书
  • fail2ban——日志分析工具,检测暴力破解尝试并自动封禁 IP
  • Docker Compose——通过 YAML 声明式定义多容器服务栈
  • CDN(内容分发网络)——将静态资源缓存到全球边缘节点,降低访问延迟
  • 反向代理——Nginx 接收外部请求并将其转发到内部服务

知识关联

原理讲解

搭建个人网站的核心流程可以概括为一句话:买服务器 → 装软件 → 放代码 → 绑域名 → 加 HTTPS → 监控维护。每一步都有成熟的命令行工具支撑。本章不是让你理解一个复杂系统,而是通过完成一个"真网站"来串联之前学过的所有 Linux 技能——从 SSH 连接、包管理、Nginx 配置到 Docker 化部署和监控。记住:你不是在"学命令",你是在"搭网站"——每个命令都有一个看得见的结果。

示例代码

准备工作

一台云服务器(Ubuntu 24.04 LTS)、一个域名(可选)。最低配置即可,约 50-100 元/月。

首次连接

ssh root@your_server_ip
adduser alice
usermod -aG sudo alice
ssh-keygen -t ed25519  # 输出: Generating public/private ed25519 key pair.
ssh-copy-id alice@your_server_ip  # 输出: Number of key(s) added: 1

基础环境

sudo apt update && sudo apt upgrade -y  # 输出: Reading package lists... Done
sudo timedatectl set-timezone Asia/Shanghai
sudo hostnamectl set-hostname myblog
sudo apt install -y vim git curl wget htop nginx  # 输出: Setting up nginx (1.24.0) ...

防火墙

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable  # 输出: Firewall is active and enabled on system startup

配置 Nginx

sudo mkdir -p /var/www/myblog
sudo chown -R $USER:$USER /var/www/myblog

# Nginx 配置 /etc/nginx/sites-available/myblog:
# server {
#     listen 80;
#     server_name _;
#     root /var/www/myblog;
#     index index.html;
#     location / { try_files $uri $uri/ =404; }
# }

sudo ln -s /etc/nginx/sites-available/myblog /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl restart nginx  # 输出: nginx: the configuration file syntax is ok

域名与 HTTPS

# DNS 添加 A 记录指向服务器 IP
# 修改 Nginx 配置中的 server_name

# Let's Encrypt 免费证书
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com  # 输出: Congratulations! You have successfully enabled HTTPS
sudo certbot renew --dry-run  # 输出: Congratulations, all renewals succeeded

安全加固

# /etc/ssh/sshd_config:
# PermitRootLogin no
# PasswordAuthentication no
sudo systemctl reload sshd

sudo apt install -y fail2ban
sudo systemctl enable fail2ban  # 输出: Created symlink /etc/systemd/system/multi-user.target.wants/fail2ban.service → ...
sudo apt install -y unattended-upgrades

部署清单

  1. 购买云服务器
  2. 创建用户,配置 SSH 密钥
  3. 更新系统,安装工具
  4. 配置 UFW 防火墙
  5. 安装配置 Nginx
  6. 上传网站文件
  7. 绑定域名 + HTTPS
  8. 安全加固

Docker 化部署

用 Docker 部署让环境完全可重复,不再有"在我机器上能跑"的问题。

# Dockerfile — 基于 Nginx 的静态站
FROM nginx:alpine AS build
COPY ./dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

# Dockerfile — 动态站(Node.js 示例)
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD wget -qO- http://localhost/health || exit 1
# docker-compose.yml — 全栈网站环境(新版 Docker Compose V2 已废弃 version 字段)
services:
  web:
    build: .
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped
    depends_on:
      - api

  api:
    image: node:20-alpine
    command: node server.js
    working_dir: /app
    volumes:
      - ./api:/app
    environment:
      - DB_HOST=db
      - DB_USER=app
      - DB_PASSWORD=${DB_PASSWORD}
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mariadb:11
    volumes:
      - db_data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=myblog
      - MYSQL_USER=app
      - MYSQL_PASSWORD=${DB_PASSWORD}
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  db_data:
# 一键部署(Docker Compose V2 命令为 docker compose,旧版 V1 用 docker-compose)
docker compose up -d  # 输出: Container myblog-web-1  Started
docker compose logs -f
docker compose ps  # 输出: NAME                IMAGE               COMMAND                  SERVICE

# 更新
git pull
docker compose build web
docker compose up -d web
💡 .env 文件 敏感信息不要硬编码在 docker-compose.yml 中。创建 .env 文件并加入 .gitignoreMYSQL_ROOT_PASSWORD=xxx,docker-compose 自动读取。

选择 CMS 系统

需要博客/内容管理功能时,选择现成的 CMS 比自建更高效。

CMS语言数据库适用场景学习成本
TypechoPHPMySQL/SQLite轻量博客,极简主义
WordPressPHPMySQL通用网站,生态最大
HaloJavaMySQL/PostgreSQL现代博客,Docker 友好
GhostNode.jsSQLite/MySQL专业内容创作
# WordPress 一键部署(docker-compose 片段)
services:
  wordpress:
    image: wordpress:6-php8.2-fpm-alpine
    volumes:
      - wp_data:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
      WORDPRESS_DB_NAME: wordpress
      WORDPRESS_TABLE_PREFIX: wp_
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy

  # Nginx 反向代理给 WordPress
  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx-wp.conf:/etc/nginx/conf.d/default.conf:ro
      - wp_data:/var/www/html:ro
    ports:
      - "80:80"
      - "443:443"
    restart: unless-stopped
    depends_on:
      - wordpress

CDN 加速

CDN 将静态资源缓存到离用户最近的节点,显著降低服务器负载和用户访问延迟。

# 阿里云 CDN 配置要点
# 1. 添加加速域名 → 选择"图片小文件"或"全站加速"
# 2. 源站设置 → 填写你的服务器 IP 或域名
# 3. 缓存配置:
#    - 静态文件(.css/.js/.jpg/.png/.woff2)→ 缓存 30 天
#    - HTML → 缓存 10 分钟或不缓存
#    - API → 不缓存
# 4. HTTPS 配置 → 上传证书或开启免费证书
# 5. CNAME → 将域名 CNAME 到 CDN 分配的域名
# Nginx 配置配合 CDN
location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
    expires 30d;
    add_header Cache-Control "public, immutable";
    add_header X-Cache-Status $upstream_cache_status;
    access_log off;
}

location / {
    # 防止 CDN 缓存 HTML 页面
    add_header Cache-Control "no-cache, no-store, must-revalidate";
}

# 获取真实 IP(当 CDN 在 Nginx 之前)
set_real_ip_from 103.xxx.xxx.0/24;  # CDN 回源 IP 列表
real_ip_header X-Forwarded-For;
real_ip_recursive on;
⚠️ 回源成本 CDN 按流量计费,回源流量单价往往高于 CDN 边缘流量。合理设置缓存时间(长缓存+版本号更新),减少回源请求。同时注意 CNAME 生效后 DNS 记录会指向 CDN,回源 IP 需要白名单放行。

网站性能优化

优化项操作效果
Gzip 压缩Nginx 启用 gzip文本类资源减少 60-80%
图片优化WebP 格式 + 懒加载图片体积减少 30-50%
浏览器缓存Cache-Control + ETag重复访问加载时间减半
HTTP/2Nginx 启用 http2多路复用,减少连接开销
资源压缩CSS/JS 合并+压缩减少 HTTP 请求数
CDN静态资源走 CDN就近访问,延迟降低
# Nginx 优化配置
server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    # Gzip
    gzip on;
    gzip_types text/plain text/css application/json application/javascript
               image/svg+xml application/xml text/xml;
    gzip_min_length 256;
    gzip_comp_level 6;
    gzip_vary on;

    # 静态文件缓存
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
        add_header Last-Modified $sent_http_Last_Modified;
        etag on;
    }

    # 安全头部
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
}
💡 测速工具curl -w "Connect: %{time_connect}s, TTFB: %{time_starttransfer}s, Total: %{time_total}s\n" -o /dev/null -s https://yourdomain.com 快速测时。再用 PageSpeed Insights 进行综合评估。

监控与备份

#!/bin/bash
# /usr/local/bin/site-monitor.sh — 网站监控脚本
set -euo pipefail

SITE_URL="https://yourdomain.com"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx/yyy/zzz"
BACKUP_DIR="/var/backups/site"
DATE=$(date +%Y%m%d-%H%M%S)

# 1. 检查 HTTP 状态码
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" $SITE_URL)
if [ "$HTTP_CODE" != "200" ]; then
    curl -X POST -H "Content-type: application/json" \
        -d "{\"text\":\"🚨 $SITE_URL 返回 $HTTP_CODE\"}" \
        $SLACK_WEBHOOK
fi

# 2. SSL 证书过期检查
EXPIRY=$(echo | openssl s_client -servername yourdomain.com \
    -connect yourdomain.com:443 2>/dev/null | \
    openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt 7 ]; then
    echo "⚠️ 证书将在 $DAYS_LEFT 天后过期"
fi

# 3. 全量备份
mkdir -p $BACKUP_DIR/$DATE
tar czf $BACKUP_DIR/$DATE/site-files.tar.gz /var/www/
mysqldump -u root -p$MYSQL_PASSWORD myblog > $BACKUP_DIR/$DATE/db.sql
# 保留最近 7 天,删除更早的
find $BACKUP_DIR -maxdepth 1 -type d -mtime +7 -exec rm -rf {} \;

# 4. 磁盘与内存检查
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | sed 's/%//')
if [ $DISK_USAGE -gt 85 ]; then
    echo "⚠️ 磁盘使用率 $DISK_USAGE%"
fi
# 设置定时任务
sudo cp site-monitor.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/site-monitor.sh

# cron 每 5 分钟监控 + 每天 3AM 备份
cat <
💡 备份 3-2-1 原则 至少 3 份副本、2 种介质、1 份异地。定时将备份 rsync 到另一台服务器或对象存储:rsync -avz $BACKUP_DIR/ user@remote:/backups/site/

常见错误

错误表现根因正确做法
SSH 连接被拒绝(Permission denied)未正确配置公钥,或 ~/.ssh 权限设置不当~/.ssh 权限必须是 700,authorized_keys 必须是 600
UFW 启用后 SSH 断了在启用 UFW 之前没有放行 SSH 端口(22)ufw allow 22/tcpufw enable;或在云控制台通过 VNC 恢复
绑定域名后访问不了DNS 解析未生效、Nginx server_name 不匹配、防火墙未放行 80/443dig 确认解析、nginx -t 检查配置、ufw status 检查防火墙
certbot 获取证书失败域名未正确解析到服务器 IP,或 80 端口不可达先确保 curl http://yourdomain.com 能返回 200,再运行 certbot
Docker Compose 中服务之间连接不上未在同一个网络或服务名写错Compose 自动创建默认网络,用服务名(service name)而非 localhost 连接

最佳实践

实践原理示例
禁止 root 直接 SSH 登录减少暴力破解攻击面/etc/ssh/sshd_configPermitRootLogin no
使用 SSH 密钥而非密码密钥 2048/4096 位安全性远高于密码ssh-keygen -t ed25519 生成密钥,ssh-copy-id 部署公钥
启用自动安全更新及时修复已知漏洞,减少被扫描攻击的风险apt install unattended-upgrades
敏感信息用环境变量而非硬编码防止配置文件中泄露密码和 API Key.env 文件 + .gitignore,Compose 自动读取
定期测试备份可恢复性备份不可恢复等于没有备份每月至少一次完整恢复演练,从备份恢复到测试环境验证
使用基础设施即代码(IaC)环境可重复、可审计、可回滚用 Docker Compose 或 Ansible 而非手打命令

练习题

  1. (概念)UFW 放行端口的顺序重要吗?为什么必须先放行 SSH 再启用防火墙?
  2. (概念)为什么要配置 PermitRootLogin noPasswordAuthentication no?它们分别防御什么类型的攻击?
  3. (实操)在一台云服务器或本地虚拟机中,从头完成以下操作:创建普通用户并加入 sudo 组 → 配置 SSH 密钥登录(禁用密码)→ 安装 Nginx → 部署一个静态页面(内容自定)→ 配置 UFW 只放行 22、80、443 端口。
  4. (实操)在已部署的静态网站基础上,使用 certbot 申请 Let's Encrypt 证书(或用自签名证书模拟),配置 HTTPS 和 HTTP→HTTPS 301 跳转。用 curl -I https://yourdomain 验证。
  5. (🔍 挑战)实践 3-2-1 备份原则:编写脚本将网站文件打包 + 数据库导出,备份到本地目录;再配置 rsync 将备份同步到另一个服务器或对象存储(如阿里云 OSS);设置 cron 每天凌晨 3 点执行。验证备份文件可以从零恢复。
点击查看答案
  1. (概念)很重要——如果先启用防火墙再放行 SSH,当前 SSH 连接会立即中断,导致被锁在服务器外。顺序必须是先 ufw allow 22/tcpufw enable
  2. (概念)PermitRootLogin no 禁止 root 直接 SSH 登录,防御针对 root 用户的暴力破解(root 用户名已知,爆破成本低);PasswordAuthentication no 禁用密码认证,防御密码暴力破解和撞库攻击——即使密码泄露,没有私钥也无法登录。
  3. (实操)命令序列:useradd -m -G sudo user && passwd user → 本地 ssh-copy-id user@host → SSH 配置 PermitRootLogin no PasswordAuthentication nosudo nginx -t && sudo systemctl reload sshdsudo apt install nginx && echo "Hello" | sudo tee /var/www/html/index.htmlsudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw enable
  4. (实操)sudo certbot --nginx -d yourdomain.com 自动完成证书申请和 Nginx 配置。验证:curl -I https://yourdomain.com 返回 200 且 Locationhttp:// 跳转。先配置 HTTP→HTTPS 301 跳转:在 Nginx server block 中添加 return 301 https://$host$request_uri;。如果无真实域名,可用自签名证书模拟流程。
  5. (🔍 挑战)脚本要点:tar -czf /backup/www-$(date +%F).tar.gz /var/wwwrsync -avz /backup/ user@remote:/backup/。3-2-1 验证:检查本地、远程、不同介质各有一份。恢复演练必须定期执行——真正的备份是在成功恢复之后才确认有效的。

学习检查点

学完本章后,请检验自己是否掌握以下内容:

检查项自测问题验证方法
概念理解能用自己的话解释 SSH 密钥登录比密码登录更安全的原因尝试向他人讲解
命令操作能不查文档完成 Nginx 配置、UFW 防火墙设置、HTTPS 证书申请在终端实际执行
原理掌握能说出 Let's Encrypt 证书申请和自动续期的工作原理画出流程图
故障排查能独立排查 SSH 连接失败、域名解析问题、HTTPS 证书错误模拟故障并修复
最佳实践能说明为什么需要禁用 root 直接 SSH 登录和密码认证对比不同方案

本章总结

本章完整覆盖了从购买云服务器到部署可访问网站的全流程——这是将之前的 Linux 知识串联起来的最佳实践。从 SSH 安全配置到 Nginx 部署,从 Docker 化到 HTTPS 证书,从性能优化到监控备份,每一步都服务于"让网站稳定、安全、快速地运行"这个目标。Linux 新手入门之路在此画上一个句号,但真正的运维实践才刚刚开始。

速查表

命令/步骤用途
ssh-keygen -t ed25519生成 SSH 密钥对
ssh-copy-id user@host部署公钥到服务器
sudo ufw allow 22/tcp放行 SSH 端口
sudo nginx -t && sudo systemctl reload nginx测试并重载 Nginx 配置
sudo certbot --nginx -d domain.com自动申请 HTTPS 证书
docker compose up -d一键启动全栈服务
sudo apt install unattended-upgrades启用自动安全更新
sudo apt install fail2ban安装暴力破解防御
curl -w "Connect: %{time_connect}s\nTTFB: %{time_starttransfer}s\n" -o /dev/null -s https://site测量网站响应时间
备份 3-2-1 原则3 份副本、2 种介质、1 份异地

学习路径建议

延伸阅读

常见问题

买云服务器选什么配置?
个人网站起步建议 2 核 2GB(2C4G 更富裕),40GB 系统盘 + 20GB 数据盘。带宽 3-5Mbps 够用。操作系统选 Ubuntu 22.04 LTS 或 Rocky Linux 9。国内厂商选阿里云、腾讯云,海外选 Vultr、DigitalOcean 或 Hetzner。先买按量计费,稳定后转包年包月。
Let's Encrypt 证书多久续期一次?
Let's Encrypt 证书有效期为 90 天,建议自动化续期。certbot 默认安装 systemd timer 每天检查两次,到期前 30 天自动续期。验证续期是否正常:certbot renew --dry-run。也可以用 acme.sh 替代 certbot,支持更多 DNS 服务商。
网站上线后首要安全措施是什么?
三步:① 修改 SSH 端口(默认 22 改高位端口),仅允许密钥登录,禁止 PasswordAuthentication。② 配置防火墙(ufw 或 nftables),只开放 80、443、SSH 端口。③ 开启自动安全更新:sudo apt install unattended-upgrades。完成这三步后再部署网站。
↑ 回到顶部