Files
GraphRAGAgent/k8s/deploy.sh
T
admin 347f9dcae5 fix: deploy.sh 后端域名 nginx 配置 — /docs /redoc 走 backend 而非 frontend
后端域名 (-backend.plfai.cn) 不需要前端,location / 应直接代理到 backend:8000,
否则 /docs /redoc /openapi.json 被错误转发到前端 nginx 导致 504。
2026-06-17 23:29:24 +08:00

210 lines
8.2 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# GraphRAG Studio — 一键部署脚本 (test + prod 双环境)
# 用法: bash k8s/deploy.sh
# 前提: k3s 集群已运行, docker 可用, registry.plfai.cn 可推送
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
REGISTRY="registry.plfai.cn"
BACKEND_IMAGE="${REGISTRY}/graphrag-backend"
FRONTEND_IMAGE="${REGISTRY}/graphrag-frontend"
KUBECTL="k3s kubectl"
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${BLUE}[$(date +%H:%M:%S)]${NC} $1"; }
ok() { echo -e "${GREEN}[OK]${NC} $1"; }
err() { echo -e "${RED}[ERR]${NC} $1"; exit 1; }
# ──────────────── 加载密钥 ────────────────
if [ -f "$PROJECT_DIR/backend/.env" ]; then
set -a; source "$PROJECT_DIR/backend/.env"; set +a
else
err "请先创建 backend/.env(参考 backend/.env.example"
fi
if [ -z "$DEEPSEEK_API_KEY" ] || [ "$DEEPSEEK_API_KEY" = "sk-your-key-here" ]; then
err "请在 backend/.env 中填入 DEEPSEEK_API_KEY"
fi
# ──────────────── 1. 构建镜像 ────────────────
log "Building backend image..."
cd "$PROJECT_DIR"
docker build --network host -f backend/Dockerfile -t "${BACKEND_IMAGE}:latest" . || err "backend build"
docker tag "${BACKEND_IMAGE}:latest" "${BACKEND_IMAGE}:v1"
ok "Backend built"
log "Building frontend image..."
docker build --network host -f frontend/Dockerfile -t "${FRONTEND_IMAGE}:latest" . || err "frontend build"
docker tag "${FRONTEND_IMAGE}:latest" "${FRONTEND_IMAGE}:v1"
ok "Frontend built"
# ──────────────── 2. 推送镜像 ────────────────
log "Pushing images to registry..."
docker push "${BACKEND_IMAGE}:latest" "${BACKEND_IMAGE}:v1" >/dev/null 2>&1
docker push "${FRONTEND_IMAGE}:latest" "${FRONTEND_IMAGE}:v1" >/dev/null 2>&1
ok "Images pushed"
# ──────────────── 3. 导入 containerd ────────────────
log "Importing into containerd..."
docker save "${BACKEND_IMAGE}:v1" | k3s ctr images import - >/dev/null 2>&1
docker save "${FRONTEND_IMAGE}:v1" | k3s ctr images import - >/dev/null 2>&1
ok "Containerd loaded"
# ──────────────── 4. 部署 Kubernetes ────────────────
deploy_env() {
local NS="$1" label="$2"
log "Deploying ${label} (ns: ${NS})..."
# 创建 namespace
$KUBECTL create namespace "$NS" --dry-run=client -o yaml | $KUBECTL apply -f - 2>/dev/null
# apply ConfigMap
$KUBECTL apply -n "$NS" -f "$SCRIPT_DIR/base/configmap.yaml" 2>/dev/null
# envsubst 注入密钥 → apply Secret
envsubst < "$SCRIPT_DIR/base/secret.yaml" | $KUBECTL apply -n "$NS" -f - 2>/dev/null
# apply 其余 base 资源
for f in "$SCRIPT_DIR/base"/*.yaml; do
case "$(basename "$f")" in
kustomization.yaml|configmap.yaml|secret.yaml) continue ;;
*) $KUBECTL apply -n "$NS" -f "$f" 2>/dev/null ;;
esac
done
# 修正镜像 tag
$KUBECTL set image deployment/backend "backend=${BACKEND_IMAGE}:v1" -n "$NS" 2>/dev/null
$KUBECTL set image deployment/frontend "frontend=${FRONTEND_IMAGE}:v1" -n "$NS" 2>/dev/null
# 修正 backend command(镜像 CMD 可能为 sleep
$KUBECTL patch deployment backend -n "$NS" --type json -p '[
{"op":"add","path":"/spec/template/spec/containers/0/command","value":["/opt/venv/bin/python","-m","uvicorn","main:app","--host","0.0.0.0","--port","8000"]}
]' 2>/dev/null
# 节点亲和性
$KUBECTL patch deployment backend -n "$NS" --type merge -p \
'{"spec":{"template":{"spec":{"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"kubernetes.io/hostname","operator":"In","values":["k8smaster"]}]}]}}}}}}}' 2>/dev/null
$KUBECTL patch deployment frontend -n "$NS" --type merge -p \
'{"spec":{"template":{"spec":{"affinity":{"nodeAffinity":{"requiredDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"kubernetes.io/hostname","operator":"In","values":["plf-cvm-worker","k8smaster"]}]}]}}}}}}}' 2>/dev/null
ok "${label} deployed"
}
deploy_env "graphrag-test" "TEST"
deploy_env "graphrag-prod" "PROD"
# ──────────────── 5. 等待就绪 ────────────────
log "Waiting for pods..."
for ns in graphrag-test graphrag-prod; do
$KUBECTL wait --for=condition=ready pod -l component=backend -n "$ns" --timeout=360s 2>/dev/null && ok "${ns} backend ready" || log " ${ns} backend still starting"
$KUBECTL wait --for=condition=ready pod -l component=frontend -n "$ns" --timeout=60s 2>/dev/null && ok "${ns} frontend ready" || log " ${ns} frontend still starting"
done
# ──────────────── 6. 配置 nginx ────────────────
log "Setting up nginx..."
setup_nginx() {
local domain="$1" frontend_ip="$2" backend_ip="$3" backend_only="${4:-false}"
if [ "$backend_only" = "true" ]; then
cat > "/etc/nginx/conf.d/${domain}.conf" << NGINX
server {
listen 443 ssl;
server_name ${domain};
ssl_certificate /etc/letsencrypt/live/plfai.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/plfai.cn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://${backend_ip}:8000;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_read_timeout 120s;
proxy_buffering off;
}
}
server {
listen 80;
server_name ${domain};
return 301 https://\$server_name\$request_uri;
}
NGINX
else
cat > "/etc/nginx/conf.d/${domain}.conf" << NGINX
server {
listen 443 ssl;
server_name ${domain};
ssl_certificate /etc/letsencrypt/live/plfai.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/plfai.cn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location /api/ {
proxy_pass http://${backend_ip}:8000;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_read_timeout 120s;
proxy_buffering off;
}
location / {
proxy_pass http://${frontend_ip}:80;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}
}
server {
listen 80;
server_name ${domain};
return 301 https://\$server_name\$request_uri;
}
NGINX
fi
ok "nginx: ${domain}"
}
TEST_FE=$($KUBECTL get svc frontend -n graphrag-test -o jsonpath='{.spec.clusterIP}')
TEST_BE=$($KUBECTL get svc backend -n graphrag-test -o jsonpath='{.spec.clusterIP}')
PROD_FE=$($KUBECTL get svc frontend -n graphrag-prod -o jsonpath='{.spec.clusterIP}')
PROD_BE=$($KUBECTL get svc backend -n graphrag-prod -o jsonpath='{.spec.clusterIP}')
setup_nginx "test-graphrag.plfai.cn" "$TEST_FE" "$TEST_BE"
setup_nginx "test-graphrag-backend.plfai.cn" "$TEST_BE" "$TEST_BE" true
setup_nginx "graphrag.plfai.cn" "$PROD_FE" "$PROD_BE"
setup_nginx "graphrag-backend.plfai.cn" "$PROD_BE" "$PROD_BE" true
nginx -t >/dev/null 2>&1 && nginx -s reload >/dev/null 2>&1
ok "nginx reloaded"
# ──────────────── 7. 验证 ────────────────
echo ""
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE} GraphRAG Studio 部署完成${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo "Test: https://test-graphrag.plfai.cn"
echo "Prod: https://graphrag.plfai.cn"
echo ""
for url in \
"https://test-graphrag.plfai.cn" \
"https://graphrag.plfai.cn" \
"https://test-graphrag-backend.plfai.cn/api/v1/health" \
"https://graphrag-backend.plfai.cn/api/v1/health"; do
code=$(curl -sk -o /dev/null -w "%{http_code}" --connect-timeout 5 "$url" 2>/dev/null || echo "ERR")
echo " $url → HTTP $code"
done
echo ""
echo -e "${GREEN}Done.${NC}"