refactor(deploy): TaxBaik 성공 사례(run 458) 기반 단일 빌드/배포 파이프라인 개편 및 텔레그램 연동 강화
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Snapshot Admin Deployment / build-and-deploy (push) Failing after 44s
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 7s
Deploy to Production / Build & Deploy to Production (push) Failing after 1m33s
Quant Engine CI/CD Pipeline / validate-ui-and-storage (push) Has been skipped
Snapshot Admin Deployment / build-and-deploy (push) Failing after 44s
Quant Engine CI/CD Pipeline / validate-core (push) Failing after 7s
Deploy to Production / Build & Deploy to Production (push) Failing after 1m33s
This commit is contained in:
+100
-318
@@ -7,18 +7,16 @@ on:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
DEPLOY_HOST: 172.17.0.1
|
DEPLOY_HOST: 172.17.0.1
|
||||||
# NOTE: Gitea와 운영서버가 같은 호스트에 있음 (hz-prod-01)
|
|
||||||
# 구조: 공인 IP 178.104.200.7/quant → Nginx reverse proxy → localhost:5000 (quantengine)
|
|
||||||
# 배포: .NET DLL을 /home/kjh2064/quantengine_active에 배포
|
|
||||||
# Nginx 설정: /etc/nginx/sites-available/gitea-ip.conf (이미 구성됨)
|
|
||||||
DEPLOY_USER: kjh2064
|
DEPLOY_USER: kjh2064
|
||||||
DEPLOY_PATH: /home/kjh2064/quantengine_active
|
DEPLOY_PATH: /home/kjh2064/quantengine_active
|
||||||
SERVICE_NAME: quantengine
|
SERVICE_NAME: quantengine
|
||||||
DOTNET_VERSION: '10.0.x'
|
DOTNET_VERSION: '10.0.x'
|
||||||
|
TELEGRAM_BOT_TOKEN_DEFAULT: "8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0"
|
||||||
|
TELEGRAM_CHAT_ID_DEFAULT: "-5460205872"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-test:
|
build-and-deploy:
|
||||||
name: Build Release Package
|
name: Build & Deploy to Production
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -42,12 +40,19 @@ jobs:
|
|||||||
|
|
||||||
- name: "[GATE] Run Core Validations"
|
- name: "[GATE] Run Core Validations"
|
||||||
run: |
|
run: |
|
||||||
# CI 게이트: 핵심 검증 먼저 실행
|
|
||||||
echo "🔐 Running critical CI validations..."
|
echo "🔐 Running critical CI validations..."
|
||||||
python3 tools/validate_no_direct_api_trading_v1.py || exit 1
|
python3 tools/validate_no_direct_api_trading_v1.py || exit 1
|
||||||
python3 tools/validate_specs.py || exit 1
|
python3 tools/validate_specs.py || exit 1
|
||||||
echo "✅ All critical validations passed"
|
echo "✅ All critical validations passed"
|
||||||
|
|
||||||
|
- name: Ensure Temp Directory and Mock Packet
|
||||||
|
run: |
|
||||||
|
mkdir -p Temp
|
||||||
|
# 빈 패킷 객체를 생성하여 dotnet test/run 시 IO Exception 방어
|
||||||
|
if [ ! -f Temp/final_decision_packet_active.json ]; then
|
||||||
|
echo '{"active_decision": "PASS", "details": "CI dummy packet"}' > Temp/final_decision_packet_active.json
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Restore Dependencies
|
- name: Restore Dependencies
|
||||||
run: dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
|
run: dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
|
||||||
|
|
||||||
@@ -64,7 +69,6 @@ jobs:
|
|||||||
dotnet test tests/unit \
|
dotnet test tests/unit \
|
||||||
-c Release \
|
-c Release \
|
||||||
--no-build \
|
--no-build \
|
||||||
--logger "trx;LogFileName=test-results.trx" \
|
|
||||||
|| echo "⚠️ Some tests failed (non-blocking for web service)"
|
|| echo "⚠️ Some tests failed (non-blocking for web service)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -75,329 +79,107 @@ jobs:
|
|||||||
--no-build \
|
--no-build \
|
||||||
-o ./publish-output
|
-o ./publish-output
|
||||||
|
|
||||||
echo "📦 Package size:"
|
|
||||||
du -sh ./publish-output
|
|
||||||
|
|
||||||
- name: Create Deployment Archive
|
|
||||||
run: |
|
|
||||||
cd publish-output
|
|
||||||
tar -czf ../quant-engine-release-${{ github.run_number }}.tar.gz .
|
|
||||||
cd ..
|
|
||||||
ls -lh quant-engine-release-${{ github.run_number }}.tar.gz
|
|
||||||
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: quant-engine-release
|
|
||||||
path: quant-engine-release-${{ github.run_number }}.tar.gz
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
deploy-to-prod:
|
|
||||||
name: Deploy to Production Server
|
|
||||||
needs: build-and-test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout Code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- name: Download Artifact
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: quant-engine-release
|
|
||||||
|
|
||||||
- name: Setup SSH
|
- name: Setup SSH
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
chmod 700 ~/.ssh
|
chmod 700 ~/.ssh
|
||||||
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
# SSH_PRIVATE_KEY가 평문 PEM이든 base64든 유연하게 처리
|
||||||
|
if echo "${{ secrets.SSH_PRIVATE_KEY }}" | grep -q "BEGIN"; then
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||||
|
else
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_ed25519 || echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||||
|
fi
|
||||||
chmod 600 ~/.ssh/id_ed25519
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
|
||||||
- name: Create Backup
|
- name: Package Artifact
|
||||||
run: |
|
run: |
|
||||||
echo "📦 Creating backup of current deployment..."
|
tar -czf quant_engine_deploy.tgz -C ./publish-output .
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
echo "✓ Package size: $(du -sh quant_engine_deploy.tgz | cut -f1)"
|
||||||
set -e
|
|
||||||
BACKUP_DIR="/home/kjh2064/quantengine_backup"
|
|
||||||
BACKUP_NAME="quantengine_$(date +%Y%m%d_%H%M%S)"
|
|
||||||
|
|
||||||
# Create backup without stopping the service (minimize downtime)
|
- name: Deploy & Verify on Server
|
||||||
mkdir -p $BACKUP_DIR
|
run: |
|
||||||
if [ -d ${{ env.DEPLOY_PATH }} ]; then
|
set -e
|
||||||
cp -r ${{ env.DEPLOY_PATH }} "$BACKUP_DIR/$BACKUP_NAME"
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
echo "✅ Backup created: $BACKUP_DIR/$BACKUP_NAME"
|
COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
DEPLOY_HOST="${{ env.DEPLOY_HOST }}"
|
||||||
|
DEPLOY_USER="${{ env.DEPLOY_USER }}"
|
||||||
|
|
||||||
|
# 텔레그램 설정 바인딩 (Secret에 없을 경우 기본값 백업 사용)
|
||||||
|
TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}"
|
||||||
|
[ -z "$TELEGRAM_BOT_TOKEN" ] && TELEGRAM_BOT_TOKEN="${{ env.TELEGRAM_BOT_TOKEN_DEFAULT }}"
|
||||||
|
TELEGRAM_CHAT_ID="${{ secrets.TELEGRAM_CHAT_ID }}"
|
||||||
|
[ -z "$TELEGRAM_CHAT_ID" ] && TELEGRAM_CHAT_ID="${{ env.TELEGRAM_CHAT_ID_DEFAULT }}"
|
||||||
|
|
||||||
# Keep only last 5 backups
|
send_telegram() {
|
||||||
BACKUP_COUNT=$(ls -1 $BACKUP_DIR | wc -l)
|
local text="$1"
|
||||||
if [ "$BACKUP_COUNT" -gt 5 ]; then
|
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||||
OLD_BACKUPS=$(ls -1t $BACKUP_DIR | tail -n +6)
|
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||||
for backup in $OLD_BACKUPS; do
|
--data-urlencode "text=${text}" \
|
||||||
rm -rf "$BACKUP_DIR/$backup"
|
-d "parse_mode=HTML" >/dev/null || true
|
||||||
done
|
}
|
||||||
echo "🧹 Old backups cleaned"
|
|
||||||
fi
|
notify_failure() {
|
||||||
else
|
local exit_code=$?
|
||||||
echo "⚠️ No existing deployment found"
|
send_telegram "❌ <b>QuantEngine 배포 실패</b>
|
||||||
|
|
||||||
|
커밋: <code>${COMMIT}</code>
|
||||||
|
시간: <code>${TIMESTAMP}</code>
|
||||||
|
단계: deploy-to-prod (SSH Execution)"
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap notify_failure ERR
|
||||||
|
|
||||||
|
echo "=== Deploying QuantEngine $COMMIT ($TIMESTAMP) ==="
|
||||||
|
|
||||||
|
# 1. 아티팩트 복사
|
||||||
|
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 \
|
||||||
|
quant_engine_deploy.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/quantengine_${TIMESTAMP}.tgz"
|
||||||
|
|
||||||
|
# 2. 원격 배포 명령어 통합 (SSH 1회 연결)
|
||||||
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 \
|
||||||
|
-o ServerAliveInterval=10 \
|
||||||
|
"$DEPLOY_USER@$DEPLOY_HOST" bash << REMOTE
|
||||||
|
set -e
|
||||||
|
DEPLOY_HOME="/home/kjh2064"
|
||||||
|
DEPLOY_DIR="\$DEPLOY_HOME/deployments/quantengine_${TIMESTAMP}"
|
||||||
|
|
||||||
|
echo "--- [1/4] 압축 해제 ---"
|
||||||
|
mkdir -p "\$DEPLOY_DIR"
|
||||||
|
tar -xzf "/tmp/quantengine_${TIMESTAMP}.tgz" -C "\$DEPLOY_DIR"
|
||||||
|
rm -f "/tmp/quantengine_${TIMESTAMP}.tgz"
|
||||||
|
|
||||||
|
echo "--- [2/4] 심볼릭 링크 전환 ---"
|
||||||
|
ln -sfn "\$DEPLOY_DIR" "${{ env.DEPLOY_PATH }}"
|
||||||
|
|
||||||
|
echo "--- [3/4] 서비스 재시작 ---"
|
||||||
|
sudo /usr/bin/systemctl restart ${{ env.SERVICE_NAME }}
|
||||||
|
|
||||||
|
echo "--- [4/4] 헬스 체크 ---"
|
||||||
|
ATTEMPTS=20
|
||||||
|
for i in \\\$(seq 1 \\\$ATTEMPTS); do
|
||||||
|
STATUS=\\\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/ 2>/dev/null || echo "000")
|
||||||
|
if [ "\\\$STATUS" = "200" ]; then
|
||||||
|
echo "✓ 헬스체크 성공 (시도 \\\$i/\\\$ATTEMPTS, HTTP 200)"
|
||||||
|
# 구 배포 폴더 정리 (최근 5개만 보존)
|
||||||
|
ls -1dt \$DEPLOY_HOME/deployments/quantengine_* 2>/dev/null | tail -n +6 | xargs rm -rf 2>/dev/null || true
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
EOF
|
if [ "\\\$i" -eq "\\\$ATTEMPTS" ]; then
|
||||||
|
echo "=== FATAL: 서비스가 헬스체크 응답을 하지 않음 ===" >&2
|
||||||
- name: Deploy Package
|
systemctl is-active ${{ env.SERVICE_NAME }} >&2 || true
|
||||||
run: |
|
journalctl -u ${{ env.SERVICE_NAME }} --no-pager -n 50 >&2
|
||||||
echo "📤 Deploying package to production..."
|
|
||||||
|
|
||||||
ARCHIVE_NAME=$(ls -1 quant-engine-release-*.tar.gz | head -1)
|
|
||||||
|
|
||||||
# Create temporary directory on remote
|
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
|
||||||
"mkdir -p /tmp/quant-deploy && chmod 777 /tmp/quant-deploy"
|
|
||||||
|
|
||||||
# Transfer archive
|
|
||||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$ARCHIVE_NAME" \
|
|
||||||
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/quant-deploy/
|
|
||||||
|
|
||||||
echo "✅ Package transferred"
|
|
||||||
|
|
||||||
- name: Extract and Install
|
|
||||||
run: |
|
|
||||||
echo "📦 Extracting and installing..."
|
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DEPLOY_PATH="${{ env.DEPLOY_PATH }}"
|
|
||||||
ARCHIVE_NAME=$(ls -1 /tmp/quant-deploy/quant-engine-release-*.tar.gz | head -1)
|
|
||||||
|
|
||||||
# Create deployment directory
|
|
||||||
mkdir -p "$DEPLOY_PATH"
|
|
||||||
|
|
||||||
# Extract new package
|
|
||||||
tar -xzf "$ARCHIVE_NAME" -C "$DEPLOY_PATH"
|
|
||||||
echo "✅ Package extracted to $DEPLOY_PATH"
|
|
||||||
|
|
||||||
# Verify key files
|
|
||||||
if [ -f "$DEPLOY_PATH/QuantEngine.Web.dll" ]; then
|
|
||||||
echo "✅ QuantEngine.Web.dll verified"
|
|
||||||
else
|
|
||||||
echo "❌ QuantEngine.Web.dll not found!"
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
echo " 대기 중... (\\\$i/\\\$ATTEMPTS, HTTP \\\$STATUS)"
|
||||||
# Cleanup temp
|
|
||||||
rm -rf /tmp/quant-deploy
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Restart Service
|
|
||||||
run: |
|
|
||||||
echo "🔄 Restarting quantengine service to apply changes (Downtime minimal)..."
|
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
echo "⏹️ Restarting quantengine service..."
|
|
||||||
sudo systemctl restart ${{ env.SERVICE_NAME }}
|
|
||||||
sleep 3
|
sleep 3
|
||||||
|
|
||||||
# Check status
|
|
||||||
if sudo systemctl is-active --quiet ${{ env.SERVICE_NAME }}; then
|
|
||||||
echo "✅ ${{ env.SERVICE_NAME }} restarted successfully"
|
|
||||||
sudo systemctl status ${{ env.SERVICE_NAME }} | head -5
|
|
||||||
else
|
|
||||||
echo "❌ ${{ env.SERVICE_NAME }} failed to start"
|
|
||||||
sudo systemctl status ${{ env.SERVICE_NAME }}
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
|
|
||||||
- name: Health Check
|
|
||||||
run: |
|
|
||||||
echo "🧪 Running health checks on remote host..."
|
|
||||||
|
|
||||||
# Wait for service to be ready (localhost:5000/quant/ through Kestrel inside remote host)
|
|
||||||
for i in {1..30}; do
|
|
||||||
HTTP_CODE=$(ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
|
||||||
'curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:5000/quant/' || echo "000")
|
|
||||||
|
|
||||||
if [ "$HTTP_CODE" = "200" ]; then
|
|
||||||
echo "✅ Health check passed (HTTP $HTTP_CODE inside remote host)"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "⏳ Waiting for service... (attempt $i/30, HTTP $HTTP_CODE)"
|
|
||||||
sleep 2
|
|
||||||
done
|
done
|
||||||
|
REMOTE
|
||||||
|
|
||||||
if [ "$HTTP_CODE" != "200" ]; then
|
echo "✓ 배포 완료: quantengine_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||||
echo "❌ Health check failed after 60 seconds"
|
send_telegram "✅ <b>QuantEngine 배포 완료</b>
|
||||||
echo "Service logs:"
|
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
커밋: <code>${COMMIT}</code>
|
||||||
"sudo journalctl -u ${{ env.SERVICE_NAME }} -n 20" || true
|
시간: <code>${TIMESTAMP}</code>
|
||||||
exit 1
|
대상: <code>${DEPLOY_HOST}</code>"
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Verify Deployment
|
|
||||||
run: |
|
|
||||||
echo "📊 Verifying deployment..."
|
|
||||||
|
|
||||||
# Check MudBlazor is loaded (via public IP)
|
|
||||||
PUBLIC_IP="178.104.200.7"
|
|
||||||
MUDBLAZOR_CHECK=$(curl -s "http://$PUBLIC_IP/quant/" | grep -c "MudBlazor" || echo "0")
|
|
||||||
|
|
||||||
if [ "$MUDBLAZOR_CHECK" -gt "0" ]; then
|
|
||||||
echo "✅ MudBlazor UI loaded successfully"
|
|
||||||
else
|
|
||||||
echo "⚠️ MudBlazor might not be loaded correctly"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get page title
|
|
||||||
PAGE_TITLE=$(curl -s "http://$PUBLIC_IP/quant/" | grep -o "<title>.*</title>" | head -1)
|
|
||||||
echo "📄 Page title: $PAGE_TITLE"
|
|
||||||
|
|
||||||
- name: Generate Deployment Report
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
cat > deployment-report.txt << EOF
|
|
||||||
═══════════════════════════════════════════════════════
|
|
||||||
Quant Engine v9 Deployment Report
|
|
||||||
═══════════════════════════════════════════════════════
|
|
||||||
|
|
||||||
Deployment Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
|
||||||
Run Number: ${{ github.run_number }}
|
|
||||||
Commit: ${{ github.sha }}
|
|
||||||
Branch: ${{ github.ref }}
|
|
||||||
|
|
||||||
🎯 Target Environment
|
|
||||||
Server: hz-prod-01
|
|
||||||
Internal IP: ${{ env.DEPLOY_HOST }}
|
|
||||||
Public IP: 178.104.200.7
|
|
||||||
Deploy Path: ${{ env.DEPLOY_PATH }}
|
|
||||||
Service: ${{ env.SERVICE_NAME }}
|
|
||||||
|
|
||||||
📊 Deployment Status: COMPLETED
|
|
||||||
|
|
||||||
✅ Release Build: Successful
|
|
||||||
✅ Package Created: 24MB+
|
|
||||||
✅ Backup Created: /home/kjh2064/quantengine_backup/
|
|
||||||
✅ Package Deployed: ${{ env.DEPLOY_PATH }}
|
|
||||||
✅ Service Started: ${{ env.SERVICE_NAME }}
|
|
||||||
✅ Health Check: PASS (localhost:5000)
|
|
||||||
✅ MudBlazor UI: Verified via public IP
|
|
||||||
|
|
||||||
🌐 Access Information
|
|
||||||
Public URL: http://178.104.200.7/quant/
|
|
||||||
Service Port: 127.0.0.1:5000
|
|
||||||
Nginx Config: /etc/nginx/sites-available/gitea-ip.conf
|
|
||||||
|
|
||||||
📝 Service Architecture
|
|
||||||
- Nginx (reverse proxy) listens on port 80/443
|
|
||||||
- /quant/ path → localhost:5000 (quantengine service)
|
|
||||||
- quantengine runs as user kjh2064
|
|
||||||
- WorkingDirectory: /home/kjh2064/quantengine_active
|
|
||||||
|
|
||||||
🔍 Monitoring & Logs
|
|
||||||
- Service: sudo systemctl status ${{ env.SERVICE_NAME }}
|
|
||||||
- Logs: sudo journalctl -u ${{ env.SERVICE_NAME }} -f
|
|
||||||
- Nginx: sudo tail -f /var/log/nginx/error.log
|
|
||||||
- Deployment Log: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
|
|
||||||
🔄 Rollback Command (if needed):
|
|
||||||
ssh kjh2064@${{ env.DEPLOY_HOST }} 'LATEST=\$(ls -t /home/kjh2064/quantengine_backup | head -1); cp -r /home/kjh2064/quantengine_backup/\$LATEST/* /home/kjh2064/quantengine_active/ && sudo systemctl restart ${{ env.SERVICE_NAME }}'
|
|
||||||
|
|
||||||
═══════════════════════════════════════════════════════
|
|
||||||
EOF
|
|
||||||
cat deployment-report.txt
|
|
||||||
|
|
||||||
- name: Upload Deployment Report
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: deployment-report
|
|
||||||
path: deployment-report.txt
|
|
||||||
retention-days: 90
|
|
||||||
|
|
||||||
- name: Notify Telegram
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
STATUS=${{ job.status }}
|
|
||||||
if [ "$STATUS" = "success" ]; then
|
|
||||||
EMOJI="✅"
|
|
||||||
TEXT="*Quant Engine v9 Deployment SUCCESS* $EMOJI%0A• Run: #${{ github.run_number }}%0A• Commit: ${{ github.sha }}%0A• Service: ${{ env.SERVICE_NAME }}%0A• URL: http://178.104.200.7/quant/"
|
|
||||||
else
|
|
||||||
EMOJI="❌"
|
|
||||||
TEXT="*Quant Engine v9 Deployment FAILED* $EMOJI%0A• Run: #${{ github.run_number }}%0A• Commit: ${{ github.sha }}%0A• Service: ${{ env.SERVICE_NAME }}%0A• URL: http://178.104.200.7/quant/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -s -X POST "https://api.telegram.org/bot8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0/sendMessage" \
|
|
||||||
-d "chat_id=-5460205872" \
|
|
||||||
-d "text=$TEXT" \
|
|
||||||
-d "parse_mode=Markdown"
|
|
||||||
|
|
||||||
post-deployment:
|
|
||||||
name: Post-Deployment Checks
|
|
||||||
needs: deploy-to-prod
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: success()
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Performance Baseline
|
|
||||||
run: |
|
|
||||||
echo "📈 Collecting performance metrics via Public IP..."
|
|
||||||
|
|
||||||
# Page load time
|
|
||||||
START=$(date +%s%N)
|
|
||||||
curl -s http://178.104.200.7/quant/ > /dev/null
|
|
||||||
END=$(date +%s%N)
|
|
||||||
LOAD_TIME=$(( (END - START) / 1000000 ))
|
|
||||||
|
|
||||||
echo "⏱️ Page load time: ${LOAD_TIME}ms"
|
|
||||||
|
|
||||||
if [ $LOAD_TIME -lt 2000 ]; then
|
|
||||||
echo "✅ Load time acceptable (< 2s)"
|
|
||||||
else
|
|
||||||
echo "⚠️ Load time slightly slow (> 2s), but acceptable"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Create Deployment Checklist
|
|
||||||
run: |
|
|
||||||
cat > deployment-checklist.txt << 'EOF'
|
|
||||||
✅ Quant Engine v9 Deployment Complete
|
|
||||||
|
|
||||||
Web Service:
|
|
||||||
[✓] Release build successful (24MB)
|
|
||||||
[✓] Deployed to: http://178.104.200.7/quant/
|
|
||||||
[✓] nginx restarted
|
|
||||||
[✓] Health check: HTTP 200 OK
|
|
||||||
[✓] MudBlazor UI verified
|
|
||||||
[✓] Page load time: < 2s
|
|
||||||
|
|
||||||
Backup & Recovery:
|
|
||||||
[✓] Backup created: /var/www/quant_backup/
|
|
||||||
[✓] 5 previous backups retained
|
|
||||||
[✓] Rollback ready
|
|
||||||
|
|
||||||
Next Steps:
|
|
||||||
[ ] Monitor nginx logs: ssh kjh2064@178.104.200.7 'sudo tail -f /var/log/nginx/error.log'
|
|
||||||
[ ] Check dashboard: http://178.104.200.7/quant/
|
|
||||||
[ ] Verify all components loaded
|
|
||||||
[ ] Test responsive design (mobile/tablet)
|
|
||||||
[ ] Monitor performance metrics
|
|
||||||
|
|
||||||
GAS Deployment (Manual):
|
|
||||||
[ ] Deploy gas_data_feed.gs to Google Apps Script
|
|
||||||
[ ] Deploy live_outcome_ledger.gs
|
|
||||||
[ ] Test signal tracking
|
|
||||||
|
|
||||||
Documentation:
|
|
||||||
[ ] DEPLOYMENT_GUIDE.md
|
|
||||||
[ ] DEPLOYMENT_STEPS.md
|
|
||||||
[ ] UI_COMPLETENESS_REPORT.md
|
|
||||||
[ ] V9_HARDENING_IMPLEMENTATION_ROADMAP.md
|
|
||||||
EOF
|
|
||||||
cat deployment-checklist.txt
|
|
||||||
|
|
||||||
- name: Upload Checklist
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: post-deployment-checklist
|
|
||||||
path: deployment-checklist.txt
|
|
||||||
retention-days: 30
|
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ concurrency:
|
|||||||
group: snapshot-admin-deploy-main
|
group: snapshot-admin-deploy-main
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
DEPLOY_HOST: 178.104.200.7
|
||||||
|
DEPLOY_USER: kjh2064
|
||||||
|
TELEGRAM_BOT_TOKEN_DEFAULT: "8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0"
|
||||||
|
TELEGRAM_CHAT_ID_DEFAULT: "-5460205872"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-deploy:
|
build-and-deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -33,38 +39,84 @@ jobs:
|
|||||||
echo "[deploy] compressing publish output"
|
echo "[deploy] compressing publish output"
|
||||||
tar -czf quantengine.tar.gz -C ./publish .
|
tar -czf quantengine.tar.gz -C ./publish .
|
||||||
|
|
||||||
- name: Deploy to Host via Local SSH
|
- name: Setup SSH
|
||||||
env:
|
|
||||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
|
||||||
run: |
|
run: |
|
||||||
echo "[deploy] setting up SSH and deploying shadow copy"
|
|
||||||
mkdir -p ~/.ssh
|
mkdir -p ~/.ssh
|
||||||
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
chmod 700 ~/.ssh
|
||||||
|
if echo "${{ secrets.SSH_PRIVATE_KEY }}" | grep -q "BEGIN"; then
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||||
|
else
|
||||||
|
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_ed25519 || echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
|
||||||
|
fi
|
||||||
chmod 600 ~/.ssh/id_ed25519
|
chmod 600 ~/.ssh/id_ed25519
|
||||||
|
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
|
||||||
|
|
||||||
# Upload artifact and deploy script to host
|
- name: Deploy & Verify on Server
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "mkdir -p /home/kjh2064/tmp"
|
|
||||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 quantengine.tar.gz kjh2064@178.104.200.7:/home/kjh2064/tmp/quantengine.tar.gz
|
|
||||||
|
|
||||||
# Execute hot deploy script
|
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "chmod +x /home/kjh2064/tmp/deploy.sh 2>/dev/null || true"
|
|
||||||
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 tools/deploy_quantengine.sh kjh2064@178.104.200.7:/home/kjh2064/tmp/deploy.sh
|
|
||||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "chmod +x /home/kjh2064/tmp/deploy.sh && /home/kjh2064/tmp/deploy.sh"
|
|
||||||
|
|
||||||
- name: Verify Public Routes
|
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
root_html=$(curl -s "http://178.104.200.7/quant/")
|
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||||
ops_html=$(curl -s "http://178.104.200.7/quant/operations")
|
COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
DEPLOY_HOST="${{ env.DEPLOY_HOST }}"
|
||||||
|
DEPLOY_USER="${{ env.DEPLOY_USER }}"
|
||||||
|
|
||||||
|
TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}"
|
||||||
|
[ -z "$TELEGRAM_BOT_TOKEN" ] && TELEGRAM_BOT_TOKEN="${{ env.TELEGRAM_BOT_TOKEN_DEFAULT }}"
|
||||||
|
TELEGRAM_CHAT_ID="${{ secrets.TELEGRAM_CHAT_ID }}"
|
||||||
|
[ -z "$TELEGRAM_CHAT_ID" ] && TELEGRAM_CHAT_ID="${{ env.TELEGRAM_CHAT_ID_DEFAULT }}"
|
||||||
|
|
||||||
|
send_telegram() {
|
||||||
|
local text="$1"
|
||||||
|
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||||
|
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||||
|
--data-urlencode "text=${text}" \
|
||||||
|
-d "parse_mode=HTML" >/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
notify_failure() {
|
||||||
|
local exit_code=$?
|
||||||
|
send_telegram "❌ <b>Snapshot Admin 배포 실패</b>
|
||||||
|
|
||||||
|
커밋: <code>${COMMIT}</code>
|
||||||
|
시간: <code>${TIMESTAMP}</code>
|
||||||
|
단계: snapshot_admin_deploy (Deploy Execution)"
|
||||||
|
exit "$exit_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap notify_failure ERR
|
||||||
|
|
||||||
|
echo "=== Deploying Snapshot Admin $COMMIT ($TIMESTAMP) ==="
|
||||||
|
|
||||||
|
# 1. 원격지 임시 폴더 생성 및 업로드
|
||||||
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p /home/kjh2064/tmp"
|
||||||
|
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 quantengine.tar.gz "$DEPLOY_USER@$DEPLOY_HOST:/home/kjh2064/tmp/quantengine.tar.gz"
|
||||||
|
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 tools/deploy_quantengine.sh "$DEPLOY_USER@$DEPLOY_HOST:/home/kjh2064/tmp/deploy.sh"
|
||||||
|
|
||||||
|
# 2. 배포 스크립트 실행
|
||||||
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$DEPLOY_USER@$DEPLOY_HOST" "chmod +x /home/kjh2064/tmp/deploy.sh && /home/kjh2064/tmp/deploy.sh"
|
||||||
|
|
||||||
|
# 3. 배포 성공 검증
|
||||||
|
echo "=== Verifying Public Routes ==="
|
||||||
|
root_html=$(curl -sf "http://${DEPLOY_HOST}/quant/" 2>/dev/null || echo "")
|
||||||
|
ops_html=$(curl -sf "http://${DEPLOY_HOST}/quant/operations" 2>/dev/null || echo "")
|
||||||
|
|
||||||
root_code=$(printf '%s' "$root_html" | grep -q "Quant Engine" && echo 200 || echo 500)
|
root_code=$(printf '%s' "$root_html" | grep -q "Quant Engine" && echo 200 || echo 500)
|
||||||
ops_code=$(printf '%s' "$ops_html" | grep -q "Operational Report" && echo 200 || echo 500)
|
ops_code=$(printf '%s' "$ops_html" | grep -q "Operational Report" && echo 200 || echo 500)
|
||||||
|
|
||||||
echo "/quant/ -> ${root_code}"
|
echo "/quant/ -> ${root_code}"
|
||||||
echo "/quant/operations -> ${ops_code}"
|
echo "/quant/operations -> ${ops_code}"
|
||||||
|
|
||||||
if [ "$root_code" != "200" ]; then
|
if [ "$root_code" != "200" ]; then
|
||||||
echo "Deployment content check failed for /quant/"
|
echo "Deployment content check failed for /quant/" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ "$ops_code" != "200" ]; then
|
if [ "$ops_code" != "200" ]; then
|
||||||
echo "Deployment content check failed for /quant/operations"
|
echo "Deployment content check failed for /quant/operations" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "✓ 배포 완료: quantengine_${TIMESTAMP} @ $DEPLOY_HOST"
|
||||||
|
send_telegram "✅ <b>Snapshot Admin 배포 완료</b>
|
||||||
|
|
||||||
|
커밋: <code>${COMMIT}</code>
|
||||||
|
시간: <code>${TIMESTAMP}</code>
|
||||||
|
대상: <code>${DEPLOY_HOST}</code>"
|
||||||
|
|||||||
Reference in New Issue
Block a user