Part 5: Production Deployment and Best Practices
Introduction
Production Deployment Workflow
Complete Deployment Pipeline
name: Deploy to Production
on:
release:
types: [published]
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy'
required: true
type: choice
options:
- staging
- production
version:
description: 'Version to deploy (leave empty for latest)'
required: false
type: string
env:
NODE_VERSION: '18'
AWS_REGION: 'us-east-1'
jobs:
prepare:
name: Prepare Deployment
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
environment: ${{ steps.env.outputs.environment }}
should-deploy: ${{ steps.check.outputs.should-deploy }}
steps:
- uses: actions/checkout@v3
- name: Determine version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
VERSION="${{ inputs.version }}"
elif [ "${{ github.event_name }}" == "release" ]; then
VERSION="${{ github.event.release.tag_name }}"
else
VERSION=$(cat package.json | jq -r .version)
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "π·οΈ Deploying version: $VERSION"
- name: Determine environment
id: env
run: |
if [ -n "${{ inputs.environment }}" ]; then
ENV="${{ inputs.environment }}"
elif [ "${{ github.event_name }}" == "release" ]; then
ENV="production"
else
ENV="staging"
fi
echo "environment=$ENV" >> $GITHUB_OUTPUT
echo "π Target environment: $ENV"
- name: Check if should deploy
id: check
run: |
# Add your deployment checks here
# Example: check if version already deployed
echo "should-deploy=true" >> $GITHUB_OUTPUT
build:
name: Build Docker Image
runs-on: ubuntu-latest
needs: prepare
if: needs.prepare.outputs.should-deploy == 'true'
steps:
- uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v1
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ env.AWS_REGION }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: ./services/auth
file: ./services/auth/Dockerfile
push: true
tags: |
${{ secrets.ECR_REGISTRY }}/auth-service:${{ needs.prepare.outputs.version }}
${{ secrets.ECR_REGISTRY }}/auth-service:latest
cache-from: type=registry,ref=${{ secrets.ECR_REGISTRY }}/auth-service:buildcache
cache-to: type=registry,ref=${{ secrets.ECR_REGISTRY }}/auth-service:buildcache,mode=max
build-args: |
NODE_ENV=production
VERSION=${{ needs.prepare.outputs.version }}
deploy:
name: Deploy to ${{ needs.prepare.outputs.environment }}
runs-on: ubuntu-latest
needs: [prepare, build]
environment:
name: ${{ needs.prepare.outputs.environment }}
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy to ECS
id: deploy
run: |
# Update ECS task definition
NEW_TASK_DEF=$(aws ecs describe-task-definition \
--task-definition auth-service-${{ needs.prepare.outputs.environment }} \
--query 'taskDefinition' | \
jq --arg IMAGE "${{ secrets.ECR_REGISTRY }}/auth-service:${{ needs.prepare.outputs.version }}" \
'.containerDefinitions[0].image = $IMAGE |
del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)')
# Register new task definition
NEW_TASK_ARN=$(echo $NEW_TASK_DEF | \
aws ecs register-task-definition --cli-input-json file:///dev/stdin | \
jq -r '.taskDefinition.taskDefinitionArn')
# Update service
aws ecs update-service \
--cluster my-cluster-${{ needs.prepare.outputs.environment }} \
--service auth-service \
--task-definition $NEW_TASK_ARN \
--force-new-deployment
# Wait for deployment to complete
aws ecs wait services-stable \
--cluster my-cluster-${{ needs.prepare.outputs.environment }} \
--services auth-service
# Get service URL
URL="https://auth.${{ needs.prepare.outputs.environment }}.example.com"
echo "url=$URL" >> $GITHUB_OUTPUT
echo "β
Deployed to: $URL"
- name: Run database migrations
run: |
# Run migrations in ECS
aws ecs run-task \
--cluster my-cluster-${{ needs.prepare.outputs.environment }} \
--task-definition auth-migrations \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[${{ secrets.SUBNET_IDS }}],securityGroups=[${{ secrets.SECURITY_GROUP_ID }}],assignPublicIp=ENABLED}"
echo "β
Database migrations completed"
- name: Health check
run: |
echo "π₯ Running health check..."
for i in {1..30}; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" ${{ steps.deploy.outputs.url }}/health)
if [ $HTTP_CODE -eq 200 ]; then
echo "β
Health check passed!"
exit 0
fi
echo "Attempt $i/30: HTTP $HTTP_CODE - Waiting 10s..."
sleep 10
done
echo "β Health check failed after 5 minutes"
exit 1
- name: Smoke tests
run: |
echo "π§ͺ Running smoke tests..."
# Test critical endpoints
curl -f ${{ steps.deploy.outputs.url }}/api/v1/auth/health || exit 1
curl -f ${{ steps.deploy.outputs.url }}/api/v1/auth/version || exit 1
echo "β
Smoke tests passed!"
notify:
name: Notify Deployment Status
runs-on: ubuntu-latest
needs: [prepare, deploy]
if: always()
steps:
- name: Notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ needs.deploy.result }}
text: |
Deployment to ${{ needs.prepare.outputs.environment }}
Version: ${{ needs.prepare.outputs.version }}
Status: ${{ needs.deploy.result }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
- name: Create deployment record
if: needs.deploy.result == 'success'
uses: actions/github-script@v6
with:
script: |
await github.rest.repos.createDeployment({
owner: context.repo.owner,
repo: context.repo.repo,
ref: context.sha,
environment: '${{ needs.prepare.outputs.environment }}',
description: 'Deployed version ${{ needs.prepare.outputs.version }}',
auto_merge: false,
required_contexts: []
});Blue-Green Deployment
Canary Deployment
Rollback Strategy
Security Best Practices
Secret Scanning
OIDC Authentication (No Long-Lived Credentials)
Monitoring and Observability
Best Practices Checklist
Workflow Organization
Environment Protection Rules
Comprehensive Testing Strategy
Key Takeaways
Final Thoughts
Last updated