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 接收外部请求并将其转发到内部服务
知识关联
- 前置知识:2.11:SSH 深入 SSH 深入、3.4:Nginx Web服务器 Nginx Web 服务器、3.1:Docker 容器入门 Docker 容器入门
- 后续影响:本章是 4.9:CI/CD 持续部署 CI/CD 持续部署、4.13:服务器初始化规范 服务器初始化规范和 4.14:运维故障案例集 运维故障案例集的基础
- 配套技术:GitHub Actions(CI/CD)、Cloudflare(CDN + DNS)、Prometheus(监控)
原理讲解
搭建个人网站的核心流程可以概括为一句话:买服务器 → 装软件 → 放代码 → 绑域名 → 加 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
部署清单
- 购买云服务器
- 创建用户,配置 SSH 密钥
- 更新系统,安装工具
- 配置 UFW 防火墙
- 安装配置 Nginx
- 上传网站文件
- 绑定域名 + HTTPS
- 安全加固
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 文件并加入 .gitignore:MYSQL_ROOT_PASSWORD=xxx,docker-compose 自动读取。选择 CMS 系统
需要博客/内容管理功能时,选择现成的 CMS 比自建更高效。
| CMS | 语言 | 数据库 | 适用场景 | 学习成本 |
|---|---|---|---|---|
| Typecho | PHP | MySQL/SQLite | 轻量博客,极简主义 | 低 |
| WordPress | PHP | MySQL | 通用网站,生态最大 | 中 |
| Halo | Java | MySQL/PostgreSQL | 现代博客,Docker 友好 | 低 |
| Ghost | Node.js | SQLite/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/2 | Nginx 启用 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/tcp 再 ufw enable;或在云控制台通过 VNC 恢复 |
| 绑定域名后访问不了 | DNS 解析未生效、Nginx server_name 不匹配、防火墙未放行 80/443 | 用 dig 确认解析、nginx -t 检查配置、ufw status 检查防火墙 |
| certbot 获取证书失败 | 域名未正确解析到服务器 IP,或 80 端口不可达 | 先确保 curl http://yourdomain.com 能返回 200,再运行 certbot |
| Docker Compose 中服务之间连接不上 | 未在同一个网络或服务名写错 | Compose 自动创建默认网络,用服务名(service name)而非 localhost 连接 |
最佳实践
| 实践 | 原理 | 示例 |
|---|---|---|
| 禁止 root 直接 SSH 登录 | 减少暴力破解攻击面 | /etc/ssh/sshd_config 中 PermitRootLogin no |
| 使用 SSH 密钥而非密码 | 密钥 2048/4096 位安全性远高于密码 | ssh-keygen -t ed25519 生成密钥,ssh-copy-id 部署公钥 |
| 启用自动安全更新 | 及时修复已知漏洞,减少被扫描攻击的风险 | apt install unattended-upgrades |
| 敏感信息用环境变量而非硬编码 | 防止配置文件中泄露密码和 API Key | .env 文件 + .gitignore,Compose 自动读取 |
| 定期测试备份可恢复性 | 备份不可恢复等于没有备份 | 每月至少一次完整恢复演练,从备份恢复到测试环境验证 |
| 使用基础设施即代码(IaC) | 环境可重复、可审计、可回滚 | 用 Docker Compose 或 Ansible 而非手打命令 |
练习题
- (概念)UFW 放行端口的顺序重要吗?为什么必须先放行 SSH 再启用防火墙?
- (概念)为什么要配置
PermitRootLogin no和PasswordAuthentication no?它们分别防御什么类型的攻击? - (实操)在一台云服务器或本地虚拟机中,从头完成以下操作:创建普通用户并加入 sudo 组 → 配置 SSH 密钥登录(禁用密码)→ 安装 Nginx → 部署一个静态页面(内容自定)→ 配置 UFW 只放行 22、80、443 端口。
- (实操)在已部署的静态网站基础上,使用 certbot 申请 Let's Encrypt 证书(或用自签名证书模拟),配置 HTTPS 和 HTTP→HTTPS 301 跳转。用
curl -I https://yourdomain验证。 - (🔍 挑战)实践 3-2-1 备份原则:编写脚本将网站文件打包 + 数据库导出,备份到本地目录;再配置 rsync 将备份同步到另一个服务器或对象存储(如阿里云 OSS);设置 cron 每天凌晨 3 点执行。验证备份文件可以从零恢复。
点击查看答案
- (概念)很重要——如果先启用防火墙再放行 SSH,当前 SSH 连接会立即中断,导致被锁在服务器外。顺序必须是先
ufw allow 22/tcp再ufw enable。 - (概念)
PermitRootLogin no禁止 root 直接 SSH 登录,防御针对 root 用户的暴力破解(root 用户名已知,爆破成本低);PasswordAuthentication no禁用密码认证,防御密码暴力破解和撞库攻击——即使密码泄露,没有私钥也无法登录。 - (实操)命令序列:
useradd -m -G sudo user && passwd user→ 本地ssh-copy-id user@host→ SSH 配置PermitRootLogin no PasswordAuthentication no→sudo nginx -t && sudo systemctl reload sshd→sudo apt install nginx && echo "Hello" | sudo tee /var/www/html/index.html→sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw enable。 - (实操)
sudo certbot --nginx -d yourdomain.com自动完成证书申请和 Nginx 配置。验证:curl -I https://yourdomain.com返回 200 且Location无http://跳转。先配置 HTTP→HTTPS 301 跳转:在 Nginx server block 中添加return 301 https://$host$request_uri;。如果无真实域名,可用自签名证书模拟流程。 - (🔍 挑战)脚本要点:
tar -czf /backup/www-$(date +%F).tar.gz /var/www→rsync -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 份异地 |
学习路径建议
- 运维标准化可看 4.13:服务器初始化规范 服务器初始化规范
- 自动化部署可看 4.9:CI/CD 持续部署 CI/CD 持续部署
- 系统监控可看 3.12:系统监控与告警 系统监控方案
- 故障排查进阶可看 4.14:运维故障案例集 运维故障案例集
延伸阅读
- Docker Compose 官方文档
- Certbot 使用指南
- SSL Labs — HTTPS 配置安全评估
- PageSpeed Insights — 网站性能评分
- 推荐书籍:《Web 运维实战——从入门到生产环境》
常见问题
买云服务器选什么配置?
个人网站起步建议 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。完成这三步后再部署网站。