🎯 学完能做什么

独立把前后端项目部署到云服务器,配置域名、HTTPS,搭建 CI/CD 自动化流水线。

👤 适合谁

会写前端代码但不会部署的开发者。你已经能在本地跑起项目,现在要让全世界都能访问。

⏱️ 预计时间

22 个专题,每个 20-40 分钟。建议边学边在真实服务器上操作。

模块页数核心内容
总览2课程地图、为什么前端要学部署
Linux / SSH5SSH 密钥、常用命令、日志、权限、监控
Docker5容器概念、Dockerfile、Compose、数据卷、优化
Nginx4反向代理、静态托管、HTTPS、排障
CI/CD3GitHub Actions、自动测试、自动部署
阿里云实战3ECS 初始化、部署实战、上线检查

📚 前置知识

基本的终端/命令行使用经验。了解 HTTP 协议和前后端交互流程。会用 Git 基本操作。

🛠️ 需要准备

一台云服务器(推荐阿里云 ECS 入门级)、一个域名(可选)、本地安装 Docker Desktop。

🚫 不会部署的痛点

项目写完了但上不了线。每次改动要找后端帮忙。环境不一致导致"本地好好的线上就挂了"。简历只有项目截图,没有线上链接。

✅ 会部署的优势

独立完成从开发到上线的全流程。简历可以放线上链接。理解生产环境的限制和要求。面试加分项——运维能力。

本地开发生产环境
localhost:3000https://your-domain.com
npm run dev 热更新构建后的静态文件 / Docker 容器
本地数据库远程数据库(需要密码、防火墙)
只有你一个人访问任何人都可以访问
崩了刷新就好崩了用户就流失
# 整个流程你将在本课程中完整学到: # 1. 本地开发完成 → git push git push origin main # 2. CI/CD 自动触发 → 运行测试、构建镜像 # GitHub Actions 自动执行 # 3. 镜像推送到服务器 → Docker 启动新容器 ssh user@server "docker pull myapp && docker compose up -d" # 4. Nginx 反向代理 → 用户通过域名访问 # https://your-app.com → Nginx → Docker 容器 → 你的应用
💡 学习建议:一定要准备一台真实的云服务器。看再多教程不如亲手操作一次。阿里云新用户有几十块钱/年的入门级 ECS。
🧠 小测验

前端项目部署到生产环境后,用户通过什么访问?

概念前端类比
SSH类似浏览器的 HTTPS,加密通信通道
密钥对类似 JWT,公钥放服务器,私钥留本地
ssh config类似 .env 文件,存连接配置
# 密码登录(不推荐,每次要输密码) ssh root@123.45.67.89 # 指定端口(默认 22) ssh -p 2222 root@123.45.67.89 # 第一次连接会提示指纹确认 # The authenticity of host '123.45.67.89' can't be established. # → 输入 yes
# 生成 ED25519 密钥(推荐,比 RSA 更安全更快) ssh-keygen -t ed25519 -C "your@email.com" # 提示输入保存路径 → 回车使用默认 ~/.ssh/id_ed25519 # 提示输入密码 → 可以回车跳过(或设置密码更安全) # 生成的文件: # ~/.ssh/id_ed25519 ← 私钥(永远不要泄露!) # ~/.ssh/id_ed25519.pub ← 公钥(放到服务器上) # 把公钥复制到服务器 ssh-copy-id root@123.45.67.89 # 之后就可以免密登录了! # 或者手动复制 cat ~/.ssh/id_ed25519.pub | ssh root@123.45.67.89 "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
# 编辑 ~/.ssh/config Host myserver HostName 123.45.67.89 User root Port 22 IdentityFile ~/.ssh/id_ed25519 Host staging HostName 123.45.67.90 User deploy Port 2222 IdentityFile ~/.ssh/id_ed25519 # 之后直接用别名连接 ssh myserver # 等同于 ssh root@123.45.67.89 ssh staging # 等同于 ssh -p 2222 deploy@123.45.67.90
⚠️ 安全提醒
1. 私钥文件权限必须是 600:chmod 600 ~/.ssh/id_ed25519
2. 永远不要把私钥提交到 Git
3. 生产服务器建议禁用密码登录,只允许密钥登录
📋 SSH 命令速查
命令用途
ssh user@host连接服务器
ssh-keygen -t ed25519生成密钥对
ssh-copy-id user@host复制公钥到服务器
scp file user@host:path上传文件
scp user@host:path file下载文件
ssh -L 8080:localhost:3000 host端口转发
✏️ 填空练习

生成 SSH 密钥对的推荐命令是 ssh-keygen -t ,把公钥复制到服务器用 命令。

🧠 小测验

SSH 密钥对中,哪个文件放到服务器上?

# 查看当前目录 pwd # 列出文件(详细信息 + 隐藏文件) ls -la # 切换目录 cd /var/log # 绝对路径 cd .. # 上级目录 cd ~ # 用户主目录 # 创建目录(含父目录) mkdir -p /app/logs # 复制 / 移动 / 删除 cp file.txt /backup/ # 复制文件 cp -r dir/ /backup/ # 复制目录 mv old.txt new.txt # 移动/重命名 rm file.txt # 删除文件 rm -rf dir/ # 删除目录(⚠️ 慎用!) # 查看文件内容 cat file.txt # 全部内容 head -20 file.txt # 前 20 行 tail -50 file.txt # 后 50 行 tail -f /var/log/app.log # 实时跟踪日志 # 搜索文件内容 grep "error" app.log # 搜索关键词 grep -rn "TODO" /app/src/ # 递归搜索 + 显示行号
# 查看所有进程 ps aux # 查找特定进程 ps aux | grep nginx ps aux | grep node # 杀死进程 kill PID # 优雅终止 kill -9 PID # 强制终止 # 后台运行(不随终端关闭而停止) nohup node server.js & # 后台运行 nohup node server.js > app.log 2>&1 & # 输出重定向到文件 # 查看端口占用 lsof -i :3000 # 查看 3000 端口被谁占用 netstat -tlnp # 查看所有监听端口(需安装 net-tools) ss -tlnp # 更现代的替代方案
# 测试网络连通性 ping google.com curl -I https://your-site.com # 只看响应头 curl https://api.example.com # GET 请求 # 下载文件 wget https://example.com/file.tar.gz curl -O https://example.com/file.tar.gz # DNS 查询 dig your-domain.com nslookup your-domain.com # 防火墙(Ubuntu/CentOS) ufw allow 80 # Ubuntu: 开放 80 端口 ufw allow 443 firewall-cmd --add-port=80/tcp --permanent # CentOS
💡 记不住没关系:收藏这个页面,用到时 Ctrl+F 搜索。Linux 命令不需要背,需要肌肉记忆——多用几次就熟了。

