集成docker

This commit is contained in:
ZTMYO
2025-09-05 11:59:33 +08:00
parent dc7e4ffd3c
commit 4b064be4ba
11 changed files with 907 additions and 4 deletions

31
.env.docker Normal file
View File

@@ -0,0 +1,31 @@
# 小石榴图文社区 Docker 环境配置
# 复制此文件为 .env 并根据实际情况修改配置
# 数据库配置
DB_HOST=mysql
DB_USER=xiaoshiliu_user
DB_PASSWORD=123456
DB_NAME=xiaoshiliu
DB_PORT=3306
# JWT配置
JWT_SECRET=xiaoshiliu_secret_key_2025_docker
JWT_EXPIRES_IN=7d
REFRESH_TOKEN_EXPIRES_IN=30d
# 上传配置
UPLOAD_MAX_SIZE=50mb
# API配置
API_BASE_URL=http://localhost:3001
# 前端构建配置
VITE_API_BASE_URL=http://localhost:3001/api
# 服务端口配置
FRONTEND_PORT=80
BACKEND_PORT=3001
DB_PORT_EXTERNAL=3306
# 生产环境标识
NODE_ENV=production

158
deploy.ps1 Normal file
View File

@@ -0,0 +1,158 @@
#!/usr/bin/env pwsh
# 小石榴图文社区 Docker 一键部署脚本
# PowerShell 版本
param(
[switch]$Build,
[switch]$Stop,
[switch]$Clean,
[switch]$Logs,
[switch]$Status,
[switch]$Help
)
# 颜色输出函数
function Write-ColorOutput {
param(
[string]$Message,
[string]$Color = "White"
)
Write-Host $Message -ForegroundColor $Color
}
# 显示帮助信息
function Show-Help {
Write-ColorOutput "小石榴图文社区 Docker 部署脚本" "Cyan"
Write-ColorOutput "用法: .\deploy.ps1 [选项]" "Yellow"
Write-ColorOutput ""
Write-ColorOutput "选项:" "Green"
Write-ColorOutput " -Build 强制重新构建镜像并启动服务" "White"
Write-ColorOutput " -Stop 停止所有服务" "White"
Write-ColorOutput " -Clean 清理所有容器、镜像和数据卷" "White"
Write-ColorOutput " -Logs 查看服务日志" "White"
Write-ColorOutput " -Status 查看服务状态" "White"
Write-ColorOutput " -Help 显示此帮助信息" "White"
Write-ColorOutput ""
Write-ColorOutput "示例:" "Green"
Write-ColorOutput " .\deploy.ps1 # 启动服务" "White"
Write-ColorOutput " .\deploy.ps1 -Build # 重新构建并启动" "White"
Write-ColorOutput " .\deploy.ps1 -Stop # 停止服务" "White"
}
# 检查Docker是否安装
function Test-Docker {
try {
docker --version | Out-Null
docker-compose --version | Out-Null
return $true
}
catch {
Write-ColorOutput "错误: 未找到 Docker 或 Docker Compose" "Red"
Write-ColorOutput "请先安装 Docker Desktop: https://www.docker.com/products/docker-desktop" "Yellow"
return $false
}
}
# 检查环境变量文件
function Test-EnvFile {
if (-not (Test-Path ".env")) {
if (Test-Path ".env.docker") {
Write-ColorOutput "复制 .env.docker 到 .env" "Yellow"
Copy-Item ".env.docker" ".env"
} else {
Write-ColorOutput "警告: 未找到 .env 文件,将使用默认配置" "Yellow"
}
}
}
# 启动服务
function Start-Services {
Write-ColorOutput "启动小石榴图文社区服务..." "Green"
if ($Build) {
Write-ColorOutput "重新构建镜像..." "Yellow"
docker-compose down
docker-compose build --no-cache
}
docker-compose up -d
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "服务启动成功!" "Green"
Write-ColorOutput ""
Write-ColorOutput "访问地址:" "Cyan"
Write-ColorOutput " 前端: http://localhost" "White"
Write-ColorOutput " 后端API: http://localhost:3001" "White"
Write-ColorOutput " 数据库: localhost:3306" "White"
Write-ColorOutput ""
Write-ColorOutput "使用 '.\deploy.ps1 -Logs' 查看日志" "Yellow"
Write-ColorOutput "使用 '.\deploy.ps1 -Status' 查看服务状态" "Yellow"
} else {
Write-ColorOutput "服务启动失败!" "Red"
Write-ColorOutput "请检查日志: docker-compose logs" "Yellow"
}
}
# 停止服务
function Stop-Services {
Write-ColorOutput "停止小石榴图文社区服务..." "Yellow"
docker-compose down
if ($LASTEXITCODE -eq 0) {
Write-ColorOutput "服务已停止" "Green"
} else {
Write-ColorOutput "停止服务时出现错误" "Red"
}
}
# 清理资源
function Clean-Resources {
Write-ColorOutput "警告: 此操作将删除所有容器、镜像和数据!" "Red"
$confirm = Read-Host "确认继续? (y/N)"
if ($confirm -eq "y" -or $confirm -eq "Y") {
Write-ColorOutput "清理Docker资源..." "Yellow"
docker-compose down -v --rmi all
docker system prune -f
Write-ColorOutput "清理完成" "Green"
} else {
Write-ColorOutput "操作已取消" "Yellow"
}
}
# 查看日志
function Show-Logs {
Write-ColorOutput "查看服务日志 (按 Ctrl+C 退出):" "Cyan"
docker-compose logs -f
}
# 查看状态
function Show-Status {
Write-ColorOutput "服务状态:" "Cyan"
docker-compose ps
Write-ColorOutput ""
Write-ColorOutput "资源使用情况:" "Cyan"
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
}
# 主逻辑
if ($Help) {
Show-Help
exit 0
}
if (-not (Test-Docker)) {
exit 1
}
Test-EnvFile
switch ($true) {
$Stop { Stop-Services }
$Clean { Clean-Resources }
$Logs { Show-Logs }
$Status { Show-Status }
default { Start-Services }
}
Write-ColorOutput "操作完成" "Green"

