更新taiji

This commit is contained in:
Ubuntu
2025-12-28 12:05:37 +00:00
parent 41cdef4607
commit 78a2394426
18 changed files with 512 additions and 2172 deletions
-129
View File
@@ -1,129 +0,0 @@
# AKS 部署总结
## ✅ 部署状态
### 已成功部署的服务
1. **MCP Server** ✅
- 状态: 运行中
- 健康检查: 通过
- 服务端点: `http://mcp-server:8000` (集群内)
- 端口转发: `kubectl port-forward svc/mcp-server 8002:8000 -n taiji-ai`
2. **LiteLLM Gateway** ✅
- 状态: 运行中
- 健康检查: 通过(OpenRouter 账户需要充值)
- 服务端点: `http://litellm-gateway:4000` (集群内)
- 端口转发: `kubectl port-forward svc/litellm-gateway 4000:4000 -n taiji-ai`
3. **Data Ingestion** ✅
- 状态: 运行中
- 服务端点: `http://data-ingestion:8000` (集群内)
- 端口转发: `kubectl port-forward svc/data-ingestion 8001:8000 -n taiji-ai`
### 需要关注的服务
1. **NATS** ⚠️
- 状态: 部分 Pod 在创建中
- 问题: 旧 Pod 使用 amd64 镜像导致失败,新 Pod 正在创建
- 建议: 等待新 Pod 完全启动后,删除旧的 CrashLoopBackOff Pod
2. **Prometheus & Grafana** ⚠️
- 状态: PVC 挂载问题
- 建议: 检查存储类配置
## 📊 部署信息
- **AKS 集群**: taiji-ai-pda
- **资源组**: taiji-ai-pda
- **命名空间**: taiji-ai
- **ACR**: taiji.azurecr.io
- **节点架构**: ARM64
## 🔧 测试命令
### 健康检查
```bash
# MCP Server
kubectl port-forward svc/mcp-server 8002:8000 -n taiji-ai
curl http://localhost:8002/health
# Data Ingestion
kubectl port-forward svc/data-ingestion 8001:8000 -n taiji-ai
curl http://localhost:8001/health
# LiteLLM Gateway
kubectl port-forward svc/litellm-gateway 4000:4000 -n taiji-ai
curl -H "Authorization: Bearer sk-taiji-master-key" http://localhost:4000/health
```
### 查看日志
```bash
# MCP Server
kubectl logs -f deployment/mcp-server -n taiji-ai
# Data Ingestion
kubectl logs -f deployment/data-ingestion -n taiji-ai
# LiteLLM Gateway
kubectl logs -f deployment/litellm-gateway -n taiji-ai
```
### 查看服务状态
```bash
kubectl get pods -n taiji-ai
kubectl get services -n taiji-ai
kubectl get deployments -n taiji-ai
```
## ⚠️ 已知问题
1. **OpenRouter 账户余额不足**
- LiteLLM Gateway 健康检查显示模型端点不可用
- 需要访问 https://openrouter.ai/settings/credits 充值
2. **NATS 集群配置**
- 当前配置为集群模式,但可能需要调整
- 如果不需要集群模式,可以简化为单节点部署
3. **存储类**
- Prometheus 和 Grafana 的 PVC 可能需要检查存储类配置
- 当前使用 `managed-csi`,确保 AKS 集群支持
## 🚀 下一步
1. **清理旧 Pod**
```bash
kubectl delete pod -l app=nats -n taiji-ai --field-selector=status.phase!=Running
```
2. **配置 Ingress**(可选)
- 如果需要外部访问,配置 Ingress
- 参考 `ingress.yaml` 文件
3. **监控配置**
- 配置 Prometheus 和 Grafana
- 设置监控仪表板
4. **OpenRouter 充值**
- 访问 https://openrouter.ai/settings/credits
- 充值后 LiteLLM Gateway 将正常工作
## 📝 部署文件位置
所有 Kubernetes 配置文件位于: `/home/taiji/tools/taiji-AI-PAD/k8s/`
- `namespace.yaml` - 命名空间
- `configmap.yaml` - 配置映射
- `secrets.yaml.template` - Secret 模板
- `*-deployment.yaml` - 各服务部署配置
- `ingress.yaml` - Ingress 配置
- `test-deployment.sh` - 测试脚本
## 🎉 部署完成
核心服务(MCP Server、LiteLLM Gateway、Data Ingestion)已成功部署并运行!
-125
View File
@@ -1,125 +0,0 @@
# AKS 快速部署指南
## 🚀 5 步快速部署
### 步骤 1: 准备 Azure 资源
```bash
# 设置变量(根据实际情况修改)
export RESOURCE_GROUP="taiji-ai-rg"
export LOCATION="southeastasia"
export AKS_NAME="taiji-aks"
export ACR_NAME="taijiacr"
# 创建资源组
az group create --name $RESOURCE_GROUP --location $LOCATION
# 创建 ACR
az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --sku Basic
# 创建 AKS 集群(最小配置)
az aks create \
--resource-group $RESOURCE_GROUP \
--name $AKS_NAME \
--node-count 2 \
--node-vm-size Standard_B2s \
--enable-addons monitoring \
--attach-acr $ACR_NAME \
--generate-ssh-keys
```
### 步骤 2: 构建并推送镜像
```bash
cd k8s
./build-and-push.sh $ACR_NAME
```
### 步骤 3: 创建 Secrets
```bash
# 使用交互式脚本
./create-secrets.sh
# 或手动创建
kubectl create secret generic taiji-secrets \
--from-literal=database-url="postgresql+asyncpg://user:pass@server.postgres.database.azure.com:5432/db?sslmode=require" \
--from-literal=async-database-url="postgresql+asyncpg://user:pass@server.postgres.database.azure.com:5432/db?sslmode=require" \
--from-literal=redis-url="rediss://:password@server.redis.cache.windows.net:6380/0?ssl_cert_reqs=required" \
--from-literal=jwt-secret="your-secret-key" \
--from-literal=encryption-key="your-encryption-key" \
--from-literal=litellm-master-key="sk-taiji-master-key" \
--from-literal=litellm-api-key="sk-taiji-master-key" \
--from-literal=openrouter-api-key="your-openrouter-key" \
--from-literal=rapidapi-key="your-rapidapi-key" \
--namespace=taiji-ai
```
### 步骤 4: 部署到 AKS
```bash
./deploy.sh $ACR_NAME $RESOURCE_GROUP $AKS_NAME
```
### 步骤 5: 验证部署
```bash
# 查看所有 Pod 状态
kubectl get pods -n taiji-ai
# 查看服务
kubectl get services -n taiji-ai
# 查看日志
kubectl logs -f deployment/mcp-server -n taiji-ai
```
## 📝 重要提示
1. **数据库和 Redis**: 确保 Azure Database for PostgreSQL 和 Azure Cache for Redis 的防火墙规则允许 AKS 节点 IP 访问。
2. **ACR 访问**: 如果 AKS 未自动附加 ACR,需要创建 ACR 拉取密钥:
```bash
kubectl create secret docker-registry acr-secret \
--docker-server=${ACR_NAME}.azurecr.io \
--docker-username=<service-principal-id> \
--docker-password=<service-principal-password> \
--namespace=taiji-ai
```
3. **Ingress**: 默认配置使用 Azure Application Gateway。如果使用 NGINX Ingress,请修改 `ingress.yaml`。
4. **域名**: 部署 Ingress 前,请确保域名 DNS 已正确配置。
## 🔧 常用命令
```bash
# 查看所有资源
kubectl get all -n taiji-ai
# 扩展服务
kubectl scale deployment/mcp-server --replicas=5 -n taiji-ai
# 更新镜像
kubectl set image deployment/mcp-server mcp-server=${ACR_NAME}.azurecr.io/mcp-server:latest -n taiji-ai
# 进入 Pod
kubectl exec -it <pod-name> -n taiji-ai -- /bin/bash
# 查看事件
kubectl get events -n taiji-ai --sort-by='.lastTimestamp'
```
## 🆘 故障排查
如果 Pod 无法启动:
1. 检查 Pod 状态: `kubectl describe pod <pod-name> -n taiji-ai`
2. 查看日志: `kubectl logs <pod-name> -n taiji-ai`
3. 检查 Secret: `kubectl get secret taiji-secrets -n taiji-ai -o yaml`
4. 检查 ConfigMap: `kubectl get configmap taiji-config -n taiji-ai -o yaml`
## 📚 更多信息
详细文档请参考 [README.md](./README.md)
-328
View File
@@ -1,328 +0,0 @@
# AKS 部署指南
本文档说明如何将 taiji-AI-PAD 平台部署到 Azure Kubernetes Service (AKS)。
## 📋 前置要求
1. **Azure 账户和订阅**
2. **已安装的工具**:
- Azure CLI (`az`)
- kubectl
- Docker
- envsubst (通常包含在 gettext 包中)
3. **Azure 资源**:
- AKS 集群
- Azure Container Registry (ACR)
- Azure Database for PostgreSQL
- Azure Cache for Redis
## 🚀 快速开始
### 1. 准备 Azure 资源
#### 创建 AKS 集群
```bash
# 设置变量
RESOURCE_GROUP="taiji-ai-rg"
LOCATION="southeastasia"
AKS_NAME="taiji-aks"
ACR_NAME="taijiacr"
# 创建资源组
az group create --name $RESOURCE_GROUP --location $LOCATION
# 创建 ACR
az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --sku Basic
# 创建 AKS 集群
az aks create \
--resource-group $RESOURCE_GROUP \
--name $AKS_NAME \
--node-count 3 \
--enable-addons monitoring \
--attach-acr $ACR_NAME \
--generate-ssh-keys
```
#### 配置 Azure 数据库和 Redis
确保您已经创建了:
- Azure Database for PostgreSQL
- Azure Cache for Redis
记录连接字符串,稍后需要配置到 Secret 中。
### 2. 构建和推送 Docker 镜像
```bash
# 给脚本添加执行权限
chmod +x build-and-push.sh
# 构建并推送所有镜像
./build-and-push.sh $ACR_NAME
```
这将构建并推送以下镜像:
- `litellm-gateway:latest`
- `data-ingestion:latest`
- `mcp-server:latest`
### 3. 创建 Kubernetes Secrets
#### 方式1: 使用交互式脚本
```bash
chmod +x create-secrets.sh
./create-secrets.sh
```
#### 方式2: 使用 kubectl 命令
```bash
kubectl create secret generic taiji-secrets \
--from-literal=database-url="postgresql+asyncpg://..." \
--from-literal=redis-url="rediss://..." \
--from-literal=jwt-secret="your-jwt-secret" \
--from-literal=encryption-key="your-encryption-key" \
--from-literal=litellm-master-key="sk-taiji-master-key" \
--from-literal=openrouter-api-key="your-openrouter-key" \
--namespace=taiji-ai
```
#### 方式3: 使用 Azure Key Vault (推荐生产环境)
1. 安装 Azure Key Vault Provider:
```bash
kubectl apply -f https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/master/deployment/secrets-store-csi-driver.yaml
```
2. 创建 Key Vault 并存储密钥
3. 配置 SecretProviderClass (参考 Azure 文档)
### 4. 部署到 AKS
```bash
# 给脚本添加执行权限
chmod +x deploy.sh
# 部署所有服务
./deploy.sh $ACR_NAME $RESOURCE_GROUP $AKS_NAME
```
## 📁 文件结构
```
k8s/
├── namespace.yaml # 命名空间定义
├── configmap.yaml # 非敏感配置
├── secrets.yaml.template # Secret 模板(参考用)
├── nats-deployment.yaml # NATS 消息队列部署
├── model-gateway-deployment.yaml # LiteLLM 网关部署
├── data-ingestion-deployment.yaml # 数据接入服务部署
├── mcp-server-deployment.yaml # MCP 服务器部署
├── monitoring-deployment.yaml # Prometheus 和 Grafana
├── ingress.yaml # Ingress 配置
├── deploy.sh # 部署脚本
├── build-and-push.sh # 镜像构建脚本
├── create-secrets.sh # Secret 创建脚本
└── README.md # 本文档
```
## 🔧 配置说明
### 环境变量
所有敏感信息存储在 Kubernetes Secret 中,非敏感配置存储在 ConfigMap 中。
**Secret 中的配置**:
- `database-url`: PostgreSQL 连接字符串
- `redis-url`: Redis 连接字符串
- `jwt-secret`: JWT 签名密钥
- `encryption-key`: 数据加密密钥
- `litellm-master-key`: LiteLLM 主密钥
- `openrouter-api-key`: OpenRouter API 密钥
- `rapidapi-key`: RapidAPI 密钥(可选)
- `azure-storage-connection-string`: Azure 存储连接字符串(可选)
**ConfigMap 中的配置**:
- NATS URL
- LiteLLM URL
- OpenRouter Base URL
- 应用环境配置
### 资源限制
每个服务的默认资源限制:
| 服务 | CPU 请求 | CPU 限制 | 内存请求 | 内存限制 |
|------|---------|----------|----------|----------|
| NATS | 100m | 500m | 256Mi | 512Mi |
| Model Gateway | 250m | 1000m | 512Mi | 1Gi |
| Data Ingestion | 250m | 1000m | 512Mi | 1Gi |
| MCP Server | 250m | 500m | 512Mi | 1Gi |
### 自动扩缩容 (HPA)
所有主要服务都配置了 HorizontalPodAutoscaler:
- **最小副本数**: 2-3
- **最大副本数**: 8-10
- **CPU 阈值**: 70%
- **内存阈值**: 80%
## 🌐 Ingress 配置
### 选项1: Azure Application Gateway (推荐)
如果使用 Azure Application Gateway Ingress Controller,配置文件已包含相应注解。
### 选项2: NGINX Ingress
如果使用 NGINX Ingress Controller,请:
1. 安装 NGINX Ingress Controller
2. 在 `ingress.yaml` 中取消注释 NGINX 配置
3. 注释掉 AGIC 配置
### 域名配置
默认配置使用以下域名:
- `api.taiji-ai.com` - MCP Server 和 Data Ingestion API
- `gateway.taiji-ai.com` - Model Gateway (可选)
请根据实际情况修改域名和 TLS 证书配置。
## 📊 监控
### Prometheus
Prometheus 自动收集以下服务的指标:
- LiteLLM Gateway
- MCP Server
- Data Ingestion
访问方式:
```bash
kubectl port-forward svc/prometheus 9090:9090 -n taiji-ai
# 然后访问 http://localhost:9090
```
### Grafana
Grafana 已配置 Prometheus 作为数据源。
访问方式:
```bash
kubectl port-forward svc/grafana 3000:3000 -n taiji-ai
# 然后访问 http://localhost:3000
# 默认用户名: admin
# 密码: 在 Secret 中配置
```
## 🔍 故障排查
### 查看 Pod 状态
```bash
kubectl get pods -n taiji-ai
```
### 查看 Pod 日志
```bash
# MCP Server
kubectl logs -f deployment/mcp-server -n taiji-ai
# Model Gateway
kubectl logs -f deployment/litellm-gateway -n taiji-ai
# Data Ingestion
kubectl logs -f deployment/data-ingestion -n taiji-ai
```
### 查看服务状态
```bash
kubectl get services -n taiji-ai
kubectl get ingress -n taiji-ai
```
### 检查 Secret 和 ConfigMap
```bash
kubectl get secrets -n taiji-ai
kubectl get configmap -n taiji-ai
kubectl describe secret taiji-secrets -n taiji-ai
```
### 常见问题
1. **镜像拉取失败**
- 检查 ACR 是否正确附加到 AKS
- 验证 `acr-secret` 是否存在
2. **Pod 无法启动**
- 检查 Secret 是否包含所有必需的键
- 查看 Pod 事件: `kubectl describe pod <pod-name> -n taiji-ai`
3. **服务无法访问**
- 检查 Service 和 Ingress 配置
- 验证域名 DNS 配置
4. **数据库连接失败**
- 检查 Azure Database for PostgreSQL 防火墙规则
- 验证连接字符串格式
## 🔄 更新部署
### 更新镜像
```bash
# 1. 构建新镜像
./build-and-push.sh $ACR_NAME
# 2. 重启部署(触发拉取新镜像)
kubectl rollout restart deployment/mcp-server -n taiji-ai
kubectl rollout restart deployment/litellm-gateway -n taiji-ai
kubectl rollout restart deployment/data-ingestion -n taiji-ai
```
### 更新配置
```bash
# 更新 ConfigMap
kubectl apply -f configmap.yaml
# 更新 Secret
kubectl create secret generic taiji-secrets \
--from-literal=... \
--namespace=taiji-ai \
--dry-run=client -o yaml | kubectl apply -f -
# 重启相关 Pod
kubectl rollout restart deployment/<service-name> -n taiji-ai
```
## 🧹 清理
删除所有资源:
```bash
kubectl delete namespace taiji-ai
```
## 📚 参考资源
- [AKS 文档](https://docs.microsoft.com/azure/aks/)
- [Azure Container Registry 文档](https://docs.microsoft.com/azure/container-registry/)
- [Kubernetes 文档](https://kubernetes.io/docs/)
- [Azure Key Vault Provider](https://azure.github.io/secrets-store-csi-driver-provider-azure/)
## 🆘 获取帮助
如遇问题,请检查:
1. Pod 日志
2. 服务事件
3. Ingress 状态
4. Azure 资源健康状态
-94
View File
@@ -1,94 +0,0 @@
#!/bin/bash
# 构建并推送 Docker 镜像到 ACR 的脚本
# 使用方法: ./build-and-push.sh <ACR_NAME>
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# 检查参数
if [ $# -lt 1 ]; then
echo -e "${RED}错误: 缺少参数${NC}"
echo "使用方法: $0 <ACR_NAME>"
echo "示例: $0 myregistry"
exit 1
fi
ACR_NAME=$1
ACR_URL="${ACR_NAME}.azurecr.io"
echo -e "${GREEN}开始构建和推送镜像到 ACR...${NC}"
echo "ACR 名称: $ACR_NAME"
echo "ACR URL: $ACR_URL"
# 检查 Docker 是否安装
if ! command -v docker &> /dev/null; then
echo -e "${RED}错误: Docker 未安装${NC}"
exit 1
fi
# 检查 az 是否安装
if ! command -v az &> /dev/null; then
echo -e "${RED}错误: Azure CLI 未安装${NC}"
exit 1
fi
# 登录到 ACR
echo -e "${YELLOW}登录到 ACR...${NC}"
az acr login --name $ACR_NAME
# 获取项目根目录
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# 设置构建平台(ARM64)
PLATFORM="linux/arm64"
# 检查并设置 buildx
if ! docker buildx ls | grep -q multiarch-builder; then
echo -e "${YELLOW}创建 buildx builder...${NC}"
docker buildx create --name multiarch-builder --use --bootstrap 2>/dev/null || true
fi
# 构建和推送 Model Gateway
echo -e "${YELLOW}构建 Model Gateway 镜像 (${PLATFORM})...${NC}"
cd "$PROJECT_ROOT/services/model-gateway"
docker buildx build --platform ${PLATFORM} -t ${ACR_URL}/litellm-gateway:latest --push . || {
echo -e "${YELLOW}尝试使用标准构建...${NC}"
docker build -t ${ACR_URL}/litellm-gateway:latest .
docker push ${ACR_URL}/litellm-gateway:latest
}
echo -e "${GREEN}Model Gateway 镜像已推送${NC}"
# 构建和推送 Data Ingestion
echo -e "${YELLOW}构建 Data Ingestion 镜像 (${PLATFORM})...${NC}"
cd "$PROJECT_ROOT/services/data-ingestion"
docker buildx build --platform ${PLATFORM} -t ${ACR_URL}/data-ingestion:latest --push . || {
echo -e "${YELLOW}尝试使用标准构建...${NC}"
docker build -t ${ACR_URL}/data-ingestion:latest .
docker push ${ACR_URL}/data-ingestion:latest
}
echo -e "${GREEN}Data Ingestion 镜像已推送${NC}"
# 构建和推送 MCP Server
echo -e "${YELLOW}构建 MCP Server 镜像 (${PLATFORM})...${NC}"
cd "$PROJECT_ROOT/services/mcp-server"
docker buildx build --platform ${PLATFORM} -t ${ACR_URL}/mcp-server:latest --push . || {
echo -e "${YELLOW}尝试使用标准构建...${NC}"
docker build -t ${ACR_URL}/mcp-server:latest .
docker push ${ACR_URL}/mcp-server:latest
}
echo -e "${GREEN}MCP Server 镜像已推送${NC}"
echo -e "${GREEN}所有镜像构建和推送完成!${NC}"
echo ""
echo "镜像列表:"
echo " - ${ACR_URL}/litellm-gateway:latest"
echo " - ${ACR_URL}/data-ingestion:latest"
echo " - ${ACR_URL}/mcp-server:latest"
-34
View File
@@ -1,34 +0,0 @@
# ConfigMap - 非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
name: taiji-config
namespace: taiji-ai
data:
# NATS配置
nats-url: "nats://nats:4222"
# LiteLLM配置
litellm-url: "http://litellm-gateway:4000"
litellm-config-path: "/app/config/litellm_simple.yaml"
# OpenRouter配置
openrouter-base-url: "https://openrouter.ai/api/v1"
# RapidAPI配置
rapidapi-host: "rapidapi.com"
rapidapi-base-url: "https://rapidapi.com"
# 应用配置
environment: "production"
debug: "false"
log-level: "INFO"
# 健康检查配置
health-check-interval: "30"
# 资源限制
max-agents-per-user: "100"
agent-execution-timeout: "300"
tool-execution-timeout: "60"
-110
View File
@@ -1,110 +0,0 @@
#!/bin/bash
# 创建 Kubernetes Secrets 的交互式脚本
# 使用方法: ./create-secrets.sh
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${GREEN}创建 Kubernetes Secrets${NC}"
echo ""
# 检查 kubectl
if ! command -v kubectl &> /dev/null; then
echo -e "${RED}错误: kubectl 未安装${NC}"
exit 1
fi
# 检查命名空间
NAMESPACE="taiji-ai"
if ! kubectl get namespace $NAMESPACE &> /dev/null; then
echo -e "${YELLOW}创建命名空间 $NAMESPACE...${NC}"
kubectl create namespace $NAMESPACE
fi
# 提示输入各个 Secret 值
echo -e "${YELLOW}请输入以下配置信息(按 Enter 跳过可选项):${NC}"
echo ""
read -p "数据库 URL (DATABASE_URL): " DATABASE_URL
read -p "异步数据库 URL (ASYNC_DATABASE_URL): " ASYNC_DATABASE_URL
read -p "Redis URL (REDIS_URL): " REDIS_URL
read -p "JWT Secret: " JWT_SECRET
read -p "加密密钥 (ENCRYPTION_KEY): " ENCRYPTION_KEY
read -p "LiteLLM Master Key: " LITELLM_MASTER_KEY
read -p "OpenRouter API Key: " OPENROUTER_API_KEY
read -p "RapidAPI Key (可选): " RAPIDAPI_KEY
read -p "Azure Storage Connection String (可选): " AZURE_STORAGE_CONNECTION
# 构建 kubectl 命令
SECRET_ARGS=()
if [ -n "$DATABASE_URL" ]; then
SECRET_ARGS+=("--from-literal=database-url=$DATABASE_URL")
fi
if [ -n "$ASYNC_DATABASE_URL" ]; then
SECRET_ARGS+=("--from-literal=async-database-url=$ASYNC_DATABASE_URL")
fi
if [ -n "$REDIS_URL" ]; then
SECRET_ARGS+=("--from-literal=redis-url=$REDIS_URL")
fi
if [ -n "$JWT_SECRET" ]; then
SECRET_ARGS+=("--from-literal=jwt-secret=$JWT_SECRET")
fi
if [ -n "$ENCRYPTION_KEY" ]; then
SECRET_ARGS+=("--from-literal=encryption-key=$ENCRYPTION_KEY")
fi
if [ -n "$LITELLM_MASTER_KEY" ]; then
SECRET_ARGS+=("--from-literal=litellm-master-key=$LITELLM_MASTER_KEY")
SECRET_ARGS+=("--from-literal=litellm-api-key=$LITELLM_MASTER_KEY")
fi
if [ -n "$OPENROUTER_API_KEY" ]; then
SECRET_ARGS+=("--from-literal=openrouter-api-key=$OPENROUTER_API_KEY")
fi
if [ -n "$RAPIDAPI_KEY" ]; then
SECRET_ARGS+=("--from-literal=rapidapi-key=$RAPIDAPI_KEY")
fi
if [ -n "$AZURE_STORAGE_CONNECTION" ]; then
SECRET_ARGS+=("--from-literal=azure-storage-connection-string=$AZURE_STORAGE_CONNECTION")
fi
# 删除现有 Secret(如果存在)
if kubectl get secret taiji-secrets -n $NAMESPACE &> /dev/null; then
echo -e "${YELLOW}删除现有 Secret...${NC}"
kubectl delete secret taiji-secrets -n $NAMESPACE
fi
# 创建 Secret
if [ ${#SECRET_ARGS[@]} -gt 0 ]; then
echo -e "${YELLOW}创建 Secret...${NC}"
kubectl create secret generic taiji-secrets \
"${SECRET_ARGS[@]}" \
--namespace=$NAMESPACE
echo -e "${GREEN}Secret 创建成功!${NC}"
else
echo -e "${RED}错误: 没有提供任何 Secret 值${NC}"
exit 1
fi
# 验证 Secret
echo ""
echo -e "${YELLOW}验证 Secret:${NC}"
kubectl get secret taiji-secrets -n $NAMESPACE
echo ""
echo -e "${GREEN}完成!${NC}"
-153
View File
@@ -1,153 +0,0 @@
# Data Ingestion 服务部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: data-ingestion
namespace: taiji-ai
labels:
app: data-ingestion
component: ingestion
spec:
replicas: 2
selector:
matchLabels:
app: data-ingestion
template:
metadata:
labels:
app: data-ingestion
component: ingestion
spec:
containers:
- name: data-ingestion
image: ${ACR_NAME}.azurecr.io/data-ingestion:latest
imagePullPolicy: Always
ports:
- containerPort: 8000
name: http
protocol: TCP
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: database-url
- name: ASYNC_DATABASE_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: async-database-url
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: redis-url
- name: NATS_URL
valueFrom:
configMapKeyRef:
name: taiji-config
key: nats-url
- name: RAPIDAPI_KEY
valueFrom:
secretKeyRef:
name: taiji-secrets
key: rapidapi-key
- name: RAPIDAPI_HOST
valueFrom:
configMapKeyRef:
name: taiji-config
key: rapidapi-host
- name: OPENROUTER_API_KEY
valueFrom:
secretKeyRef:
name: taiji-secrets
key: openrouter-api-key
- name: OPENROUTER_BASE_URL
valueFrom:
configMapKeyRef:
name: taiji-config
key: openrouter-base-url
- name: PYTHONUNBUFFERED
value: "1"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
volumeMounts:
- name: logs
mountPath: /app/logs
- name: cache
mountPath: /app/cache
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
volumes:
- name: logs
emptyDir: {}
- name: cache
emptyDir: {}
imagePullSecrets:
- name: acr-secret
---
apiVersion: v1
kind: Service
metadata:
name: data-ingestion
namespace: taiji-ai
labels:
app: data-ingestion
spec:
type: ClusterIP
ports:
- port: 8000
targetPort: 8000
protocol: TCP
name: http
selector:
app: data-ingestion
---
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: data-ingestion-hpa
namespace: taiji-ai
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: data-ingestion
minReplicas: 2
maxReplicas: 8
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
-130
View File
@@ -1,130 +0,0 @@
#!/bin/bash
# AKS 部署脚本
# 使用方法: ./deploy.sh <ACR_NAME> <AKS_RESOURCE_GROUP> <AKS_CLUSTER_NAME>
set -e
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 检查参数
if [ $# -lt 3 ]; then
echo -e "${RED}错误: 缺少参数${NC}"
echo "使用方法: $0 <ACR_NAME> <AKS_RESOURCE_GROUP> <AKS_CLUSTER_NAME>"
echo "示例: $0 myregistry myresourcegroup myakscluster"
exit 1
fi
ACR_NAME=$1
AKS_RESOURCE_GROUP=$2
AKS_CLUSTER_NAME=$3
echo -e "${GREEN}开始部署到 AKS...${NC}"
echo "ACR 名称: $ACR_NAME"
echo "资源组: $AKS_RESOURCE_GROUP"
echo "AKS 集群: $AKS_CLUSTER_NAME"
# 检查 kubectl 是否安装
if ! command -v kubectl &> /dev/null; then
echo -e "${RED}错误: kubectl 未安装${NC}"
exit 1
fi
# 检查 az 是否安装
if ! command -v az &> /dev/null; then
echo -e "${RED}错误: Azure CLI 未安装${NC}"
exit 1
fi
# 获取 AKS 凭据
echo -e "${YELLOW}获取 AKS 集群凭据...${NC}"
az aks get-credentials --resource-group $AKS_RESOURCE_GROUP --name $AKS_CLUSTER_NAME --overwrite-existing
# 检查是否已连接到集群
if ! kubectl cluster-info &> /dev/null; then
echo -e "${RED}错误: 无法连接到 AKS 集群${NC}"
exit 1
fi
# 创建命名空间
echo -e "${YELLOW}创建命名空间...${NC}"
kubectl apply -f namespace.yaml
# 创建 ACR 拉取密钥(如果需要)
echo -e "${YELLOW}配置 ACR 访问...${NC}"
# 检查 AKS 是否已附加 ACR
ACR_ATTACHED=$(az aks show -n $AKS_CLUSTER_NAME -g $AKS_RESOURCE_GROUP --query "servicePrincipalProfile" -o tsv)
if [ -z "$ACR_ATTACHED" ]; then
echo "附加 ACR 到 AKS..."
az aks update -n $AKS_CLUSTER_NAME -g $AKS_RESOURCE_GROUP --attach-acr $ACR_NAME
else
echo "ACR 已附加,跳过..."
fi
# 创建 Secret(提示用户)
echo -e "${YELLOW}请确保已创建 Secret:${NC}"
echo " kubectl create secret generic taiji-secrets --from-literal=... --namespace=taiji-ai"
echo " 或使用 Azure Key Vault Provider"
read -p "是否已创建 Secret? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${RED}请先创建 Secret 后再继续${NC}"
exit 1
fi
# 替换配置文件中的变量
echo -e "${YELLOW}替换配置变量...${NC}"
export ACR_NAME=$ACR_NAME
# 应用 ConfigMap
echo -e "${YELLOW}应用 ConfigMap...${NC}"
kubectl apply -f configmap.yaml
# 应用所有部署配置
echo -e "${YELLOW}部署 NATS...${NC}"
envsubst < nats-deployment.yaml | kubectl apply -f -
echo -e "${YELLOW}部署 Model Gateway...${NC}"
envsubst < model-gateway-deployment.yaml | kubectl apply -f -
echo -e "${YELLOW}部署 Data Ingestion...${NC}"
envsubst < data-ingestion-deployment.yaml | kubectl apply -f -
echo -e "${YELLOW}部署 MCP Server...${NC}"
envsubst < mcp-server-deployment.yaml | kubectl apply -f -
echo -e "${YELLOW}部署监控服务...${NC}"
kubectl apply -f monitoring-deployment.yaml
# 等待部署完成
echo -e "${YELLOW}等待部署就绪...${NC}"
kubectl wait --for=condition=available --timeout=300s deployment/nats -n taiji-ai || true
kubectl wait --for=condition=available --timeout=300s deployment/litellm-gateway -n taiji-ai || true
kubectl wait --for=condition=available --timeout=300s deployment/data-ingestion -n taiji-ai || true
kubectl wait --for=condition=available --timeout=300s deployment/mcp-server -n taiji-ai || true
# 应用 Ingress(可选)
read -p "是否部署 Ingress? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
echo -e "${YELLOW}部署 Ingress...${NC}"
kubectl apply -f ingress.yaml
fi
# 显示部署状态
echo -e "${GREEN}部署完成!${NC}"
echo ""
echo "查看部署状态:"
echo " kubectl get pods -n taiji-ai"
echo " kubectl get services -n taiji-ai"
echo " kubectl get ingress -n taiji-ai"
echo ""
echo "查看日志:"
echo " kubectl logs -f deployment/mcp-server -n taiji-ai"
echo " kubectl logs -f deployment/litellm-gateway -n taiji-ai"
echo " kubectl logs -f deployment/data-ingestion -n taiji-ai"
-147
View File
@@ -1,147 +0,0 @@
# Ingress 配置 - 使用 Azure Application Gateway 或 NGINX Ingress
# 根据您的 AKS 配置选择相应的 Ingress Controller
---
# 选项1: Azure Application Gateway Ingress Controller (AGIC)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: taiji-ingress
namespace: taiji-ai
annotations:
# Azure Application Gateway Ingress Controller 注解
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/ssl-redirect: "true"
appgw.ingress.kubernetes.io/connection-draining: "true"
appgw.ingress.kubernetes.io/connection-draining-timeout: "30"
appgw.ingress.kubernetes.io/backend-protocol: "http"
appgw.ingress.kubernetes.io/request-timeout: "300"
appgw.ingress.kubernetes.io/health-probe-path: "/health"
appgw.ingress.kubernetes.io/health-probe-interval: "30"
appgw.ingress.kubernetes.io/health-probe-timeout: "10"
appgw.ingress.kubernetes.io/health-probe-unhealthy-threshold: "3"
# CORS 配置
appgw.ingress.kubernetes.io/cors-allow-origin: "*"
appgw.ingress.kubernetes.io/cors-allow-methods: "GET,POST,PUT,DELETE,OPTIONS"
appgw.ingress.kubernetes.io/cors-allow-headers: "*"
# 证书配置(使用 Azure Key Vault)
# cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- api.taiji-ai.com
- gateway.taiji-ai.com
secretName: taiji-tls-cert
rules:
# MCP Server API
- host: api.taiji-ai.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: mcp-server
port:
number: 8000
- path: /health
pathType: Exact
backend:
service:
name: mcp-server
port:
number: 8000
- path: /docs
pathType: Prefix
backend:
service:
name: mcp-server
port:
number: 8000
- path: /openapi.json
pathType: Exact
backend:
service:
name: mcp-server
port:
number: 8000
# Data Ingestion API
- host: api.taiji-ai.com
http:
paths:
- path: /ingestion
pathType: Prefix
backend:
service:
name: data-ingestion
port:
number: 8000
# Model Gateway (内部使用,可选暴露)
- host: gateway.taiji-ai.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: litellm-gateway
port:
number: 4000
---
# 选项2: NGINX Ingress Controller (如果使用 NGINX 而不是 AGIC)
# 取消注释以下配置,并注释掉上面的 AGIC 配置
# apiVersion: networking.k8s.io/v1
# kind: Ingress
# metadata:
# name: taiji-ingress-nginx
# namespace: taiji-ai
# annotations:
# kubernetes.io/ingress.class: nginx
# cert-manager.io/cluster-issuer: letsencrypt-prod
# nginx.ingress.kubernetes.io/ssl-redirect: "true"
# nginx.ingress.kubernetes.io/proxy-body-size: "50m"
# nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
# nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
# nginx.ingress.kubernetes.io/cors-allow-origin: "*"
# nginx.ingress.kubernetes.io/cors-allow-methods: "GET,POST,PUT,DELETE,OPTIONS"
# nginx.ingress.kubernetes.io/cors-allow-headers: "*"
# spec:
# tls:
# - hosts:
# - api.taiji-ai.com
# - gateway.taiji-ai.com
# secretName: taiji-tls-cert
# rules:
# - host: api.taiji-ai.com
# http:
# paths:
# - path: /api
# pathType: Prefix
# backend:
# service:
# name: mcp-server
# port:
# number: 8000
# - path: /ingestion
# pathType: Prefix
# backend:
# service:
# name: data-ingestion
# port:
# number: 8000
# - host: gateway.taiji-ai.com
# http:
# paths:
# - path: /
# pathType: Prefix
# backend:
# service:
# name: litellm-gateway
# port:
# number: 4000
-183
View File
@@ -1,183 +0,0 @@
# MCP Server 部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
namespace: taiji-ai
labels:
app: mcp-server
component: server
spec:
replicas: 3
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
component: server
spec:
containers:
- name: mcp-server
image: ${ACR_NAME}.azurecr.io/mcp-server:latest
imagePullPolicy: Always
ports:
- containerPort: 8000
name: http
protocol: TCP
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: database-url
- name: ASYNC_DATABASE_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: async-database-url
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: redis-url
- name: NATS_URL
valueFrom:
configMapKeyRef:
name: taiji-config
key: nats-url
- name: LITELLM_URL
valueFrom:
configMapKeyRef:
name: taiji-config
key: litellm-url
- name: LITELLM_API_KEY
valueFrom:
secretKeyRef:
name: taiji-secrets
key: litellm-api-key
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: taiji-secrets
key: jwt-secret
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: taiji-secrets
key: encryption-key
- name: AZURE_STORAGE_CONNECTION_STRING
valueFrom:
secretKeyRef:
name: taiji-secrets
key: azure-storage-connection-string
optional: true
- name: ENVIRONMENT
valueFrom:
configMapKeyRef:
name: taiji-config
key: environment
- name: DEBUG
valueFrom:
configMapKeyRef:
name: taiji-config
key: debug
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: taiji-config
key: log-level
- name: PYTHONUNBUFFERED
value: "1"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "500m"
volumeMounts:
- name: logs
mountPath: /app/logs
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
volumes:
- name: logs
emptyDir: {}
imagePullSecrets:
- name: acr-secret
---
apiVersion: v1
kind: Service
metadata:
name: mcp-server
namespace: taiji-ai
labels:
app: mcp-server
spec:
type: ClusterIP
ports:
- port: 8000
targetPort: 8000
protocol: TCP
name: http
selector:
app: mcp-server
---
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: mcp-server-hpa
namespace: taiji-ai
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: mcp-server
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
---
# PodDisruptionBudget - 确保高可用
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: mcp-server-pdb
namespace: taiji-ai
spec:
minAvailable: 2
selector:
matchLabels:
app: mcp-server
-188
View File
@@ -1,188 +0,0 @@
# LiteLLM Model Gateway 部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: litellm-gateway
namespace: taiji-ai
labels:
app: litellm-gateway
component: gateway
spec:
replicas: 2
selector:
matchLabels:
app: litellm-gateway
template:
metadata:
labels:
app: litellm-gateway
component: gateway
spec:
containers:
- name: litellm-gateway
image: ${ACR_NAME}.azurecr.io/litellm-gateway:latest
imagePullPolicy: Always
ports:
- containerPort: 4000
name: http
protocol: TCP
env:
- name: LITELLM_MASTER_KEY
valueFrom:
secretKeyRef:
name: taiji-secrets
key: litellm-master-key
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: database-url
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: taiji-secrets
key: redis-url
- name: OPENROUTER_API_KEY
valueFrom:
secretKeyRef:
name: taiji-secrets
key: openrouter-api-key
- name: OPENROUTER_BASE_URL
valueFrom:
configMapKeyRef:
name: taiji-config
key: openrouter-base-url
- name: LITELLM_CONFIG_PATH
valueFrom:
configMapKeyRef:
name: taiji-config
key: litellm-config-path
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
volumeMounts:
- name: litellm-config
mountPath: /app/config
readOnly: true
- name: logs
mountPath: /app/logs
livenessProbe:
httpGet:
path: /health
port: 4000
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 25
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: 4000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 2
volumes:
- name: litellm-config
configMap:
name: litellm-config
- name: logs
emptyDir: {}
imagePullSecrets:
- name: acr-secret
---
apiVersion: v1
kind: Service
metadata:
name: litellm-gateway
namespace: taiji-ai
labels:
app: litellm-gateway
spec:
type: ClusterIP
ports:
- port: 4000
targetPort: 4000
protocol: TCP
name: http
selector:
app: litellm-gateway
---
# LiteLLM 配置文件 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: litellm-config
namespace: taiji-ai
data:
litellm_simple.yaml: |
model_list:
- model_name: gpt-3.5-turbo
litellm_params:
model: openrouter/gpt-3.5-turbo
api_key: os.environ/OPENROUTER_API_KEY
api_base: https://openrouter.ai/api/v1
- model_name: openrouter-gpt-3.5-turbo
litellm_params:
model: openrouter/gpt-3.5-turbo
api_key: os.environ/OPENROUTER_API_KEY
api_base: https://openrouter.ai/api/v1
- model_name: openrouter-gpt-4o-mini
litellm_params:
model: openrouter/gpt-4o-mini
api_key: os.environ/OPENROUTER_API_KEY
api_base: https://openrouter.ai/api/v1
- model_name: openrouter-claude-3.5-sonnet
litellm_params:
model: openrouter/anthropic/claude-3.5-sonnet
api_key: os.environ/OPENROUTER_API_KEY
api_base: https://openrouter.ai/api/v1
- model_name: openrouter-claude-3-opus
litellm_params:
model: openrouter/anthropic/claude-3-opus
api_key: os.environ/OPENROUTER_API_KEY
api_base: https://openrouter.ai/api/v1
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL
redis_url: os.environ/REDIS_URL
---
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: litellm-gateway-hpa
namespace: taiji-ai
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: litellm-gateway
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
-269
View File
@@ -1,269 +0,0 @@
# Prometheus 和 Grafana 监控服务部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: taiji-ai
labels:
app: prometheus
component: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
component: monitoring
spec:
containers:
- name: prometheus
image: prom/prometheus:latest
ports:
- containerPort: 9090
name: http
protocol: TCP
args:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
- '--web.enable-lifecycle'
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "2Gi"
cpu: "1000m"
volumeMounts:
- name: prometheus-config
mountPath: /etc/prometheus
readOnly: true
- name: prometheus-data
mountPath: /prometheus
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
- name: prometheus-data
persistentVolumeClaim:
claimName: prometheus-pvc
---
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: taiji-ai
labels:
app: prometheus
spec:
type: ClusterIP
ports:
- port: 9090
targetPort: 9090
protocol: TCP
name: http
selector:
app: prometheus
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: grafana
namespace: taiji-ai
labels:
app: grafana
component: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: grafana
template:
metadata:
labels:
app: grafana
component: monitoring
spec:
containers:
- name: grafana
image: grafana/grafana:latest
ports:
- containerPort: 3000
name: http
protocol: TCP
env:
- name: GF_SECURITY_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: taiji-secrets
key: grafana-admin-password
optional: true
- name: GF_SERVER_ROOT_URL
value: "http://grafana.taiji-ai.com"
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: grafana-data
mountPath: /var/lib/grafana
- name: grafana-dashboards
mountPath: /etc/grafana/provisioning/dashboards
- name: grafana-datasources
mountPath: /etc/grafana/provisioning/datasources
volumes:
- name: grafana-data
persistentVolumeClaim:
claimName: grafana-pvc
- name: grafana-dashboards
configMap:
name: grafana-dashboards
- name: grafana-datasources
configMap:
name: grafana-datasources
---
apiVersion: v1
kind: Service
metadata:
name: grafana
namespace: taiji-ai
labels:
app: grafana
spec:
type: ClusterIP
ports:
- port: 3000
targetPort: 3000
protocol: TCP
name: http
selector:
app: grafana
---
# Prometheus 配置
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: taiji-ai
data:
prometheus.yml: |
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'litellm-gateway'
kubernetes_sd_configs:
- role: endpoints
namespaces:
names:
- taiji-ai
relabel_configs:
- source_labels: [__meta_kubernetes_service_name]
action: keep
regex: litellm-gateway
- job_name: 'mcp-server'
kubernetes_sd_configs:
- role: endpoints
namespaces:
names:
- taiji-ai
relabel_configs:
- source_labels: [__meta_kubernetes_service_name]
action: keep
regex: mcp-server
- job_name: 'data-ingestion'
kubernetes_sd_configs:
- role: endpoints
namespaces:
names:
- taiji-ai
relabel_configs:
- source_labels: [__meta_kubernetes_service_name]
action: keep
regex: data-ingestion
---
# Grafana 数据源配置
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-datasources
namespace: taiji-ai
data:
prometheus.yaml: |
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
editable: true
---
# Grafana 仪表板配置
apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-dashboards
namespace: taiji-ai
data:
dashboard-provider.yaml: |
apiVersion: 1
providers:
- name: 'Default'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards
---
# 持久化存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: prometheus-pvc
namespace: taiji-ai
spec:
accessModes:
- ReadWriteOnce
storageClassName: managed-csi
resources:
requests:
storage: 50Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: grafana-pvc
namespace: taiji-ai
spec:
accessModes:
- ReadWriteOnce
storageClassName: managed-csi
resources:
requests:
storage: 10Gi
-8
View File
@@ -1,8 +0,0 @@
apiVersion: v1
kind: Namespace
metadata:
name: taiji-ai
labels:
name: taiji-ai
environment: production
-111
View File
@@ -1,111 +0,0 @@
# NATS 消息队列部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: nats
namespace: taiji-ai
labels:
app: nats
component: messaging
spec:
replicas: 3
selector:
matchLabels:
app: nats
template:
metadata:
labels:
app: nats
component: messaging
spec:
containers:
- name: nats
image: nats:2.10-alpine
ports:
- containerPort: 4222
name: client
protocol: TCP
- containerPort: 6222
name: routing
protocol: TCP
- containerPort: 8222
name: monitoring
protocol: TCP
command:
- "/nats-server"
args:
- "-js"
- "-m"
- "8222"
- "-cluster"
- "nats://0.0.0.0:6222"
- "-routes"
- "nats://nats-0.nats.taiji-ai.svc.cluster.local:6222,nats://nats-1.nats.taiji-ai.svc.cluster.local:6222,nats://nats-2.nats.taiji-ai.svc.cluster.local:6222"
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumeMounts:
- name: nats-data
mountPath: /data
livenessProbe:
httpGet:
path: /healthz
port: 8222
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /healthz
port: 8222
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: nats-data
persistentVolumeClaim:
claimName: nats-pvc
---
apiVersion: v1
kind: Service
metadata:
name: nats
namespace: taiji-ai
labels:
app: nats
spec:
type: ClusterIP
ports:
- port: 4222
targetPort: 4222
protocol: TCP
name: client
- port: 6222
targetPort: 6222
protocol: TCP
name: routing
- port: 8222
targetPort: 8222
protocol: TCP
name: monitoring
selector:
app: nats
---
# NATS 持久化存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nats-pvc
namespace: taiji-ai
spec:
accessModes:
- ReadWriteOnce
storageClassName: managed-csi # Azure 托管存储类
resources:
requests:
storage: 10Gi
-70
View File
@@ -1,70 +0,0 @@
# Kubernetes Secrets 模板
# 注意:这是一个模板文件,实际部署时请使用以下方式创建 Secret:
#
# 方式1: 使用 kubectl 命令
# kubectl create secret generic taiji-secrets \
# --from-literal=database-url="postgresql+asyncpg://..." \
# --from-literal=redis-url="rediss://..." \
# --namespace=taiji-ai
#
# 方式2: 使用 Azure Key Vault (推荐)
# 安装 Azure Key Vault Provider: https://azure.github.io/secrets-store-csi-driver-provider-azure/
#
# 方式3: 使用 sealed-secrets (推荐用于 GitOps)
# https://github.com/bitnami-labs/sealed-secrets
apiVersion: v1
kind: Secret
metadata:
name: taiji-secrets
namespace: taiji-ai
type: Opaque
stringData:
# 数据库连接字符串 (Azure Database for PostgreSQL)
database-url: "postgresql+asyncpg://USER:PASSWORD@SERVER.postgres.database.azure.com:5432/DATABASE?sslmode=require"
async-database-url: "postgresql+asyncpg://USER:PASSWORD@SERVER.postgres.database.azure.com:5432/DATABASE?sslmode=require"
# Redis连接字符串 (Azure Cache for Redis)
redis-url: "rediss://:PASSWORD@SERVER.redis.cache.windows.net:6380/0?ssl_cert_reqs=required"
# JWT密钥
jwt-secret: "CHANGE_THIS_SECRET_KEY_IN_PRODUCTION_USE_STRONG_RANDOM_STRING"
# 加密密钥
encryption-key: "CHANGE_THIS_ENCRYPTION_KEY_USE_STRONG_RANDOM_STRING"
# LiteLLM API密钥
litellm-master-key: "sk-taiji-master-key"
litellm-api-key: "sk-taiji-master-key"
# OpenRouter API密钥
openrouter-api-key: "YOUR_OPENROUTER_API_KEY"
# RapidAPI配置
rapidapi-key: "YOUR_RAPIDAPI_KEY"
# Azure存储连接字符串 (可选)
azure-storage-connection-string: "DefaultEndpointsProtocol=https;AccountName=ACCOUNT;AccountKey=KEY;EndpointSuffix=core.windows.net"
---
# ACR (Azure Container Registry) 拉取密钥
# 创建方式:
# kubectl create secret docker-registry acr-secret \
# --docker-server=${ACR_NAME}.azurecr.io \
# --docker-username=${SP_APP_ID} \
# --docker-password=${SP_PASSWORD} \
# --namespace=taiji-ai
#
# 或者使用 AKS 与 ACR 集成(推荐):
# az aks update -n <AKS_CLUSTER_NAME> -g <RESOURCE_GROUP> --attach-acr <ACR_NAME>
apiVersion: v1
kind: Secret
metadata:
name: acr-secret
namespace: taiji-ai
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: <BASE64_ENCODED_DOCKER_CONFIG>
# 注意:实际使用时请使用上述命令创建,不要直接使用 base64 编码的配置
-93
View File
@@ -1,93 +0,0 @@
#!/bin/bash
# 测试部署的服务
# 使用方法: ./test-deployment.sh
set -e
export PATH=$HOME/bin:$PATH
NAMESPACE="taiji-ai"
echo "=========================================="
echo "测试 AKS 部署的服务"
echo "=========================================="
echo ""
# 1. 检查 Pod 状态
echo "1. 检查 Pod 状态..."
kubectl get pods -n $NAMESPACE
echo ""
# 2. 检查服务
echo "2. 检查服务..."
kubectl get services -n $NAMESPACE
echo ""
# 3. 测试 MCP Server 健康检查
echo "3. 测试 MCP Server 健康检查..."
MCP_POD=$(kubectl get pods -l app=mcp-server -n $NAMESPACE -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -n "$MCP_POD" ]; then
echo "MCP Server Pod: $MCP_POD"
kubectl exec -n $NAMESPACE $MCP_POD -- curl -s http://localhost:8000/health || echo "健康检查失败"
else
echo "未找到 MCP Server Pod"
fi
echo ""
# 4. 测试 Data Ingestion 健康检查
echo "4. 测试 Data Ingestion 健康检查..."
DI_POD=$(kubectl get pods -l app=data-ingestion -n $NAMESPACE -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -n "$DI_POD" ]; then
echo "Data Ingestion Pod: $DI_POD"
kubectl exec -n $NAMESPACE $DI_POD -- curl -s http://localhost:8000/health || echo "健康检查失败"
else
echo "未找到 Data Ingestion Pod"
fi
echo ""
# 5. 测试 LiteLLM Gateway 健康检查
echo "5. 测试 LiteLLM Gateway 健康检查..."
LG_POD=$(kubectl get pods -l app=litellm-gateway -n $NAMESPACE -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || echo "")
if [ -n "$LG_POD" ]; then
echo "LiteLLM Gateway Pod: $LG_POD"
kubectl exec -n $NAMESPACE $LG_POD -- curl -s -H "Authorization: Bearer sk-taiji-master-key" http://localhost:4000/health || echo "健康检查失败"
else
echo "未找到 LiteLLM Gateway Pod"
fi
echo ""
# 6. 测试服务端点(通过 port-forward)
echo "6. 测试服务端点..."
echo "注意: 以下测试需要手动运行 port-forward 命令"
echo ""
echo "测试 MCP Server:"
echo " kubectl port-forward svc/mcp-server 8002:8000 -n $NAMESPACE"
echo " curl http://localhost:8002/health"
echo ""
echo "测试 Data Ingestion:"
echo " kubectl port-forward svc/data-ingestion 8001:8000 -n $NAMESPACE"
echo " curl http://localhost:8001/health"
echo ""
echo "测试 LiteLLM Gateway:"
echo " kubectl port-forward svc/litellm-gateway 4000:4000 -n $NAMESPACE"
echo " curl -H 'Authorization: Bearer sk-taiji-master-key' http://localhost:4000/health"
echo ""
# 7. 检查日志(最近10行)
echo "7. 检查服务日志(最近10行)..."
echo ""
echo "MCP Server 日志:"
kubectl logs -l app=mcp-server -n $NAMESPACE --tail=10 2>&1 | head -10 || echo "无法获取日志"
echo ""
echo "Data Ingestion 日志:"
kubectl logs -l app=data-ingestion -n $NAMESPACE --tail=10 2>&1 | head -10 || echo "无法获取日志"
echo ""
echo "LiteLLM Gateway 日志:"
kubectl logs -l app=litellm-gateway -n $NAMESPACE --tail=10 2>&1 | head -10 || echo "无法获取日志"
echo ""
echo "=========================================="
echo "测试完成"
echo "=========================================="
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/env python3
"""
重新初始化数据库表
用于在数据库表丢失时重新创建所有表结构
"""
import sys
import os
import asyncio
# 添加services/mcp-server到路径,以便导入模块
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'services', 'mcp-server'))
from sqlalchemy.ext.asyncio import create_async_engine
from models import Base
from config import settings
from database import prepare_database_url
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
async def reinit_database():
"""重新初始化数据库表"""
try:
# 准备数据库URL
database_url = prepare_database_url(settings.database_url)
# 确保使用asyncpg驱动
if database_url.startswith("postgresql://") and "+asyncpg" not in database_url:
database_url = database_url.replace("postgresql://", "postgresql+asyncpg://", 1)
logger.info(f"正在连接到数据库: {database_url.split('@')[1] if '@' in database_url else '本地数据库'}")
# 创建数据库引擎
engine = create_async_engine(database_url, echo=False)
logger.info("开始创建数据库表...")
# 创建所有表
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
logger.info("✓ 数据库表创建成功!")
# 列出所有创建的表
async with engine.begin() as conn:
# 获取所有表名
if "postgresql" in database_url:
result = await conn.execute(
"""
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name
"""
)
else:
# SQLite
result = await conn.execute(
"""
SELECT name
FROM sqlite_master
WHERE type='table' AND name NOT LIKE 'sqlite_%'
ORDER BY name
"""
)
tables = result.fetchall()
table_names = [row[0] for row in tables]
logger.info(f"\n已创建的表列表(共 {len(table_names)} 个):")
for i, table_name in enumerate(table_names, 1):
logger.info(f" {i}. {table_name}")
await engine.dispose()
logger.info("\n数据库初始化完成!")
logger.info("注意:此脚本只创建表结构,不会创建初始数据。")
logger.info("如果需要创建初始数据,请运行服务或使用其他初始化脚本。")
return True
except Exception as e:
logger.error(f"✗ 数据库初始化失败: {e}")
import traceback
traceback.print_exc()
return False
async def main():
"""主函数"""
print("="*80)
print("重新初始化数据库表")
print("="*80)
print()
# 确认操作
print("警告:此操作将创建所有数据库表。")
print("如果表已存在,SQLAlchemy不会删除或修改现有表。")
print()
success = await reinit_database()
if success:
print("\n" + "="*80)
print("✓ 数据库表初始化成功!")
print("="*80)
sys.exit(0)
else:
print("\n" + "="*80)
print("✗ 数据库表初始化失败!")
print("="*80)
sys.exit(1)
if __name__ == "__main__":
asyncio.run(main())
+392
View File
@@ -0,0 +1,392 @@
#!/usr/bin/env python3
"""
创建管理员账户脚本
用于创建4个管理员角色:
- 超级管理员 (super_admin)
- 计费管理员 (billing_admin)
- 运维管理员 (operations_admin)
- 渠道管理员 (channel_admin)
"""
import requests
import sys
import os
import asyncio
from typing import Optional
import bcrypt
# 添加services/mcp-server到路径,以便导入模块
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'services', 'mcp-server'))
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker
from sqlalchemy import select
from models import User, Channel
from config import settings
def get_password_hash(password: str) -> str:
"""加密密码(使用bcrypt)"""
password_bytes = password.encode('utf-8')
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password_bytes, salt)
return hashed.decode('utf-8')
BASE_URL = "http://localhost:8002"
# 要创建的管理员列表
ADMINS = [
{
"name": "超级管理员",
"email": "superadmin@taiji-ai.com",
"password": "Admin@123456",
"role": "super_admin",
},
{
"name": "计费管理员",
"email": "newbilling@test.com",
"password": "Billing@123456",
"role": "billing_admin",
"channel_name": "测试渠道", # 计费管理员也需要关联渠道
"channel_email": "test-channel@test.com", # 共享渠道邮箱
},
{
"name": "运维管理员",
"email": "newops@test.com",
"password": "Ops@123456",
"role": "operations_admin",
"channel_name": "测试渠道", # 运维管理员也需要关联渠道
"channel_email": "test-channel@test.com", # 共享渠道邮箱(与计费管理员共享)
},
{
"name": "渠道管理员",
"email": "channel-a@test.com",
"password": "ChannelA@123456",
"role": "channel_admin",
"channel_name": "渠道A", # 渠道名称
}
]
def login_admin(email: str, password: str, role: str = "super_admin") -> Optional[str]:
"""登录管理员账户,返回token"""
try:
resp = requests.post(
f"{BASE_URL}/api/auth/login",
json={
"email": email,
"password": password,
"role": role
},
timeout=10
)
if resp.status_code == 200:
data = resp.json()
token = data.get("data", {}).get("token")
if token:
print(f" ✓ 登录成功: {email}")
return token
else:
print(f" ✗ 登录失败: 响应中未找到token")
return None
else:
error = resp.json().get("detail", resp.text)
print(f" ✗ 登录失败: {error}")
return None
except Exception as e:
print(f" ✗ 登录出错: {e}")
return None
async def create_admin_via_api(token: str, admin_info: dict) -> bool:
"""通过API创建管理员(需要先创建渠道)"""
try:
# 注意:API只能创建billing_admin和operations_admin
# 超级管理员和渠道管理员需要直接操作数据库
if admin_info["role"] not in ["billing_admin", "operations_admin"]:
print(f" ⚠ 跳过: {admin_info['role']} 需要通过数据库直接创建")
return False
# 先创建或获取渠道
channel_id = None
if admin_info.get("channel_name"):
channel_id = await get_or_create_channel_for_api(admin_info)
# 构建请求数据
request_data = {
"name": admin_info["name"],
"email": admin_info["email"],
"password": admin_info["password"],
"role": admin_info["role"]
}
if channel_id:
request_data["channelId"] = str(channel_id)
resp = requests.post(
f"{BASE_URL}/api/admin/admins/create",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json=request_data,
timeout=10
)
if resp.status_code == 200:
data = resp.json()
channel_id_from_response = data.get("data", {}).get("channelId")
print(f" ✓ 创建成功: {admin_info['email']}" + (f" (渠道ID: {channel_id_from_response})" if channel_id_from_response else ""))
return True
else:
error = resp.json().get("detail", resp.text)
if "邮箱已被使用" in error or "already exists" in error.lower():
print(f" ⚠ 已存在: {admin_info['email']}")
# 如果已存在,尝试更新channel_id
if channel_id:
await update_existing_user_channel(admin_info["email"], channel_id)
return True # 已存在也算成功
else:
print(f" ✗ 创建失败: {error}")
return False
except Exception as e:
print(f" ✗ 创建出错: {e}")
import traceback
traceback.print_exc()
return False
async def get_or_create_channel_for_api(admin_info: dict) -> str:
"""为API创建获取或创建渠道,返回channel_id字符串"""
try:
database_url = settings.database_url
if database_url.startswith("postgresql://") and "+asyncpg" not in database_url:
database_url = database_url.replace("postgresql://", "postgresql+asyncpg://", 1)
engine = create_async_engine(database_url, echo=False)
AsyncSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with AsyncSessionLocal() as session:
channel_name = admin_info.get("channel_name", f"渠道-{admin_info['name']}")
channel_email = admin_info.get("channel_email", f"channel-{channel_name.lower().replace(' ', '-')}@test.com")
channel = await get_or_create_channel(session, channel_email, channel_name)
await session.commit()
channel_id = str(channel.id)
await engine.dispose()
return channel_id
except Exception as e:
print(f" ⚠ 创建渠道失败: {e}")
return None
async def update_existing_user_channel(email: str, channel_id: str):
"""更新已存在用户的channel_id"""
try:
database_url = settings.database_url
if database_url.startswith("postgresql://") and "+asyncpg" not in database_url:
database_url = database_url.replace("postgresql://", "postgresql+asyncpg://", 1)
engine = create_async_engine(database_url, echo=False)
AsyncSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with AsyncSessionLocal() as session:
result = await session.execute(select(User).where(User.email == email))
user = result.scalar_one_or_none()
if user:
import uuid
user.channel_id = uuid.UUID(channel_id)
await session.commit()
print(f" ✓ 已更新用户的渠道ID: {channel_id}")
await engine.dispose()
except Exception as e:
print(f" ⚠ 更新用户渠道ID失败: {e}")
async def get_or_create_channel(session: AsyncSession, channel_email: str, channel_name: str) -> Channel:
"""获取或创建渠道"""
# 先查找是否已存在
result = await session.execute(
select(Channel).where(Channel.email == channel_email)
)
channel = result.scalar_one_or_none()
if channel:
return channel
# 创建新渠道
channel = Channel(
name=channel_name,
email=channel_email,
password_hash=get_password_hash("Channel@123456"), # 默认密码
commission_rate=10.0,
channel_credit=0,
custom_agent_cpu=2,
custom_agent_memory=4,
status="active",
)
session.add(channel)
await session.flush() # 获取ID但不提交
await session.refresh(channel)
print(f" ✓ 创建渠道: {channel_name} (ID: {channel.id})")
return channel
async def create_admin_via_db(admin_info: dict) -> bool:
"""直接通过数据库创建管理员"""
engine = None
try:
# 准备数据库URL
database_url = settings.database_url
if database_url.startswith("postgresql://") and "+asyncpg" not in database_url:
database_url = database_url.replace("postgresql://", "postgresql+asyncpg://", 1)
# 创建数据库引擎
engine = create_async_engine(database_url, echo=False)
AsyncSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async with AsyncSessionLocal() as session:
# 如果是渠道管理员、计费管理员或运维管理员,需要先创建或获取渠道
channel_id = None
if admin_info["role"] in ["channel_admin", "billing_admin", "operations_admin"]:
# 为管理员创建对应的渠道
channel_name = admin_info.get("channel_name", f"渠道-{admin_info['name']}")
# 使用一个统一的渠道邮箱(如果多个管理员共享同一个渠道)
channel_email = admin_info.get("channel_email", f"channel-{channel_name.lower().replace(' ', '-')}@test.com")
channel = await get_or_create_channel(session, channel_email, channel_name)
channel_id = channel.id
await session.commit() # 提交渠道创建
# 检查用户是否已存在
result = await session.execute(
select(User).where(User.email == admin_info["email"])
)
existing_user = result.scalar_one_or_none()
if existing_user:
print(f" ⚠ 用户已存在: {admin_info['email']}")
# 更新角色和密码
existing_user.role = admin_info["role"]
existing_user.password_hash = get_password_hash(admin_info["password"])
existing_user.hashed_password = existing_user.password_hash
existing_user.name = admin_info["name"]
existing_user.username = admin_info["email"].split("@")[0]
existing_user.full_name = admin_info["name"]
existing_user.is_active = True
if admin_info["role"] == "super_admin":
existing_user.is_admin = True
# 如果是需要渠道的角色,更新channel_id
if channel_id and admin_info["role"] in ["channel_admin", "billing_admin", "operations_admin"]:
existing_user.channel_id = channel_id
await session.commit()
print(f" ✓ 更新成功: {admin_info['email']}" + (f" (渠道ID: {channel_id})" if channel_id else ""))
return True
# 创建新用户
password_hash = get_password_hash(admin_info["password"])
user = User(
name=admin_info["name"],
email=admin_info["email"],
password_hash=password_hash,
hashed_password=password_hash,
username=admin_info["email"].split("@")[0],
full_name=admin_info["name"],
role=admin_info["role"],
channel_id=channel_id, # 关联渠道ID
is_active=True,
is_admin=(admin_info["role"] == "super_admin"),
status="active",
balance=0,
credit_limit=0,
)
session.add(user)
await session.commit()
await session.refresh(user)
print(f" ✓ 创建成功: {admin_info['email']} (角色: {admin_info['role']})" + (f" (渠道ID: {channel_id})" if channel_id else ""))
return True
except Exception as e:
print(f" ✗ 数据库创建失败: {e}")
import traceback
traceback.print_exc()
return False
finally:
if engine:
await engine.dispose()
async def main():
"""主函数"""
print("="*80)
print("创建管理员账户")
print("="*80)
print()
# 首先尝试登录默认admin账户
print("步骤1: 尝试登录默认管理员账户...")
default_admin_email = "admin@taiji-ai.com"
default_admin_password = "admin123"
token = login_admin(default_admin_email, default_admin_password, "super_admin")
# 如果没有默认admin,尝试创建超级管理员
if not token:
print("\n步骤2: 默认管理员不存在,直接创建超级管理员...")
super_admin = ADMINS[0] # 第一个是超级管理员
success = await create_admin_via_db(super_admin)
if success:
print("\n步骤3: 使用新创建的超级管理员登录...")
token = login_admin(super_admin["email"], super_admin["password"], "super_admin")
else:
print(" ✗ 无法创建超级管理员,请检查数据库连接")
return
if not token:
print(" ✗ 无法获取管理员token,请检查服务是否运行")
return
print(f"\n步骤4: 创建其他管理员账户...")
print("-" * 80)
results = []
for admin in ADMINS:
print(f"\n创建 {admin['name']} ({admin['email']})...")
# 超级管理员和渠道管理员需要直接操作数据库
if admin["role"] in ["super_admin", "channel_admin"]:
success = await create_admin_via_db(admin)
else:
# billing_admin和operations_admin可以通过API创建
success = await create_admin_via_api(token, admin)
results.append({
"name": admin["name"],
"email": admin["email"],
"role": admin["role"],
"success": success
})
# 输出结果汇总
print("\n" + "="*80)
print("创建结果汇总")
print("="*80)
print(f"\n{'角色':<20} {'邮箱':<35} {'状态'}")
print("-" * 80)
for result in results:
status = "✓ 成功" if result["success"] else "✗ 失败"
print(f"{result['name']:<20} {result['email']:<35} {status}")
success_count = sum(1 for r in results if r["success"])
print(f"\n总计: {success_count}/{len(results)} 个账户创建成功")
# 输出账户信息
print("\n" + "="*80)
print("账户信息")
print("="*80)
for admin in ADMINS:
print(f"{admin['name']:<20} | {admin['email']:<35} | 密码: {admin['password']}")
if __name__ == "__main__":
asyncio.run(main())