📋 最高频命令 Top 15
命令用途记忆
ls -la列出文件list all
cd切换目录change dir
cat / head / tail查看文件
grep搜索内容
tail -f实时看日志follow
ps aux查看进程process status
kill / kill -9杀进程
lsof -i :PORT查端口list open files
df -h查磁盘disk free
free -h查内存
top系统监控
chmod改权限change mode
curlHTTP 请求
scp远程复制secure copy
tar -xzf解压extract gzip file
前端调试服务器排障
浏览器 Console应用日志文件
Network 面板Nginx access.log
React DevToolssystemctl status / journalctl
console.logtail -f app.log
# 系统日志 /var/log/syslog # Ubuntu 系统日志 /var/log/messages # CentOS 系统日志 /var/log/auth.log # 登录认证日志(谁在尝试 SSH) # Nginx 日志 /var/log/nginx/access.log # 访问日志 /var/log/nginx/error.log # 错误日志 # 应用日志(看你的配置) /app/logs/app.log /var/log/your-app/ # Docker 容器日志 docker logs container_name docker logs -f --tail 100 container_name # 实时跟踪最后100行
# 1. 实时跟踪日志(最常用) tail -f /var/log/nginx/error.log # 2. 搜索错误关键词 grep "error" app.log grep -i "error\|fail\|exception" app.log # 搜索多个关键词 # 3. 查看特定时间段的日志 grep "2024-01-15 14:" app.log # 14点的日志 # 4. 统计错误次数 grep -c "500" /var/log/nginx/access.log # 500 错误有多少次 # 5. 查看最近的日志 tail -100 app.log # 最后 100 行 journalctl -u nginx --since "1 hour ago" # systemd 服务最近1小时 # 6. 按大小查看日志文件(避免日志撑满磁盘) du -sh /var/log/* | sort -rh | head -10 # 7. 日志轮转(自动压缩旧日志) # 配置 /etc/logrotate.d/your-app
# 网站打不开了?按这个流程排查: # 1. 服务器能不能连上? ping your-server-ip # 2. 端口开了吗? curl -I http://your-server-ip:80 # 3. Nginx 在运行吗? systemctl status nginx nginx -t # 检查配置语法 # 4. 应用在运行吗? docker ps # 看容器状态 curl http://localhost:3000 # 在服务器上直接测试应用 # 5. 看错误日志 tail -50 /var/log/nginx/error.log docker logs your-app --tail 50 # 6. 防火墙放行了吗? ufw status # Ubuntu firewall-cmd --list-all # CentOS
⚠️ 日志安全:日志中可能包含敏感信息(用户 IP、请求参数)。不要把日志发到公开频道。定期清理旧日志,避免磁盘被撑满。
✏️ 填空练习

实时跟踪日志文件的命令是 /var/log/app.log,搜索日志中的错误用 "error" app.log

🧠 小测验

查看 Docker 容器实时日志的命令是?

👤 为什么不用 root

root 拥有最高权限,任何误操作都可能摧毁系统。应用被黑后 root 权限意味着全盘失守。创建专用用户是安全基本功。

🔐 权限数字含义

rwx = 4+2+1 = 7
rw- = 4+2+0 = 6
r-- = 4+0+0 = 4
三位数字:属主-属组-其他

# 创建新用户(带主目录) useradd -m -s /bin/bash deploy # 设置密码 passwd deploy # 添加到 sudo 组(可以执行 sudo 命令) usermod -aG sudo deploy # Ubuntu usermod -aG wheel deploy # CentOS # 切换用户 su - deploy # 查看当前用户 whoami # 查看用户所属组 groups deploy # 删除用户 userdel -r old_user # -r 同时删除主目录
# 查看权限 ls -la # -rw-r--r-- 1 deploy deploy 1234 Jan 15 app.js # │├─┤├─┤├─┤ # │ │ │ └── 其他用户:只读(r--=4) # │ │ └────── 属组:只读(r--=4) # │ └────────── 属主:读写(rw-=6) # └──────────── 文件类型(-=文件 d=目录) # 修改权限 chmod 755 script.sh # 属主可执行,其他只读+执行 chmod 644 config.yml # 属主读写,其他只读 chmod 600 ~/.ssh/id_ed25519 # 只有属主可读写(SSH密钥必须) # 修改文件所有者 chown deploy:deploy /app -R # 递归修改目录所有者 # 常见权限组合 # 755 → 目录、可执行脚本 # 644 → 普通文件、配置文件 # 600 → 密钥、密码文件 # 700 → 私有目录(如 .ssh)
⚠️ 安全最佳实践
1. 应用用专用用户运行(如 deploy),不用 root
2. SSH 密钥权限必须是 600
3. 禁用 root SSH 登录:PermitRootLogin no
4. 不要给 chmod 777,这意味着任何人都能读写执行
📋 权限速查
权限数字用途
rwxr-xr-x755目录/脚本
rw-r--r--644普通文件
rw-------600密钥/密码
rwx------700私有目录
🧠 小测验

SSH 私钥文件的权限应该设置为?

