HardПрактика4 min

Модификации бэкенда

Расширения бэкенда CRC для 6 ролей: валидация API, мониторинг, безопасность IAM, выбор архитектуры, аналитика и AI

Обзор модов Чанка 3

Бэкенд-моды расширяют базовый счётчик посещений, превращая его в демонстрацию специализированных навыков.

Developer Mod: Schemas

Задача

Добавить валидацию API с помощью JSON Schema или OpenAPI. Документировать API.

Реализация: OpenAPI спецификация

# openapi.yaml
openapi: 3.0.3
info:
  title: Cloud Resume API
  description: Visitor counter API for Cloud Resume Challenge
  version: 1.0.0
servers:
  - url: https://api.ivanpetrov.com
paths:
  /count:
    get:
      summary: Get and increment visitor count
      operationId: getCount
      responses:
        '200':
          description: Current visitor count
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
                    minimum: 0
                    example: 42
                required:
                  - count
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Error:
      type: object
      properties:
        error:
          type: string
          example: "Internal server error"

Валидация в Lambda

import json
from jsonschema import validate, ValidationError

RESPONSE_SCHEMA = {
    "type": "object",
    "required": ["count"],
    "properties": {
        "count": {"type": "integer", "minimum": 0}
    }
}

def lambda_handler(event, context):
    count = get_and_increment()
    body = {"count": count}

    # Validate response before sending
    try:
        validate(instance=body, schema=RESPONSE_SCHEMA)
    except ValidationError as e:
        return {
            'statusCode': 500,
            'body': json.dumps({'error': f'Response validation failed: {e.message}'})
        }

    return {
        'statusCode': 200,
        'headers': {'Content-Type': 'application/json'},
        'body': json.dumps(body)
    }

DevOps Mod: Monitor Lizard

Задача

Настроить мониторинг и алертинг для serverless-функций. Создать дашборд с ключевыми метриками.

Реализация: CloudWatch Dashboard

# Создание CloudWatch алармов
aws cloudwatch put-metric-alarm \
  --alarm-name "crc-lambda-errors" \
  --metric-name Errors \
  --namespace AWS/Lambda \
  --statistic Sum \
  --period 300 \
  --threshold 5 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1 \
  --dimensions Name=FunctionName,Value=visitor-counter \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:crc-alerts

aws cloudwatch put-metric-alarm \
  --alarm-name "crc-lambda-duration" \
  --metric-name Duration \
  --namespace AWS/Lambda \
  --statistic Average \
  --period 300 \
  --threshold 3000 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 2 \
  --dimensions Name=FunctionName,Value=visitor-counter

Custom Metrics

import boto3

cloudwatch = boto3.client('cloudwatch')

def lambda_handler(event, context):
    count = get_and_increment()

    # Custom metric: visitor count
    cloudwatch.put_metric_data(
        Namespace='CRC/VisitorCounter',
        MetricData=[{
            'MetricName': 'VisitorCount',
            'Value': count,
            'Unit': 'Count'
        }]
    )

    return {'statusCode': 200, 'body': json.dumps({'count': count})}

Security Mod: Check Privilege

Задача

Реализовать минимальные IAM-привилегии для каждого компонента. Документировать модель угроз.

Реализация: Granular IAM Policy

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DynamoDBSpecificTable",
      "Effect": "Allow",
      "Action": [
        "dynamodb:UpdateItem",
        "dynamodb:GetItem"
      ],
      "Resource": "arn:aws:dynamodb:us-east-1:*:table/visitor-counter",
      "Condition": {
        "ForAllValues:StringEquals": {
          "dynamodb:LeadingKeys": ["visitors"]
        }
      }
    },
    {
      "Sid": "CloudWatchLogs",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:us-east-1:*:log-group:/aws/lambda/visitor-counter:*"
    }
  ]
}

Модель угроз для CRC

Угроза                  | Вероятность | Влияние | Митигация
────────────────────────┼─────────────┼─────────┼──────────────────
DDoS на API             | Средняя     | Среднее | Rate limiting, WAF
SQL Injection           | Низкая      | Высокое | NoSQL, валидация
Утечка ключей           | Средняя     | Высокое | IAM Role, нет ключей в коде
Неавторизованный доступ | Низкая      | Среднее | IAM least privilege
Инфляция счётчика       | Низкая      | Низкое  | Rate limiting

Architect Mod: Choose Your Layer

Задача

Сравнить serverless vs контейнеры vs VM для бэкенда CRC. Анализ стоимости при разных нагрузках.

