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
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user