前端性能监控服务器性能监控
Chrome Performance 面板top / htop
Memory 面板free -h
Lighthouse 评分负载均值(load average)
Network 瀑布图iftop / nethogs
# 启动 top top # top 界面关键指标: # load average: 0.5, 0.3, 0.2 ← 1分钟/5分钟/15分钟 平均负载 # → 小于 CPU 核数 = 正常,大于核数 = 过载 # %Cpu: 20.0 us, 5.0 sy ← 用户态/内核态 CPU 占用 # MiB Mem: 4096 total, 2048 free ← 内存总量和空闲量 # MiB Swap: 2048 total, 2048 free ← 交换分区(如果在用说明内存不够了) # top 快捷键 # M → 按内存排序 # P → 按 CPU 排序 # q → 退出 # 推荐使用 htop(更好看的 top) sudo apt install htop # Ubuntu htop
# 查看内存使用 free -h # total used free shared buff/cache available # Mem: 4.0Gi 1.2Gi 0.5Gi 100Mi 2.3Gi 2.5Gi # → 看 available 列,这才是真正可用的内存 # 查看磁盘使用 df -h # Filesystem Size Used Avail Use% Mounted on # /dev/vda1 40G 25G 15G 63% / # → Use% 超过 85% 就该清理了 # 查看哪个目录占空间大 du -sh /* 2>/dev/null | sort -rh | head -10 du -sh /var/log/* | sort -rh | head -5 # 清理常见大文件 docker system prune -a # 清理 Docker 无用镜像/容器 journalctl --vacuum-size=100M # 清理 systemd 日志 > /var/log/big-log-file # 清空日志文件(不删除)
💡 监控三板斧:服务器变慢 → ① top 看 CPU 和内存 → ② df -h 看磁盘 → ③ lsof -i 看网络连接。90% 的问题能定位到。
📋 监控命令速查
命令用途关注指标
top / htopCPU + 内存 实时load average, %CPU
free -h内存概览available 列
df -h磁盘空间Use% 列
du -sh dir目录大小找大文件
uptime运行时间+负载load average
iostat磁盘 I/O%util 列

📈 Prometheus + Grafana:现代化监控入门

🔍 Prometheus 是什么

Prometheus 是一个时序数据库 + 采集系统。它主动 pull(拉取)你应用暴露的 /metrics 接口,把指标存起来。你可以用 PromQL 查询语言分析历史趋势,比 top 强大得多。

📊 Grafana 是什么

Grafana 是数据可视化面板,连接 Prometheus 数据源,把指标画成折线图、热图。你可以配置告警——比如"错误率超过 1% 发钉钉通知"。

# FastAPI 接入 Prometheus(3 行代码) # pip install prometheus-fastapi-instrumentator from fastapi import FastAPI from prometheus_fastapi_instrumentator import Instrumentator app = FastAPI() # 自动暴露 /metrics 接口,记录: # - 每个接口的请求次数(http_requests_total) # - 响应时间分布(http_request_duration_seconds) # - 当前活跃请求数(http_requests_in_progress) Instrumentator().instrument(app).expose(app) # 访问 http://your-server:8000/metrics 即可看到所有指标
# docker-compose.yml:本地跑起完整监控栈 version: "3" services: prometheus: image: prom/prometheus ports: ["9090:9090"] volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml grafana: image: grafana/grafana ports: ["3000:3000"] environment: GF_SECURITY_ADMIN_PASSWORD: admin # prometheus.yml # scrape_configs: # - job_name: "myapp" # static_configs: # - targets: ["app:8000"] # 采集应用的 /metrics # 访问 http://localhost:3000 → 添加 Prometheus 数据源 → 导入 FastAPI 看板
💡 关注这 4 个核心指标(RED 方法论):Request Rate(QPS)/ Error Rate(错误率)/ Duration(响应时间 P50/P99)/ Saturation(CPU/内存饱和度)。这 4 个数字能定位 90% 的线上问题。
🧠 小测验

free -h 输出中,判断内存是否够用应该看哪个值?

前端概念Docker 类比
package.json + 代码Dockerfile(构建说明)
node_modules + dist镜像(Image,构建产物)
运行中的应用容器(Container,运行中的实例)
npm registryDocker Hub(镜像仓库)
npm install + npm startdocker pull + docker run

📦 镜像 Image

只读的模板,包含应用代码 + 运行环境 + 依赖。类似一个"快照"。可以从 Docker Hub 拉取,也可以自己构建。

🏃 容器 Container

镜像的运行实例。一个镜像可以启动多个容器。容器之间互相隔离,有独立的文件系统和网络。

☁️ 仓库 Registry

存储和分发镜像的服务。Docker Hub 是最大的公共仓库,也可以搭建私有仓库。

# 拉取镜像 docker pull nginx:latest docker pull node:20-alpine # alpine 版本更小 # 运行容器 docker run -d -p 80:80 nginx # -d 后台运行 # -p 端口映射(宿主机:容器) # nginx 镜像名 # 查看运行中的容器 docker ps docker ps -a # 包含已停止的 # 停止 / 启动 / 删除容器 docker stop container_id docker start container_id docker rm container_id # 进入容器内部(像 SSH 进入服务器) docker exec -it container_id /bin/sh # 查看容器日志 docker logs -f container_id # 查看所有镜像 docker images # 删除镜像 docker rmi image_name # 清理未使用的资源 docker system prune -a
# 创建一个测试页面 echo "<h1>Hello Docker!</h1>" > /tmp/index.html # 运行 Nginx 容器,挂载自定义页面 docker run -d \ --name my-nginx \ -p 8080:80 \ -v /tmp/index.html:/usr/share/nginx/html/index.html:ro \ nginx # 访问 http://localhost:8080 就能看到 Hello Docker! # 清理 docker stop my-nginx && docker rm my-nginx
💡 为什么用 Docker:① 环境一致(本地和线上一样)② 隔离性(不同应用互不干扰)③ 快速部署(秒级启动)④ 易于扩展(多开容器即可)。
✏️ 填空练习

在后台运行一个将宿主机 3000 端口映射到容器 80 端口的 Nginx 容器:docker run -p nginx

🧠 小测验

Docker 镜像和容器的关系是?

