# 摄影作品集项目部署文档 本文档详细说明了摄影作品集项目的完整部署流程,包括前端、管理后台、后端 API 和数据库的部署配置。 ## 📋 目录 - [系统要求](#系统要求) - [环境配置](#环境配置) - [后端部署](#后端部署) - [前端部署](#前端部署) - [管理后台部署](#管理后台部署) - [数据库配置](#数据库配置) - [Web 服务器配置](#web-服务器配置) - [SSL 证书配置](#ssl-证书配置) - [监控和备份](#监控和备份) - [故障排除](#故障排除) ## 🖥️ 系统要求 ### 硬件要求 - **CPU**: 2 核心以上 - **内存**: 4GB 以上 (推荐 8GB) - **存储**: 20GB 以上 SSD 存储 - **网络**: 稳定的互联网连接 ### 软件要求 - **操作系统**: Ubuntu 20.04 LTS / CentOS 8 / Debian 11 - **Docker**: 20.10 或更高版本 - **Docker Compose**: 2.0 或更高版本 - **Git**: 2.20 或更高版本 - **域名**: 用于生产环境部署 ## ⚙️ 环境配置 ### 1. 安装 Docker 和 Docker Compose ```bash # Ubuntu/Debian curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh sudo usermod -aG docker $USER # 安装 Docker Compose sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose # 验证安装 docker --version docker-compose --version ``` ### 2. 克隆项目仓库 ```bash git clone photography cd photography ``` ### 3. 创建环境变量文件 ```bash # 复制环境变量模板 cp .env.example .env # 编辑环境变量 nano .env ``` ## 🚀 后端部署 ### 1. 后端 Docker 配置 查看后端 Dockerfile: ```dockerfile # backend/Dockerfile FROM golang:1.21-alpine AS builder # 设置工作目录 WORKDIR /app # 安装依赖 RUN apk add --no-cache git ca-certificates tzdata # 复制 go mod 文件 COPY go.mod go.sum ./ RUN go mod download # 复制源代码 COPY . . # 构建应用 RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main cmd/server/main.go # 生产环境镜像 FROM alpine:latest # 安装必要的包 RUN apk --no-cache add ca-certificates wget # 创建用户 RUN addgroup -S appgroup && adduser -S appuser -G appgroup # 设置工作目录 WORKDIR /app # 从构建阶段复制文件 COPY --from=builder /app/main . COPY --from=builder /app/configs ./configs COPY --from=builder /app/migrations ./migrations # 创建必要的目录 RUN mkdir -p uploads logs && chown -R appuser:appgroup /app # 切换到非 root 用户 USER appuser # 暴露端口 EXPOSE 8080 # 健康检查 HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 # 启动命令 CMD ["./main"] ``` ### 2. 后端 Docker Compose 服务 ```yaml # docker-compose.yml - 后端部分 services: # PostgreSQL 数据库 postgres: image: postgres:15-alpine container_name: photography_postgres restart: unless-stopped environment: POSTGRES_DB: ${DB_NAME:-photography} POSTGRES_USER: ${DB_USER:-postgres} POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C" volumes: - postgres_data:/var/lib/postgresql/data - ./backend/migrations:/docker-entrypoint-initdb.d ports: - "127.0.0.1:5432:5432" networks: - photography_network healthcheck: test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-photography}"] interval: 30s timeout: 10s retries: 5 # Redis 缓存 redis: image: redis:7-alpine container_name: photography_redis restart: unless-stopped command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} volumes: - redis_data:/data ports: - "127.0.0.1:6379:6379" networks: - photography_network healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 30s timeout: 10s retries: 5 # 后端 API 服务 backend: build: context: ./backend dockerfile: Dockerfile container_name: photography_backend restart: unless-stopped depends_on: postgres: condition: service_healthy redis: condition: service_healthy environment: # 数据库配置 DB_HOST: postgres DB_PORT: 5432 DB_NAME: ${DB_NAME:-photography} DB_USER: ${DB_USER:-postgres} DB_PASSWORD: ${DB_PASSWORD} # Redis 配置 REDIS_HOST: redis REDIS_PORT: 6379 REDIS_PASSWORD: ${REDIS_PASSWORD} # JWT 配置 JWT_SECRET: ${JWT_SECRET} JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-24h} # 服务器配置 PORT: 8080 GIN_MODE: release # 文件存储配置 STORAGE_TYPE: ${STORAGE_TYPE:-local} STORAGE_PATH: /app/uploads MAX_UPLOAD_SIZE: ${MAX_UPLOAD_SIZE:-10MB} # 日志配置 LOG_LEVEL: ${LOG_LEVEL:-info} LOG_FORMAT: json volumes: - ./backend/uploads:/app/uploads - ./backend/logs:/app/logs - ./backend/configs:/app/configs ports: - "127.0.0.1:8080:8080" networks: - photography_network healthcheck: test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/health"] interval: 30s timeout: 10s retries: 5 start_period: 60s volumes: postgres_data: driver: local redis_data: driver: local networks: photography_network: driver: bridge ipam: config: - subnet: 172.20.0.0/16 ``` ### 3. 启动后端服务 ```bash # 构建并启动后端服务 docker-compose up -d postgres redis docker-compose up -d backend # 查看日志 docker-compose logs -f backend # 验证服务状态 curl http://localhost:8080/health ``` ## 🌐 前端部署 ### 1. 前端构建配置 ```bash # 进入前端目录 cd frontend/ # 安装依赖 bun install # 构建生产版本 bun run build # 验证构建结果 ls -la out/ ``` ### 2. 前端部署脚本 创建前端部署脚本: ```bash #!/bin/bash # scripts/deploy-frontend.sh set -e echo "🚀 开始部署前端..." # 配置变量 FRONTEND_DIR="/home/gitea/www/photography" BACKUP_DIR="/home/gitea/backups/photography-frontend" BUILD_DIR="./frontend/out" # 创建备份 echo "📦 创建当前版本备份..." mkdir -p "$BACKUP_DIR" if [ -d "$FRONTEND_DIR" ]; then tar -czf "$BACKUP_DIR/frontend-$(date +%Y%m%d-%H%M%S).tar.gz" -C "$FRONTEND_DIR" . fi # 创建部署目录 echo "📁 准备部署目录..." mkdir -p "$FRONTEND_DIR" # 部署新版本 echo "🔄 部署新版本..." rsync -av --delete "$BUILD_DIR/" "$FRONTEND_DIR/" # 设置权限 echo "🔐 设置文件权限..." chown -R gitea:gitea "$FRONTEND_DIR" chmod -R 755 "$FRONTEND_DIR" # 清理旧备份 (保留最近5个) echo "🧹 清理旧备份..." cd "$BACKUP_DIR" ls -t frontend-*.tar.gz | tail -n +6 | xargs -r rm echo "✅ 前端部署完成!" echo "🌐 访问地址: https://photography.iriver.top" ``` ## 🛠️ 管理后台部署 ### 1. 管理后台构建配置 ```bash # 进入管理后台目录 cd admin/ # 安装依赖 npm install # 构建生产版本 npm run build # 验证构建结果 ls -la dist/ ``` ### 2. 管理后台部署脚本 ```bash #!/bin/bash # scripts/deploy-admin.sh set -e echo "🚀 开始部署管理后台..." # 配置变量 ADMIN_DIR="/home/gitea/www/photography-admin" BACKUP_DIR="/home/gitea/backups/photography-admin" BUILD_DIR="./admin/dist" # 创建备份 echo "📦 创建当前版本备份..." mkdir -p "$BACKUP_DIR" if [ -d "$ADMIN_DIR" ]; then tar -czf "$BACKUP_DIR/admin-$(date +%Y%m%d-%H%M%S).tar.gz" -C "$ADMIN_DIR" . fi # 创建部署目录 echo "📁 准备部署目录..." mkdir -p "$ADMIN_DIR" # 部署新版本 echo "🔄 部署新版本..." rsync -av --delete "$BUILD_DIR/" "$ADMIN_DIR/" # 设置权限 echo "🔐 设置文件权限..." chown -R gitea:gitea "$ADMIN_DIR" chmod -R 755 "$ADMIN_DIR" # 清理旧备份 (保留最近5个) echo "🧹 清理旧备份..." cd "$BACKUP_DIR" ls -t admin-*.tar.gz | tail -n +6 | xargs -r rm echo "✅ 管理后台部署完成!" echo "🌐 访问地址: https://admin.photography.iriver.top" ``` ## 🗄️ 数据库配置 ### 1. 数据库初始化 ```sql -- 创建数据库 CREATE DATABASE photography; CREATE USER photography_user WITH ENCRYPTED PASSWORD 'your_password'; GRANT ALL PRIVILEGES ON DATABASE photography TO photography_user; -- 使用数据库 \c photography; -- 创建必要的扩展 CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 设置时区 SET timezone = 'Asia/Shanghai'; ``` ### 2. 数据库备份脚本 ```bash #!/bin/bash # scripts/backup.sh set -e # 配置变量 DB_HOST="${DB_HOST:-postgres}" DB_NAME="${DB_NAME:-photography}" DB_USER="${DB_USER:-postgres}" BACKUP_DIR="/backups" DATE=$(date +%Y%m%d-%H%M%S) echo "🗄️ 开始数据库备份..." # 创建备份目录 mkdir -p "$BACKUP_DIR" # 执行备份 pg_dump -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" \ --no-password --verbose --clean --no-acl --no-owner \ -f "$BACKUP_DIR/photography-$DATE.sql" # 压缩备份文件 gzip "$BACKUP_DIR/photography-$DATE.sql" # 清理旧备份 (保留最近7天) find "$BACKUP_DIR" -name "photography-*.sql.gz" -mtime +7 -delete echo "✅ 数据库备份完成: photography-$DATE.sql.gz" ``` ## 🌐 Web 服务器配置 ### Caddy 配置文件 ```caddyfile # docs/deployment/Caddyfile # 前端网站 photography.iriver.top { # 根目录 root * /home/gitea/www/photography # 静态文件服务 file_server # SPA 路由支持 try_files {path} /index.html # 压缩 encode gzip # 缓存配置 header { # 静态资源缓存 Cache-Control "public, max-age=31536000" { path *.js *.css *.png *.jpg *.jpeg *.gif *.ico *.woff *.woff2 } # HTML 文件不缓存 Cache-Control "no-cache" { path *.html } # 安全头 X-Frame-Options "DENY" X-Content-Type-Options "nosniff" Referrer-Policy "strict-origin-when-cross-origin" } # 日志 log { output file /var/log/caddy/photography.log format json } } # 管理后台 admin.photography.iriver.top { # 根目录 root * /home/gitea/www/photography-admin # 静态文件服务 file_server # SPA 路由支持 try_files {path} /index.html # 压缩 encode gzip # 基本认证 (可选) # basicauth { # admin $2a$14$encrypted_password_hash # } # 缓存配置 header { Cache-Control "public, max-age=31536000" { path *.js *.css *.png *.jpg *.jpeg *.gif *.ico *.woff *.woff2 } Cache-Control "no-cache" { path *.html } X-Frame-Options "SAMEORIGIN" X-Content-Type-Options "nosniff" } # 日志 log { output file /var/log/caddy/admin.photography.log format json } } # API 服务 api.photography.iriver.top { # 反向代理到后端服务 reverse_proxy localhost:8080 { # 健康检查 health_uri /health health_interval 30s health_timeout 10s # 请求头 header_up Host {upstream_hostport} header_up X-Real-IP {remote_host} header_up X-Forwarded-Proto {scheme} header_up X-Forwarded-For {remote_host} } # CORS 处理 header { Access-Control-Allow-Origin "https://photography.iriver.top, https://admin.photography.iriver.top" Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" Access-Control-Allow-Headers "Content-Type, Authorization" Access-Control-Max-Age "86400" } # 预检请求处理 @options method OPTIONS respond @options 204 # 上传文件大小限制 request_body { max_size 10MB } # 日志 log { output file /var/log/caddy/api.photography.log format json } } # 全局配置 { # 自动 HTTPS auto_https on # Let's Encrypt 邮箱 email admin@iriver.top # 日志级别 log { level INFO } } ``` ## 🔒 SSL 证书配置 Caddy 会自动处理 SSL 证书,但也可以手动配置: ```bash # 手动获取证书 (如果需要) certbot certonly --webroot -w /var/www/html -d photography.iriver.top -d admin.photography.iriver.top -d api.photography.iriver.top # 设置自动续期 echo "0 3 * * * certbot renew --quiet && systemctl reload caddy" | crontab - ``` ## 📊 监控和备份 ### 1. 系统监控脚本 ```bash #!/bin/bash # scripts/monitor.sh # 检查服务状态 check_service() { local service=$1 local url=$2 echo "检查 $service..." if curl -f -s "$url" > /dev/null; then echo "✅ $service 正常运行" else echo "❌ $service 服务异常" # 发送告警 (可集成邮件、微信等) # send_alert "$service 服务异常" fi } # 检查磁盘空间 check_disk() { local usage=$(df / | tail -1 | awk '{print $5}' | sed 's/%//') echo "磁盘使用率: $usage%" if [ "$usage" -gt 80 ]; then echo "⚠️ 磁盘空间不足" # send_alert "磁盘空间不足: $usage%" fi } # 检查内存使用 check_memory() { local usage=$(free | grep Mem | awk '{printf "%.1f", $3/$2 * 100.0}') echo "内存使用率: $usage%" } # 执行检查 echo "🔍 开始系统监控..." check_service "前端" "https://photography.iriver.top" check_service "管理后台" "https://admin.photography.iriver.top" check_service "API" "https://api.photography.iriver.top/health" check_disk check_memory echo "✅ 监控检查完成" ``` ### 2. 自动备份任务 ```bash # 设置定时任务 crontab -e # 添加以下内容: # 每天凌晨2点备份数据库 0 2 * * * /path/to/photography/scripts/backup.sh # 每周日凌晨3点重启服务 (可选) 0 3 * * 0 cd /path/to/photography && docker-compose restart # 每小时检查一次服务状态 0 * * * * /path/to/photography/scripts/monitor.sh ``` ## 🐛 故障排除 ### 常见问题和解决方案 #### 1. 后端服务无法启动 ```bash # 查看日志 docker-compose logs backend # 检查配置 docker-compose config # 重新构建 docker-compose build backend --no-cache ``` #### 2. 数据库连接失败 ```bash # 检查数据库状态 docker-compose ps postgres # 检查数据库日志 docker-compose logs postgres # 手动连接测试 docker-compose exec postgres psql -U postgres -d photography ``` #### 3. 前端/管理后台访问异常 ```bash # 检查文件权限 ls -la /home/gitea/www/photography/ ls -la /home/gitea/www/photography-admin/ # 检查 Caddy 配置 caddy validate --config /etc/caddy/Caddyfile # 重新加载 Caddy systemctl reload caddy ``` #### 4. SSL 证书问题 ```bash # 检查证书状态 curl -I https://photography.iriver.top # 查看 Caddy 日志 tail -f /var/log/caddy/photography.log # 手动重新申请证书 systemctl stop caddy caddy run --config /etc/caddy/Caddyfile ``` ### 日志文件位置 - **后端日志**: `./backend/logs/` - **Caddy 日志**: `/var/log/caddy/` - **数据库日志**: `docker-compose logs postgres` - **系统日志**: `/var/log/syslog` ## 🚀 部署命令速查 ```bash # 完整部署流程 git pull origin main cd photography # 后端部署 docker-compose build backend docker-compose up -d # 前端部署 cd frontend && bun run build && cd .. ./scripts/deploy-frontend.sh # 管理后台部署 cd admin && npm run build && cd .. ./scripts/deploy-admin.sh # 重启 Web 服务器 sudo systemctl reload caddy # 检查服务状态 docker-compose ps curl https://photography.iriver.top curl https://admin.photography.iriver.top curl https://api.photography.iriver.top/health ``` ## 📞 技术支持 如果在部署过程中遇到问题,请: 1. 查看相关日志文件 2. 检查系统资源使用情况 3. 验证配置文件语法 4. 联系技术支持团队 --- 📝 **文档版本**: v1.0 🕒 **最后更新**: 2024年 👥 **维护团队**: 摄影作品集项目组