feat(phase8): CI/CD Pipeline 與 K8s 部署自動化
Phase 8 CI/CD 藍圖: - GitHub Actions deploy-prod.yml (沿用 AIOPS 成熟模式) - Signal Worker K8s Deployment - Telegram Notify 閉環 - Bootstrap 自動化腳本 架構鐵律: - Build: 110 金庫 (Harbor + Self-Hosted Runner) - Deploy: 120 K3s Master - 嚴禁 Docker Compose,K8s 唯一合法部署 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
214
scripts/bootstrap_prod.sh
Executable file
214
scripts/bootstrap_prod.sh
Executable file
@@ -0,0 +1,214 @@
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# AWOOOI Production Bootstrap Script
|
||||
# =============================================================================
|
||||
# Phase 8: 全自動化初始腳本
|
||||
#
|
||||
# 功能:
|
||||
# A. 讀取本地 .env 中的機密
|
||||
# B. 產出 03-secrets.yaml 並 kubectl apply
|
||||
# C. 自動 git add, commit, push
|
||||
#
|
||||
# 用法:
|
||||
# ./scripts/bootstrap_prod.sh
|
||||
#
|
||||
# 前置條件:
|
||||
# - kubectl 已配置 (KUBECONFIG 指向 120 K3s)
|
||||
# - .env 檔案已填寫機密
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 顏色定義
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
echo -e "${BLUE}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ AWOOOI Production Bootstrap Script (Phase 8) ║${NC}"
|
||||
echo -e "${BLUE}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# 配置
|
||||
# =============================================================================
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
ENV_FILE="${PROJECT_ROOT}/.env"
|
||||
SECRETS_TEMPLATE="${PROJECT_ROOT}/k8s/awoooi-prod/03-secrets.example.yaml"
|
||||
SECRETS_OUTPUT="${PROJECT_ROOT}/k8s/awoooi-prod/03-secrets.yaml"
|
||||
K8S_NAMESPACE="awoooi-prod"
|
||||
|
||||
# =============================================================================
|
||||
# Step 1: 檢查前置條件
|
||||
# =============================================================================
|
||||
echo -e "${YELLOW}[1/5] 檢查前置條件...${NC}"
|
||||
|
||||
# 檢查 .env 檔案
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo -e "${RED}❌ 錯誤: .env 檔案不存在${NC}"
|
||||
echo " 請複製 .env.example 並填入機密:"
|
||||
echo " cp .env.example .env"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN} ✓ .env 檔案存在${NC}"
|
||||
|
||||
# 檢查 kubectl
|
||||
if ! command -v kubectl &> /dev/null; then
|
||||
echo -e "${RED}❌ 錯誤: kubectl 未安裝${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN} ✓ kubectl 已安裝${NC}"
|
||||
|
||||
# 檢查 K8s 連線
|
||||
if ! kubectl cluster-info &> /dev/null; then
|
||||
echo -e "${RED}❌ 錯誤: 無法連接 K8s 叢集${NC}"
|
||||
echo " 請確認 KUBECONFIG 設定正確"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN} ✓ K8s 叢集連線正常${NC}"
|
||||
|
||||
# =============================================================================
|
||||
# Step 2: 讀取 .env 並產生 secrets.yaml
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo -e "${YELLOW}[2/5] 讀取 .env 並產生 K8s Secrets...${NC}"
|
||||
|
||||
# 載入 .env
|
||||
set -a
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
|
||||
# 檢查必要的環境變數
|
||||
REQUIRED_VARS=(
|
||||
"DATABASE_URL"
|
||||
"REDIS_URL"
|
||||
"OPENCLAW_TG_BOT_TOKEN"
|
||||
"OPENCLAW_TG_CHAT_ID"
|
||||
)
|
||||
|
||||
for var in "${REQUIRED_VARS[@]}"; do
|
||||
if [ -z "${!var:-}" ]; then
|
||||
echo -e "${RED}❌ 錯誤: 環境變數 $var 未設定${NC}"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo -e "${GREEN} ✓ 所有必要環境變數已設定${NC}"
|
||||
|
||||
# 產生 secrets.yaml
|
||||
cat > "$SECRETS_OUTPUT" << EOF
|
||||
# AWOOOI Production Secrets
|
||||
# 自動產生於: $(date -Iseconds)
|
||||
# ⚠️ 此檔案包含機密,請勿提交至 Git
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: awoooi-secrets
|
||||
namespace: ${K8S_NAMESPACE}
|
||||
type: Opaque
|
||||
stringData:
|
||||
# 資料庫
|
||||
DATABASE_URL: "${DATABASE_URL:-postgresql+asyncpg://awoooi:changeme@192.168.0.188:5432/awoooi_prod}"
|
||||
|
||||
# Redis
|
||||
REDIS_URL: "${REDIS_URL:-redis://192.168.0.188:6380/10}"
|
||||
|
||||
# AI 服務
|
||||
GEMINI_API_KEY: "${GEMINI_API_KEY:-}"
|
||||
CLAUDE_API_KEY: "${CLAUDE_API_KEY:-}"
|
||||
|
||||
# Telegram Gateway
|
||||
OPENCLAW_TG_BOT_TOKEN: "${OPENCLAW_TG_BOT_TOKEN}"
|
||||
OPENCLAW_TG_CHAT_ID: "${OPENCLAW_TG_CHAT_ID}"
|
||||
OPENCLAW_TG_USER_WHITELIST: "${OPENCLAW_TG_USER_WHITELIST:-${OPENCLAW_TG_CHAT_ID}}"
|
||||
|
||||
# Webhook 安全
|
||||
WEBHOOK_HMAC_SECRET: "${WEBHOOK_HMAC_SECRET:-$(openssl rand -hex 32)}"
|
||||
|
||||
# JWT (未來擴展)
|
||||
JWT_SECRET: "${JWT_SECRET:-$(openssl rand -hex 32)}"
|
||||
JWT_ALGORITHM: "HS256"
|
||||
EOF
|
||||
|
||||
echo -e "${GREEN} ✓ 已產生 k8s/awoooi-prod/03-secrets.yaml${NC}"
|
||||
|
||||
# =============================================================================
|
||||
# Step 3: 套用 K8s Secrets
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo -e "${YELLOW}[3/5] 套用 K8s Secrets 至 ${K8S_NAMESPACE}...${NC}"
|
||||
|
||||
# 確保 namespace 存在
|
||||
kubectl create namespace "$K8S_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - 2>/dev/null || true
|
||||
|
||||
# 套用 secrets
|
||||
kubectl apply -f "$SECRETS_OUTPUT" --namespace="$K8S_NAMESPACE"
|
||||
echo -e "${GREEN} ✓ K8s Secrets 已套用${NC}"
|
||||
|
||||
# 驗證
|
||||
echo ""
|
||||
echo -e "${BLUE} 驗證 Secrets:${NC}"
|
||||
kubectl get secret awoooi-secrets -n "$K8S_NAMESPACE" -o jsonpath='{.metadata.name}' && echo " exists"
|
||||
|
||||
# =============================================================================
|
||||
# Step 4: 清理敏感檔案 (不提交到 Git)
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo -e "${YELLOW}[4/5] 清理敏感檔案...${NC}"
|
||||
|
||||
# 確保 secrets.yaml 在 .gitignore
|
||||
if ! grep -q "03-secrets.yaml" "${PROJECT_ROOT}/.gitignore" 2>/dev/null; then
|
||||
echo "k8s/awoooi-prod/03-secrets.yaml" >> "${PROJECT_ROOT}/.gitignore"
|
||||
echo -e "${GREEN} ✓ 已將 03-secrets.yaml 加入 .gitignore${NC}"
|
||||
fi
|
||||
|
||||
# 刪除敏感檔案
|
||||
rm -f "$SECRETS_OUTPUT"
|
||||
echo -e "${GREEN} ✓ 已刪除本地 secrets.yaml (僅保留在 K8s)${NC}"
|
||||
|
||||
# =============================================================================
|
||||
# Step 5: Git Commit & Push
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo -e "${YELLOW}[5/5] Git Commit & Push...${NC}"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# 檢查是否有變更
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo -e "${BLUE} ℹ 沒有變更需要提交${NC}"
|
||||
else
|
||||
git add .
|
||||
git commit -m "chore: Phase 8 CI/CD bootstrap
|
||||
|
||||
- Add deploy-prod.yml GitHub Actions workflow
|
||||
- Add Signal Worker K8s deployment
|
||||
- Update Harbor image paths
|
||||
- Configure Telegram notification
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>"
|
||||
|
||||
echo -e "${GREEN} ✓ Git commit 完成${NC}"
|
||||
|
||||
# Push
|
||||
git push origin main
|
||||
echo -e "${GREEN} ✓ Git push 完成${NC}"
|
||||
fi
|
||||
|
||||
# =============================================================================
|
||||
# 完成
|
||||
# =============================================================================
|
||||
echo ""
|
||||
echo -e "${GREEN}╔══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}║ Bootstrap 完成! ║${NC}"
|
||||
echo -e "${GREEN}╚══════════════════════════════════════════════════════════════╝${NC}"
|
||||
echo ""
|
||||
echo -e "下一步:"
|
||||
echo -e " 1. 確認 110 主機 GitHub Runner 已啟動"
|
||||
echo -e " 2. 前往 GitHub Actions 查看部署狀態"
|
||||
echo -e " 3. 監控 Telegram 接收部署通知"
|
||||
echo ""
|
||||
echo -e "${BLUE}🔗 GitHub Actions: https://github.com/$(git remote get-url origin | sed 's/.*github.com[:/]\(.*\)\.git/\1/')/actions${NC}"
|
||||
Reference in New Issue
Block a user