# 选择基础镜像(alpine 版本更小) FROM node:20-alpine # 设置工作目录 WORKDIR /app # 先复制 package 文件(利用 Docker 缓存层) COPY package*.json ./ # 安装依赖(只在 package.json 变化时重新执行) RUN npm ci --only=production # 复制应用代码 COPY . . # 暴露端口(文档性质,不会真的开放) EXPOSE 3000 # 启动命令 CMD ["node", "server.js"]
FROM python:3.12-slim WORKDIR /app # 先复制依赖文件(利用缓存层) COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY . . EXPOSE 8000 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# 阶段1:构建 FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build # 阶段2:运行(只包含构建产物,镜像更小) FROM nginx:alpine COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] # 最终镜像只有 ~30MB,而不是 ~1GB
# .dockerignore — 不要复制进镜像的文件 node_modules .git .env *.md dist .DS_Store
⚠️ 常见错误
1. 忘写 .dockerignore → node_modules 被复制进镜像,又被重新安装
2. COPY 代码在 npm install 前面 → 改一行代码就要重新安装依赖
3. 用 npm install 而不是 npm ci → 不能保证版本一致
# 构建镜像(在 Dockerfile 所在目录) docker build -t my-app:v1 . # -t 给镜像起名和标签 # . 构建上下文(当前目录) # 运行镜像 docker run -d -p 3000:3000 --name my-app my-app:v1 # 查看构建历史(看每层的大小) docker history my-app:v1
📋 Dockerfile 指令速查
指令用途示例
FROM基础镜像FROM node:20-alpine
WORKDIR工作目录WORKDIR /app
COPY复制文件COPY . .
RUN执行命令RUN npm ci
EXPOSE暴露端口EXPOSE 3000
CMD启动命令CMD ["node","app.js"]
ENV环境变量ENV NODE_ENV=production
ARG构建参数ARG VERSION=latest
🧠 小测验

Dockerfile 中为什么要先 COPY package.json 再 COPY 整个项目?

手动方式Docker Compose
一个个 docker run一个 YAML 文件定义所有服务
手动管理网络自动创建内部网络
手动设置依赖顺序depends_on 声明依赖
命令很长记不住docker compose up -d
# docker-compose.yml version: "3.8" services: # Web 应用 app: build: . # 用当前目录的 Dockerfile 构建 ports: - "3000:3000" # 端口映射 environment: - DATABASE_URL=mysql://root:secret@db:3306/myapp - REDIS_URL=redis://redis:6379 depends_on: - db - redis restart: unless-stopped # 崩了自动重启 # MySQL 数据库 db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: secret MYSQL_DATABASE: myapp ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql # 数据持久化 # Redis 缓存 redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data # 命名卷(数据不会随容器删除而丢失) volumes: mysql_data: redis_data:
# 启动所有服务(后台) docker compose up -d # 查看服务状态 docker compose ps # 查看某个服务的日志 docker compose logs -f app # 重新构建并启动 docker compose up -d --build # 停止所有服务 docker compose down # 停止并删除数据卷(⚠️ 会丢失数据) docker compose down -v # 只启动某个服务 docker compose up -d db # 在容器中执行命令 docker compose exec app sh docker compose exec db mysql -u root -p
💡 服务间通信:Docker Compose 会自动创建内部网络,服务之间用服务名作为主机名。比如 app 连接数据库用 db:3306,连 Redis 用 redis:6379
✏️ 填空练习

在后台启动所有服务的命令是 docker compose ,查看 app 服务实时日志是 docker compose logs app

🧠 小测验

Docker Compose 中,app 服务连接 MySQL 应该使用什么地址?

前端概念Docker 对应
.env 文件environment / env_file
localStorage(持久化)Volumes(数据卷)
sessionStorage(临时)容器内文件系统(容器删除即丢失)
# 方式1:docker run -e docker run -d -e NODE_ENV=production -e DB_HOST=db my-app # 方式2:docker-compose.yml 中直接写 services: app: environment: - NODE_ENV=production - DB_HOST=db - DB_PASSWORD=secret # ⚠️ 密码不要提交到 Git # 方式3:使用 .env 文件(推荐) services: app: env_file: - .env # 从文件加载环境变量 # .env 文件(加入 .gitignore!) NODE_ENV=production DB_HOST=db DB_PASSWORD=my-secret-password REDIS_URL=redis://redis:6379
# 问题:容器删除后,容器内的数据全部丢失 # 解决:用数据卷把数据存在宿主机上 # 方式1:命名卷(推荐,Docker 自动管理路径) services: db: image: mysql:8.0 volumes: - mysql_data:/var/lib/mysql # 命名卷 volumes: mysql_data: # 声明命名卷 # 方式2:绑定挂载(指定宿主机路径) services: app: volumes: - ./src:/app/src # 开发时用,代码变化实时同步 - ./nginx.conf:/etc/nginx/nginx.conf:ro # :ro 只读挂载 # 查看卷 docker volume ls docker volume inspect mysql_data # 清理无用卷 docker volume prune
⚠️ 注意事项
1. .env 文件必须加入 .gitignore,不要提交密码到代码仓库
2. 数据库一定要用数据卷,否则 docker compose down 就丢数据
3. docker compose down -v 会删除数据卷,生产环境慎用
📋 环境变量与数据卷速查
场景用法
传递配置environment:env_file:
数据持久化命名卷 volumes: [vol:/path]
开发热更新绑定挂载 volumes: [./src:/app/src]
只读配置绑定挂载 + :ro
🧠 小测验

容器删除后数据还在的前提是?

📏 为什么要优化

镜像越小 → 拉取更快 → 部署更快 → 存储更省 → 攻击面更小(安全)。

🎯 优化目标

Node.js 应用:< 200MB
前端静态站:< 50MB
Go/Rust 应用:< 20MB

# 1. 选择更小的基础镜像 FROM node:20 # ~1GB FROM node:20-slim # ~200MB FROM node:20-alpine # ~130MB ← 推荐 # 2. 多阶段构建(前端项目必用) FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine # 最终镜像只有 nginx + 构建产物 COPY --from=builder /app/dist /usr/share/nginx/html # 3. 合并 RUN 层,减少镜像层数 # ❌ 每个 RUN 都是一层 RUN apt-get update RUN apt-get install -y curl RUN rm -rf /var/lib/apt/lists/* # ✅ 合并为一层 RUN apt-get update && \ apt-get install -y curl && \ rm -rf /var/lib/apt/lists/* # 4. 善用 .dockerignore # 避免 node_modules、.git 等大目录被 COPY 进去
# Docker 构建是逐层缓存的 # 如果某层的输入没变 → 直接用缓存 → 跳过执行 # ✅ 正确顺序(改代码不会重新安装依赖) COPY package*.json ./ # 第1层:只有 package 变化时才重新执行下面的 RUN npm ci # 第2层:依赖安装(会被缓存) COPY . . # 第3层:每次改代码都会变 RUN npm run build # 第4层:每次重新构建 # ❌ 错误顺序(改一行代码就要重新安装依赖) COPY . . # 任何文件变化都会破坏缓存 RUN npm ci # 每次都重新安装 → 慢! RUN npm run build # 查看镜像各层大小 docker history my-app:latest
💡 快速对比:构建优化前后,运行 docker images 比较大小。如果镜像 > 500MB,一定有优化空间。
📋 镜像优化清单
策略效果
使用 alpine 基础镜像减少 70-80% 大小
多阶段构建只保留运行时必要文件
合并 RUN 指令减少层数
.dockerignore减少构建上下文
npm ci --only=production不安装 devDependencies
先 COPY package.json利用缓存加速构建
🧠 小测验

