单体服务 Docker 部署 - 自建方案
方向一 · 方案B:完全自主搭建,不依赖云数据库
适用场景:预算有限、学习目的、完全掌控
一、方案概述
1.1 与阿里云方案的区别
| 对比项 | 阿里云方案 | 自建方案 |
|---|---|---|
| 数据库 | 云 RDS(托管) | 自己 Docker 部署 MySQL |
| 缓存 | 云 Redis(托管) | 自己 Docker 部署 Redis |
| 成本 | 较高(RDS+Redis费用) | 较低(只需 ECS) |
| 运维 | 云厂商负责数据库运维 | 自己负责备份、监控 |
| 可靠性 | 高(自动备份、主备切换) | 依赖自己的运维能力 |
| 适用场景 | 生产环境、企业项目 | 测试环境、个人项目、学习 |
1.2 架构图
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 自建单体部署架构 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 用户访问 https://example.com │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ ECS 云服务器(一台搞定) │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Docker 容器网络 │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │
│ │ │ │ Nginx │ ─▶ │ Backend │ ─▶ │ MySQL │ │ Redis │ │ │ │
│ │ │ │ :443 │ │ :8080 │ │ :3306 │ │ :6379 │ │ │ │
│ │ │ │ │ │ │ ─▶ │ (容器) │ │ (容器) │ │ │ │
│ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ │ ▼ ▼ │ │ │
│ │ │ ┌─────────────────────────────┐ │ │ │
│ │ │ │ 数据持久化(宿主机目录) │ │ │ │
│ │ │ │ /home/deploy/mysql/data │ │ │ │
│ │ │ │ /home/deploy/redis/data │ │ │ │
│ │ │ └─────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └───────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 优点:成本低、完全可控、适合学习 │
│ 缺点:需要自己运维数据库、做好备份 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
二、服务器准备
(与阿里云方案相同,参考前文)
三、完整 docker-compose.yml
bash
# ═══════════════════════════════════════════════════════════════════════════════
# 文件位置:/home/deploy/docker-compose.yml
#
# 创建命令:
cd /home/deploy
cat > docker-compose.yml << 'EOF'
# ═══════════════════════════════════════════════════════════════════════════════
# Docker Compose 完整配置 - 自建方案(包含 MySQL、Redis)
#
# 【需要修改的地方】搜索【改】标记
# ═══════════════════════════════════════════════════════════════════════════════
version: '3.8'
services:
# ═══════════════════════════════════════════════════════════════════════════════
# Nginx - 反向代理、静态文件、HTTPS
# ═══════════════════════════════════════════════════════════════════════════════
nginx:
image: nginx:1.24-alpine
container_name: nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
- ./frontend/dist:/usr/share/nginx/html:ro
- ./ssl:/etc/nginx/ssl:ro
- ./nginx/logs:/var/log/nginx
depends_on:
- backend
networks:
- app-network
# ═══════════════════════════════════════════════════════════════════════════════
# Backend - Spring Boot 后端
# ═══════════════════════════════════════════════════════════════════════════════
backend:
image: openjdk:17-jdk-slim
container_name: backend
restart: always
working_dir: /app
command: java -jar -Xms512m -Xmx1024m -Dspring.profiles.active=prod app.jar
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │【改】JVM 内存参数 │
# │ 2G 服务器:-Xms256m -Xmx512m │
# │ 4G 服务器:-Xms512m -Xmx1024m │
# │ 8G 服务器:-Xms1g -Xmx2g │
# └─────────────────────────────────────────────────────────────────────────────┘
volumes:
- ./backend/app.jar:/app/app.jar:ro
- ./backend/logs:/app/logs
environment:
- TZ=Asia/Shanghai
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ 数据库连接配置 │
# │ • mysql 是 Docker 网络内的服务名,自动解析为 MySQL 容器 IP │
# │ • 3306 是 MySQL 默认端口 │
# │ • mydb 是数据库名(需要与 MySQL 容器的 MYSQL_DATABASE 一致) │
# └───────────────────────────────────────────────────────────────────────────┘
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=MyPassword123!
# ┌───────────────────────────────────────────────────────────────────────────┐
# │【改】数据库密码,必须与 MySQL 容器的 MYSQL_ROOT_PASSWORD 一致 │
# └───────────────────────────────────────────────────────────────────────────┘
- SPRING_REDIS_HOST=redis
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ Redis 连接配置 │
# │ • redis 是 Docker 网络内的服务名 │
# │ • 如果 Redis 设置了密码,加上:SPRING_REDIS_PASSWORD=xxx │
# └───────────────────────────────────────────────────────────────────────────┘
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
# ┌───────────────────────────────────────────────────────────────────────────┐
# │ depends_on + condition: service_healthy │
# │ • 等待 MySQL 和 Redis 健康检查通过后再启动 Backend │
# │ • 避免 Backend 启动时数据库还没准备好 │
# └───────────────────────────────────────────────────────────────────────────┘
networks:
- app-network
# ═══════════════════════════════════════════════════════════════════════════════
# MySQL - 数据库
# ═══════════════════════════════════════════════════════════════════════════════
mysql:
image: mysql:8.0
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ MySQL 镜像选择: │
# │ • mysql:8.0 → MySQL 8.0(推荐,性能好) │
# │ • mysql:5.7 → MySQL 5.7(旧项目兼容) │
# │ • mariadb:10.11 → MariaDB(MySQL 开源分支) │
# └─────────────────────────────────────────────────────────────────────────────┘
container_name: mysql
restart: always
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/conf:/etc/mysql/conf.d
- ./mysql/init:/docker-entrypoint-initdb.d
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ 数据卷说明: │
# │ │
# │ ./mysql/data:/var/lib/mysql │
# │ → 数据库文件持久化 │
# │ → 容器删除后数据不丢失 │
# │ → ⚠️ 首次启动会初始化,需要等几分钟 │
# │ │
# │ ./mysql/conf:/etc/mysql/conf.d │
# │ → 自定义 MySQL 配置文件目录 │
# │ → 可以放 my.cnf 配置文件 │
# │ │
# │ ./mysql/init:/docker-entrypoint-initdb.d │
# │ → 初始化脚本目录 │
# │ → 首次启动时自动执行 .sql 或 .sh 文件 │
# │ → 可以放建表 SQL │
# └─────────────────────────────────────────────────────────────────────────────┘
environment:
- TZ=Asia/Shanghai
- MYSQL_ROOT_PASSWORD=MyPassword123!
# ┌───────────────────────────────────────────────────────────────────────────┐
# │【改】MYSQL_ROOT_PASSWORD: root 用户密码 │
# │ • 必须设置! │
# │ • 建议强密码:大小写字母 + 数字 + 符号 │
# │ • 示例:MyP@ssw0rd!2024 │
# └───────────────────────────────────────────────────────────────────────────┘
- MYSQL_DATABASE=mydb
# ┌───────────────────────────────────────────────────────────────────────────┐
# │【改】MYSQL_DATABASE: 自动创建的数据库名 │
# │ • 首次启动时自动创建 │
# │ • 与 Backend 的 SPRING_DATASOURCE_URL 中的数据库名一致 │
# └───────────────────────────────────────────────────────────────────────────┘
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --default-authentication-plugin=mysql_native_password
- --max_connections=500
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ MySQL 启动参数说明: │
# │ │
# │ --character-set-server=utf8mb4 │
# │ → 默认字符集 utf8mb4 │
# │ → 支持中文、emoji │
# │ → utf8mb4 是真正的 UTF-8(utf8 只支持 3 字节,不支持 emoji) │
# │ │
# │ --collation-server=utf8mb4_unicode_ci │
# │ → 排序规则 │
# │ → ci = case insensitive(不区分大小写) │
# │ │
# │ --default-authentication-plugin=mysql_native_password │
# │ → 使用旧版认证方式 │
# │ → 兼容旧版客户端和连接库 │
# │ → MySQL 8 默认用 caching_sha2_password,某些驱动不支持 │
# │ │
# │ --max_connections=500 │
# │ → 最大连接数 │
# │ → 默认 151,生产环境可调大 │
# │ │
# │ 其他常用参数: │
# │ --innodb_buffer_pool_size=1G → InnoDB 缓冲池(内存大可调大) │
# │ --slow_query_log=1 → 开启慢查询日志 │
# │ --long_query_time=2 → 慢查询阈值(秒) │
# └─────────────────────────────────────────────────────────────────────────────┘
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ healthcheck: 健康检查配置 │
# │ │
# │ test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] │
# │ → 检查命令:mysqladmin ping │
# │ → 如果 MySQL 正常运行,返回 "mysqld is alive" │
# │ │
# │ interval: 10s │
# │ → 每 10 秒检查一次 │
# │ │
# │ timeout: 5s │
# │ → 检查超时时间 5 秒 │
# │ │
# │ retries: 5 │
# │ → 连续失败 5 次才认为不健康 │
# │ │
# │ start_period: 30s │
# │ → 启动后 30 秒内的失败不计入重试 │
# │ → MySQL 初始化需要时间 │
# └─────────────────────────────────────────────────────────────────────────────┘
networks:
- app-network
# ═══════════════════════════════════════════════════════════════════════════════
# Redis - 缓存
# ═══════════════════════════════════════════════════════════════════════════════
redis:
image: redis:7-alpine
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Redis 镜像选择: │
# │ • redis:7-alpine → Redis 7 轻量版(推荐) │
# │ • redis:7 → Redis 7 完整版 │
# │ • redis:6-alpine → Redis 6(旧项目) │
# └─────────────────────────────────────────────────────────────────────────────┘
container_name: redis
restart: always
volumes:
- ./redis/data:/data
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ ./redis/data:/data │
# │ → Redis 数据持久化目录 │
# │ → 存储 RDB 快照和 AOF 日志 │
# └─────────────────────────────────────────────────────────────────────────────┘
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ Redis 启动参数说明: │
# │ │
# │ --appendonly yes │
# │ → 开启 AOF 持久化 │
# │ → 每次写操作都记录到日志,数据更安全 │
# │ → 与 RDB 快照配合使用 │
# │ │
# │ --maxmemory 256mb │
# │ → 限制最大内存 256MB │
# │ →【改】根据服务器内存调整: │
# │ 2G 服务器:128mb-256mb │
# │ 4G 服务器:256mb-512mb │
# │ 8G 服务器:512mb-1gb │
# │ │
# │ --maxmemory-policy allkeys-lru │
# │ → 内存满时的淘汰策略 │
# │ → allkeys-lru:淘汰最近最少使用的 key │
# │ → 其他策略: │
# │ volatile-lru → 只淘汰有过期时间的 key │
# │ allkeys-random → 随机淘汰 │
# │ noeviction → 不淘汰,内存满时拒绝写入 │
# │ │
# │ 如果需要密码: │
# │ --requirepass YourRedisPassword │
# │ Backend 也需要配置 SPRING_REDIS_PASSWORD │
# └─────────────────────────────────────────────────────────────────────────────┘
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ 健康检查:redis-cli ping │
# │ → 正常返回 "PONG" │
# └─────────────────────────────────────────────────────────────────────────────┘
networks:
- app-network
networks:
app-network:
driver: bridge
EOF
echo "✅ docker-compose.yml 创建完成!"
四、Nginx 配置
4.1 主配置文件
bash
# ═══════════════════════════════════════════════════════════════════════════════
# 文件位置:/home/deploy/nginx/nginx.conf
# ═══════════════════════════════════════════════════════════════════════════════
mkdir -p /home/deploy/nginx/conf.d
cat > /home/deploy/nginx/nginx.conf << 'EOF'
# Nginx 主配置文件
user nginx;
# 运行 Nginx 的用户
worker_processes auto;
# 工作进程数,auto 自动等于 CPU 核心数
# 举例:2 核 CPU 就是 2 个工作进程
error_log /var/log/nginx/error.log warn;
# 错误日志路径和级别
# 级别:debug, info, notice, warn, error, crit, alert, emerg
pid /var/run/nginx.pid;
# PID 文件位置
events {
worker_connections 10240;
# 每个工作进程的最大连接数
# 总连接数 = worker_processes × worker_connections
# 举例:4核 × 10240 = 40960 并发连接
use epoll;
# Linux 下使用 epoll 事件模型,性能最好
}
http {
include /etc/nginx/mime.types;
# 导入 MIME 类型配置
# 让浏览器正确识别 css、js、图片等文件类型
default_type application/octet-stream;
# 默认 MIME 类型
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time';
# ┌─────────────────────────────────────────────────────────────────────────┐
# │ 日志格式说明: │
# │ $remote_addr → 客户端 IP │
# │ $remote_user → 认证用户名 │
# │ $time_local → 访问时间 │
# │ $request → 请求行(GET /api/xxx HTTP/1.1) │
# │ $status → HTTP 状态码 │
# │ $body_bytes_sent → 响应体大小 │
# │ $http_referer → 来源页面 │
# │ $http_user_agent → 浏览器标识 │
# │ $http_x_forwarded_for → 代理链 IP │
# │ $request_time → 请求处理时间 │
# └─────────────────────────────────────────────────────────────────────────┘
access_log /var/log/nginx/access.log main;
# 访问日志
sendfile on;
# 零拷贝传输,提高静态文件传输效率
tcp_nopush on;
# 优化数据包发送
tcp_nodelay on;
# 禁用 Nagle 算法,减少延迟
keepalive_timeout 65;
# 长连接超时时间(秒)
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript
text/xml application/xml application/xml+rss text/javascript
image/svg+xml;
# ┌─────────────────────────────────────────────────────────────────────────┐
# │ Gzip 压缩配置: │
# │ gzip on → 开启压缩 │
# │ gzip_vary on → 添加 Vary: Accept-Encoding 响应头 │
# │ gzip_min_length → 最小压缩大小,小于 1024 字节不压缩 │
# │ gzip_comp_level → 压缩级别 1-9,6 是性能和压缩率的平衡 │
# │ gzip_types → 要压缩的 MIME 类型 │
# │ │
# │ 效果:减少传输大小,加快加载速度 │
# │ 举例:100KB 的 JS 压缩后可能只有 30KB │
# └─────────────────────────────────────────────────────────────────────────┘
# 导入站点配置
include /etc/nginx/conf.d/*.conf;
}
EOF
4.2 站点配置(HTTPS)
bash
# ═══════════════════════════════════════════════════════════════════════════════
# 文件位置:/home/deploy/nginx/conf.d/default.conf
#
#【改】把所有 example.com 换成你的域名
# ═══════════════════════════════════════════════════════════════════════════════
cat > /home/deploy/nginx/conf.d/default.conf << 'EOF'
# HTTP → HTTPS 重定向
server {
listen 80;
server_name example.com www.example.com;
# 【改】换成你的域名
# Let's Encrypt 证书验证路径(申请证书时需要)
location /.well-known/acme-challenge/ {
root /usr/share/nginx/html;
}
# 其他请求重定向到 HTTPS
location / {
return 301 https://$server_name$request_uri;
# 301 永久重定向
# $server_name 当前域名
# $request_uri 原始请求路径
}
}
# HTTPS 配置
server {
listen 443 ssl http2;
# 443: HTTPS 端口
# ssl: 启用 SSL/TLS
# http2: 启用 HTTP/2 协议(更快)
server_name example.com www.example.com;
# 【改】换成你的域名
# ═══════════════════════════════════════════════════════════════════════════
# SSL 证书配置
# ═══════════════════════════════════════════════════════════════════════════
ssl_certificate /etc/nginx/ssl/example.com.pem;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
# 【改】换成你的证书文件名
ssl_session_timeout 1d;
# SSL 会话缓存超时时间
ssl_session_cache shared:SSL:50m;
# SSL 会话缓存,50MB 可存储约 20 万个会话
ssl_session_tickets off;
# 禁用 session tickets(安全考虑)
ssl_protocols TLSv1.2 TLSv1.3;
# 只允许 TLS 1.2 和 1.3(安全版本)
# 不要启用 TLSv1.0/1.1(有安全漏洞)
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# 加密套件,优先使用安全性高的
ssl_prefer_server_ciphers off;
# 优先使用客户端的加密套件偏好
# ═══════════════════════════════════════════════════════════════════════════
# 安全响应头
# ═══════════════════════════════════════════════════════════════════════════
add_header X-Frame-Options "SAMEORIGIN" always;
# 防止点击劫持,只允许同源 iframe 嵌入
add_header X-Content-Type-Options "nosniff" always;
# 防止 MIME 类型嗅探攻击
add_header X-XSS-Protection "1; mode=block" always;
# 启用 XSS 过滤
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# HSTS:告诉浏览器只用 HTTPS 访问
# max-age=31536000:一年内强制 HTTPS
# includeSubDomains:子域名也强制 HTTPS
# ═══════════════════════════════════════════════════════════════════════════
# 请求限制
# ═══════════════════════════════════════════════════════════════════════════
client_max_body_size 100M;
# 最大请求体大小
# 【改】如果有大文件上传,调大这个值
# 举例:视频网站设置 1G:client_max_body_size 1G;
# ═══════════════════════════════════════════════════════════════════════════
# 前端静态文件
# ═══════════════════════════════════════════════════════════════════════════
location / {
root /usr/share/nginx/html;
# 静态文件根目录
# 对应宿主机:/home/deploy/frontend/dist
index index.html;
# 默认首页
try_files $uri $uri/ /index.html;
# ┌─────────────────────────────────────────────────────────────────────┐
# │ try_files: 文件查找顺序 │
# │ │
# │ $uri → 先找请求的文件(如 /css/style.css) │
# │ $uri/ → 再找目录(如 /about/) │
# │ /index.html → 都没有就返回 index.html │
# │ │
# │ 作用:支持 Vue/React 的前端路由(History 模式) │
# │ 举例: │
# │ 用户访问 /user/123 │
# │ → 服务器没有 /user/123 这个文件 │
# │ → 返回 index.html │
# │ → 前端 JS 根据路由渲染 UserDetail 组件 │
# └─────────────────────────────────────────────────────────────────────┘
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
# 缓存 30 天
add_header Cache-Control "public, immutable";
# public: 可以被代理缓存
# immutable: 文件不会变(前端打包带 hash)
access_log off;
# 静态资源不记录访问日志,减少日志量
}
}
# ═══════════════════════════════════════════════════════════════════════════
# API 反向代理
# ═══════════════════════════════════════════════════════════════════════════
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
# ┌─────────────────────────────────────────────────────────────────────┐
# │ rewrite: URL 重写 │
# │ │
# │ ^/api/(.*)$ → 匹配 /api/ 开头的请求 │
# │ (.*) → 捕获 /api/ 后面的部分 │
# │ /$1 → 重写为捕获的部分 │
# │ break → 停止后续 rewrite │
# │ │
# │ 举例: │
# │ /api/user/list → /user/list │
# │ /api/order/1 → /order/1 │
# │ │
# │ 为什么这样设计: │
# │ • 前端统一用 /api 前缀区分 API 请求 │
# │ • 后端不需要 /api 前缀 │
# │ • Nginx 负责去掉前缀 │
# └─────────────────────────────────────────────────────────────────────┘
proxy_pass http://backend:8080;
# ┌─────────────────────────────────────────────────────────────────────┐
# │ proxy_pass: 反向代理目标 │
# │ │
# │ backend → Docker 服务名,自动解析为容器 IP │
# │ 8080 → 后端服务端口 │
# │ │
# │ 其他写法举例: │
# │ http://127.0.0.1:8080 → 本机 8080 端口 │
# │ http://10.0.0.10:8080 → 指定 IP │
# └─────────────────────────────────────────────────────────────────────┘
proxy_http_version 1.1;
# 使用 HTTP/1.1 协议代理
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;
# ┌─────────────────────────────────────────────────────────────────────┐
# │ proxy_set_header: 设置转发给后端的请求头 │
# │ │
# │ Host $host │
# │ → 原始域名(example.com) │
# │ → 后端可以通过 request.getHeader("Host") 获取 │
# │ │
# │ X-Real-IP $remote_addr │
# │ → 客户端真实 IP │
# │ → 后端可以获取用户真实 IP │
# │ │
# │ X-Forwarded-For $proxy_add_x_forwarded_for │
# │ → 代理链 IP 列表 │
# │ → 格式:客户端IP, 代理1IP, 代理2IP │
# │ │
# │ X-Forwarded-Proto $scheme │
# │ → 原始协议(http 或 https) │
# │ → 后端判断请求是否 HTTPS │
# └─────────────────────────────────────────────────────────────────────┘
proxy_set_header Cookie $http_cookie;
proxy_pass_header Set-Cookie;
# ┌─────────────────────────────────────────────────────────────────────┐
# │ Cookie 转发配置: │
# │ │
# │ proxy_set_header Cookie $http_cookie │
# │ → 把请求中的 Cookie 转发给后端 │
# │ │
# │ proxy_pass_header Set-Cookie │
# │ → 把后端响应的 Set-Cookie 头透传给浏览器 │
# │ │
# │ 作用:支持后端 Session │
# └─────────────────────────────────────────────────────────────────────┘
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# 超时配置(秒)
# connect: 连接后端超时
# send: 发送请求超时
# read: 读取响应超时
}
}
EOF
五、部署步骤
5.1 创建目录结构
bash
mkdir -p /home/deploy/{frontend/dist,backend/logs,nginx/conf.d,nginx/logs,mysql/data,mysql/conf,mysql/init,redis/data,ssl}
5.2 上传项目文件
bash
# 在你的本地电脑执行
# 1. 前端打包
cd 你的前端项目目录
npm install
npm run build
# 2. 上传前端
scp -r dist/* root@你的服务器IP:/home/deploy/frontend/dist/
# 3. 后端打包
cd 你的后端项目目录
mvn clean package -DskipTests
# 4. 上传后端 JAR
scp target/你的项目.jar root@你的服务器IP:/home/deploy/backend/app.jar
# 5. 上传 SSL 证书
scp example.com.pem root@你的服务器IP:/home/deploy/ssl/
scp example.com.key root@你的服务器IP:/home/deploy/ssl/
5.3 启动服务
bash
cd /home/deploy
# 启动
docker compose up -d
# 查看状态
docker compose ps
# 查看日志
docker compose logs -f
六、数据库备份
bash
# ═══════════════════════════════════════════════════════════════════════════════
# 创建备份脚本
# 位置:/home/deploy/backup.sh
# ═══════════════════════════════════════════════════════════════════════════════
cat > /home/deploy/backup.sh << 'EOF'
#!/bin/bash
# 配置
BACKUP_DIR="/home/deploy/backups"
DATE=$(date +%Y%m%d_%H%M%S)
KEEP_DAYS=7 # 保留最近 7 天的备份
# 创建备份目录
mkdir -p $BACKUP_DIR
# 备份 MySQL
docker exec mysql mysqldump -uroot -pMyPassword123! --all-databases | gzip > $BACKUP_DIR/mysql_$DATE.sql.gz
# 【改】把 MyPassword123! 换成你的 MySQL 密码
# 删除旧备份
find $BACKUP_DIR -name "mysql_*.sql.gz" -mtime +$KEEP_DAYS -delete
echo "备份完成: $BACKUP_DIR/mysql_$DATE.sql.gz"
EOF
chmod +x /home/deploy/backup.sh
# 添加定时任务(每天凌晨 3 点备份)
echo "0 3 * * * /home/deploy/backup.sh >> /var/log/backup.log 2>&1" >> /etc/crontab
七、常用命令
bash
cd /home/deploy
# 服务管理
docker compose up -d # 启动
docker compose down # 停止
docker compose restart # 重启全部
docker compose restart backend # 重启单个服务
# 日志查看
docker compose logs -f # 所有日志
docker compose logs -f backend # 后端日志
docker compose logs --tail=100 backend # 最近 100 行
# 进入容器
docker exec -it mysql mysql -uroot -p # 进入 MySQL
docker exec -it redis redis-cli # 进入 Redis
docker exec -it backend bash # 进入后端容器
# 更新部署
# 上传新文件后:
docker compose restart backend # 重启后端
docker compose restart nginx # 重启前端