refactor: Strengthen deploy-prod.yml with comprehensive checks and logging
Deploy to Production / Build Release (push) Failing after 26s
Deploy to Production / Pre-Deployment Verification (push) Has been skipped
Deploy to Production / Deploy to Production (push) Has been skipped
Deploy to Production / Post-Deployment Reporting (push) Successful in 1s

【 개선사항 】
1. Build 단계 분리: metadata 생성, artifact 관리
2. Pre-deployment 검증: SSH, secrets, artifact, connectivity
3. 실제 배포: SSH를 통한 원격 배포, symlink 관리
4. 헬스 체크: 10회 재시도, 상세 검증
5. 배포 후 검증: 실제 서비스 상태 확인
6. 완벽한 에러 처리: 각 단계별 fail-fast
7. 배포 결과 리포팅: 성공/실패 알림

【 구조 】
- Build: .NET 빌드 + 아티팩트 생성
- Pre-deploy-check: SSH/Secrets/Artifact/Connectivity 검증
- Deploy: 실제 배포 + 헬스 체크
- Post-deploy: 배포 결과 리포팅

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 22:21:22 +09:00
parent 571d299d8a
commit 30fb70223c
+209 -24
View File
@@ -9,11 +9,21 @@ concurrency:
group: deploy-prod-main
cancel-in-progress: false
env:
DEPLOY_HOST: 178.104.200.7
DEPLOY_USER: kjh2064
DEPLOY_PORT: 22
SERVICE_NAME: quantengine
DOTNET_VERSION: '10.0.x'
jobs:
deploy:
name: Deploy
build:
name: Build Release
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 15
outputs:
artifact-name: ${{ steps.metadata.outputs.artifact }}
commit-hash: ${{ steps.metadata.outputs.commit }}
steps:
- name: Checkout
@@ -22,39 +32,214 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '10.0.x'
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Build Release
run: |
dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release --no-restore
- name: Publish
run: dotnet publish src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release -o ./publish
- name: Package
- name: Generate Metadata
id: metadata
run: |
COMMIT=$(git rev-parse --short HEAD)
tar -czf quantengine-${COMMIT}.tar.gz -C ./publish .
echo "ARTIFACT=quantengine-${COMMIT}.tar.gz" >> $GITHUB_ENV
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
ARTIFACT="quantengine-${TIMESTAMP}-${COMMIT}.tar.gz"
echo "artifact=${ARTIFACT}" >> $GITHUB_OUTPUT
echo "commit=${COMMIT}" >> $GITHUB_OUTPUT
echo "timestamp=${TIMESTAMP}" >> $GITHUB_OUTPUT
- name: Restore & Build
run: |
dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release --no-restore -p:ContinuousIntegrationBuild=true
- name: Publish
run: |
dotnet publish src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj \
-c Release \
-o ./publish \
--no-restore \
--no-build
- name: Package Artifact
run: |
tar -czf "${{ steps.metadata.outputs.artifact }}" -C ./publish .
ls -lh "${{ steps.metadata.outputs.artifact }}"
file "${{ steps.metadata.outputs.artifact }}"
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: quantengine-${{ github.run_number }}
name: build-${{ github.run_number }}
path: quantengine-*.tar.gz
retention-days: 7
- name: Verify Secrets
pre-deploy-check:
name: Pre-Deployment Verification
runs-on: ubuntu-latest
needs: build
timeout-minutes: 5
steps:
- name: Verify SSH Key
run: |
if [ -z "${{ secrets.QUANTENGINE_DB_PASSWORD }}" ]; then
echo "ERROR: DB password not configured"
if [ -z "${{ secrets.SSH_KEY }}" ]; then
echo "ERROR: SSH_KEY secret not configured"
exit 1
fi
echo "OK: Secrets verified"
echo "OK: SSH key configured"
- name: Deploy to Production
- name: Verify DB Secrets
run: |
echo "Deployment triggered: ${{ github.run_number }}"
echo "Commit: $(git rev-parse --short HEAD)"
echo "Artifact: ${{ env.ARTIFACT }}"
if [ -z "${{ secrets.QUANTENGINE_DB_PASSWORD }}" ]; then
echo "ERROR: QUANTENGINE_DB_PASSWORD secret not configured"
exit 1
fi
echo "OK: DB password configured"
- name: Verify Artifact
run: |
if [ "${{ needs.build.outputs.artifact-name }}" = "" ]; then
echo "ERROR: Build artifact not found"
exit 1
fi
echo "OK: Artifact: ${{ needs.build.outputs.artifact-name }}"
- name: SSH Connectivity Test
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
if ssh -i ~/.ssh/deploy_key -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} 'echo OK' 2>/dev/null; then
echo "OK: SSH connectivity verified"
else
echo "ERROR: Cannot connect via SSH"
exit 1
fi
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [ build, pre-deploy-check ]
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Download Artifact
uses: actions/download-artifact@v3
with:
name: build-${{ github.run_number }}
- name: Setup SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KEY }}" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null
- name: Deploy to Server
id: deploy
run: |
set -e
ARTIFACT="${{ needs.build.outputs.artifact-name }}"
COMMIT="${{ needs.build.outputs.commit-hash }}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DEPLOY_DIR="/home/${{ env.DEPLOY_USER }}/deployments/quantengine_${TIMESTAMP}_${COMMIT}"
echo "Artifact: $ARTIFACT"
echo "Deploy Dir: $DEPLOY_DIR"
# Upload artifact
scp -i ~/.ssh/deploy_key -P ${{ env.DEPLOY_PORT }} \
"$ARTIFACT" ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/
# Deploy via SSH
ssh -i ~/.ssh/deploy_key -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'DEPLOY_SCRIPT'
set -e
ARTIFACT="${{ needs.build.outputs.artifact-name }}"
DEPLOY_DIR="${{ env.DEPLOY_DIR }}"
# Create deployment directory
mkdir -p "$DEPLOY_DIR"
# Extract artifact
tar -xzf "/tmp/$ARTIFACT" -C "$DEPLOY_DIR"
# Update symlink
ln -sfn "$DEPLOY_DIR" ~/quantengine_active
# Health check
sleep 2
if curl -sf http://127.0.0.1:5000/Account/Login > /dev/null; then
echo "OK: Health check passed"
else
echo "WARNING: Health check may have issues"
fi
echo "Deployment completed: $DEPLOY_DIR"
DEPLOY_SCRIPT
echo "deploy-dir=${DEPLOY_DIR}" >> $GITHUB_OUTPUT
- name: Health Check
id: health
timeout-minutes: 2
run: |
for i in {1..10}; do
if curl -sf http://${{ env.DEPLOY_HOST }}:5000/Account/Login > /dev/null 2>&1; then
echo "✓ Health check passed (attempt $i)"
exit 0
fi
echo "Attempt $i/10..."
sleep 3
done
echo "ERROR: Health check failed after 10 attempts"
exit 1
- name: Verify Deployment
run: |
ssh -i ~/.ssh/deploy_key -p ${{ env.DEPLOY_PORT }} ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'VERIFY_SCRIPT'
ACTIVE=$(readlink ~/quantengine_active)
echo "=== Deployment Verification ==="
echo "Active deployment: $ACTIVE"
ls -lhd "$ACTIVE"
echo ""
echo "=== Service Status ==="
systemctl is-active quantengine.service
VERIFY_SCRIPT
post-deploy:
name: Post-Deployment Reporting
runs-on: ubuntu-latest
if: always()
needs: [ build, deploy ]
steps:
- name: Deployment Summary
run: |
echo "=== Deployment Summary ==="
echo "Run: ${{ github.run_number }}"
echo "Commit: ${{ needs.build.outputs.commit-hash }}"
echo "Artifact: ${{ needs.build.outputs.artifact-name }}"
echo "Status: ${{ job.status }}"
- name: Success Notification
if: success()
run: |
echo "✅ Deployment successful"
echo "Server: ${{ env.DEPLOY_HOST }}"
echo "Service: ${{ env.SERVICE_NAME }}"
- name: Failure Notification
if: failure()
run: |
echo "❌ Deployment failed"
echo "Check logs for details"
exit 1