多阶段构建的核心好处是?

前端概念Nginx 类比
webpack-dev-server proxyNginx 反向代理
Vue Router 路由分发Nginx location 规则
环境变量 API_BASE_URLupstream 配置

🔄 什么是反向代理

用户访问 your-domain.com:80 → Nginx 接收 → 转发给 localhost:3000(你的应用)。用户不知道后端应用运行在哪个端口。

💡 为什么需要

① 用 80/443 标准端口 ② 一台服务器跑多个应用 ③ 负载均衡 ④ SSL 终结(HTTPS)⑤ 静态文件缓存

# /etc/nginx/conf.d/my-app.conf server { listen 80; server_name your-domain.com; # 所有请求转发给后端应用 location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; 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; } } # 测试配置语法 # nginx -t # 重新加载配置(不停服务) # nginx -s reload
# 一台服务器上跑多个应用 server { listen 80; server_name your-domain.com; # 前端 SPA location / { root /var/www/frontend/dist; try_files $uri $uri/ /index.html; # SPA 路由支持 } # API 请求转发到后端 location /api/ { proxy_pass http://localhost:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } # WebSocket 支持 location /ws/ { proxy_pass http://localhost:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } }
# docker-compose.yml 中的 Nginx 反代 services: nginx: image: nginx:alpine ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro depends_on: - app app: build: . expose: - "3000" # 不映射到宿主机,只在 Docker 网络内可见 # nginx.conf 中用服务名连接 # proxy_pass http://app:3000;
⚠️ 常见错误
1. 修改配置后忘了 nginx -s reload
2. proxy_pass 末尾的 / 会影响路径拼接
3. SPA 没加 try_files → 刷新页面 404
✏️ 填空练习

Nginx 反向代理的核心指令是 ,修改配置后用 nginx -s 重新加载。

🧠 小测验

SPA 应用在 Nginx 中需要添加什么配置来支持前端路由?