Анализ стоимости

Нагрузка: 100,000 запросов/месяц, 200 мс среднее время

Lambda (128 MB):
  Compute: 100K * 0.2s * 128MB/1024 = 2.5 GB-sec = $0.00 (Free Tier)
  Requests: 100K = $0.00 (Free Tier)
  Итого: $0.00

Fargate (0.25 vCPU, 0.5 GB):
  Compute: $0.25 * 720h = $9.12/мес
  Итого: $9.12/мес

EC2 (t3.micro):
  Instance: $0.0104/час * 720h = $7.49/мес
  Итого: $7.49/мес

При 100M запросов/мес:
  Lambda: ~$20/мес
  Fargate: ~$9/мес (тот же контейнер)
  EC2: ~$7.50/мес (тот же инстанс)

Вывод: Lambda экономичнее при низких нагрузках, контейнеры/VM -- при высоких и постоянных.

Data Engineer Mod: Count Things

Задача

Реализовать несколько типов счётчиков и агрегаций: по странице, по дню, по источнику трафика.

Реализация: расширенная схема DynamoDB

from datetime import datetime, timezone
from decimal import Decimal

def lambda_handler(event, context):
    now = datetime.now(timezone.utc)
    today = now.strftime('%Y-%m-%d')
    page = event.get('queryStringParameters', {}).get('page', '/')
    referrer = event.get('headers', {}).get('referer', 'direct')
    source = extract_domain(referrer) if referrer != 'direct' else 'direct'

    # 1. Total counter
    total = increment_counter('total-visitors')

    # 2. Daily counter
    increment_counter(f'daily-{today}')

    # 3. Page counter
    increment_counter(f'page-{page}')

    # 4. Source counter
    increment_counter(f'source-{source}')

    return {
        'statusCode': 200,
        'body': json.dumps({
            'count': total,
            'date': today
        })
    }

def increment_counter(counter_id):
    response = table.update_item(
        Key={'id': counter_id},
        UpdateExpression='ADD #c :val',
        ExpressionAttributeNames={'#c': 'count'},
        ExpressionAttributeValues={':val': Decimal('1')},
        ReturnValues='UPDATED_NEW'
    )
    return int(response['Attributes']['count'])

AI Engineer Mod: Make it Sentient

Задача

Создать chatbot, который отвечает на вопросы о вашем резюме и опыте, используя RAG (Retrieval-Augmented Generation).

Реализация: RAG с Bedrock

import boto3
import json

bedrock = boto3.client('bedrock-runtime')

RESUME_CONTEXT = """
Иван Петров, Cloud Engineer. 2 года опыта в AWS.
Сертификации: AWS Cloud Practitioner, AWS SAA.
Навыки: Python, Terraform, Lambda, DynamoDB, CloudFormation.
Проекты: Cloud Resume Challenge с serverless бэкендом.
"""

def lambda_handler(event, context):
    body = json.loads(event.get('body', '{}'))
    question = body.get('question', '')

    if not question:
        return {'statusCode': 400, 'body': json.dumps({'error': 'No question'})}

    prompt = f"""Based on this resume, answer the question.
Resume: {RESUME_CONTEXT}
Question: {question}
Answer concisely in 2-3 sentences."""

    response = bedrock.invoke_model(
        modelId='anthropic.claude-3-haiku-20240307-v1:0',
        body=json.dumps({
            'anthropic_version': 'bedrock-2023-05-31',
            'max_tokens': 256,
            'messages': [{'role': 'user', 'content': prompt}]
        })
    )

    result = json.loads(response['body'].read())
    answer = result['content'][0]['text']

    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': '*'
        },
        'body': json.dumps({'answer': answer})
    }

Выбор мода: что делать

Если вы целитесь в... Мод бэкенда Сложность
Cloud Developer Schemas (OpenAPI) Средняя
DevOps/SRE Monitor Lizard Средняя
Security Engineer Check Privilege Высокая
Solutions Architect Choose Your Layer Средняя
Data Engineer Count Things Средняя
AI Engineer Make it Sentient Высокая

Проверь себя

Зачем в Security Mod используется Condition 'dynamodb:LeadingKeys' в IAM-политике?

При какой нагрузке Lambda становится дороже, чем контейнер (Fargate)?

Какой мод бэкенда рекомендуется для целевой роли DevOps/SRE?

Что такое RAG (Retrieval-Augmented Generation) в AI Mod 'Make it Sentient'?

Что демонстрирует Developer Mod 'Schemas' в контексте бэкенда CRC?