Files
taiji-AI-PAD/scripts/aks-migration.sh
2026-02-02 14:08:13 +00:00

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 "$@"