173
deploy.sh Normal file
View File

@@ -0,0 +1,173 @@
#!/bin/bash
# 小石榴图文社区 Docker 一键部署脚本
# Bash 版本
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 显示帮助信息
show_help() {
echo -e "${CYAN}小石榴图文社区 Docker 部署脚本${NC}"
echo -e "${YELLOW}用法: ./deploy.sh [选项]${NC}"
echo ""
echo -e "${GREEN}选项:${NC}"
echo -e " --build 强制重新构建镜像并启动服务"
echo -e " --stop 停止所有服务"
echo -e " --clean 清理所有容器、镜像和数据卷"
echo -e " --logs 查看服务日志"
echo -e " --status 查看服务状态"
echo -e " --help 显示此帮助信息"
echo ""
echo -e "${GREEN}示例:${NC}"
echo -e " ./deploy.sh # 启动服务"
echo -e " ./deploy.sh --build # 重新构建并启动"
echo -e " ./deploy.sh --stop # 停止服务"
}
# 检查Docker是否安装
check_docker() {
if ! command -v docker &> /dev/null; then
echo -e "${RED}错误: 未找到 Docker${NC}"
echo -e "${YELLOW}请先安装 Docker: https://docs.docker.com/get-docker/${NC}"
exit 1
fi
if ! command -v docker-compose &> /dev/null; then
echo -e "${RED}错误: 未找到 Docker Compose${NC}"
echo -e "${YELLOW}请先安装 Docker Compose: https://docs.docker.com/compose/install/${NC}"
exit 1
fi
}
# 检查环境变量文件
check_env_file() {
if [ ! -f ".env" ]; then
if [ -f ".env.docker" ]; then
echo -e "${YELLOW}复制 .env.docker 到 .env${NC}"
cp .env.docker .env
else
echo -e "${YELLOW}警告: 未找到 .env 文件,将使用默认配置${NC}"
fi
fi
}
# 启动服务
start_services() {
echo -e "${GREEN}启动小石榴图文社区服务...${NC}"
if [ "$1" = "--build" ]; then
echo -e "${YELLOW}重新构建镜像...${NC}"
docker-compose down
docker-compose build --no-cache
fi
docker-compose up -d
if [ $? -eq 0 ]; then
echo -e "${GREEN}服务启动成功!${NC}"
echo ""
echo -e "${CYAN}访问地址:${NC}"
echo -e " 前端: http://localhost"
echo -e " 后端API: http://localhost:3001"
echo -e " 数据库: localhost:3306"
echo ""
echo -e "${YELLOW}使用 './deploy.sh --logs' 查看日志${NC}"
echo -e "${YELLOW}使用 './deploy.sh --status' 查看服务状态${NC}"
else
echo -e "${RED}服务启动失败!${NC}"
echo -e "${YELLOW}请检查日志: docker-compose logs${NC}"
exit 1
fi
}
# 停止服务
stop_services() {
echo -e "${YELLOW}停止小石榴图文社区服务...${NC}"
docker-compose down
if [ $? -eq 0 ]; then
echo -e "${GREEN}服务已停止${NC}"
else
echo -e "${RED}停止服务时出现错误${NC}"
exit 1
fi
}
# 清理资源
clean_resources() {
echo -e "${RED}警告: 此操作将删除所有容器、镜像和数据!${NC}"
read -p "确认继续? (y/N): " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}清理Docker资源...${NC}"
docker-compose down -v --rmi all
docker system prune -f
echo -e "${GREEN}清理完成${NC}"
else
echo -e "${YELLOW}操作已取消${NC}"
fi
}
# 查看日志
show_logs() {
echo -e "${CYAN}查看服务日志 (按 Ctrl+C 退出):${NC}"
docker-compose logs -f
}
# 查看状态
show_status() {
echo -e "${CYAN}服务状态:${NC}"
docker-compose ps
echo ""
echo -e "${CYAN}资源使用情况:${NC}"
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
}
# 主逻辑
case "$1" in
--help)
show_help
exit 0
;;
--stop)
check_docker
stop_services
;;
--clean)
check_docker
clean_resources
;;
--logs)
check_docker
show_logs
;;
--status)
check_docker
show_status
;;
--build)
check_docker
check_env_file
start_services --build
;;
"")
check_docker
check_env_file
start_services
;;
*)
echo -e "${RED}未知选项: $1${NC}"
show_help
exit 1
;;
esac
echo -e "${GREEN}操作完成${NC}"

