462 lines
17 KiB
YAML
462 lines
17 KiB
YAML
name: '🌐 Awen域名反向代理 · 部署与监控'
|
||
|
||
# ═══════════════════════════════════════════════════════════
|
||
# Awen 域名反向代理部署工作流
|
||
# 编号: ZY-WF-AWEN-PROXY
|
||
# 守护: 铸渊 · ICE-GL-ZY001
|
||
# 版权: 国作登字-2026-A-00037559
|
||
#
|
||
# 架构:
|
||
# 用户 → Awen域名(DNS→新加坡) → ZY-SVR-002 Nginx → Awen广州后端
|
||
#
|
||
# 功能:
|
||
# 1. deploy — 部署/更新 Nginx 反向代理配置到 ZY-SVR-002
|
||
# 2. setup-ssl — 为 Awen 域名申请 Let's Encrypt SSL 证书
|
||
# 3. health — 检查代理链路健康状态(代理→后端→SSL)
|
||
# 4. rollback — 回滚到上一份 Nginx 配置
|
||
#
|
||
# 所需 GitHub Secrets:
|
||
# ZY_SERVER_HOST — ZY-SVR-002 新加坡服务器 IP
|
||
# ZY_SERVER_KEY — ZY-SVR-002 SSH 私钥
|
||
# ZY_SERVER_USER — ZY-SVR-002 SSH 用户名
|
||
# AWEN_DOMAIN — Awen 的域名 (如: example.com)
|
||
# AWEN_BACKEND_HOST — Awen 广州服务器 IP
|
||
# AWEN_BACKEND_PORT — Awen 后端服务端口 (默认 80)
|
||
# ═══════════════════════════════════════════════════════════
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
paths:
|
||
- 'server/nginx/awen-domain-proxy.conf'
|
||
workflow_dispatch:
|
||
inputs:
|
||
action:
|
||
description: '执行动作'
|
||
required: true
|
||
default: 'deploy'
|
||
type: choice
|
||
options:
|
||
- deploy
|
||
- setup-ssl
|
||
- health
|
||
- rollback
|
||
|
||
concurrency:
|
||
group: awen-domain-proxy
|
||
cancel-in-progress: false
|
||
|
||
permissions:
|
||
contents: read
|
||
issues: write
|
||
|
||
jobs:
|
||
# ═══ §1 部署 Nginx 反向代理配置 ═══
|
||
deploy:
|
||
name: '🚀 部署反向代理'
|
||
if: >
|
||
github.event.inputs.action == 'deploy' ||
|
||
github.event.inputs.action == '' ||
|
||
github.event_name == 'push'
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 10
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: '🔍 检查必要 Secrets'
|
||
env:
|
||
AWEN_DOMAIN: ${{ secrets.AWEN_DOMAIN }}
|
||
AWEN_BACKEND_HOST: ${{ secrets.AWEN_BACKEND_HOST }}
|
||
ZY_SERVER_HOST: ${{ secrets.ZY_SERVER_HOST }}
|
||
run: |
|
||
MISSING=""
|
||
[ -z "$AWEN_DOMAIN" ] && MISSING="${MISSING}AWEN_DOMAIN "
|
||
[ -z "$AWEN_BACKEND_HOST" ] && MISSING="${MISSING}AWEN_BACKEND_HOST "
|
||
[ -z "$ZY_SERVER_HOST" ] && MISSING="${MISSING}ZY_SERVER_HOST "
|
||
if [ -n "$MISSING" ]; then
|
||
echo "❌ 缺少必要的 GitHub Secrets: $MISSING"
|
||
echo ""
|
||
echo "═══ 配置指南 ═══"
|
||
echo "请在仓库 Settings → Secrets → Actions 中添加:"
|
||
echo " AWEN_DOMAIN — Awen的域名 (如: example.com)"
|
||
echo " AWEN_BACKEND_HOST — Awen广州服务器IP"
|
||
echo " AWEN_BACKEND_PORT — 后端端口 (默认80,可选)"
|
||
echo " ZY_SERVER_HOST — 新加坡服务器IP (ZY-SVR-002)"
|
||
echo " ZY_SERVER_KEY — 新加坡服务器SSH私钥"
|
||
echo " ZY_SERVER_USER — 新加坡服务器SSH用户名"
|
||
exit 1
|
||
fi
|
||
echo "✅ 所有必要 Secrets 已配置"
|
||
|
||
- name: '🔐 配置SSH'
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
if head -1 ~/.ssh/zy_key | grep -q "BEGIN" && tail -1 ~/.ssh/zy_key | grep -q "END"; then
|
||
echo "✅ SSH私钥格式正确"
|
||
else
|
||
echo "❌ SSH私钥格式异常"
|
||
exit 1
|
||
fi
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: '🔍 SSH连接测试'
|
||
run: |
|
||
ssh -i ~/.ssh/zy_key -o BatchMode=yes -o ConnectTimeout=10 \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
'echo "✅ SSH连接成功 · $(hostname) · $(date)"'
|
||
|
||
- name: '📦 生成并部署 Nginx 配置'
|
||
env:
|
||
AWEN_DOMAIN: ${{ secrets.AWEN_DOMAIN }}
|
||
AWEN_BACKEND_HOST: ${{ secrets.AWEN_BACKEND_HOST }}
|
||
AWEN_BACKEND_PORT: ${{ secrets.AWEN_BACKEND_PORT }}
|
||
run: |
|
||
BACKEND_PORT="${AWEN_BACKEND_PORT:-80}"
|
||
|
||
echo "═══ Awen 域名反向代理部署 ═══"
|
||
echo "域名: ${AWEN_DOMAIN}"
|
||
echo "后端: ${AWEN_BACKEND_HOST}:${BACKEND_PORT}"
|
||
echo "代理服务器: ZY-SVR-002 (新加坡)"
|
||
echo ""
|
||
|
||
# 复制模板并注入实际值
|
||
cp server/nginx/awen-domain-proxy.conf /tmp/awen-domain-proxy.conf
|
||
sed -i "s/AWEN_DOMAIN_PLACEHOLDER/${AWEN_DOMAIN}/g" /tmp/awen-domain-proxy.conf
|
||
sed -i "s/AWEN_BACKEND_HOST_PLACEHOLDER/${AWEN_BACKEND_HOST}/g" /tmp/awen-domain-proxy.conf
|
||
sed -i "s/AWEN_BACKEND_PORT_PLACEHOLDER/${BACKEND_PORT}/g" /tmp/awen-domain-proxy.conf
|
||
|
||
echo "📋 生成的配置:"
|
||
cat /tmp/awen-domain-proxy.conf
|
||
echo ""
|
||
|
||
# 备份现有配置(如果存在)
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
"if [ -f /etc/nginx/sites-available/awen-proxy.conf ]; then
|
||
sudo cp /etc/nginx/sites-available/awen-proxy.conf \
|
||
/etc/nginx/sites-available/awen-proxy.conf.bak.\$(date +%Y%m%d%H%M%S)
|
||
echo '✅ 已备份现有配置'
|
||
fi
|
||
sudo mkdir -p /opt/zhuyuan/data/logs /var/www/certbot"
|
||
|
||
# 上传配置
|
||
scp -i ~/.ssh/zy_key \
|
||
/tmp/awen-domain-proxy.conf \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }}:/tmp/awen-domain-proxy.conf
|
||
|
||
# 安装并启用配置
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << 'DEPLOY_CMD'
|
||
|
||
set -e
|
||
|
||
# 安装配置
|
||
sudo cp /tmp/awen-domain-proxy.conf /etc/nginx/sites-available/awen-proxy.conf
|
||
sudo ln -sf /etc/nginx/sites-available/awen-proxy.conf /etc/nginx/sites-enabled/awen-proxy.conf
|
||
|
||
# 验证 Nginx 配置
|
||
echo "🔍 验证 Nginx 配置..."
|
||
if sudo nginx -t 2>&1; then
|
||
echo "✅ Nginx 配置验证通过"
|
||
sudo systemctl reload nginx
|
||
echo "✅ Nginx 已重载"
|
||
else
|
||
echo "❌ Nginx 配置验证失败"
|
||
# 回滚
|
||
sudo rm -f /etc/nginx/sites-enabled/awen-proxy.conf
|
||
LATEST_BAK=$(ls -t /etc/nginx/sites-available/awen-proxy.conf.bak.* 2>/dev/null | head -1)
|
||
if [ -n "$LATEST_BAK" ]; then
|
||
sudo cp "$LATEST_BAK" /etc/nginx/sites-available/awen-proxy.conf
|
||
sudo ln -sf /etc/nginx/sites-available/awen-proxy.conf /etc/nginx/sites-enabled/awen-proxy.conf
|
||
sudo nginx -t && sudo systemctl reload nginx
|
||
echo "↩️ 已回滚到上一份配置"
|
||
fi
|
||
exit 1
|
||
fi
|
||
|
||
DEPLOY_CMD
|
||
|
||
- name: '💚 部署后健康检查'
|
||
env:
|
||
AWEN_DOMAIN: ${{ secrets.AWEN_DOMAIN }}
|
||
AWEN_BACKEND_HOST: ${{ secrets.AWEN_BACKEND_HOST }}
|
||
AWEN_BACKEND_PORT: ${{ secrets.AWEN_BACKEND_PORT }}
|
||
run: |
|
||
BACKEND_PORT="${AWEN_BACKEND_PORT:-80}"
|
||
echo "═══ 部署后健康检查 ═══"
|
||
echo ""
|
||
|
||
# 检查 Nginx 是否正常
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
"systemctl is-active nginx && echo '✅ Nginx运行中' || echo '❌ Nginx未运行'"
|
||
|
||
# 检查域名 HTTP 响应
|
||
sleep 2
|
||
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
|
||
--resolve "${AWEN_DOMAIN}:80:${{ secrets.ZY_SERVER_HOST }}" \
|
||
"http://${AWEN_DOMAIN}/" 2>/dev/null || echo "000")
|
||
echo "HTTP 状态码 (通过代理): ${HTTP_CODE}"
|
||
|
||
if [ "$HTTP_CODE" = "000" ] || [ "$HTTP_CODE" = "502" ]; then
|
||
echo "⚠️ 后端暂时不可达 (广州服务器可能未就绪)"
|
||
echo " 代理配置已部署成功,待后端上线后自动生效"
|
||
elif [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 400 ]; then
|
||
echo "✅ 代理链路正常"
|
||
else
|
||
echo "⚠️ 状态码 ${HTTP_CODE} — 请检查后端服务"
|
||
fi
|
||
|
||
# ═══ §2 SSL证书申请 ═══
|
||
setup-ssl:
|
||
name: '🔒 SSL证书配置'
|
||
if: github.event.inputs.action == 'setup-ssl'
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 10
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: '🔐 配置SSH'
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: '🔒 申请 Let''s Encrypt SSL 证书'
|
||
env:
|
||
AWEN_DOMAIN: ${{ secrets.AWEN_DOMAIN }}
|
||
run: |
|
||
if [ -z "$AWEN_DOMAIN" ]; then
|
||
echo "❌ AWEN_DOMAIN 未配置"
|
||
exit 1
|
||
fi
|
||
|
||
echo "🔒 为 ${AWEN_DOMAIN} 申请SSL证书"
|
||
echo ""
|
||
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << SSLCMD
|
||
|
||
set -e
|
||
|
||
# 安装 certbot(如未安装)
|
||
if ! command -v certbot &> /dev/null; then
|
||
echo "📦 安装 certbot..."
|
||
sudo apt-get update -qq
|
||
sudo apt-get install -y -qq certbot python3-certbot-nginx
|
||
fi
|
||
|
||
# 确保 webroot 目录存在
|
||
sudo mkdir -p /var/www/certbot
|
||
|
||
# 申请证书(使用 webroot 模式,不中断 Nginx)
|
||
sudo certbot certonly \
|
||
--webroot -w /var/www/certbot \
|
||
-d ${AWEN_DOMAIN} \
|
||
--non-interactive \
|
||
--agree-tos \
|
||
--email admin@${AWEN_DOMAIN} \
|
||
--no-eff-email \
|
||
|| {
|
||
echo "⚠️ Webroot 模式失败,尝试 standalone 模式..."
|
||
sudo certbot certonly \
|
||
--nginx \
|
||
-d ${AWEN_DOMAIN} \
|
||
--non-interactive \
|
||
--agree-tos \
|
||
--email admin@${AWEN_DOMAIN} \
|
||
--no-eff-email
|
||
}
|
||
|
||
echo "✅ SSL证书申请成功"
|
||
|
||
# 更新 Nginx 配置添加 SSL
|
||
CONF="/etc/nginx/sites-available/awen-proxy.conf"
|
||
if [ -f "\$CONF" ] && ! grep -q "listen 443 ssl" "\$CONF"; then
|
||
echo "📝 使用 certbot 自动配置 SSL..."
|
||
sudo certbot --nginx -d ${AWEN_DOMAIN} --non-interactive --redirect 2>&1 || {
|
||
echo "❌ certbot --nginx 配置失败,SSL未启用"
|
||
exit 1
|
||
}
|
||
echo "✅ SSL 已通过 certbot --nginx 启用"
|
||
else
|
||
echo "ℹ️ SSL 配置已存在或配置文件不存在"
|
||
fi
|
||
|
||
# 设置自动续期 cron
|
||
if ! crontab -l 2>/dev/null | grep -q "certbot renew"; then
|
||
(crontab -l 2>/dev/null; echo "0 3 1 * * certbot renew --quiet --post-hook 'systemctl reload nginx'") | crontab -
|
||
echo "✅ SSL 自动续期 cron 已配置 (每月1日 03:00)"
|
||
fi
|
||
|
||
SSLCMD
|
||
|
||
- name: '💚 SSL验证'
|
||
env:
|
||
AWEN_DOMAIN: ${{ secrets.AWEN_DOMAIN }}
|
||
run: |
|
||
echo "🔍 验证 HTTPS: https://${AWEN_DOMAIN}"
|
||
sleep 3
|
||
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" "https://${AWEN_DOMAIN}/" 2>/dev/null || echo "000")
|
||
echo "HTTPS状态码: ${HTTP_CODE}"
|
||
if [ "$HTTP_CODE" != "000" ]; then
|
||
echo "✅ HTTPS可访问"
|
||
else
|
||
echo "⚠️ HTTPS暂时不可访问 (可能需要等待DNS传播)"
|
||
fi
|
||
|
||
# ═══ §3 健康检查 (代理+后端+SSL) ═══
|
||
health:
|
||
name: '💚 代理链路健康检查'
|
||
if: github.event.inputs.action == 'health'
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 5
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: '🔐 配置SSH'
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: '💚 全链路健康检查'
|
||
env:
|
||
AWEN_DOMAIN: ${{ secrets.AWEN_DOMAIN }}
|
||
AWEN_BACKEND_HOST: ${{ secrets.AWEN_BACKEND_HOST }}
|
||
AWEN_BACKEND_PORT: ${{ secrets.AWEN_BACKEND_PORT }}
|
||
run: |
|
||
BACKEND_PORT="${AWEN_BACKEND_PORT:-80}"
|
||
echo "═══ Awen 域名代理 · 健康检查报告 ═══"
|
||
echo "时间: $(date -u '+%Y-%m-%d %H:%M:%S UTC')"
|
||
echo ""
|
||
|
||
ISSUES=""
|
||
|
||
# §1 代理服务器 Nginx 状态
|
||
echo "§1 代理服务器 (ZY-SVR-002 · 新加坡)"
|
||
ssh -i ~/.ssh/zy_key -o ConnectTimeout=10 \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << 'HEALTH' || {
|
||
echo "❌ 无法连接到代理服务器"
|
||
ISSUES="${ISSUES}proxy_ssh_fail "
|
||
}
|
||
|
||
echo " Nginx: $(systemctl is-active nginx 2>/dev/null || echo 'unknown')"
|
||
echo " 配置: $([ -f /etc/nginx/sites-enabled/awen-proxy.conf ] && echo '✅ 已启用' || echo '❌ 未启用')"
|
||
echo " 日志行数: $(wc -l < /opt/zhuyuan/data/logs/nginx-awen-proxy.log 2>/dev/null || echo 'N/A')"
|
||
|
||
# 检查 SSL 证书有效期
|
||
if [ -f /etc/letsencrypt/live/${AWEN_DOMAIN}/fullchain.pem ]; then
|
||
EXPIRY=$(openssl x509 -enddate -noout -in /etc/letsencrypt/live/${AWEN_DOMAIN}/fullchain.pem 2>/dev/null | cut -d= -f2)
|
||
echo " SSL证书到期: ${EXPIRY:-unknown}"
|
||
else
|
||
echo " SSL证书: 未配置"
|
||
fi
|
||
|
||
HEALTH
|
||
|
||
echo ""
|
||
|
||
# §2 后端可达性 (从代理服务器测试)
|
||
echo "§2 后端服务器 (Awen · 广州)"
|
||
if [ -n "$AWEN_BACKEND_HOST" ]; then
|
||
BACKEND_CODE=$(ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} \
|
||
"curl -sf -o /dev/null -w '%{http_code}' --connect-timeout 10 \
|
||
http://${AWEN_BACKEND_HOST}:${BACKEND_PORT}/health 2>/dev/null || echo '000'" || echo "000")
|
||
echo " 健康端点: ${BACKEND_CODE}"
|
||
if [ "$BACKEND_CODE" = "000" ]; then
|
||
echo " ❌ 后端不可达"
|
||
ISSUES="${ISSUES}backend_unreachable "
|
||
elif [ "$BACKEND_CODE" -ge 200 ] && [ "$BACKEND_CODE" -lt 400 ]; then
|
||
echo " ✅ 后端正常"
|
||
else
|
||
echo " ⚠️ 后端异常 (${BACKEND_CODE})"
|
||
ISSUES="${ISSUES}backend_error "
|
||
fi
|
||
else
|
||
echo " ⏳ AWEN_BACKEND_HOST 未配置"
|
||
fi
|
||
|
||
echo ""
|
||
|
||
# §3 端到端测试 (通过域名访问)
|
||
echo "§3 端到端测试 (通过域名)"
|
||
if [ -n "$AWEN_DOMAIN" ]; then
|
||
# HTTP
|
||
E2E_HTTP=$(curl -sf -o /dev/null -w "%{http_code}" \
|
||
--resolve "${AWEN_DOMAIN}:80:${{ secrets.ZY_SERVER_HOST }}" \
|
||
"http://${AWEN_DOMAIN}/" 2>/dev/null || echo "000")
|
||
echo " HTTP: ${E2E_HTTP}"
|
||
|
||
# HTTPS
|
||
E2E_HTTPS=$(curl -sf -o /dev/null -w "%{http_code}" \
|
||
"https://${AWEN_DOMAIN}/" 2>/dev/null || echo "000")
|
||
echo " HTTPS: ${E2E_HTTPS}"
|
||
else
|
||
echo " ⏳ AWEN_DOMAIN 未配置"
|
||
fi
|
||
|
||
echo ""
|
||
echo "═══ 检查完成 ═══"
|
||
|
||
# 如果有严重问题,退出非0(可触发通知)
|
||
if echo "$ISSUES" | grep -q "proxy_ssh_fail\|backend_unreachable"; then
|
||
echo ""
|
||
echo "⚠️ 发现问题: $ISSUES"
|
||
exit 1
|
||
fi
|
||
|
||
# ═══ §4 回滚 ═══
|
||
rollback:
|
||
name: '↩️ 回滚配置'
|
||
if: github.event.inputs.action == 'rollback'
|
||
runs-on: ubuntu-latest
|
||
timeout-minutes: 5
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: '🔐 配置SSH'
|
||
env:
|
||
SSH_KEY: ${{ secrets.ZY_SERVER_KEY }}
|
||
run: |
|
||
mkdir -p ~/.ssh
|
||
echo "$SSH_KEY" > ~/.ssh/zy_key
|
||
chmod 600 ~/.ssh/zy_key
|
||
ssh-keyscan -H ${{ secrets.ZY_SERVER_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
|
||
|
||
- name: '↩️ 回滚到上一份配置'
|
||
run: |
|
||
ssh -i ~/.ssh/zy_key \
|
||
${{ secrets.ZY_SERVER_USER }}@${{ secrets.ZY_SERVER_HOST }} << 'ROLLBACK'
|
||
|
||
LATEST_BAK=$(ls -t /etc/nginx/sites-available/awen-proxy.conf.bak.* 2>/dev/null | head -1)
|
||
if [ -n "$LATEST_BAK" ]; then
|
||
echo "↩️ 回滚到: $LATEST_BAK"
|
||
sudo cp "$LATEST_BAK" /etc/nginx/sites-available/awen-proxy.conf
|
||
if sudo nginx -t 2>&1; then
|
||
sudo systemctl reload nginx
|
||
echo "✅ 回滚成功"
|
||
else
|
||
echo "❌ 回滚后配置仍无效"
|
||
sudo rm -f /etc/nginx/sites-enabled/awen-proxy.conf
|
||
sudo nginx -t && sudo systemctl reload nginx
|
||
echo "⚠️ 已禁用 Awen 代理配置"
|
||
fi
|
||
else
|
||
echo "❌ 没有可回滚的备份"
|
||
exit 1
|
||
fi
|
||
|
||
ROLLBACK
|