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
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>
379 lines
11 KiB
Bash
Executable File
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
|