View File

@@ -2,6 +2,204 @@
本文档详细介绍了小石榴图文社区项目的部署流程和配置说明。
## 部署方式
项目支持两种部署方式:
1. **Docker 一键部署**(推荐)- 简单快速,适合生产环境
2. **传统部署** - 手动配置,适合开发环境
---
## 🐳 Docker 一键部署(推荐)
### 环境要求
- Docker >= 20.0
- Docker Compose >= 2.0
- 可用内存 >= 2GB
- 可用磁盘空间 >= 5GB
### 快速开始
#### 1. 克隆项目
```bash
git clone <项目地址>
cd XiaoShiLiu
```
#### 2. 配置环境变量(可选)
```bash
# 复制环境配置文件
cp .env.docker .env
# 编辑配置文件(可选)
# 默认配置已经可以直接使用
```
#### 3. 一键启动
**Windows 用户:**
```powershell
# 启动服务
.\deploy.ps1
# 重新构建并启动
.\deploy.ps1 -Build
# 查看服务状态
.\deploy.ps1 -Status
# 查看日志
.\deploy.ps1 -Logs
# 停止服务
.\deploy.ps1 -Stop
```
**Linux/macOS 用户:**
```bash
# 给脚本执行权限
chmod +x deploy.sh
# 启动服务
./deploy.sh
# 重新构建并启动
./deploy.sh --build
# 查看服务状态
./deploy.sh --status
# 查看日志
./deploy.sh --logs
# 停止服务
./deploy.sh --stop
```
#### 4. 访问应用
服务启动成功后,可以通过以下地址访问:
| 服务 | 地址 | 说明 |
|------|------|------|
| 前端界面 | http://localhost | 主要访问入口 |
| 后端API | http://localhost:3001 | API接口 |
| 数据库 | localhost:3306 | MySQL数据库 |
### Docker 部署架构
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │ │ Backend │ │ MySQL │
│ (Nginx) │◄───┤ (Express) │◄───┤ (Database) │
│ Port: 80 │ │ Port: 3001 │ │ Port: 3306 │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### 环境变量配置
项目使用 `.env` 文件进行配置,主要配置项:
```env
# 数据库配置
DB_HOST=mysql
DB_USER=xiaoshiliu_user
DB_PASSWORD=123456
DB_NAME=xiaoshiliu
# JWT配置
JWT_SECRET=xiaoshiliu_secret_key_2025_docker
JWT_EXPIRES_IN=7d
# 上传配置
UPLOAD_MAX_SIZE=50mb
# API配置
API_BASE_URL=http://localhost:3001
```
### 常用命令
```bash
# 查看服务状态
docker-compose ps
# 查看服务日志
docker-compose logs -f
# 重启特定服务
docker-compose restart backend
# 进入容器
docker-compose exec backend bash
docker-compose exec mysql mysql -u root -p
# 备份数据库
docker-compose exec mysql mysqldump -u root -p xiaoshiliu > backup.sql
# 恢复数据库
docker-compose exec -T mysql mysql -u root -p xiaoshiliu < backup.sql
```
### 数据持久化
Docker 部署使用数据卷进行数据持久化:
- `mysql_data`: MySQL 数据库文件
- `backend_uploads`: 后端上传文件
### 故障排除
#### 1. 端口冲突
如果遇到端口冲突,可以修改 `docker-compose.yml` 中的端口映射:
```yaml
services:
frontend:
ports:
- "8080:80" # 修改前端端口
backend:
ports:
- "3002:3001" # 修改后端端口
```
#### 2. 内存不足
确保系统有足够的内存,可以通过以下命令查看资源使用:
```bash
docker stats
```
#### 3. 数据库连接失败
检查数据库服务是否正常启动:
```bash
docker-compose logs mysql
```
#### 4. 清理和重置
如果遇到问题需要重新开始:
```bash
# Windows
.\deploy.ps1 -Clean
# Linux/macOS
./deploy.sh --clean
```
---
## 📋 传统部署方式
## 环境要求
| 组件 | 版本要求 | 说明 |

