Что такое Serverless
Serverless — модель облачных вычислений, в которой провайдер полностью управляет инфраструктурой. Разработчик пишет только бизнес-логику, не думая о серверах, масштабировании и provisioning.
Важно: "Serverless" не означает "без серверов". Серверы есть, но они полностью абстрагированы — вы не управляете ими, не настраиваете и не масштабируете вручную.
Serverless = FaaS + BaaS
| Компонент | Описание | Примеры |
|---|---|---|
| FaaS (Function as a Service) | Запуск функций по событиям | AWS Lambda, Google Cloud Functions, Azure Functions |
| BaaS (Backend as a Service) | Управляемые backend-сервисы | Firebase, AWS DynamoDB, Auth0, Supabase |
FaaS: Functions as a Service
Модель выполнения
Событие (HTTP, Queue, Schedule, S3 upload)
│
▼
┌─────────────────────┐
│ Cloud Provider │
│ │
│ 1. Получить событие │
│ 2. Создать sandbox │ ← Cold Start (если нет тёплого)
│ 3. Загрузить код │
│ 4. Выполнить │
│ 5. Вернуть ответ │
│ 6. Заморозить / ─── │ ← Warm instance (reuse)
│ удалить sandbox │ ← Kill после ~15 min простоя
└─────────────────────┘
AWS Lambda — пример функции
# handler.py — AWS Lambda function
import json
import os
import boto3
from datetime import datetime, timezone
# Initialized once per cold start, reused across invocations
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ['ORDERS_TABLE'])
def handler(event, context):
"""
Process incoming order from API Gateway.
Triggered by HTTP POST /orders
"""
try:
body = json.loads(event['body'])
order = {
'order_id': context.aws_request_id,
'user_id': body['user_id'],
'items': body['items'],
'total': calculate_total(body['items']),
'status': 'pending',
'created_at': datetime.now(timezone.utc).isoformat(),
}
table.put_item(Item=order)
return {
'statusCode': 201,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({
'order_id': order['order_id'],
'status': order['status'],
}),
}
except KeyError as e:
return {
'statusCode': 400,
'body': json.dumps({'error': f'Missing field: {str(e)}'}),
}
except Exception as e:
print(f'Error processing order: {e}') # Goes to CloudWatch
return {
'statusCode': 500,
'body': json.dumps({'error': 'Internal server error'}),
}
def calculate_total(items):
return sum(item['price'] * item['quantity'] for item in items)
Serverless Framework — определение инфраструктуры
# serverless.yml — Infrastructure as Code for Lambda
service: orders-api
provider:
name: aws
runtime: python3.12
region: eu-west-1
memorySize: 256
timeout: 10
environment:
ORDERS_TABLE: !Ref OrdersTable
functions:
createOrder:
handler: handler.handler
events:
- httpApi:
path: /orders
method: POST
reservedConcurrency: 100 # Limit max concurrent executions
processOrder:
handler: processor.handler
events:
- sqs:
arn: !GetAtt OrderQueue.Arn
batchSize: 10
timeout: 30
dailyReport:
handler: reports.handler
events:
- schedule: cron(0 8 * * ? *) # Every day at 8:00 UTC
resources:
Resources:
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: orders
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: order_id
AttributeType: S
KeySchema:
- AttributeName: order_id
KeyType: HASH
Cold Start
Cold Start — задержка при первом вызове функции, когда платформа создаёт новый sandbox, загружает код и инициализирует рантайм.
Анатомия Cold Start
Cold Start: [Init Runtime] [Load Code] [Init Dependencies] [Execute]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~
Cold start overhead (100ms - 10s) Actual work
Warm Start: [Execute]
~~~~~~~~
Only actual work (1-10ms overhead)
Факторы, влияющие на Cold Start
| Фактор | Влияние | Оптимизация |
|---|---|---|
| Язык | Java/C# > Python > Node.js > Go/Rust | Выбирать Go/Rust для критичных по latency |
| Размер пакета | Больше пакет = дольше загрузка | Tree-shaking, минимальные зависимости |
| Memory | Больше памяти = больше CPU | Увеличить memory (пропорционально CPU) |
| VPC | +200-500ms для ENI attach | Использовать VPC только при необходимости |
| Init код | DB connections, SDK init | Инициализировать вне handler |
Стратегии минимизации Cold Start
# ANTI-PATTERN: initialization inside handler
def handler(event, context):
import heavy_library # Loaded every cold start
db = connect_to_database() # Connection every cold start
return process(event, db)
# CORRECT: initialization outside handler (reused across invocations)
import heavy_library # Loaded once per cold start
db = connect_to_database() # Connection once per cold start
def handler(event, context):
return process(event, db) # Reuses warm connection
Provisioned Concurrency — AWS Lambda позволяет предварительно прогревать N экземпляров:
functions:
criticalApi:
handler: handler.handler
provisionedConcurrency: 5 # Always 5 warm instances ready
Сравнение Cold Start по языкам
| Язык | Cold Start (типичный) | Warm Invocation |
|---|---|---|
| Go | 30-80ms | <1ms |
| Rust | 30-80ms | <1ms |
| Python | 100-300ms | 1-5ms |
| Node.js | 100-300ms | 1-5ms |
| Java | 500ms-3s | 1-10ms |
| .NET | 300ms-2s | 1-5ms |
Когда использовать Serverless
Идеально подходит
| Сценарий | Почему |
|---|---|
| Event-driven обработка | S3 upload → resize image → save |
| API с переменной нагрузкой | 0 запросов ночью, пики днём |
| Cron-задачи | Ежедневные отчёты, очистка данных |
| Webhooks | Обработка callback от сторонних сервисов |
| MVP и прототипы | Быстрый запуск без управления инфраструктурой |
| Data pipelines | ETL, обработка потоков из Kinesis/SQS |
Не подходит
| Сценарий | Почему |
|---|---|
| Долгие процессы | Лимит 15 минут (Lambda), costly |
| Стабильная высокая нагрузка | Дороже, чем reserved instances |
| Stateful приложения | WebSocket, long-polling — ограниченно |
| Low-latency требования | Cold start неприемлем для <10ms SLA |
| Большие монолиты | Не масштабируются по функциям |
| GPU/ML inference | Ограниченные ресурсы |
Экономика Serverless
Сценарий: API с 1M запросов/месяц, 200ms среднее время, 256MB memory
AWS Lambda:
1M requests × $0.20/1M = $0.20
1M × 200ms × 256MB = 51,200 GB-seconds
51,200 × $0.0000166667 = $0.85
Итого: ~$1.05/месяц
EC2 t3.micro (постоянно):
$0.0104/час × 730 часов = $7.59/месяц
Break-even: ~10M запросов/месяц при постоянной нагрузке
Serverless Anti-Patterns
1. Mega-Function
# ANTI-PATTERN: one function does everything
def handler(event, context):
if event['path'] == '/users':
return handle_users(event)
elif event['path'] == '/orders':
return handle_orders(event)
elif event['path'] == '/payments':
return handle_payments(event)
# ... 50 more routes
Проблема: огромный размер пакета, долгий cold start, невозможность масштабировать отдельные эндпоинты.
Решение: одна функция = одна ответственность.
2. Lambda-to-Lambda Synchronous Calls
Client → Lambda A → Lambda B → Lambda C → Response
↓ waiting ↓ waiting ↓ working
Проблема: каждая Lambda платит за ожидание. Cascading latency, double billing.
Решение: асинхронная коммуникация через SQS/SNS/EventBridge или Step Functions.
3. Serverless Monolith
Весь монолит в одной Lambda. Получаем все минусы serverless (лимиты, cold start) без плюсов (гранулярное масштабирование).
4. Ignoring Cold Start в пути критических запросов
Использование Java Lambda для API с SLA <100ms без Provisioned Concurrency.
5. Overuse для стабильной нагрузки
24/7 нагрузка 1000 RPS
Lambda: ~$800/месяц
ECS Fargate: ~$200/месяц
EC2 Reserved: ~$50/месяц
Serverless паттерны
Event-Driven Processing
S3 Upload → Lambda (resize) → S3 (thumbnails)
→ Lambda (metadata) → DynamoDB
→ Lambda (notify) → SNS → Email
API Gateway + Lambda
API Gateway → Lambda → DynamoDB
→ Lambda → RDS Proxy → PostgreSQL
→ Lambda → S3
Fan-Out / Fan-In
SNS Topic → Lambda 1 (email)
→ Lambda 2 (push notification)
→ Lambda 3 (analytics)
→ SQS → Lambda 4 (batch processing)
Step Functions — оркестрация
# AWS Step Functions — state machine
Comment: "Order processing workflow"
StartAt: ValidateOrder
States:
ValidateOrder:
Type: Task
Resource: "arn:aws:lambda:...:validate"
Next: CheckInventory
Catch:
- ErrorEquals: ["ValidationError"]
Next: RejectOrder
CheckInventory:
Type: Task
Resource: "arn:aws:lambda:...:check-inventory"
Next: ProcessPayment
ProcessPayment:
Type: Task
Resource: "arn:aws:lambda:...:process-payment"
Retry:
- ErrorEquals: ["PaymentTimeout"]
MaxAttempts: 3
BackoffRate: 2
Next: FulfillOrder
FulfillOrder:
Type: Task
Resource: "arn:aws:lambda:...:fulfill"
End: true
RejectOrder:
Type: Task
Resource: "arn:aws:lambda:...:reject"
End: true
Итоги
| Концепция | Суть |
|---|---|
| Serverless | Полная абстракция инфраструктуры |
| FaaS | Функции по событиям (Lambda) |
| BaaS | Управляемые backend-сервисы |
| Cold Start | Задержка инициализации при первом вызове |
| Provisioned Concurrency | Предварительный прогрев Lambda |
| Step Functions | Оркестрация serverless-функций |
Правило выбора: Serverless — не серебряная пуля. Если нагрузка стабильная и высокая, containers (ECS/Kubernetes) будут дешевле. Serverless идеален для event-driven, переменной нагрузки и быстрых прототипов.