server { listen 80; server_name static.your-domain.com; # 静态文件根目录 root /var/www/static; index index.html; # 开启 gzip 压缩(JS/CSS 体积减少 60-80%) gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_min_length 1024; # 小于 1KB 不压缩 # 缓存策略:带 hash 的资源长期缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ { expires 1y; # 缓存 1 年 add_header Cache-Control "public, immutable"; access_log off; # 不记录静态资源访问日志 } # HTML 不缓存(确保用户能拿到最新页面) location ~* \.html$ { add_header Cache-Control "no-cache"; } }
server { listen 80; server_name app.your-domain.com; root /var/www/app/dist; # SPA 路由支持 location / { try_files $uri $uri/ /index.html; } # 静态资源长缓存(Vite/Webpack 打包后文件名带 hash) location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; } # API 代理 location /api/ { proxy_pass http://localhost:8000; } # gzip gzip on; gzip_types text/plain text/css application/json application/javascript text/xml image/svg+xml; # 安全头 add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; }
💡 缓存策略要点:构建工具(Vite/Webpack)给 JS/CSS 文件名加 hash(如 app.a1b2c3.js),内容变则 hash 变。所以可以设 expires 1y(长期缓存)。HTML 不加 hash,所以设 no-cache(每次检查是否更新)。
📋 Nginx 静态托管速查
配置用途
root /path设置静态文件根目录
gzip on开启 gzip 压缩
expires 1y设置缓存时间
try_filesSPA 路由支持
access_log off关闭访问日志
🧠 小测验

为什么 HTML 文件要设置 no-cache 而 JS/CSS 可以设置 1 年缓存?

🔒 为什么需要 HTTPS

① 数据加密传输 ② 浏览器不再显示"不安全" ③ SEO 加分 ④ 很多 API(如地理位置)只在 HTTPS 下可用 ⑤ HTTP/2 需要 HTTPS

🆓 Let's Encrypt

免费的 SSL 证书颁发机构。用 Certbot 工具自动申请和续期。证书有效期 90 天,Certbot 自动续期。

# Ubuntu 安装 Certbot sudo apt update sudo apt install certbot python3-certbot-nginx # 自动获取证书并配置 Nginx(最简单) sudo certbot --nginx -d your-domain.com -d www.your-domain.com # Certbot 会自动: # 1. 验证域名所有权 # 2. 下载证书到 /etc/letsencrypt/live/your-domain.com/ # 3. 修改 Nginx 配置添加 SSL # 4. 设置自动续期 # 测试自动续期 sudo certbot renew --dry-run # 手动续期 sudo certbot renew
# HTTP → HTTPS 重定向 server { listen 80; server_name your-domain.com www.your-domain.com; return 301 https://$server_name$request_uri; } # HTTPS 配置 server { listen 443 ssl http2; server_name your-domain.com; # SSL 证书路径(Certbot 自动生成) ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; # SSL 安全配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; # 安全头 add_header Strict-Transport-Security "max-age=31536000" always; add_header X-Content-Type-Options "nosniff"; add_header X-Frame-Options "SAMEORIGIN"; # 你的应用配置 location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto https; } }
⚠️ 注意事项
1. 申请证书前确保域名已经解析到服务器 IP
2. 防火墙要开放 80 和 443 端口
3. 证书有效期 90 天,Certbot 会自动续期(确认 cron job 存在)
4. 如果用 Docker,需要额外配置证书挂载
📋 HTTPS 配置速查
步骤命令
安装 Certbotapt install certbot python3-certbot-nginx
获取证书certbot --nginx -d domain.com
测试续期certbot renew --dry-run
检查证书certbot certificates
HTTP 重定向return 301 https://...
✏️ 填空练习

Let's Encrypt 证书有效期是 天,自动获取证书的工具是

🧠 小测验

用 Certbot 获取证书前,需要先确保什么?

# 第一步:检查配置语法 nginx -t # nginx: configuration file /etc/nginx/nginx.conf test is successful ← 没问题 # nginx: [emerg] unknown directive "proxypass" ← 故意拼错用于演示,正确写法是 proxy_pass # 第二步:查看错误日志 tail -50 /var/log/nginx/error.log # 第三步:测试连通性 curl -I http://localhost # Nginx 自身 curl -I http://localhost:3000 # 后端应用 curl -I http://your-domain.com # 从外部访问
# 问题1:502 Bad Gateway # 原因:后端应用没启动 / 端口不对 # 排查: docker ps # 检查容器是否在运行 curl http://localhost:3000 # 直接测试后端 # 修复:确认后端应用正常运行,端口和 proxy_pass 一致 # 问题2:403 Forbidden # 原因:文件权限不对 / Nginx worker 用户没有读取权限 # 排查: ls -la /var/www/your-site/ # 检查文件权限 # 修复: chmod -R 755 /var/www/your-site/ chown -R www-data:www-data /var/www/your-site/ # 问题3:SPA 刷新 404 # 原因:没配置 try_files # 修复:加上 try_files $uri $uri/ /index.html; # 问题4:配置修改不生效 # 原因:忘了重载配置 nginx -s reload # 问题5:端口被占用 # 排查: lsof -i :80 # 修复:停掉占用进程,或换端口
# 在 Nginx 配置中增加调试信息 location / { # 在响应头中加入调试信息 add_header X-Debug-Server "app-1"; add_header X-Debug-Upstream "$upstream_addr"; proxy_pass http://localhost:3000; } # 查看完整请求/响应头 curl -v http://your-domain.com # 查看 Nginx 完整配置 nginx -T # 输出所有已加载的配置 # 查看 Nginx 状态(需要 stub_status 模块) location /nginx_status { stub_status; allow 127.0.0.1; deny all; }
📋 Nginx 排障速查
状态码常见原因排查方式
502后端应用没启动curl localhost:PORT
403文件权限ls -la + chown
404路径不对/缺try_files检查 root 和 location
504后端响应超时增加 proxy_read_timeout
413上传文件太大增加 client_max_body_size
🧠 小测验

Nginx 返回 502 Bad Gateway 最可能的原因是?

前端概念GitHub Actions 类比
npm scriptsWorkflow steps
package.json scripts 节.github/workflows/*.yml
husky pre-commiton: push / pull_request
并行的 npm run test & npm run lint并行的 jobs

📋 Workflow

一个 YAML 文件定义的自动化流程。放在 .github/workflows/ 目录下。

🏃 Job

Workflow 中的一个任务单元。多个 Job 可以并行运行,也可以设置依赖顺序。

📌 Step

Job 中的一个步骤。每个 step 可以运行命令或使用 action。

# .github/workflows/ci.yml name: CI # 触发条件:push 到 main 分支 或 PR 到 main on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest # 运行环境 steps: # 拉取代码 - uses: actions/checkout@v4 # 安装 Node.js - uses: actions/setup-node@v4 with: node-version: 20 cache: 'npm' # 缓存 npm 依赖 # 安装依赖 - run: npm ci # 运行 lint - run: npm run lint # 运行测试 - run: npm run test # 构建 - run: npm run build
# .github/workflows/ci.yml name: Python CI on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install dependencies run: | pip install -r requirements.txt pip install pytest - name: Run tests run: pytest - name: Run linter run: | pip install ruff ruff check .
💡 Secrets 管理:敏感信息(服务器密码、API Key)不要写在 YAML 里。在 GitHub 仓库 → Settings → Secrets 中配置,用 ${{ secrets.MY_SECRET }} 引用。
📋 GitHub Actions 速查
概念说明
Workflow 文件位置.github/workflows/*.yml
触发条件on: push / pull_request / schedule
运行环境runs-on: ubuntu-latest
使用 Actionuses: actions/checkout@v4
运行命令run: npm test
使用密钥${{ secrets.KEY }}
缓存依赖cache: 'npm'
✏️ 填空练习

GitHub Actions 的 Workflow 文件放在 目录下,引用密钥的语法是 ${{ secrets. }}

🧠 小测验

GitHub Actions 中,多个 job 默认是怎么执行的?

✅ CI 的价值

每次提交自动跑测试,代码质量有保障。构建失败第一时间发现,不会把有问题的代码部署到线上

🔄 典型 CI 流程

① 代码 lint ② 单元测试 ③ 构建 ④ (可选)E2E 测试 ⑤ 构建 Docker 镜像

# .github/workflows/ci.yml name: CI Pipeline on: push: branches: [main, develop] pull_request: jobs: # Job 1: 代码检查 lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: npm - run: npm ci - run: npm run lint - run: npm run type-check # TypeScript 类型检查 # Job 2: 单元测试(和 lint 并行) test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: npm - run: npm ci - run: npm run test -- --coverage # 上传测试覆盖率报告 - uses: actions/upload-artifact@v4 with: name: coverage path: coverage/ # Job 3: 构建(依赖 lint 和 test 通过) build: needs: [lint, test] # 等 lint 和 test 都通过 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 cache: npm - run: npm ci - run: npm run build # 保存构建产物 - uses: actions/upload-artifact@v4 with: name: dist path: dist/
# Job 4: 构建 Docker 镜像(只在 main 分支) docker: needs: build if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Build and push uses: docker/build-push-action@v5 with: push: true tags: | your-user/your-app:latest your-user/your-app:${{ github.sha }}
💡 PR 检查:在 GitHub 仓库 Settings → Branches 中设置保护规则,要求 CI 通过后才能合并 PR。这样可以保证 main 分支始终是可构建的。
🧠 小测验

在 GitHub Actions 中,needs: [lint, test] 的含义是?

🔑 准备工作

① 服务器上已安装 Docker
② GitHub Secrets 中配置服务器 IP、用户名、SSH 私钥
③ 服务器上已有 Compose 文件(如 compose.yamldocker-compose.yml

🔄 部署流程

① CI 通过 → ② SSH 连接服务器 → ③ 拉取最新镜像 → ④ 重启容器 → ⑤ 健康检查

# .github/workflows/deploy.yml name: Deploy on: push: branches: [main] jobs: deploy: needs: [test, build] # 确保 CI 通过 runs-on: ubuntu-latest steps: - name: Deploy to server via SSH uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | cd /app/my-project git pull origin main docker compose pull docker compose up -d --build docker system prune -f echo "Deploy complete!"
# CI 中构建并推送镜像到 Docker Hub # 然后 SSH 到服务器拉取新镜像 jobs: build-and-push: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - uses: docker/build-push-action@v5 with: push: true tags: your-user/your-app:latest deploy: needs: build-and-push runs-on: ubuntu-latest steps: - uses: appleboy/ssh-action@v1 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} script: | docker pull your-user/your-app:latest cd /app docker compose up -d # 健康检查 sleep 5 curl -f http://localhost:3000/health || exit 1 echo "Deploy success!"
# 在 GitHub 仓库 → Settings → Secrets → Actions 中添加: SERVER_HOST → 123.45.67.89 (服务器 IP) SERVER_USER → deploy (SSH 用户名) SSH_PRIVATE_KEY → -----BEGIN... (SSH 私钥内容) DOCKER_USERNAME → your-dockerhub-id (Docker Hub 用户名) DOCKER_TOKEN → dckr_pat_xxxxx (Docker Hub Access Token)
⚠️ 部署安全
1. 用专用的 deploy 用户,不要用 root
2. SSH 密钥只给最小权限
3. 部署后加健康检查(curl /health)
4. 考虑添加回滚策略(保留上一个版本的镜像)
🧠 小测验

GitHub Actions 中存储服务器密码等敏感信息应该用?

🛒 购买建议

新用户入门级 ECS(2核4G,Ubuntu 22.04)。年付更划算。选离你近的地域(如华东2-上海)。

📋 初始化清单

① SSH 密钥登录 ② 创建 deploy 用户 ③ 安全组配置 ④ 安装 Docker ⑤ 安装 Nginx ⑥ 配置防火墙

# 用阿里云控制台设置的密码首次登录 ssh root@your-server-ip # 更新系统 apt update && apt upgrade -y # 创建部署用户 useradd -m -s /bin/bash deploy passwd deploy usermod -aG sudo deploy # 配置 SSH 密钥登录 mkdir -p /home/deploy/.ssh # 把你本地的公钥粘贴进去 vim /home/deploy/.ssh/authorized_keys chmod 700 /home/deploy/.ssh chmod 600 /home/deploy/.ssh/authorized_keys chown -R deploy:deploy /home/deploy/.ssh # 禁用 root 密码登录(可选但推荐) vim /etc/ssh/sshd_config # PermitRootLogin no # PasswordAuthentication no systemctl restart sshd
# Ubuntu 安装 Docker(官方脚本) curl -fsSL https://get.docker.com | sh # 让 deploy 用户可以使用 docker(不需要 sudo) usermod -aG docker deploy # 安装 Docker Compose 插件(之后命令用 docker compose) apt install docker-compose-plugin -y # 或独立安装 # curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose # chmod +x /usr/local/bin/docker-compose # 验证安装 docker --version docker compose version # 配置 Docker 镜像加速(阿里云) # 阿里云控制台 → 容器镜像服务 → 镜像加速器 → 复制加速地址 mkdir -p /etc/docker cat > /etc/docker/daemon.json << EOF { "registry-mirrors": ["https://xxxxx.mirror.aliyuncs.com"] } EOF systemctl daemon-reload systemctl restart docker
# 安装 Nginx apt install nginx -y systemctl enable nginx systemctl start nginx # 配置防火墙 ufw allow OpenSSH ufw allow 'Nginx Full' # 开放 80 和 443 ufw enable ufw status # ⚠️ 还需要在阿里云控制台配置安全组 # ECS → 安全组 → 添加规则: # 80/80 TCP 0.0.0.0/0 (HTTP) # 443/443 TCP 0.0.0.0/0 (HTTPS) # 22/22 TCP 你的IP/32 (SSH,限制来源IP更安全) # 创建应用目录 mkdir -p /app chown deploy:deploy /app
⚠️ 安全提醒
1. 阿里云安全组 + 服务器防火墙 = 双重保护
2. SSH 端口建议改成非标准端口(如 2222)
3. 安装 fail2ban 防暴力破解:apt install fail2ban
📋 初始化命令清单
步骤命令
更新系统apt update && apt upgrade -y
创建用户useradd -m -s /bin/bash deploy
安装 Dockercurl -fsSL https://get.docker.com | sh
安装 Nginxapt install nginx -y
开防火墙ufw allow 80; ufw allow 443
应用目录mkdir -p /app && chown deploy /app

📋 部署方案

Git 拉代码到服务器 → Nginx 直接托管静态文件。这是最简单的部署方式,适合静态站和学习项目。

🗂️ 目标结构

/app/learning/ → 项目代码
Nginx → 托管静态文件
域名(可选)→ 指向服务器 IP

# 以 deploy 用户登录服务器 ssh deploy@your-server-ip # 安装 Git(如果没有) sudo apt install git -y # 克隆项目 cd /app git clone https://github.com/your-user/your-repo.git learning cd learning # 查看文件结构 ls -la # index.html # python-course/index.html # java-course/index.html # mysql-course/index.html # redis-course/index.html # devops-course/index.html # course.css # course.js
# 创建 Nginx 配置 sudo vim /etc/nginx/conf.d/learning.conf server { listen 80; server_name your-domain.com; # 或 _ 匹配所有 root /app/learning; index index.html; # 静态文件缓存 location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ { expires 7d; add_header Cache-Control "public"; } # 所有路径都尝试找文件 location / { try_files $uri $uri/ $uri/index.html =404; } # gzip 压缩 gzip on; gzip_types text/plain text/css application/javascript; } # 测试配置 sudo nginx -t # 重载配置 sudo nginx -s reload
# 1. 在域名服务商添加 DNS 解析 # 类型: A 记录 # 主机记录: @ 或 learning # 记录值: 你的服务器 IP # 2. 等待 DNS 生效(通常几分钟到几小时) dig your-domain.com # 3. 配置 HTTPS(可选) sudo certbot --nginx -d your-domain.com # 4. 自动更新代码的简单脚本 cat > /app/update.sh << 'EOF' #!/bin/bash cd /app/learning git pull origin main echo "Updated at $(date)" EOF chmod +x /app/update.sh # 以后更新只需要: /app/update.sh
💡 自动部署进阶:配合前面学的 GitHub Actions,push 代码后自动 SSH 到服务器执行 git pull,实现全自动部署。

🎯 实战任务

按照上面的步骤,把本学习站部署到你的 ECS 上:

  1. SSH 登录服务器,git clone 项目
  2. 配置 Nginx,用浏览器通过 IP 访问验证
  3. (可选)配置域名和 HTTPS

✅ 为什么需要检查清单

飞行员起飞前要检查清单,上线也一样。人会忘事,清单不会。每次上线都过一遍,养成习惯。

📋 使用方式

打印这个页面或收藏起来。每次部署新项目上线时,按顺序逐项确认。

□ SSH 已配置密钥登录 □ 已禁用 root 远程密码登录 □ 已创建专用 deploy 用户 □ 防火墙已开启,仅开放必要端口(80/443/SSH) □ 阿里云安全组规则已配置 □ 敏感信息(密码、API Key)使用环境变量,不在代码中 □ .env 文件已在 .gitignore 中 □ HTTPS 已配置(Let's Encrypt) □ Certbot 自动续期已验证
□ 系统已更新(apt update && apt upgrade) □ Docker 已安装并正常运行 □ Docker 镜像加速已配置(国内服务器) □ Nginx 已安装并设为开机自启 □ 应用目录权限正确(deploy 用户可读写) □ 磁盘空间充足(df -h → Use% < 80%) □ 内存充足(free -h → available > 500MB) □ 时区已设置正确(timedatectl)
□ 应用容器正常运行(docker ps) □ 应用日志无错误(docker logs app) □ 数据库连接正常 □ Redis 连接正常(如果用到) □ 数据卷已配置(数据库数据不会丢失) □ 健康检查接口可访问(curl localhost:PORT/health)

🩺 Health Check 接口设计

✅ 存活探针 vs 就绪探针

存活探针(Liveness):进程活着吗?失败则重启容器。
GET /health/live → 只要进程正常就返回 200。

就绪探针(Readiness):能处理请求吗?失败则从负载均衡摘除。
GET /health/ready → 检查 DB、Redis 等依赖是否可用。

📋 健康检查规范

• 路径:/health/health/ready
• 200 = 健康,503 = 不健康
• 不需要认证(让监控系统可以直接访问)
• 响应要快(<500ms),不做复杂计算
• 返回各依赖状态便于排查

from fastapi import FastAPI from fastapi.responses import JSONResponse import asyncpg, redis.asyncio as aioredis app = FastAPI() @app.get("/health") async def health_check(): """存活探针:进程在就返回 200""" return {"status": "ok"} @app.get("/health/ready") async def readiness_check(): """就绪探针:检查所有依赖""" checks = {} ok = True # 检查数据库 try: conn = await asyncpg.connect(DATABASE_URL, timeout=2) await conn.execute("SELECT 1") await conn.close() checks["database"] = "ok" except Exception as e: checks["database"] = f"error: {e}" ok = False # 检查 Redis try: r = aioredis.from_url(REDIS_URL, socket_timeout=2) await r.ping() await r.aclose() checks["redis"] = "ok" except Exception as e: checks["redis"] = f"error: {e}" ok = False return JSONResponse( status_code=200 if ok else 503, content={"status": "ok" if ok else "degraded", "checks": checks}, ) # 响应示例(健康): # {"status": "ok", "checks": {"database": "ok", "redis": "ok"}} # 响应示例(不健康): # {"status": "degraded", "checks": {"database": "ok", "redis": "error: timeout"}} # HTTP 503 → 负载均衡自动摘除该节点
# docker-compose.yml 配置健康检查 services: app: image: myapp:latest healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s # 每 30 秒检查一次 timeout: 5s # 超时 5 秒认为失败 retries: 3 # 连续失败 3 次才标记不健康 start_period: 10s # 启动后 10 秒内不检查(等应用初始化)
# Nginx 上线检查清单追加: □ Health Check 接口可访问:curl localhost:8000/health → 200 □ 就绪探针可访问:curl localhost:8000/health/ready → {"status":"ok"} □ docker-compose.yml 已配置 healthcheck □ Nginx upstream 只转发到健康节点(已配置 fail_timeout)
□ Nginx 配置语法正确(nginx -t) □ 通过 IP 可以访问(curl http://SERVER_IP) □ 通过域名可以访问(curl http://your-domain.com) □ HTTPS 可以访问(curl https://your-domain.com) □ HTTP 自动跳转 HTTPS □ API 接口可正常调用 □ SPA 路由刷新不 404(try_files 已配置) □ gzip 压缩已开启(curl -H "Accept-Encoding: gzip" -I)
□ GitHub Actions CI 流水线正常(测试、lint、构建) □ CD 自动部署已配置且测试通过 □ GitHub Secrets 已正确配置 □ 部署后健康检查通过 □ 回滚方案已准备(保留上一版本镜像或 git revert)
□ 知道如何查看应用日志(docker logs -f) □ 知道如何查看 Nginx 日志 □ 知道如何检查 CPU/内存/磁盘 □ 有告警机制(阿里云云监控 / UptimeRobot 等) □ 日志不会撑满磁盘(已配置日志轮转或定期清理)
💡 上线后第一天:前几个小时多看日志和监控。第二天检查一下磁盘和内存使用趋势。一周后确认日志不会无限增长。
📋 紧急故障处理速查
问题排查命令
网站打不开ping IPcurl IP:80nginx -t
502 错误docker psdocker logs app
服务器很慢topfree -hdf -h
磁盘满了du -sh /*docker system prune
SSH 连不上阿里云控制台 VNC 登录 → 检查 sshd
📝 进入考试 →