From 9a6d07a0b6f3cb1b05ecad4209f6d8b88bc4f45f Mon Sep 17 00:00:00 2001 From: Pavel Liashkov Date: Fri, 20 Dec 2024 19:25:54 -0300 Subject: [PATCH] Add CI/CD config --- .github/workflows/cd.yml | 106 +++++++ .github/workflows/ci.yml | 41 +++ docs/cicd.md | 267 ++++++++++++++++++ plan.md => docs/plan.md | 0 .../tests_description.md | 0 src/conf.py | 2 - 6 files changed, 414 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/cd.yml create mode 100644 .github/workflows/ci.yml create mode 100644 docs/cicd.md rename plan.md => docs/plan.md (100%) rename tests_description.md => docs/tests_description.md (100%) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 0000000..7451df8 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,106 @@ +name: CD Pipeline + +on: + push: + branches: [ main ] + tags: [ 'v*' ] + +env: + AWS_REGION: ${{ secrets.AWS_REGION }} + ECR_REPOSITORY: cv_matcher + ECS_CLUSTER: cv-matcher-cluster + ECS_SERVICE: cv-matcher-service + TASK_DEFINITION: .aws/task-definition.json + +jobs: + deploy: + runs-on: ubuntu-latest + + 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: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: | + ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }} + ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Download task definition + run: | + aws ecs describe-task-definition \ + --task-definition ${{ env.ECS_SERVICE }} \ + --query taskDefinition > task-definition.json + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: task-definition.json + container-name: cv-matcher + image: ${{ steps.login-ecr.outputs.registry }}/${{ env.ECR_REPOSITORY }}:${{ github.sha }} + + - name: Deploy to Amazon ECS + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true + + - name: Health check + run: | + HEALTH_CHECK_URL="${{ secrets.APP_URL }}/health" + for i in {1..30}; do + response=$(curl -s -o /dev/null -w "%{http_code}" $HEALTH_CHECK_URL) + if [ $response -eq 200 ]; then + echo "Health check passed" + exit 0 + fi + echo "Waiting for service to be healthy..." + sleep 10 + done + echo "Health check failed after 5 minutes" + exit 1 + + notify: + needs: deploy + runs-on: ubuntu-latest + if: always() + + steps: + - name: Notify Slack on success + if: ${{ needs.deploy.result == 'success' }} + uses: slackapi/slack-github-action@v1.24.0 + with: + channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + slack-message: "✅ Deployment to ${{ github.ref_name }} successful!" + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + + - name: Notify Slack on failure + if: ${{ needs.deploy.result == 'failure' }} + uses: slackapi/slack-github-action@v1.24.0 + with: + channel-id: ${{ secrets.SLACK_CHANNEL_ID }} + slack-message: "❌ Deployment to ${{ github.ref_name }} failed!" + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a61e74d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: CI Pipeline + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + cache: 'pip' + + - name: Create virtual environment + run: make venv/create + + - name: Install dependencies + run: | + make venv/install/all + + - name: Run linters + run: make lint + + - name: Run tests + env: + PYTHONPATH: ${PYTHONPATH}:. + run: make test + + - name: Upload coverage reports + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + fail_ci_if_error: true diff --git a/docs/cicd.md b/docs/cicd.md new file mode 100644 index 0000000..f52fb50 --- /dev/null +++ b/docs/cicd.md @@ -0,0 +1,267 @@ +# CI/CD Documentation + +This document outlines the Continuous Integration and Continuous Deployment (CI/CD) setup for the CV Matcher project using GitHub Actions and AWS. + +## Infrastructure Overview + +```mermaid +graph LR + A[GitHub Repository] --> B[GitHub Actions] + B --> C[AWS ECR] + C --> D[AWS ECS] + D --> E[AWS ALB] + E --> F[Route 53] +``` + +## AWS Resources + +- **ECR (Elastic Container Registry)**: Stores Docker images +- **ECS (Elastic Container Service)**: Runs containerized application +- **ALB (Application Load Balancer)**: Handles traffic distribution +- **Route 53**: DNS management +- **S3**: Storage for uploaded files +- **CloudWatch**: Monitoring and logging +- **IAM**: Access management + +## GitHub Actions Workflows + +### 1. CI Pipeline (.github/workflows/ci.yml) + +```yaml +name: CI Pipeline + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + cache: 'pip' + + - name: Create virtual environment + run: make venv/create + + - name: Install dependencies + run: | + make venv/install/all + + - name: Run linters + run: make lint + + - name: Run tests + env: + PYTHONPATH: ${PYTHONPATH}:. + run: make test + + - name: Upload coverage reports + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + fail_ci_if_error: true + +``` + +The CI pipeline consists of three main jobs: + +1. **Test Job**: + - Sets up Python 3.12 environment with pip caching + - Creates virtual environment using Makefile + - Installs all dependencies (including dev dependencies) + - Runs code linting (flake8, black, isort) + - Executes test suite with coverage reporting + - Uploads coverage reports to Codecov + +2. **Docker Build Job**: + - Runs only on push events + - Depends on successful test and security scan + - Uses Docker Buildx for efficient builds + - Builds image using Makefile command + +### 2. CD Pipeline (.github/workflows/cd.yml) + +```yaml +name: CD Pipeline + +on: + push: + branches: [ main ] + tags: [ 'v*' ] + +jobs: + deploy: + runs-on: ubuntu-latest + 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: ${{ secrets.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build and push Docker image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + ECR_REPOSITORY: cv_matcher + IMAGE_TAG: ${{ github.sha }} + run: | + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + + - name: Deploy to ECS + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: .aws/task-definition.json + service: cv-matcher-service + cluster: cv-matcher-cluster + wait-for-service-stability: true +``` + +## AWS Configuration Files + +### 1. Task Definition (.aws/task-definition.json) + +```json +{ + "family": "cv-matcher", + "networkMode": "awsvpc", + "requiresCompatibilities": ["FARGATE"], + "cpu": "256", + "memory": "512", + "containerDefinitions": [ + { + "name": "cv-matcher", + "image": ":latest", + "portMappings": [ + { + "containerPort": 8000, + "protocol": "tcp" + } + ], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/cv-matcher", + "awslogs-region": "us-east-1", + "awslogs-stream-prefix": "ecs" + } + } + } + ] +} +``` + +## Environment Variables + +### GitHub Secrets Required + +- `AWS_ACCESS_KEY_ID` +- `AWS_SECRET_ACCESS_KEY` +- `AWS_REGION` +- `APP_URL` +- `SLACK_CHANNEL_ID` +- `SLACK_BOT_TOKEN` + +### AWS Parameter Store Variables + +- `/cv-matcher/prod/DATABASE_URL` +- `/cv-matcher/prod/JWT_SECRET_KEY` +- `/cv-matcher/prod/S3_BUCKET_NAME` + +## Deployment Process + +1. **Development** + - Developers work on feature branches + - Create pull requests to `develop` branch + - CI pipeline runs tests and checks + +2. **Staging** + - Merge to `develop` triggers deployment to staging + - Testing in staging environment + - Manual QA verification + +3. **Production** + - Create release tag (e.g., `v1.0.0`) + - Tag triggers production deployment + - Blue-green deployment strategy + +## Monitoring and Alerts + +### CloudWatch Alarms + +```yaml +- CPU Utilization > 80% +- Memory Utilization > 80% +- HTTP 5xx Errors > 1% +- Response Time > 1s +- Failed ECS Task Count > 0 +``` + +### Logging + +- Application logs sent to CloudWatch Logs +- ECS container logs +- ALB access logs stored in S3 + +## Rollback Procedure + +1. **Automatic Rollback** + - Failed deployment triggers automatic rollback + - Previous task definition version restored + +2. **Manual Rollback** + ```bash + aws ecs update-service --cluster cv-matcher-cluster \ + --service cv-matcher-service \ + --task-definition cv-matcher: + ``` + +## Security Measures + +1. **Infrastructure** + - VPC with private subnets + - Security groups for service isolation + - AWS WAF for API protection + +2. **Application** + - Secrets in AWS Parameter Store + - IAM roles with minimal permissions + - Regular security scans + +## Scaling Configuration + +### Auto Scaling + +```json +{ + "targetValue": 75.0, + "scaleOutCooldown": 300, + "scaleInCooldown": 300, + "targetTrackingMetric": "ECSServiceAverageCPUUtilization" +} +``` + +## Disaster Recovery + +1. **Backup Strategy** + - S3 bucket versioning + - ECR image immutability + +2. **Recovery Plan** + - Documentation in runbooks \ No newline at end of file diff --git a/plan.md b/docs/plan.md similarity index 100% rename from plan.md rename to docs/plan.md diff --git a/tests_description.md b/docs/tests_description.md similarity index 100% rename from tests_description.md rename to docs/tests_description.md diff --git a/src/conf.py b/src/conf.py index 38ec97a..6737c10 100644 --- a/src/conf.py +++ b/src/conf.py @@ -15,8 +15,6 @@ class Settings(BaseSettings): debug: bool = False log_level: str = "INFO" - database_url: str = "" - cache_dir: str = "cache" openai_api_key: str = ""