Some checks failed
CD Pipeline / deploy (push) Failing after 59s
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml) - 部署模式: rsync Python 檔案至 188 → docker restart (volume mount) - Dockerfile/requirements 變動時自動重建 Docker image - 部署通知: Telegram (開始/成功/失敗) - 健康檢查: https://mo.wooo.work/health (最多 5 次重試) - 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
418 lines
13 KiB
Bash
Executable File
418 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
||
# =============================================================================
|
||
# Docker Registry CI/CD 驗證腳本
|
||
# 用途: 驗證新的 Registry-based CI/CD 流程是否正常運作
|
||
# =============================================================================
|
||
|
||
set -e
|
||
|
||
# 顏色
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m'
|
||
|
||
# 配置
|
||
REGISTRY_URL="registry.wooo.work"
|
||
REGISTRY_LOCAL_URL="http://127.0.0.1:5000"
|
||
UAT_HOST="192.168.0.110"
|
||
UAT_USER="wooo"
|
||
GCP_HOST="35.194.233.141"
|
||
GCP_USER="wooo"
|
||
|
||
# 計數器
|
||
PASSED=0
|
||
FAILED=0
|
||
WARNINGS=0
|
||
|
||
# =============================================================================
|
||
# 輸出函數
|
||
# =============================================================================
|
||
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||
log_pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASSED++)); }
|
||
log_fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAILED++)); }
|
||
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; ((WARNINGS++)); }
|
||
|
||
section() {
|
||
echo ""
|
||
echo "=============================================="
|
||
echo -e "${BLUE}$1${NC}"
|
||
echo "=============================================="
|
||
}
|
||
|
||
# =============================================================================
|
||
# 1. 本地環境檢查
|
||
# =============================================================================
|
||
check_local_env() {
|
||
section "1. 本地環境檢查"
|
||
|
||
# Docker
|
||
if command -v docker &> /dev/null; then
|
||
log_pass "Docker 已安裝: $(docker --version | cut -d' ' -f3)"
|
||
else
|
||
log_fail "Docker 未安裝"
|
||
fi
|
||
|
||
# Git
|
||
if command -v git &> /dev/null; then
|
||
log_pass "Git 已安裝: $(git --version | cut -d' ' -f3)"
|
||
else
|
||
log_fail "Git 未安裝"
|
||
fi
|
||
|
||
# SSH
|
||
if command -v ssh &> /dev/null; then
|
||
log_pass "SSH 已安裝"
|
||
else
|
||
log_fail "SSH 未安裝"
|
||
fi
|
||
|
||
# curl
|
||
if command -v curl &> /dev/null; then
|
||
log_pass "curl 已安裝"
|
||
else
|
||
log_fail "curl 未安裝"
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 2. SSH 連線檢查
|
||
# =============================================================================
|
||
check_ssh_connections() {
|
||
section "2. SSH 連線檢查"
|
||
|
||
# UAT
|
||
if ssh -o BatchMode=yes -o ConnectTimeout=5 ${UAT_USER}@${UAT_HOST} "echo 'ok'" &> /dev/null; then
|
||
log_pass "UAT SSH 連線正常 (${UAT_HOST})"
|
||
else
|
||
log_fail "UAT SSH 連線失敗 (${UAT_HOST})"
|
||
fi
|
||
|
||
# GCP
|
||
if ssh -o BatchMode=yes -o ConnectTimeout=5 ${GCP_USER}@${GCP_HOST} "echo 'ok'" &> /dev/null; then
|
||
log_pass "GCP SSH 連線正常 (${GCP_HOST})"
|
||
else
|
||
log_warn "GCP SSH 連線失敗 (${GCP_HOST}) - 可能需要設定 SSH Key"
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 3. Registry 服務檢查
|
||
# =============================================================================
|
||
check_registry_service() {
|
||
section "3. Registry 服務檢查"
|
||
|
||
# 本地連線測試 (透過 SSH 到 UAT)
|
||
log_info "檢查 Registry 本地連線 (127.0.0.1:5000)..."
|
||
local local_status=$(ssh ${UAT_USER}@${UAT_HOST} "curl -s -o /dev/null -w '%{http_code}' ${REGISTRY_LOCAL_URL}/v2/ --max-time 10" 2>/dev/null || echo "000")
|
||
|
||
if [[ "$local_status" == "200" ]]; then
|
||
log_pass "Registry 本地連線正常 (HTTP $local_status)"
|
||
else
|
||
log_fail "Registry 本地連線失敗 (HTTP $local_status)"
|
||
fi
|
||
|
||
# 外部 HTTPS 連線測試
|
||
log_info "檢查 Registry 外部連線 (https://${REGISTRY_URL})..."
|
||
local external_status=$(curl -s -o /dev/null -w "%{http_code}" "https://${REGISTRY_URL}/v2/" --max-time 10 2>/dev/null || echo "000")
|
||
|
||
if [[ "$external_status" == "401" ]]; then
|
||
log_pass "Registry 外部連線正常 (需要認證, HTTP 401)"
|
||
elif [[ "$external_status" == "200" ]]; then
|
||
log_pass "Registry 外部連線正常 (HTTP 200)"
|
||
else
|
||
log_fail "Registry 外部連線失敗 (HTTP $external_status)"
|
||
fi
|
||
|
||
# Docker 登入測試 (如果有認證資訊)
|
||
if [[ -n "$REGISTRY_USER" && -n "$REGISTRY_PASSWORD" ]]; then
|
||
log_info "測試 Docker 登入..."
|
||
if echo "$REGISTRY_PASSWORD" | docker login "$REGISTRY_URL" -u "$REGISTRY_USER" --password-stdin &> /dev/null; then
|
||
log_pass "Docker 登入成功"
|
||
docker logout "$REGISTRY_URL" &> /dev/null
|
||
else
|
||
log_fail "Docker 登入失敗"
|
||
fi
|
||
else
|
||
log_warn "未設定 REGISTRY_USER/REGISTRY_PASSWORD,跳過登入測試"
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 4. K8s 服務狀態檢查
|
||
# =============================================================================
|
||
check_k8s_services() {
|
||
section "4. K8s 服務狀態檢查 (UAT)"
|
||
|
||
# 檢查 Pod 狀態
|
||
log_info "檢查 K8s Pod 狀態..."
|
||
local pods=$(ssh ${UAT_USER}@${UAT_HOST} "kubectl get pods -n momo -o wide 2>/dev/null" || echo "error")
|
||
|
||
if [[ "$pods" == "error" ]]; then
|
||
log_fail "無法取得 K8s Pod 狀態"
|
||
return
|
||
fi
|
||
|
||
echo "$pods"
|
||
echo ""
|
||
|
||
# momo-app
|
||
if echo "$pods" | grep -q "momo-app.*Running"; then
|
||
log_pass "momo-app Pod 運行中"
|
||
else
|
||
log_fail "momo-app Pod 異常"
|
||
fi
|
||
|
||
# momo-scheduler
|
||
if echo "$pods" | grep -q "momo-scheduler.*Running"; then
|
||
log_pass "momo-scheduler Pod 運行中"
|
||
else
|
||
log_fail "momo-scheduler Pod 異常"
|
||
fi
|
||
|
||
# momo-postgres
|
||
if echo "$pods" | grep -q "momo-postgres.*Running"; then
|
||
log_pass "momo-postgres Pod 運行中"
|
||
else
|
||
log_fail "momo-postgres Pod 異常"
|
||
fi
|
||
|
||
# 檢查 Registry Secret
|
||
log_info "檢查 Registry Secret..."
|
||
if ssh ${UAT_USER}@${UAT_HOST} "kubectl get secret registry-secret -n momo &>/dev/null"; then
|
||
log_pass "registry-secret 已建立"
|
||
else
|
||
log_warn "registry-secret 不存在,需要建立"
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 5. 應用健康檢查
|
||
# =============================================================================
|
||
check_app_health() {
|
||
section "5. 應用健康檢查"
|
||
|
||
# UAT
|
||
log_info "檢查 UAT 應用 (https://mo.wooo.work)..."
|
||
local uat_health=$(curl -s "https://mo.wooo.work/health" --max-time 10 2>/dev/null || echo "error")
|
||
|
||
if echo "$uat_health" | grep -q "healthy"; then
|
||
log_pass "UAT 應用健康檢查通過"
|
||
else
|
||
log_fail "UAT 應用健康檢查失敗"
|
||
fi
|
||
|
||
# GCP
|
||
log_info "檢查 GCP 應用 (https://momo.wooo.work)..."
|
||
local gcp_health=$(curl -s "https://momo.wooo.work/health" --max-time 10 2>/dev/null || echo "error")
|
||
|
||
if echo "$gcp_health" | grep -q "healthy"; then
|
||
log_pass "GCP 應用健康檢查通過"
|
||
else
|
||
log_warn "GCP 應用健康檢查失敗 (可能尚未部署)"
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 6. CI/CD 配置檢查
|
||
# =============================================================================
|
||
check_cicd_config() {
|
||
section "6. CI/CD 配置檢查"
|
||
|
||
# .gitlab-ci.yml
|
||
if [[ -f ".gitlab-ci.yml" ]]; then
|
||
log_pass ".gitlab-ci.yml 存在"
|
||
|
||
# 檢查 Registry URL
|
||
if grep -q "REGISTRY_URL.*registry.wooo.work" .gitlab-ci.yml; then
|
||
log_pass "REGISTRY_URL 配置正確"
|
||
else
|
||
log_fail "REGISTRY_URL 配置錯誤或缺失"
|
||
fi
|
||
|
||
# 檢查是否還有 Harbor 引用
|
||
if grep -qi "harbor" .gitlab-ci.yml; then
|
||
log_fail "仍有 Harbor 引用存在於 .gitlab-ci.yml"
|
||
else
|
||
log_pass "無 Harbor 引用"
|
||
fi
|
||
else
|
||
log_fail ".gitlab-ci.yml 不存在"
|
||
fi
|
||
|
||
# K8s 配置
|
||
for file in "k8s/04-momo-app.yaml" "k8s/05-scheduler.yaml"; do
|
||
if [[ -f "$file" ]]; then
|
||
if grep -q "registry.wooo.work" "$file"; then
|
||
log_pass "$file 使用正確的 Registry"
|
||
else
|
||
log_fail "$file 未使用 registry.wooo.work"
|
||
fi
|
||
|
||
if grep -qi "harbor" "$file"; then
|
||
log_fail "$file 仍有 Harbor 引用"
|
||
fi
|
||
else
|
||
log_warn "$file 不存在"
|
||
fi
|
||
done
|
||
|
||
# docker-compose.yml
|
||
if [[ -f "docker-compose.yml" ]]; then
|
||
if grep -qi "harbor" docker-compose.yml; then
|
||
log_fail "docker-compose.yml 仍有 Harbor 引用"
|
||
else
|
||
log_pass "docker-compose.yml 無 Harbor 引用"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 7. Registry 映像檢查
|
||
# =============================================================================
|
||
check_registry_images() {
|
||
section "7. Registry 映像檢查"
|
||
|
||
if [[ -z "$REGISTRY_USER" || -z "$REGISTRY_PASSWORD" ]]; then
|
||
log_warn "未設定認證資訊,跳過映像檢查"
|
||
return
|
||
fi
|
||
|
||
log_info "列出 Registry 中的映像..."
|
||
local catalog=$(curl -s -u "${REGISTRY_USER}:${REGISTRY_PASSWORD}" \
|
||
"https://${REGISTRY_URL}/v2/_catalog" --max-time 10 2>/dev/null)
|
||
|
||
if echo "$catalog" | grep -q "repositories"; then
|
||
log_pass "可以存取 Registry catalog"
|
||
echo "$catalog" | jq . 2>/dev/null || echo "$catalog"
|
||
else
|
||
log_fail "無法存取 Registry catalog"
|
||
fi
|
||
|
||
# 檢查主映像
|
||
log_info "檢查 wooo/momo-pro-system 映像..."
|
||
local tags=$(curl -s -u "${REGISTRY_USER}:${REGISTRY_PASSWORD}" \
|
||
"https://${REGISTRY_URL}/v2/wooo/momo-pro-system/tags/list" --max-time 10 2>/dev/null)
|
||
|
||
if echo "$tags" | grep -q "tags"; then
|
||
log_pass "wooo/momo-pro-system 映像存在"
|
||
echo "$tags" | jq . 2>/dev/null || echo "$tags"
|
||
else
|
||
log_warn "wooo/momo-pro-system 映像尚未推送"
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 8. 監控腳本檢查
|
||
# =============================================================================
|
||
check_monitoring_scripts() {
|
||
section "8. 監控腳本檢查"
|
||
|
||
# Registry 健康監控
|
||
if [[ -f "scripts/registry_health_monitor.sh" ]]; then
|
||
log_pass "Registry 健康監控腳本存在"
|
||
else
|
||
log_fail "Registry 健康監控腳本不存在"
|
||
fi
|
||
|
||
# 部署腳本庫
|
||
if [[ -f "deploy/lib/registry.sh" ]]; then
|
||
log_pass "Registry 部署函數庫存在"
|
||
else
|
||
log_fail "Registry 部署函數庫不存在"
|
||
fi
|
||
|
||
# Nginx 配置
|
||
if [[ -f "config/nginx/sites-available/registry" ]]; then
|
||
log_pass "Registry Nginx 配置存在"
|
||
else
|
||
log_warn "Registry Nginx 配置不存在"
|
||
fi
|
||
|
||
# Docker Compose
|
||
if [[ -f "docker/registry/docker-compose.yml" ]]; then
|
||
log_pass "Registry docker-compose.yml 存在"
|
||
else
|
||
log_fail "Registry docker-compose.yml 不存在"
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 9. 全域 Harbor 引用檢查
|
||
# =============================================================================
|
||
check_harbor_references() {
|
||
section "9. 全域 Harbor 引用檢查"
|
||
|
||
log_info "搜尋所有檔案中的 Harbor 引用..."
|
||
|
||
local harbor_files=$(grep -ril "harbor" \
|
||
--include="*.py" \
|
||
--include="*.yaml" \
|
||
--include="*.yml" \
|
||
--include="*.sh" \
|
||
--include="*.html" \
|
||
--include="*.md" \
|
||
. 2>/dev/null | grep -v ".git" | grep -v "node_modules" | grep -v "__pycache__" || true)
|
||
|
||
if [[ -z "$harbor_files" ]]; then
|
||
log_pass "無 Harbor 引用存在"
|
||
else
|
||
log_warn "以下檔案仍有 Harbor 引用:"
|
||
echo "$harbor_files" | while read -r file; do
|
||
echo " - $file"
|
||
done
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 總結報告
|
||
# =============================================================================
|
||
summary() {
|
||
section "驗證報告總結"
|
||
|
||
echo ""
|
||
echo -e "✅ ${GREEN}通過${NC}: $PASSED"
|
||
echo -e "❌ ${RED}失敗${NC}: $FAILED"
|
||
echo -e "⚠️ ${YELLOW}警告${NC}: $WARNINGS"
|
||
echo ""
|
||
|
||
if [[ $FAILED -eq 0 ]]; then
|
||
echo -e "${GREEN}=============================================="
|
||
echo " CI/CD 驗證通過!可以進行部署。"
|
||
echo "==============================================${NC}"
|
||
return 0
|
||
else
|
||
echo -e "${RED}=============================================="
|
||
echo " CI/CD 驗證失敗!請修復上述問題。"
|
||
echo "==============================================${NC}"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# =============================================================================
|
||
# 主程式
|
||
# =============================================================================
|
||
main() {
|
||
echo ""
|
||
echo "=============================================="
|
||
echo " Docker Registry CI/CD 驗證"
|
||
echo " $(date '+%Y-%m-%d %H:%M:%S')"
|
||
echo "=============================================="
|
||
|
||
check_local_env
|
||
check_ssh_connections
|
||
check_registry_service
|
||
check_k8s_services
|
||
check_app_health
|
||
check_cicd_config
|
||
check_registry_images
|
||
check_monitoring_scripts
|
||
check_harbor_references
|
||
|
||
summary
|
||
}
|
||
|
||
# 執行
|
||
main "$@"
|