Files
brunoandClaude 9b323ee8f8
Build & Push - 构建并推送镜像 / 构建后端镜像 (push) Failing after 0s
Build & Push - 构建并推送镜像 / 构建前端镜像 (push) Failing after 0s
Build & Push - 构建并推送镜像 / 构建通知 (push) Failing after 0s
CI - 代码检查和测试 / Python 代码检查 (push) Failing after 0s
CI - 代码检查和测试 / Python 单元测试 (push) Failing after 0s
CI - 代码检查和测试 / Frontend 代码检查 (push) Failing after 0s
CI - 代码检查和测试 / Frontend 构建测试 (push) Failing after 0s
Add complete DevOps solution with 3 deployment methods
This commit introduces a comprehensive DevOps infrastructure supporting
local development, Docker Compose, and Kubernetes deployments.

Features:
- One-click local startup script (start_app.sh)
  * Auto Python 3.12 virtual environment management
  * Smart dependency installation with USTC pip mirror
  * Concurrent backend and frontend startup
  * Graceful shutdown with Ctrl+C

- Docker deployment (docker-compose.prod.yml)
  * Multi-stage optimized Dockerfiles
  * Pull from localhost:5000 (business) and :5001 (base cache)
  * Complete service stack: Backend + Frontend + PostgreSQL + Redis + Nginx
  * Health checks and auto-restart

- Kubernetes deployment (deploy_k8s.sh)
  * Full K8s configurations (8 YAML files)
  * ConfigMap/Secret management
  * HPA auto-scaling for backend
  * Ingress routing

- CI/CD with Gitea Actions
  * Code quality checks (black, ruff, mypy)
  * Automated testing with coverage reports
  * Auto build and push images
  * Auto deploy to K8s dev/prod

