chore(ci): Update Gitea Actions CI/CD pipeline for v9 production deployment
- Configure internal IP deployment: 172.17.0.1 (hz-prod-01) - Set deployment path to /home/kjh2064/quantengine_active - Use quantengine systemd service for app management - Implement service lifecycle (stop → backup → extract → start) - Add health checks against localhost:5000 (quantengine) - Update Nginx reverse proxy verification (already configured) - Add comprehensive deployment report and Slack notifications - Include post-deployment performance metrics collection CI/CD Flow: 1. Build & Test: Release build, validation, .tar.gz creation 2. Deploy: Service stop, backup, file transfer, service start 3. Health Check: localhost:5000 verification via Nginx proxy 4. Post-Deploy: Performance metrics and deployment checklist Environment: hz-prod-01 (Public: 178.104.200.7 / Internal: 172.17.0.1) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,12 +6,14 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DEPLOY_HOST: 192.168.123.100
|
DEPLOY_HOST: 172.17.0.1
|
||||||
# NOTE: Gitea와 운영서버가 원격지의 같은 서버에 있으므로 내부 IP 사용
|
# NOTE: Gitea와 운영서버가 같은 호스트에 있음 (hz-prod-01)
|
||||||
# Gitea (CI/CD) → SSH (내부 192.168.123.100) → 운영서버
|
# 구조: 공인 IP 178.104.200.7/quant → Nginx reverse proxy → localhost:5000 (quantengine)
|
||||||
# 외부 사용자: 178.104.200.7 → nginx 포트포워딩 → 192.168.123.100/quant
|
# 배포: .NET DLL을 /home/kjh2064/quantengine_active에 배포
|
||||||
|
# Nginx 설정: /etc/nginx/sites-available/gitea-ip.conf (이미 구성됨)
|
||||||
DEPLOY_USER: kjh2064
|
DEPLOY_USER: kjh2064
|
||||||
DEPLOY_PATH: /var/www/quant
|
DEPLOY_PATH: /home/kjh2064/quantengine_active
|
||||||
|
SERVICE_NAME: quantengine
|
||||||
DOTNET_VERSION: '10.0.x'
|
DOTNET_VERSION: '10.0.x'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -105,24 +107,36 @@ jobs:
|
|||||||
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: Stop Service and Create Backup
|
||||||
run: |
|
run: |
|
||||||
echo "📦 Creating backup on production server..."
|
echo "📦 Stopping service and creating backup..."
|
||||||
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
||||||
set -e
|
set -e
|
||||||
BACKUP_DIR="/var/www/quant_backup"
|
BACKUP_DIR="/home/kjh2064/quantengine_backup"
|
||||||
BACKUP_NAME="quant_backup_$(date +%Y%m%d_%H%M%S)"
|
BACKUP_NAME="quantengine_$(date +%Y%m%d_%H%M%S)"
|
||||||
|
|
||||||
sudo mkdir -p $BACKUP_DIR
|
# Stop service
|
||||||
if [ -d ${{ env.DEPLOY_PATH }}/publish ]; then
|
echo "⏹️ Stopping quantengine service..."
|
||||||
sudo cp -r ${{ env.DEPLOY_PATH }}/publish "$BACKUP_DIR/$BACKUP_NAME"
|
sudo systemctl stop ${{ env.SERVICE_NAME }}
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Create backup
|
||||||
|
mkdir -p $BACKUP_DIR
|
||||||
|
if [ -d ${{ env.DEPLOY_PATH }} ]; then
|
||||||
|
cp -r ${{ env.DEPLOY_PATH }} "$BACKUP_DIR/$BACKUP_NAME"
|
||||||
echo "✅ Backup created: $BACKUP_DIR/$BACKUP_NAME"
|
echo "✅ Backup created: $BACKUP_DIR/$BACKUP_NAME"
|
||||||
|
|
||||||
# Keep only last 5 backups
|
# Keep only last 5 backups
|
||||||
ls -t $BACKUP_DIR | tail -n +6 | xargs -I {} sudo rm -rf "$BACKUP_DIR/{}"
|
BACKUP_COUNT=$(ls -1 $BACKUP_DIR | wc -l)
|
||||||
|
if [ "$BACKUP_COUNT" -gt 5 ]; then
|
||||||
|
OLD_BACKUPS=$(ls -1t $BACKUP_DIR | tail -n +6)
|
||||||
|
for backup in $OLD_BACKUPS; do
|
||||||
|
rm -rf "$BACKUP_DIR/$backup"
|
||||||
|
done
|
||||||
echo "🧹 Old backups cleaned"
|
echo "🧹 Old backups cleaned"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
echo "⚠️ No existing deployment found, skipping backup"
|
echo "⚠️ No existing deployment found"
|
||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -152,38 +166,41 @@ jobs:
|
|||||||
ARCHIVE_NAME=$(ls -1 /tmp/quant-deploy/quant-engine-release-*.tar.gz | head -1)
|
ARCHIVE_NAME=$(ls -1 /tmp/quant-deploy/quant-engine-release-*.tar.gz | head -1)
|
||||||
|
|
||||||
# Create deployment directory
|
# Create deployment directory
|
||||||
sudo mkdir -p "$DEPLOY_PATH/publish"
|
mkdir -p "$DEPLOY_PATH"
|
||||||
sudo chmod 777 "$DEPLOY_PATH/publish"
|
|
||||||
|
|
||||||
# Extract new package
|
# Extract new package
|
||||||
tar -xzf "$ARCHIVE_NAME" -C "$DEPLOY_PATH/publish"
|
tar -xzf "$ARCHIVE_NAME" -C "$DEPLOY_PATH"
|
||||||
echo "✅ Package extracted"
|
echo "✅ Package extracted to $DEPLOY_PATH"
|
||||||
|
|
||||||
# Set permissions
|
# Verify key files
|
||||||
sudo chown -R www-data:www-data "$DEPLOY_PATH/publish"
|
if [ -f "$DEPLOY_PATH/QuantEngine.Web.dll" ]; then
|
||||||
sudo chmod -R 755 "$DEPLOY_PATH/publish"
|
echo "✅ QuantEngine.Web.dll verified"
|
||||||
echo "✅ Permissions set"
|
else
|
||||||
|
echo "❌ QuantEngine.Web.dll not found!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# Cleanup temp
|
# Cleanup temp
|
||||||
rm -rf /tmp/quant-deploy
|
rm -rf /tmp/quant-deploy
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
- name: Restart Services
|
- name: Start Service
|
||||||
run: |
|
run: |
|
||||||
echo "🔄 Restarting services..."
|
echo "🔄 Starting quantengine service..."
|
||||||
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Restart nginx
|
# Start service
|
||||||
sudo systemctl restart nginx
|
sudo systemctl start ${{ env.SERVICE_NAME }}
|
||||||
sleep 2
|
sleep 3
|
||||||
|
|
||||||
# Check status
|
# Check status
|
||||||
if sudo systemctl is-active --quiet nginx; then
|
if sudo systemctl is-active --quiet ${{ env.SERVICE_NAME }}; then
|
||||||
echo "✅ nginx restarted successfully"
|
echo "✅ ${{ env.SERVICE_NAME }} started successfully"
|
||||||
|
sudo systemctl status ${{ env.SERVICE_NAME }} | head -5
|
||||||
else
|
else
|
||||||
echo "❌ nginx failed to start"
|
echo "❌ ${{ env.SERVICE_NAME }} failed to start"
|
||||||
sudo systemctl status nginx
|
sudo systemctl status ${{ env.SERVICE_NAME }}
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
EOF
|
EOF
|
||||||
@@ -192,13 +209,13 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "🧪 Running health checks..."
|
echo "🧪 Running health checks..."
|
||||||
|
|
||||||
# Wait for service to be ready
|
# Wait for service to be ready (localhost:5000 through Nginx)
|
||||||
for i in {1..30}; do
|
for i in {1..30}; do
|
||||||
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
||||||
http://${{ env.DEPLOY_HOST }}/quant/ || echo "000")
|
"http://127.0.0.1:5000/" || echo "000")
|
||||||
|
|
||||||
if [ "$HTTP_CODE" = "200" ]; then
|
if [ "$HTTP_CODE" = "200" ]; then
|
||||||
echo "✅ Health check passed (HTTP $HTTP_CODE)"
|
echo "✅ Health check passed (HTTP $HTTP_CODE at localhost:5000)"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -208,6 +225,9 @@ jobs:
|
|||||||
|
|
||||||
if [ "$HTTP_CODE" != "200" ]; then
|
if [ "$HTTP_CODE" != "200" ]; then
|
||||||
echo "❌ Health check failed after 60 seconds"
|
echo "❌ Health check failed after 60 seconds"
|
||||||
|
echo "Service logs:"
|
||||||
|
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
|
||||||
|
"sudo journalctl -u ${{ env.SERVICE_NAME }} -n 20" || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -215,8 +235,9 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "📊 Verifying deployment..."
|
echo "📊 Verifying deployment..."
|
||||||
|
|
||||||
# Check MudBlazor is loaded
|
# Check MudBlazor is loaded (via public IP)
|
||||||
MUDBLAZOR_CHECK=$(curl -s http://${{ env.DEPLOY_HOST }}/quant/ | grep -c "MudBlazor" || echo "0")
|
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
|
if [ "$MUDBLAZOR_CHECK" -gt "0" ]; then
|
||||||
echo "✅ MudBlazor UI loaded successfully"
|
echo "✅ MudBlazor UI loaded successfully"
|
||||||
@@ -225,7 +246,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Get page title
|
# Get page title
|
||||||
PAGE_TITLE=$(curl -s http://${{ env.DEPLOY_HOST }}/quant/ | grep -o "<title>.*</title>" | head -1)
|
PAGE_TITLE=$(curl -s "http://$PUBLIC_IP/quant/" | grep -o "<title>.*</title>" | head -1)
|
||||||
echo "📄 Page title: $PAGE_TITLE"
|
echo "📄 Page title: $PAGE_TITLE"
|
||||||
|
|
||||||
- name: Generate Deployment Report
|
- name: Generate Deployment Report
|
||||||
@@ -233,7 +254,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cat > deployment-report.txt << EOF
|
cat > deployment-report.txt << EOF
|
||||||
═══════════════════════════════════════════════════════
|
═══════════════════════════════════════════════════════
|
||||||
Quant Engine Deployment Report
|
Quant Engine v9 Deployment Report
|
||||||
═══════════════════════════════════════════════════════
|
═══════════════════════════════════════════════════════
|
||||||
|
|
||||||
Deployment Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
Deployment Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
|
||||||
@@ -241,27 +262,42 @@ jobs:
|
|||||||
Commit: ${{ github.sha }}
|
Commit: ${{ github.sha }}
|
||||||
Branch: ${{ github.ref }}
|
Branch: ${{ github.ref }}
|
||||||
|
|
||||||
Target Server: ${{ env.DEPLOY_HOST }}
|
🎯 Target Environment
|
||||||
|
Server: hz-prod-01
|
||||||
|
Internal IP: ${{ env.DEPLOY_HOST }}
|
||||||
|
Public IP: 178.104.200.7
|
||||||
Deploy Path: ${{ env.DEPLOY_PATH }}
|
Deploy Path: ${{ env.DEPLOY_PATH }}
|
||||||
|
Service: ${{ env.SERVICE_NAME }}
|
||||||
|
|
||||||
Status: COMPLETED
|
📊 Deployment Status: COMPLETED
|
||||||
|
|
||||||
✅ Release Build: Successful
|
✅ Release Build: Successful
|
||||||
✅ Package Created: 24MB
|
✅ Package Created: 24MB+
|
||||||
✅ Backup Created: /var/www/quant_backup/
|
✅ Backup Created: /home/kjh2064/quantengine_backup/
|
||||||
✅ Package Deployed: ${{ env.DEPLOY_PATH }}/publish
|
✅ Package Deployed: ${{ env.DEPLOY_PATH }}
|
||||||
✅ Services Restarted: nginx
|
✅ Service Started: ${{ env.SERVICE_NAME }}
|
||||||
✅ Health Check: PASS
|
✅ Health Check: PASS (localhost:5000)
|
||||||
✅ MudBlazor UI: Verified
|
✅ MudBlazor UI: Verified via public IP
|
||||||
|
|
||||||
Access: http://${{ env.DEPLOY_HOST }}/quant/
|
🌐 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
|
||||||
|
|
||||||
Logs:
|
📝 Service Architecture
|
||||||
- Deployment: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
- Nginx (reverse proxy) listens on port 80/443
|
||||||
- nginx: ssh kjh2064@${{ env.DEPLOY_HOST }} 'sudo tail -50 /var/log/nginx/error.log'
|
- /quant/ path → localhost:5000 (quantengine service)
|
||||||
|
- quantengine runs as user kjh2064
|
||||||
|
- WorkingDirectory: /home/kjh2064/quantengine_active
|
||||||
|
|
||||||
Rollback Command (if needed):
|
🔍 Monitoring & Logs
|
||||||
ssh kjh2064@${{ env.DEPLOY_HOST }} 'LATEST=\$(ls -t /var/www/quant_backup | head -1); sudo cp -r /var/www/quant_backup/\$LATEST/* /var/www/quant/publish/ && sudo systemctl restart nginx'
|
- 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
|
EOF
|
||||||
@@ -293,12 +329,12 @@ jobs:
|
|||||||
-d "{
|
-d "{
|
||||||
\"attachments\": [{
|
\"attachments\": [{
|
||||||
\"color\": \"$COLOR\",
|
\"color\": \"$COLOR\",
|
||||||
\"title\": \"$EMOJI Quant Engine Deployment\",
|
\"title\": \"$EMOJI Quant Engine v9 Deployment\",
|
||||||
\"text\": \"Run #${{ github.run_number }}\",
|
\"text\": \"Run #${{ github.run_number }}\",
|
||||||
\"fields\": [
|
\"fields\": [
|
||||||
{\"title\": \"Status\", \"value\": \"$STATUS\", \"short\": true},
|
{\"title\": \"Status\", \"value\": \"$STATUS\", \"short\": true},
|
||||||
{\"title\": \"Server\", \"value\": \"${{ env.DEPLOY_HOST }}\", \"short\": true},
|
{\"title\": \"Service\", \"value\": \"${{ env.SERVICE_NAME }}\", \"short\": true},
|
||||||
{\"title\": \"URL\", \"value\": \"http://${{ env.DEPLOY_HOST }}/quant/\", \"short\": false}
|
{\"title\": \"URL\", \"value\": \"http://178.104.200.7/quant/\", \"short\": false}
|
||||||
],
|
],
|
||||||
\"ts\": $(date +%s)
|
\"ts\": $(date +%s)
|
||||||
}]
|
}]
|
||||||
|
|||||||
Reference in New Issue
Block a user