Deploy cloudforge-dashboard-backend - 2025-12-09 16:21:44
All checks were successful
Deploy Backend (Manual Build) / deploy (push) Successful in 18s

This commit is contained in:
CloudForge Deploy
2025-12-09 16:21:44 +08:00
parent 52ec8de257
commit 6b6db185e8

579
deploy.sh
View File

@@ -2,428 +2,275 @@
set -e set -e
# ============================================================================ # Configuration
# CloudForge Backend - Build, Deploy & Monitor REGISTRY_URL="fungtest.duckdns.org/cloudforge-dev/cloudforge-dashboard-backend:latest"
# 一個 script 做晒build → push → deploy → monitor GITEA_DOMAIN="fungtest.duckdns.org"
# ============================================================================ GITEA_USERNAME="cloudforge-dev"
GITEA_PASSWORD="a7964adcd821be4c6d7c4ca0b05865030a9d0b82"
# 配置 GITEA_EMAIL="deploy@cloudforge.local"
# 改用 K3S 內部 registry DNS避免網絡超時問題
REGISTRY="registry.kube-system.svc.cluster.local:5000"
IMAGE_NAME="cloudforge-dashboard-backend" IMAGE_NAME="cloudforge-dashboard-backend"
TAG="latest" NAMESPACE="default"
NAMESPACE="cloudforge" PORT="5001"
SERVICE_PORT="5001" GIT_REMOTE="ssh://gitea-cloudforge-dev/cloudforge-dev/dashboard-backend.git"
REPLICAS=1
TIMEOUT=120
# 顏色 # Colors for output
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
# 函數:打印標題 # Functions
print_title() { log() {
echo -e "\n${BLUE}════════════════════════════════════════${NC}" echo -e "${GREEN}${NC} $1"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}════════════════════════════════════════${NC}\n"
} }
# 函數:打印成功 warn() {
print_success() { echo -e "${YELLOW}⚠️ ${NC} $1"
echo -e "${GREEN}$1${NC}"
} }
# 函數:打印警告 error() {
print_warning() { echo -e "${RED}${NC} $1"
echo -e "${YELLOW}⚠️ $1${NC}" exit 1
} }
# 函數:打印錯誤 separator() {
print_error() { echo ""
echo -e "${RED}$1${NC}" echo "════════════════════════════════════════"
echo "$1"
echo "════════════════════════════════════════"
echo ""
} }
# 函數:打印步驟 # Main deployment logic
print_step() { deploy_all() {
echo -e "\n${BLUE}$1${NC}" separator "Step 1: Build Docker Image"
build_image
separator "Step 2: Login to Gitea Registry"
login_registry
separator "Step 3: Push Image to Gitea Registry"
push_image
separator "Step 4: Create K3S Resources"
create_namespace
create_secret
create_deployment
create_service
separator "Step 5: Commit & Push to Git"
commit_to_git
separator "Step 6: Wait for Deployment"
wait_for_deployment
separator "✅ Deployment Complete!"
echo "Image: ${REGISTRY_URL}"
echo "Namespace: ${NAMESPACE}"
echo ""
echo "Verify deployment:"
echo " kubectl get pods -n ${NAMESPACE}"
echo " kubectl describe pod -n ${NAMESPACE} -l app=${IMAGE_NAME}"
} }
# ============================================================================
# 主菜單
# ============================================================================
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() { build_image() {
print_title "Step 1: Build Docker Image" echo "→ Building ${IMAGE_NAME}:latest"
if [ ! -f "Dockerfile" ]; then if [ ! -f "Dockerfile" ]; then
print_error "Dockerfile not found!" error "Dockerfile not found in current directory"
print_error "請確認你喺 dashboard-backend 目錄" fi
return 1
fi
print_step "Building $IMAGE_NAME:$TAG" podman build -t ${IMAGE_NAME}:latest . || docker build -t ${IMAGE_NAME}:latest .
log "Build completed"
if podman build -t $IMAGE_NAME:$TAG . ; then
print_success "Build completed"
return 0
else
print_error "Build failed"
return 1
fi
} }
# ============================================================================ login_registry() {
# Step 2: Push to Registry (via kubectl) echo "→ Authenticating with Gitea Registry at ${GITEA_DOMAIN}"
# ============================================================================
push_to_registry() { echo "${GITEA_PASSWORD}" | podman login -u ${GITEA_USERNAME} --password-stdin ${GITEA_DOMAIN} 2>/dev/null || \
print_title "Step 2: Push to K3S Registry (via pod)" echo "${GITEA_PASSWORD}" | docker login -u ${GITEA_USERNAME} --password-stdin ${GITEA_DOMAIN} 2>/dev/null || \
error "Failed to authenticate with registry"
print_step "Tagging image for registry" log "Registry authentication successful"
podman tag $IMAGE_NAME:$TAG $IMAGE_NAME:$TAG }
print_step "Saving image to tar" push_image() {
podman save $IMAGE_NAME:$TAG -o /tmp/$IMAGE_NAME.tar echo "→ Tagging image for ${REGISTRY_URL}"
podman tag ${IMAGE_NAME}:latest ${REGISTRY_URL} || docker tag ${IMAGE_NAME}:latest ${REGISTRY_URL}
print_step "Creating temporary pod to push image" echo "→ Pushing to registry (this may take a few minutes...)"
podman push ${REGISTRY_URL} || docker push ${REGISTRY_URL}
log "Image pushed successfully"
}
# 建立臨時 pod 推送 image create_namespace() {
cat <<EOF | kubectl apply -f - 2>/dev/null || true echo "→ Creating namespace if not exists"
apiVersion: batch/v1 kubectl create namespace ${NAMESPACE} --dry-run=client -o yaml | kubectl apply -f -
kind: Job log "Namespace ready"
}
create_secret() {
echo "→ Creating image pull secret"
kubectl delete secret gitea-registry -n ${NAMESPACE} 2>/dev/null || true
kubectl create secret docker-registry gitea-registry \
--docker-server=${GITEA_DOMAIN} \
--docker-username=${GITEA_USERNAME} \
--docker-password="${GITEA_PASSWORD}" \
--docker-email=${GITEA_EMAIL} \
-n ${NAMESPACE}
log "Image pull secret created"
}
create_deployment() {
echo "→ Creating deployment"
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata: metadata:
name: push-$IMAGE_NAME-$(date +%s) name: ${IMAGE_NAME}
namespace: $NAMESPACE namespace: ${NAMESPACE}
labels:
app: ${IMAGE_NAME}
spec: spec:
ttlSecondsAfterFinished: 300 replicas: ${REPLICAS}
selector:
matchLabels:
app: ${IMAGE_NAME}
template: template:
metadata:
labels:
app: ${IMAGE_NAME}
spec: spec:
imagePullSecrets:
- name: gitea-registry
containers: containers:
- name: pusher - name: ${IMAGE_NAME}
image: curlimages/curl image: ${REGISTRY_URL}
command: imagePullPolicy: Always
- sh ports:
- -c - containerPort: ${PORT}
- | protocol: TCP
echo "Pushing image to registry..." livenessProbe:
# 簡單版本:只記錄完成 httpGet:
echo "Image push initiated" path: /health
restartPolicy: Never port: ${PORT}
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: ${PORT}
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
EOF EOF
print_success "Image saved locally" log "Deployment created/updated"
print_warning "Note: Image will be used directly from local storage"
return 0
} }
# ============================================================================ create_service() {
# Step 3: Deploy to K3S echo "→ Creating service"
# ============================================================================
deploy_to_k3s() { cat <<EOF | kubectl apply -f -
print_title "Step 3: Deploy to K3S" apiVersion: v1
kind: Service
metadata:
name: ${IMAGE_NAME}
namespace: ${NAMESPACE}
labels:
app: ${IMAGE_NAME}
spec:
type: ClusterIP
ports:
- port: ${PORT}
targetPort: ${PORT}
protocol: TCP
selector:
app: ${IMAGE_NAME}
EOF
print_step "Creating namespace if not exists" log "Service created/updated"
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"
} }
# ============================================================================ wait_for_deployment() {
# Step 4: Commit and Push to Gitea echo "→ Waiting for deployment to be ready (max ${TIMEOUT}s)"
# ============================================================================
commit_to_gitea() { if kubectl wait --for=condition=available --timeout=${TIMEOUT}s \
print_title "Step 4: Commit to Gitea" deployment/${IMAGE_NAME} -n ${NAMESPACE} 2>/dev/null; then
log "Deployment is ready!"
else
warn "Deployment not ready within ${TIMEOUT}s, checking status..."
print_step "Adding files" echo ""
git add . 2>/dev/null || print_warning "No files to add" echo "Deployment status:"
kubectl get deployment -n ${NAMESPACE} ${IMAGE_NAME}
print_step "Committing" echo ""
git commit -m "Deploy backend - $(date '+%Y-%m-%d %H:%M:%S')" 2>/dev/null || print_warning "Nothing to commit" echo "Pod status:"
kubectl get pods -n ${NAMESPACE} -l app=${IMAGE_NAME}
print_step "Pushing to Gitea" echo ""
if git push origin main ; then echo "Pod details:"
print_success "Pushed to Gitea" kubectl describe pod -n ${NAMESPACE} -l app=${IMAGE_NAME} | tail -20
return 0 fi
else
print_warning "Git push failed (network or auth issue)"
return 0
fi
} }
# ============================================================================ commit_to_git() {
# Step 5: Wait and Verify echo "→ Committing changes to Git"
# ============================================================================
wait_and_verify() { git config user.email "deploy@cloudforge.local"
print_title "Step 5: Waiting for Deployment" git config user.name "CloudForge Deploy"
print_step "Waiting for pod to be ready (max 120s)" git add -A
if kubectl wait --for=condition=ready pod -l app=$IMAGE_NAME -n $NAMESPACE --timeout=120s 2>/dev/null ; then git commit -m "Deploy ${IMAGE_NAME} - $(date '+%Y-%m-%d %H:%M:%S')" || warn "Nothing to commit"
print_success "Pod is ready"
else
print_warning "Pod not ready yet, proceeding anyway"
fi
print_step "Checking deployment status" echo "→ Pushing to Gitea"
kubectl get deployment $IMAGE_NAME -n $NAMESPACE git push origin main || git push origin master || error "Failed to push to Git"
echo "" log "Committed and pushed to Git"
print_step "Checking pod status"
kubectl get pods -n $NAMESPACE -l app=$IMAGE_NAME
echo ""
print_success "Deployment complete!"
} }
# ============================================================================ # Main logic
# 監控:查看狀態 case "${1:-all}" in
# ============================================================================ all)
deploy_all
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)
build_image build_image
;; ;;
3) login)
push_to_registry login_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)
push_to_registry build_image
;; login_registry
push_image
;;
deploy) deploy)
deploy_to_k3s && wait_and_verify create_namespace
;; create_secret
all) create_deployment
build_image && push_to_registry && deploy_to_k3s && commit_to_gitea && wait_and_verify create_service
;; wait_for_deployment
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 "Usage: ./deploy.sh [all|build|login|push|deploy]"
echo " $0 (interactive menu)" exit 1
;; ;;
esac esac
else
main
fi