- Dependency caching
  * pip: USTC mirror (https://pypi.mirrors.ustc.edu.cn/simple/)
  * npm: Taobao mirror (https://registry.npmmirror.com)
  * Docker: localhost:5001 pull-through cache

- Testing and notifications
  * HTML test reports
  * Code coverage visualization
  * QQ email notifications (38455629@qq.com)

Documentation:
- DEVOPS_SOLUTION.md: Complete architecture design
- DEVOPS_QUICKSTART.md: Step-by-step guide
- README_DEVOPS.md: DevOps overview
- DEVOPS_FILES.md: File reference

Total: 30+ configuration files and scripts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:11:17 +08:00

379 lines
11 KiB
Bash
Executable File

#!/bin/bash
###############################################################################
# FastAPI Template - 本地一键启动脚本
# 功能: 自动管理虚拟环境、依赖安装、启动前后端服务
# 使用: ./start_app.sh
###############################################################################
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 项目路径
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BACKEND_DIR="$SCRIPT_DIR/backend"
FRONTEND_DIR="$SCRIPT_DIR/frontend"
VENV_DIR="$BACKEND_DIR/venv"
# PID 文件
BACKEND_PID_FILE="/tmp/fastapi-template-backend.pid"
FRONTEND_PID_FILE="/tmp/fastapi-template-frontend.pid"
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 清理函数
cleanup() {
log_info "正在停止服务..."
# 停止后端
if [ -f "$BACKEND_PID_FILE" ]; then
BACKEND_PID=$(cat "$BACKEND_PID_FILE")
if ps -p $BACKEND_PID > /dev/null 2>&1; then
log_info "停止后端服务 (PID: $BACKEND_PID)"
kill -TERM $BACKEND_PID 2>/dev/null || true
wait $BACKEND_PID 2>/dev/null || true
fi
rm -f "$BACKEND_PID_FILE"
fi
# 停止前端
if [ -f "$FRONTEND_PID_FILE" ]; then
FRONTEND_PID=$(cat "$FRONTEND_PID_FILE")
if ps -p $FRONTEND_PID > /dev/null 2>&1; then
log_info "停止前端服务 (PID: $FRONTEND_PID)"
kill -TERM $FRONTEND_PID 2>/dev/null || true
wait $FRONTEND_PID 2>/dev/null || true
fi
rm -f "$FRONTEND_PID_FILE"
fi
log_success "服务已停止"
exit 0
}
# 捕获退出信号
trap cleanup SIGINT SIGTERM EXIT
# 检查命令是否存在
check_command() {
if ! command -v $1 &> /dev/null; then
log_error "$1 未安装,请先安装 $1"
exit 1
fi
}
# 获取 Python 命令
get_python_command() {
# 优先使用 Python 3.12
if command -v python3.12 &> /dev/null; then
echo "python3.12"
elif command -v python3 &> /dev/null; then
# 检查 python3 版本
local version=$(python3 --version 2>&1 | grep -oE '[0-9]+\.[0-9]+' | head -1)
local major=$(echo $version | cut -d. -f1)
local minor=$(echo $version | cut -d. -f2)
if [ "$major" -eq 3 ] && [ "$minor" -ge 11 ]; then
echo "python3"
else
log_error "需要 Python 3.11+ 版本,当前版本: $version"
exit 1
fi
else
log_error "未找到 Python 3,请先安装"
exit 1
fi
}
# 检查必要的命令
check_dependencies() {
log_info "检查依赖..."
# 检查 Python
PYTHON_CMD=$(get_python_command)
log_info "使用 Python: $($PYTHON_CMD --version)"
check_command node
check_command npm
log_success "依赖检查通过"
}
# 设置虚拟环境
setup_venv() {
log_info "设置 Python 虚拟环境..."
if [ ! -d "$VENV_DIR" ]; then
log_info "创建虚拟环境: $VENV_DIR (使用 $PYTHON_CMD)"
$PYTHON_CMD -m venv "$VENV_DIR"
log_success "虚拟环境创建成功"
else
log_info "虚拟环境已存在"
# 检查虚拟环境的 Python 版本
if [ -f "$VENV_DIR/bin/python" ]; then
VENV_PYTHON_VERSION=$("$VENV_DIR/bin/python" --version 2>&1)
SYSTEM_PYTHON_VERSION=$($PYTHON_CMD --version 2>&1)
if [ "$VENV_PYTHON_VERSION" != "$SYSTEM_PYTHON_VERSION" ]; then
log_warning "虚拟环境 Python 版本 ($VENV_PYTHON_VERSION) 与系统版本 ($SYSTEM_PYTHON_VERSION) 不匹配"
log_warning "重新创建虚拟环境..."
rm -rf "$VENV_DIR"
$PYTHON_CMD -m venv "$VENV_DIR"
log_success "虚拟环境重新创建成功"
fi
fi
fi
# 激活虚拟环境
log_info "激活虚拟环境..."
source "$VENV_DIR/bin/activate"
# 验证激活
if ! command -v pip &> /dev/null; then
log_error "虚拟环境激活失败"
log_error "请尝试手动删除并重新创建: rm -rf $VENV_DIR"
exit 1
fi
# 显示 Python 版本
ACTIVE_PYTHON_VERSION=$(python --version 2>&1)
log_success "虚拟环境激活成功 ($ACTIVE_PYTHON_VERSION)"
}
# 安装 Python 依赖
install_python_deps() {
log_info "检查 Python 依赖..."
# 确保虚拟环境已激活
if [ -z "$VIRTUAL_ENV" ]; then
log_warning "虚拟环境未激活,正在激活..."
source "$VENV_DIR/bin/activate"
fi
# 验证 pip 可用
if ! command -v pip &> /dev/null; then
log_error "pip 命令不可用,请检查虚拟环境"
exit 1
fi
# 检查是否需要安装依赖
REQUIREMENTS_FILE="$BACKEND_DIR/requirements.txt"
INSTALLED_MARKER="$VENV_DIR/.requirements_installed"
NEED_INSTALL=false
if [ ! -f "$INSTALLED_MARKER" ]; then
log_info "首次安装依赖"
NEED_INSTALL=true
elif [ "$REQUIREMENTS_FILE" -nt "$INSTALLED_MARKER" ]; then
log_warning "requirements.txt 已更新,重新安装依赖"
NEED_INSTALL=true
else
log_info "依赖已是最新,跳过安装"
fi
if [ "$NEED_INSTALL" = true ]; then
log_info "安装 Python 依赖 (使用中科大镜像)..."
pip install --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/ --quiet
pip install -r "$REQUIREMENTS_FILE" -i https://pypi.mirrors.ustc.edu.cn/simple/ --quiet
touch "$INSTALLED_MARKER"
log_success "Python 依赖安装完成"
fi
}
# 安装 Node.js 依赖
install_node_deps() {
log_info "检查 Node.js 依赖..."
cd "$FRONTEND_DIR"
# 检查是否需要安装依赖
if [ ! -d "node_modules" ] || [ "package.json" -nt "node_modules" ]; then
log_info "安装 Node.js 依赖 (使用淘宝镜像)..."
npm install --registry=https://registry.npmmirror.com --loglevel=error
log_success "Node.js 依赖安装完成"
else
log_info "Node.js 依赖已是最新,跳过安装"
fi
}
# 检查环境变量
check_env() {
log_info "检查环境配置..."
if [ ! -f "$BACKEND_DIR/.env" ]; then
log_warning ".env 文件不存在,从 .env.example 复制"
cp "$BACKEND_DIR/.env.example" "$BACKEND_DIR/.env"
log_warning "请编辑 $BACKEND_DIR/.env 配置数据库等信息"
fi
}
# 启动后端服务
start_backend() {
log_info "启动后端服务..."
cd "$BACKEND_DIR"
# 确保虚拟环境已激活
if [ -z "$VIRTUAL_ENV" ]; then
source "$VENV_DIR/bin/activate"
fi
# 读取端口配置
if [ -f ".env" ]; then
export $(grep -v '^#' .env | xargs)
fi
BACKEND_PORT=${PORT:-5115}
# 检查端口是否被占用
if lsof -Pi :$BACKEND_PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
log_error "端口 $BACKEND_PORT 已被占用,请检查是否有其他实例在运行"
exit 1
fi
# 启动后端
nohup python run.py > /tmp/fastapi-backend.log 2>&1 &
BACKEND_PID=$!
echo $BACKEND_PID > "$BACKEND_PID_FILE"
log_success "后端服务已启动 (PID: $BACKEND_PID, Port: $BACKEND_PORT)"
# 等待后端启动
log_info "等待后端服务就绪..."
for i in {1..30}; do
if curl -s http://localhost:$BACKEND_PORT/api/v1/health/ > /dev/null 2>&1; then
log_success "后端服务就绪"
return 0
fi
sleep 1
done
log_error "后端服务启动超时,请查看日志: /tmp/fastapi-backend.log"
return 1
}
# 启动前端服务
start_frontend() {
log_info "启动前端服务..."
cd "$FRONTEND_DIR"
# 检查端口是否被占用
FRONTEND_PORT=6115
if lsof -Pi :$FRONTEND_PORT -sTCP:LISTEN -t >/dev/null 2>&1; then
log_error "端口 $FRONTEND_PORT 已被占用,请检查是否有其他实例在运行"
exit 1
fi
# 启动前端
nohup npm run dev > /tmp/fastapi-frontend.log 2>&1 &
FRONTEND_PID=$!
echo $FRONTEND_PID > "$FRONTEND_PID_FILE"
log_success "前端服务已启动 (PID: $FRONTEND_PID, Port: $FRONTEND_PORT)"
# 等待前端启动
log_info "等待前端服务就绪..."
for i in {1..30}; do
if curl -s http://localhost:$FRONTEND_PORT > /dev/null 2>&1; then
log_success "前端服务就绪"
return 0
fi
sleep 1
done
log_error "前端服务启动超时,请查看日志: /tmp/fastapi-frontend.log"
return 1
}
# 显示服务信息
show_info() {
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
log_success "FastAPI Template 服务已启动"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
echo " 📚 API 文档: http://localhost:${PORT:-5115}/docs"
echo " 🌐 前端页面: http://localhost:6115"
echo " 📊 健康检查: http://localhost:${PORT:-5115}/api/v1/health/"
echo ""
echo " 📝 后端日志: /tmp/fastapi-backend.log"
echo " 📝 前端日志: /tmp/fastapi-frontend.log"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
log_info "按 Ctrl+C 停止所有服务"
echo ""
}
# 主函数
main() {
log_info "FastAPI Template 本地启动脚本"
echo ""
# 检查依赖
check_dependencies
# 设置后端环境
setup_venv
install_python_deps
check_env
# 设置前端环境
install_node_deps
# 启动服务
start_backend
start_frontend
# 显示信息
show_info
# 保持运行
while true; do
# 检查进程是否还在运行
if [ -f "$BACKEND_PID_FILE" ]; then
BACKEND_PID=$(cat "$BACKEND_PID_FILE")
if ! ps -p $BACKEND_PID > /dev/null 2>&1; then
log_error "后端服务异常退出,请查看日志"
exit 1
fi
fi
if [ -f "$FRONTEND_PID_FILE" ]; then
FRONTEND_PID=$(cat "$FRONTEND_PID_FILE")
if ! ps -p $FRONTEND_PID > /dev/null 2>&1; then
log_error "前端服务异常退出,请查看日志"
exit 1
fi
fi
sleep 5
done
}
# 运行主函数
main