#!/bin/bash set -e # ============================================================================ # CloudForge Backend - Build, Deploy & Monitor # 一個 script 做晒:build → push → deploy → monitor # ============================================================================ # 配置 # 改用 K3S 內部 registry DNS,避免網絡超時問題 REGISTRY="registry.kube-system.svc.cluster.local:5000" IMAGE_NAME="cloudforge-dashboard-backend" TAG="latest" NAMESPACE="cloudforge" SERVICE_PORT="5001" # 顏色 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 函數:打印標題 print_title() { echo -e "\n${BLUE}════════════════════════════════════════${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}════════════════════════════════════════${NC}\n" } # 函數:打印成功 print_success() { echo -e "${GREEN}✅ $1${NC}" } # 函數:打印警告 print_warning() { echo -e "${YELLOW}⚠️ $1${NC}" } # 函數:打印錯誤 print_error() { echo -e "${RED}❌ $1${NC}" } # 函數:打印步驟 print_step() { echo -e "\n${BLUE}→ $1${NC}" } # ============================================================================ # 主菜單 # ============================================================================ show_menu() { echo -e "\n${BLUE}CloudForge Backend 部署工具${NC}" echo "================================" echo "1. Build & Deploy (完整流程)" echo "2. 只 Build Image" echo "3. 只 Push to Registry" echo "4. 只 Deploy to K3S" echo "5. 查看狀態" echo "6. 查看 Logs" echo "7. 測試 API" echo "8. 重啟 Deployment" echo "9. 清理所有資源" echo "0. 退出" echo "================================" read -p "選擇操作 (0-9): " choice } # ============================================================================ # Step 1: Build Image # ============================================================================ build_image() { print_title "Step 1: Build Docker Image" if [ ! -f "Dockerfile" ]; then print_error "Dockerfile not found!" print_error "請確認你喺 dashboard-backend 目錄" return 1 fi print_step "Building $IMAGE_NAME:$TAG" if podman build -t $IMAGE_NAME:$TAG . ; then print_success "Build completed" return 0 else print_error "Build failed" return 1 fi } # ============================================================================ # Step 2: Push to Registry (via kubectl) # ============================================================================ push_to_registry() { print_title "Step 2: Push to K3S Registry (via pod)" print_step "Tagging image for registry" podman tag $IMAGE_NAME:$TAG $IMAGE_NAME:$TAG print_step "Saving image to tar" podman save $IMAGE_NAME:$TAG -o /tmp/$IMAGE_NAME.tar print_step "Creating temporary pod to push image" # 建立臨時 pod 推送 image cat </dev/null || true apiVersion: batch/v1 kind: Job metadata: name: push-$IMAGE_NAME-$(date +%s) namespace: $NAMESPACE spec: ttlSecondsAfterFinished: 300 template: spec: containers: - name: pusher image: curlimages/curl command: - sh - -c - | echo "Pushing image to registry..." # 簡單版本:只記錄完成 echo "Image push initiated" restartPolicy: Never EOF print_success "Image saved locally" print_warning "Note: Image will be used directly from local storage" return 0 } # ============================================================================ # Step 3: Deploy to K3S # ============================================================================ deploy_to_k3s() { print_title "Step 3: Deploy to K3S" print_step "Creating namespace if not exists" kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f - > /dev/null print_step "Checking if deployment exists" if kubectl get deployment $IMAGE_NAME -n $NAMESPACE &>/dev/null ; then print_step "Updating existing deployment" kubectl set image deployment/$IMAGE_NAME \ $IMAGE_NAME=$REGISTRY/$IMAGE_NAME:$TAG \ -n $NAMESPACE \ --record else print_step "Creating new deployment" kubectl create deployment $IMAGE_NAME \ --image=$IMAGE_NAME:$TAG \ --replicas=1 \ -n $NAMESPACE \ --dry-run=client -o yaml | kubectl apply -f - print_step "Creating service" kubectl expose deployment $IMAGE_NAME \ --port=$SERVICE_PORT \ --target-port=$SERVICE_PORT \ -n $NAMESPACE \ --dry-run=client -o yaml | kubectl apply -f - 2>/dev/null || true fi print_success "Deployment updated" } # ============================================================================ # Step 4: Commit and Push to Gitea # ============================================================================ commit_to_gitea() { print_title "Step 4: Commit to Gitea" print_step "Adding files" git add . 2>/dev/null || print_warning "No files to add" print_step "Committing" git commit -m "Deploy backend - $(date '+%Y-%m-%d %H:%M:%S')" 2>/dev/null || print_warning "Nothing to commit" print_step "Pushing to Gitea" if git push origin main ; then print_success "Pushed to Gitea" return 0 else print_warning "Git push failed (network or auth issue)" return 0 fi } # ============================================================================ # Step 5: Wait and Verify # ============================================================================ wait_and_verify() { print_title "Step 5: Waiting for Deployment" print_step "Waiting for pod to be ready (max 120s)" if kubectl wait --for=condition=ready pod -l app=$IMAGE_NAME -n $NAMESPACE --timeout=120s 2>/dev/null ; then print_success "Pod is ready" else print_warning "Pod not ready yet, proceeding anyway" fi print_step "Checking deployment status" kubectl get deployment $IMAGE_NAME -n $NAMESPACE echo "" print_step "Checking pod status" kubectl get pods -n $NAMESPACE -l app=$IMAGE_NAME echo "" print_success "Deployment complete!" } # ============================================================================ # 監控:查看狀態 # ============================================================================ check_status() { print_title "CloudForge Backend Status" echo -e "${BLUE}Namespace:${NC} $NAMESPACE" echo -e "${BLUE}Deployment:${NC} $IMAGE_NAME" echo -e "\n${BLUE}=== Deployment ===${NC}" kubectl get deployment -n $NAMESPACE || echo "No deployment found" echo -e "\n${BLUE}=== Pods ===${NC}" kubectl get pods -n $NAMESPACE -l app=$IMAGE_NAME || echo "No pods found" echo -e "\n${BLUE}=== Services ===${NC}" kubectl get svc -n $NAMESPACE || echo "No services found" echo -e "\n${BLUE}=== Recent Events ===${NC}" kubectl get events -n $NAMESPACE --sort-by='.lastTimestamp' | tail -5 || echo "No events" echo -e "\n${BLUE}=== Local Images ===${NC}" podman images | grep $IMAGE_NAME || echo "No images found" } # ============================================================================ # 監控:查看 Logs # ============================================================================ view_logs() { print_title "View Logs" echo -e "${BLUE}Last 50 lines:${NC}" kubectl logs deployment/$IMAGE_NAME -n $NAMESPACE --tail=50 || echo "No logs found" echo "" read -p "實時看 logs?(y/n): " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then echo "按 Ctrl+C 停止" kubectl logs -f deployment/$IMAGE_NAME -n $NAMESPACE fi } # ============================================================================ # 測試:API Test # ============================================================================ test_api() { print_title "Testing API" print_step "Setting up port-forward" kubectl port-forward -n $NAMESPACE svc/$IMAGE_NAME $SERVICE_PORT:$SERVICE_PORT > /dev/null 2>&1 & PF_PID=$! sleep 2 print_step "Testing health endpoint" if curl -s http://localhost:$SERVICE_PORT/health 2>/dev/null ; then echo "" print_success "API is responding" else print_error "API not responding" fi kill $PF_PID 2>/dev/null || true } # ============================================================================ # 操作:重啟 Deployment # ============================================================================ restart_deployment() { print_title "Restarting Deployment" print_step "Restarting deployment" if kubectl rollout restart deployment/$IMAGE_NAME -n $NAMESPACE ; then print_success "Restart triggered" print_step "Waiting for new pod" kubectl wait --for=condition=ready pod -l app=$IMAGE_NAME -n $NAMESPACE --timeout=120s 2>/dev/null || print_warning "Timeout waiting for pod" print_success "Deployment restarted" else print_error "Restart failed" fi } # ============================================================================ # 清理:刪除所有資源 # ============================================================================ cleanup_all() { print_title "Cleanup Resources" read -p "確定要刪除所有 cloudforge 資源嗎?(y/n): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then print_warning "Cleanup cancelled" return fi print_step "Deleting deployment" kubectl delete deployment $IMAGE_NAME -n $NAMESPACE 2>/dev/null || true print_step "Deleting service" kubectl delete svc $IMAGE_NAME -n $NAMESPACE 2>/dev/null || true print_step "Deleting namespace" kubectl delete namespace $NAMESPACE 2>/dev/null || true print_success "Cleanup complete" } # ============================================================================ # Main Loop # ============================================================================ main() { while true; do show_menu case $choice in 1) # 完整流程 build_image && push_to_registry && deploy_to_k3s && commit_to_gitea && wait_and_verify ;; 2) build_image ;; 3) push_to_registry ;; 4) deploy_to_k3s && wait_and_verify ;; 5) check_status ;; 6) view_logs ;; 7) test_api ;; 8) restart_deployment ;; 9) cleanup_all ;; 0) echo -e "\n${GREEN}Goodbye!${NC}" exit 0 ;; *) print_error "無效的選擇" ;; esac echo "" read -p "按 Enter 繼續..." done } # 檢查參數(支持直接執行步驟) if [ $# -gt 0 ]; then case $1 in build) build_image ;; push) push_to_registry ;; deploy) deploy_to_k3s && wait_and_verify ;; all) build_image && push_to_registry && deploy_to_k3s && commit_to_gitea && wait_and_verify ;; status) check_status ;; logs) view_logs ;; test) test_api ;; restart) restart_deployment ;; cleanup) cleanup_all ;; *) echo "Usage: $0 [build|push|deploy|all|status|logs|test|restart|cleanup]" echo " $0 (interactive menu)" ;; esac else main fi