93
docker-compose.yml Normal file
View File

@@ -0,0 +1,93 @@
version: '3.8'
services:
# MySQL数据库服务
mysql:
image: mysql:8.0
container_name: xiaoshiliu-mysql
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-123456}
MYSQL_DATABASE: ${DB_NAME:-xiaoshiliu}
MYSQL_USER: ${DB_USER:-xiaoshiliu_user}
MYSQL_PASSWORD: ${DB_PASSWORD:-123456}
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
- ./express-project/scripts/init-database.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- xiaoshiliu-network
command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
timeout: 20s
retries: 10
# 后端API服务
backend:
build:
context: ./express-project
dockerfile: Dockerfile
container_name: xiaoshiliu-backend
restart: unless-stopped
environment:
NODE_ENV: production
PORT: 3001
DB_HOST: mysql
DB_USER: ${DB_USER:-xiaoshiliu_user}
DB_PASSWORD: ${DB_PASSWORD:-123456}
DB_NAME: ${DB_NAME:-xiaoshiliu}
DB_PORT: 3306
JWT_SECRET: ${JWT_SECRET:-xiaoshiliu_secret_key_2025}
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-7d}
REFRESH_TOKEN_EXPIRES_IN: ${REFRESH_TOKEN_EXPIRES_IN:-30d}
API_BASE_URL: http://localhost:3001
UPLOAD_MAX_SIZE: ${UPLOAD_MAX_SIZE:-50mb}
ports:
- "3001:3001"
volumes:
- backend_uploads:/app/uploads
networks:
- xiaoshiliu-network
depends_on:
mysql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# 前端Web服务
frontend:
build:
context: ./vue3-project
dockerfile: Dockerfile
args:
VITE_API_BASE_URL: http://localhost:3001/api
container_name: xiaoshiliu-frontend
restart: unless-stopped
ports:
- "80:80"
networks:
- xiaoshiliu-network
depends_on:
backend:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/"]
interval: 30s
timeout: 10s
retries: 3
volumes:
mysql_data:
driver: local
backend_uploads:
driver: local
networks:
xiaoshiliu-network:
driver: bridge

View File

@@ -0,0 +1,70 @@
# 依赖目录
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# 运行时文件
*.log
logs
*.pid
*.seed
*.pid.lock
# 覆盖率目录
coverage
.nyc_output
# 环境变量文件
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDE文件
.vscode
.idea
*.swp
*.swo
*~
# 操作系统文件
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Git文件
.git
.gitignore
# Docker文件
Dockerfile
.dockerignore
docker-compose*.yml
# 文档文件
README.md
*.md
# 测试文件
test
tests
__tests__
*.test.js
*.spec.js
# 临时文件
tmp
temp
# 上传文件目录(在容器中会重新挂载)
uploads
# 备份文件
*.bak
*.backup

