forked from xiaohei/taiji-AI-PAD
415 lines
14 KiB
Bash
Executable File
415 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
|
# ============================================================
|
|
# Taiji AI-PAD AKS 集群迁移脚本
|
|
# 从旧订阅 (Xmind运营学习专用01) 迁移到新订阅 (Xmind运营学习专用2026)
|
|
# ============================================================
|
|
|
|
set -e
|
|
|
|
# ==================== 配置变量 ====================
|
|
# 旧集群配置
|
|
OLD_SUBSCRIPTION="旧订阅ID" # 请替换为实际的旧订阅ID
|
|
OLD_RESOURCE_GROUP="Xmind运营学习专用01"
|
|
OLD_AKS_NAME="taiji-ai-pda"
|
|
|
|
# 新集群配置
|
|
NEW_SUBSCRIPTION="新订阅ID" # 请替换为实际的新订阅ID
|
|
NEW_RESOURCE_GROUP="Xmind运营学习专用2026"
|
|
NEW_AKS_NAME="taiji-ai-pda"
|
|
|
|
# ACR 配置
|
|
ACR_NAME="taiji"
|
|
ACR_LOGIN_SERVER="${ACR_NAME}.azurecr.io"
|
|
|
|
# 需要迁移的命名空间列表(用空格分隔)
|
|
NAMESPACES="taiji-ai"
|
|
|
|
# 备份目录
|
|
BACKUP_DIR="./migration-backup-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
# 颜色输出
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
# ==================== 辅助函数 ====================
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
check_prerequisites() {
|
|
log_info "检查必要工具..."
|
|
|
|
command -v az >/dev/null 2>&1 || { log_error "需要安装 Azure CLI"; exit 1; }
|
|
command -v kubectl >/dev/null 2>&1 || { log_error "需要安装 kubectl"; exit 1; }
|
|
command -v jq >/dev/null 2>&1 || { log_error "需要安装 jq"; exit 1; }
|
|
|
|
log_success "所有必要工具已安装"
|
|
}
|
|
|
|
# ==================== 阶段 1: 从旧集群导出资源 ====================
|
|
export_from_old_cluster() {
|
|
log_info "========== 阶段 1: 从旧集群导出资源 =========="
|
|
|
|
# 创建备份目录
|
|
mkdir -p "${BACKUP_DIR}"
|
|
|
|
# 切换到旧订阅
|
|
log_info "切换到旧订阅..."
|
|
az account set --subscription "${OLD_SUBSCRIPTION}"
|
|
|
|
# 获取旧集群凭据
|
|
log_info "获取旧 AKS 集群凭据..."
|
|
az aks get-credentials \
|
|
--resource-group "${OLD_RESOURCE_GROUP}" \
|
|
--name "${OLD_AKS_NAME}" \
|
|
--overwrite-existing \
|
|
--context "old-cluster"
|
|
|
|
# 设置 kubectl 上下文
|
|
kubectl config use-context "old-cluster" 2>/dev/null || \
|
|
kubectl config use-context "${OLD_AKS_NAME}"
|
|
|
|
log_info "当前集群信息:"
|
|
kubectl cluster-info
|
|
|
|
# 导出每个命名空间的资源
|
|
for NS in ${NAMESPACES}; do
|
|
log_info "导出命名空间 ${NS} 的资源..."
|
|
mkdir -p "${BACKUP_DIR}/${NS}"
|
|
|
|
# 导出命名空间定义
|
|
kubectl get namespace ${NS} -o yaml > "${BACKUP_DIR}/${NS}/namespace.yaml" 2>/dev/null || true
|
|
|
|
# 导出 Deployments
|
|
kubectl get deployments -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/deployments.yaml" 2>/dev/null || true
|
|
|
|
# 导出 Services
|
|
kubectl get services -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/services.yaml" 2>/dev/null || true
|
|
|
|
# 导出 ConfigMaps
|
|
kubectl get configmaps -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/configmaps.yaml" 2>/dev/null || true
|
|
|
|
# 导出 Secrets
|
|
kubectl get secrets -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/secrets.yaml" 2>/dev/null || true
|
|
|
|
# 导出 Ingress
|
|
kubectl get ingress -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/ingress.yaml" 2>/dev/null || true
|
|
|
|
# 导出 PersistentVolumeClaims
|
|
kubectl get pvc -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/pvc.yaml" 2>/dev/null || true
|
|
|
|
# 导出 ServiceAccounts
|
|
kubectl get serviceaccounts -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/serviceaccounts.yaml" 2>/dev/null || true
|
|
|
|
# 导出 HorizontalPodAutoscalers
|
|
kubectl get hpa -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/hpa.yaml" 2>/dev/null || true
|
|
|
|
# 导出 NetworkPolicies
|
|
kubectl get networkpolicies -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/networkpolicies.yaml" 2>/dev/null || true
|
|
|
|
# 导出 CronJobs
|
|
kubectl get cronjobs -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/cronjobs.yaml" 2>/dev/null || true
|
|
|
|
# 导出 StatefulSets
|
|
kubectl get statefulsets -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/statefulsets.yaml" 2>/dev/null || true
|
|
|
|
# 导出 DaemonSets
|
|
kubectl get daemonsets -n ${NS} -o yaml > "${BACKUP_DIR}/${NS}/daemonsets.yaml" 2>/dev/null || true
|
|
|
|
log_success "命名空间 ${NS} 资源导出完成"
|
|
done
|
|
|
|
# 导出集群级别的资源
|
|
log_info "导出集群级别资源..."
|
|
mkdir -p "${BACKUP_DIR}/cluster-resources"
|
|
|
|
# 导出 ClusterRoles
|
|
kubectl get clusterroles -o yaml > "${BACKUP_DIR}/cluster-resources/clusterroles.yaml" 2>/dev/null || true
|
|
|
|
# 导出 ClusterRoleBindings
|
|
kubectl get clusterrolebindings -o yaml > "${BACKUP_DIR}/cluster-resources/clusterrolebindings.yaml" 2>/dev/null || true
|
|
|
|
# 导出 StorageClasses
|
|
kubectl get storageclasses -o yaml > "${BACKUP_DIR}/cluster-resources/storageclasses.yaml" 2>/dev/null || true
|
|
|
|
# 导出 PersistentVolumes
|
|
kubectl get pv -o yaml > "${BACKUP_DIR}/cluster-resources/persistentvolumes.yaml" 2>/dev/null || true
|
|
|
|
# 记录当前 Pod 状态
|
|
log_info "记录当前 Pod 状态..."
|
|
for NS in ${NAMESPACES}; do
|
|
kubectl get pods -n ${NS} -o wide > "${BACKUP_DIR}/${NS}/pods-status.txt" 2>/dev/null || true
|
|
kubectl get svc -n ${NS} -o wide > "${BACKUP_DIR}/${NS}/services-status.txt" 2>/dev/null || true
|
|
done
|
|
|
|
# 获取 LoadBalancer IP
|
|
log_info "记录 LoadBalancer IP..."
|
|
kubectl get svc --all-namespaces -o wide | grep LoadBalancer > "${BACKUP_DIR}/loadbalancer-ips.txt" 2>/dev/null || true
|
|
|
|
log_success "阶段 1 完成: 所有资源已导出到 ${BACKUP_DIR}"
|
|
}
|
|
|
|
# ==================== 阶段 2: 准备新集群 ====================
|
|
prepare_new_cluster() {
|
|
log_info "========== 阶段 2: 准备新集群 =========="
|
|
|
|
# 切换到新订阅
|
|
log_info "切换到新订阅..."
|
|
az account set --subscription "${NEW_SUBSCRIPTION}"
|
|
|
|
# 获取新集群凭据
|
|
log_info "获取新 AKS 集群凭据..."
|
|
az aks get-credentials \
|
|
--resource-group "${NEW_RESOURCE_GROUP}" \
|
|
--name "${NEW_AKS_NAME}" \
|
|
--overwrite-existing \
|
|
--context "new-cluster"
|
|
|
|
# 设置 kubectl 上下文
|
|
kubectl config use-context "new-cluster" 2>/dev/null || \
|
|
kubectl config use-context "${NEW_AKS_NAME}"
|
|
|
|
log_info "新集群信息:"
|
|
kubectl cluster-info
|
|
|
|
# 附加 ACR 到新 AKS
|
|
log_info "附加 ACR 到新 AKS 集群..."
|
|
az aks update \
|
|
--resource-group "${NEW_RESOURCE_GROUP}" \
|
|
--name "${NEW_AKS_NAME}" \
|
|
--attach-acr "${ACR_NAME}" || log_warn "ACR 附加可能已存在或需要手动处理"
|
|
|
|
log_success "阶段 2 完成: 新集群已准备就绪"
|
|
}
|
|
|
|
# ==================== 阶段 3: 部署到新集群 ====================
|
|
deploy_to_new_cluster() {
|
|
log_info "========== 阶段 3: 部署到新集群 =========="
|
|
|
|
# 确保使用新集群上下文
|
|
kubectl config use-context "new-cluster" 2>/dev/null || \
|
|
kubectl config use-context "${NEW_AKS_NAME}"
|
|
|
|
# 使用项目中的 k8s 配置部署
|
|
log_info "使用项目 k8s 配置部署..."
|
|
|
|
# 创建命名空间
|
|
kubectl apply -f k8s/namespace.yaml
|
|
|
|
# 部署 Secrets 和 ConfigMap
|
|
kubectl apply -f k8s/secrets.yaml
|
|
kubectl apply -f k8s/configmap.yaml
|
|
|
|
# 部署 NATS
|
|
kubectl apply -f k8s/nats.yaml
|
|
|
|
# 等待 NATS 就绪
|
|
log_info "等待 NATS 就绪..."
|
|
kubectl wait --for=condition=ready pod -l app=nats -n taiji-ai --timeout=120s || true
|
|
|
|
# 部署应用服务
|
|
kubectl apply -f k8s/litellm-gateway.yaml
|
|
kubectl apply -f k8s/data-ingestion.yaml
|
|
kubectl apply -f k8s/mcp-server.yaml
|
|
kubectl apply -f k8s/api-gateway.yaml
|
|
|
|
# 部署监控
|
|
kubectl apply -f k8s/monitoring.yaml
|
|
|
|
# 如果有 MCP Server 的独立 ingress,也部署
|
|
if [ -f "services/mcp-server/k8s/ingress.yaml" ]; then
|
|
kubectl apply -f services/mcp-server/k8s/deployment.yaml || true
|
|
kubectl apply -f services/mcp-server/k8s/secrets.yaml || true
|
|
kubectl apply -f services/mcp-server/k8s/ingress.yaml || true
|
|
fi
|
|
|
|
# 等待所有服务就绪
|
|
log_info "等待所有服务就绪..."
|
|
for NS in ${NAMESPACES}; do
|
|
kubectl wait --for=condition=ready pods --all -n ${NS} --timeout=300s || log_warn "部分 Pod 可能未就绪"
|
|
done
|
|
|
|
log_success "阶段 3 完成: 应用已部署到新集群"
|
|
}
|
|
|
|
# ==================== 阶段 4: 验证新集群 ====================
|
|
verify_new_cluster() {
|
|
log_info "========== 阶段 4: 验证新集群 =========="
|
|
|
|
# 确保使用新集群上下文
|
|
kubectl config use-context "new-cluster" 2>/dev/null || \
|
|
kubectl config use-context "${NEW_AKS_NAME}"
|
|
|
|
log_info "检查 Pod 状态..."
|
|
for NS in ${NAMESPACES}; do
|
|
echo ""
|
|
log_info "命名空间 ${NS} 的 Pod 状态:"
|
|
kubectl get pods -n ${NS} -o wide
|
|
|
|
echo ""
|
|
log_info "命名空间 ${NS} 的 Service 状态:"
|
|
kubectl get svc -n ${NS} -o wide
|
|
done
|
|
|
|
# 获取新的 LoadBalancer IP
|
|
log_info "获取新的 LoadBalancer IP..."
|
|
NEW_LB_IP=""
|
|
for i in {1..30}; do
|
|
NEW_LB_IP=$(kubectl get svc api-gateway -n taiji-ai -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null)
|
|
if [ -n "$NEW_LB_IP" ]; then
|
|
break
|
|
fi
|
|
log_info "等待 LoadBalancer IP 分配... ($i/30)"
|
|
sleep 10
|
|
done
|
|
|
|
if [ -n "$NEW_LB_IP" ]; then
|
|
log_success "新 LoadBalancer IP: ${NEW_LB_IP}"
|
|
echo ""
|
|
echo -e "${GREEN}========================================${NC}"
|
|
echo -e "${GREEN}新集群访问地址:${NC}"
|
|
echo -e " - API Gateway: http://${NEW_LB_IP}"
|
|
echo -e " - MCP Server: http://${NEW_LB_IP}/api/mcp/"
|
|
echo -e " - Data Ingestion: http://${NEW_LB_IP}/api/data/"
|
|
echo -e " - LiteLLM Gateway: http://${NEW_LB_IP}/api/llm/"
|
|
echo -e "${GREEN}========================================${NC}"
|
|
|
|
# 保存新 IP 到文件
|
|
echo "${NEW_LB_IP}" > "${BACKUP_DIR}/new-loadbalancer-ip.txt"
|
|
else
|
|
log_warn "LoadBalancer IP 尚未分配,请稍后检查"
|
|
fi
|
|
|
|
log_success "阶段 4 完成: 新集群验证完毕"
|
|
}
|
|
|
|
# ==================== 阶段 5: DNS 切换指南 ====================
|
|
dns_switch_guide() {
|
|
log_info "========== 阶段 5: DNS 切换指南 =========="
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}========================================${NC}"
|
|
echo -e "${YELLOW}DNS 切换步骤:${NC}"
|
|
echo -e "${YELLOW}========================================${NC}"
|
|
echo ""
|
|
echo "1. 获取新集群的 LoadBalancer IP:"
|
|
echo " kubectl get svc api-gateway -n taiji-ai -o jsonpath='{.status.loadBalancer.ingress[0].ip}'"
|
|
echo ""
|
|
echo "2. 登录 Azure Portal 或你的 DNS 提供商控制台"
|
|
echo ""
|
|
echo "3. 更新 DNS A 记录,将域名指向新 IP"
|
|
echo ""
|
|
echo "4. 如果使用 Azure DNS Zone:"
|
|
echo " az network dns record-set a update \\"
|
|
echo " --resource-group <DNS_RESOURCE_GROUP> \\"
|
|
echo " --zone-name <YOUR_DOMAIN> \\"
|
|
echo " --name <SUBDOMAIN> \\"
|
|
echo " --set aRecords[0].ipv4Address=<NEW_IP>"
|
|
echo ""
|
|
echo "5. 验证 DNS 解析:"
|
|
echo " nslookup <YOUR_DOMAIN>"
|
|
echo " dig <YOUR_DOMAIN>"
|
|
echo ""
|
|
echo "6. 等待 DNS 传播 (通常 5-30 分钟,取决于 TTL)"
|
|
echo ""
|
|
echo -e "${YELLOW}========================================${NC}"
|
|
}
|
|
|
|
# ==================== 阶段 6: 清理旧集群 ====================
|
|
cleanup_old_cluster() {
|
|
log_info "========== 阶段 6: 清理旧集群 (可选) =========="
|
|
|
|
echo ""
|
|
log_warn "警告: 以下操作将删除旧集群的资源,请确保新集群已完全正常运行!"
|
|
echo ""
|
|
|
|
read -p "确认要清理旧集群吗? (yes/no): " confirm
|
|
if [ "$confirm" != "yes" ]; then
|
|
log_info "跳过旧集群清理"
|
|
return
|
|
fi
|
|
|
|
# 切换到旧订阅
|
|
az account set --subscription "${OLD_SUBSCRIPTION}"
|
|
|
|
# 获取旧集群凭据
|
|
az aks get-credentials \
|
|
--resource-group "${OLD_RESOURCE_GROUP}" \
|
|
--name "${OLD_AKS_NAME}" \
|
|
--overwrite-existing
|
|
|
|
# 缩容旧集群的 Deployments
|
|
log_info "缩容旧集群的 Deployments..."
|
|
for NS in ${NAMESPACES}; do
|
|
kubectl scale deployment --all --replicas=0 -n ${NS} || true
|
|
done
|
|
|
|
log_success "旧集群已缩容,可以在确认一切正常后删除旧订阅"
|
|
}
|
|
|
|
# ==================== 主函数 ====================
|
|
main() {
|
|
echo ""
|
|
echo -e "${GREEN}============================================================${NC}"
|
|
echo -e "${GREEN} Taiji AI-PAD AKS 集群迁移工具 ${NC}"
|
|
echo -e "${GREEN} 从: ${OLD_RESOURCE_GROUP} -> 到: ${NEW_RESOURCE_GROUP} ${NC}"
|
|
echo -e "${GREEN}============================================================${NC}"
|
|
echo ""
|
|
|
|
check_prerequisites
|
|
|
|
echo ""
|
|
echo "请选择要执行的操作:"
|
|
echo " 1) 完整迁移 (阶段1-5)"
|
|
echo " 2) 仅导出旧集群资源 (阶段1)"
|
|
echo " 3) 仅部署到新集群 (阶段2-3)"
|
|
echo " 4) 仅验证新集群 (阶段4)"
|
|
echo " 5) 查看 DNS 切换指南 (阶段5)"
|
|
echo " 6) 清理旧集群 (阶段6)"
|
|
echo ""
|
|
|
|
read -p "请输入选项 [1-6]: " choice
|
|
|
|
case $choice in
|
|
1)
|
|
export_from_old_cluster
|
|
prepare_new_cluster
|
|
deploy_to_new_cluster
|
|
verify_new_cluster
|
|
dns_switch_guide
|
|
;;
|
|
2)
|
|
export_from_old_cluster
|
|
;;
|
|
3)
|
|
prepare_new_cluster
|
|
deploy_to_new_cluster
|
|
;;
|
|
4)
|
|
verify_new_cluster
|
|
;;
|
|
5)
|
|
dns_switch_guide
|
|
;;
|
|
6)
|
|
cleanup_old_cluster
|
|
;;
|
|
*)
|
|
log_error "无效选项"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
log_success "迁移操作完成!"
|
|
echo ""
|
|
}
|
|
|
|
# 运行主函数
|
|
main "$@"
|
|
|