View File

@@ -0,0 +1,32 @@
# 使用官方Node.js运行时作为基础镜像
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm ci --only=production
# 复制应用源代码
COPY . .
# 创建非root用户
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nodejs -u 1001
# 更改文件所有权
RUN chown -R nodejs:nodejs /app
USER nodejs
# 暴露端口
EXPOSE 3001
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3001/api/health || exit 1
# 启动应用
CMD ["npm", "start"]

View File

@@ -748,7 +748,7 @@ const tagsCrudConfig = {
// 生成标签CRUD处理器
const tagsHandlers = createCrudHandlers(tagsCrudConfig)
// 标签路由240行代码减少到6行
// 标签路由
router.post('/tags', adminAuth, tagsHandlers.create)
router.put('/tags/:id', adminAuth, tagsHandlers.update)
router.delete('/tags/:id', adminAuth, tagsHandlers.deleteOne)
@@ -881,7 +881,7 @@ router.get('/test-users', adminAuth, async (req, res) => {
}
})
// 点赞路由300行代码减少到6行
// 点赞路由
router.post('/likes', adminAuth, likesHandlers.create)
router.put('/likes/:id', adminAuth, likesHandlers.update)
router.delete('/likes/:id', adminAuth, likesHandlers.deleteOne)
@@ -1041,7 +1041,7 @@ const collectionsCrudConfig = {
// 生成收藏CRUD处理器
const collectionsHandlers = createCrudHandlers(collectionsCrudConfig)
// 收藏路由268行代码减少到6行
// 收藏路由
router.post('/collections', adminAuth, collectionsHandlers.create)
router.put('/collections/:id', adminAuth, collectionsHandlers.update)
router.delete('/collections/:id', adminAuth, collectionsHandlers.deleteOne)
@@ -1196,7 +1196,7 @@ const followsCrudConfig = {
// 生成关注CRUD处理器
const followsHandlers = createCrudHandlers(followsCrudConfig)
// 关注路由291行代码减少到6行
// 关注路由
router.post('/follows', adminAuth, followsHandlers.create)
router.put('/follows/:id', adminAuth, followsHandlers.update)
router.delete('/follows/:id', adminAuth, followsHandlers.deleteOne)

View File

@@ -0,0 +1,72 @@
# 依赖目录
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# 构建输出
dist
dist-ssr
*.local
# 环境变量文件
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDE文件
.vscode
.idea
*.swp
*.swo
*~
# 操作系统文件
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Git文件
.git
.gitignore
# Docker文件
Dockerfile
.dockerignore
docker-compose*.yml
# 文档文件
README.md
*.md
# 测试文件
test
tests
__tests__
*.test.js
*.spec.js
cypress
e2e
# 临时文件
tmp
temp
# Vite缓存
.vite
# 备份文件
*.bak
*.backup
# 编辑器配置
.editorconfig
.eslintrc*
.prettierrc*

36
vue3-project/Dockerfile Normal file
View File

@@ -0,0 +1,36 @@
# 多阶段构建:构建阶段
FROM node:18-alpine as build-stage
# 设置工作目录
WORKDIR /app
# 复制package.json和package-lock.json
COPY package*.json ./
# 安装依赖
RUN npm ci
# 复制源代码
COPY . .
# 构建应用
RUN npm run build
# 生产阶段使用nginx服务静态文件
FROM nginx:alpine as production-stage
# 复制构建产物到nginx目录
COPY --from=build-stage /app/dist /usr/share/nginx/html
# 复制nginx配置文件
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 暴露端口
EXPOSE 80
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/ || exit 1
# 启动nginx
CMD ["nginx", "-g", "daemon off;"]

40
vue3-project/nginx.conf Normal file
View File

@@ -0,0 +1,40 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# 启用gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied expired no-cache no-store private must-revalidate auth;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# 处理Vue Router的history模式
location / {
try_files $uri $uri/ /index.html;
}
# API代理到后端服务
location /api/ {
proxy_pass http://backend:3001;
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;
}
# 静态资源缓存
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
}