# Cloud-Native архитектура ## Навигация - [🏠 Главная страница курса](../../../README.md) - [📚 Модуль 8](../README.md) --- ## Принципы Cloud-Native ### 1. Microservices - Разбиение монолитных приложений на независимые сервисы - Каждый сервис отвечает за одну бизнес-функцию - Независимое развертывание и масштабирование ```yaml # Kubernetes Deployment для микросервиса apiVersion: apps/v1 kind: Deployment metadata: name: user-service labels: app: user-service spec: replicas: 3 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: user-service:v1.0.0 ports: - containerPort: 8080 env: - name: DATABASE_URL valueFrom: secretKeyRef: name: user-service-secrets key: database-url - name: REDIS_URL valueFrom: configMapKeyRef: name: user-service-config key: redis-url resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 ``` ### 2. Containerization - Упаковка приложений в контейнеры - Изоляция зависимостей - Портабельность между средами ```dockerfile # Dockerfile для Node.js приложения FROM node:18-alpine # Установка рабочей директории WORKDIR /app # Копирование package.json и package-lock.json COPY package*.json ./ # Установка зависимостей RUN npm ci --only=production # Копирование исходного кода COPY . . # Создание пользователя для безопасности RUN addgroup -g 1001 -S nodejs RUN adduser -S nextjs -u 1001 # Изменение владельца файлов RUN chown -R nextjs:nodejs /app USER nextjs # Открытие порта EXPOSE 3000 # Команда запуска CMD ["npm", "start"] ``` ### 3. Orchestration - Управление контейнерами в кластере - Автоматическое масштабирование - Отказоустойчивость ```yaml # Kubernetes Service apiVersion: v1 kind: Service metadata: name: user-service labels: app: user-service spec: selector: app: user-service ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP --- # Kubernetes Ingress apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: user-service-ingress annotations: kubernetes.io/ingress.class: nginx cert-manager.io/cluster-issuer: letsencrypt-prod nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: tls: - hosts: - api.example.com secretName: user-service-tls rules: - host: api.example.com http: paths: - path: /users pathType: Prefix backend: service: name: user-service port: number: 80 ``` ### 4. API-First Design - Приоритет API в архитектуре - Версионирование API - Документирование через OpenAPI ```yaml # OpenAPI Specification openapi: 3.0.0 info: title: User Service API version: 1.0.0 description: API for managing users servers: - url: https://api.example.com/v1 description: Production server paths: /users: get: summary: Get all users operationId: getUsers parameters: - name: limit in: query schema: type: integer default: 10 - name: offset in: query schema: type: integer default: 0 responses: '200': description: List of users content: application/json: schema: type: object properties: users: type: array items: $ref: '#/components/schemas/User' total: type: integer post: summary: Create a new user operationId: createUser requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserRequest' responses: '201': description: User created content: application/json: schema: $ref: '#/components/schemas/User' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/Error' components: schemas: User: type: object properties: id: type: string format: uuid email: type: string format: email name: type: string createdAt: type: string format: date-time updatedAt: type: string format: date-time CreateUserRequest: type: object required: - email - name properties: email: type: string format: email name: type: string password: type: string minLength: 8 Error: type: object properties: code: type: string message: type: string details: type: string ``` ### 5. Event-Driven Architecture - Асинхронная обработка событий - Loose coupling между сервисами - Event sourcing и CQRS ```javascript // Event-driven микросервис на Node.js const express = require('express'); const { PubSub } = require('@google-cloud/pubsub'); const { v4: uuidv4 } = require('uuid'); const app = express(); const pubsub = new PubSub(); const topic = pubsub.topic('order-events'); app.use(express.json()); // Обработка создания заказа app.post('/orders', async (req, res) => { try { const order = { id: uuidv4(), customerId: req.body.customerId, items: req.body.items, total: req.body.total, status: 'created', createdAt: new Date().toISOString() }; // Сохранение заказа в базе данных await saveOrder(order); // Публикация события await topic.publishMessage({ data: Buffer.from(JSON.stringify({ type: 'OrderCreated', orderId: order.id, customerId: order.customerId, total: order.total, timestamp: order.createdAt })) }); res.status(201).json(order); } catch (error) { console.error('Error creating order:', error); res.status(500).json({ error: 'Internal server error' }); } }); // Обработка событий app.post('/events/order-created', async (req, res) => { try { const event = req.body; if (event.type === 'OrderCreated') { // Отправка email уведомления await sendOrderConfirmationEmail(event.customerId, event.orderId); // Обновление инвентаря await updateInventory(event.items); // Обновление статуса заказа await updateOrderStatus(event.orderId, 'confirmed'); } res.status(200).json({ status: 'processed' }); } catch (error) { console.error('Error processing event:', error); res.status(500).json({ error: 'Internal server error' }); } }); async function saveOrder(order) { // Логика сохранения заказа } async function sendOrderConfirmationEmail(customerId, orderId) { // Логика отправки email } async function updateInventory(items) { // Логика обновления инвентаря } async function updateOrderStatus(orderId, status) { // Логика обновления статуса заказа } app.listen(3000, () => { console.log('Order service listening on port 3000'); }); ``` ## Cloud-Native Patterns ### 1. Circuit Breaker - Защита от каскадных сбоев - Автоматическое восстановление - Fallback механизмы ```javascript // Circuit Breaker implementation class CircuitBreaker { constructor(service, options = {}) { this.service = service; this.failureThreshold = options.failureThreshold || 5; this.timeout = options.timeout || 60000; this.resetTimeout = options.resetTimeout || 30000; this.state = 'CLOSED'; this.failureCount = 0; this.lastFailureTime = null; } async call(...args) { if (this.state === 'OPEN') { if (Date.now() - this.lastFailureTime > this.resetTimeout) { this.state = 'HALF_OPEN'; } else { throw new Error('Circuit breaker is OPEN'); } } try { const result = await this.service(...args); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } onSuccess() { this.failureCount = 0; this.state = 'CLOSED'; } onFailure() { this.failureCount++; this.lastFailureTime = Date.now(); if (this.failureCount >= this.failureThreshold) { this.state = 'OPEN'; } } } // Использование Circuit Breaker const userService = async (userId) => { // Вызов внешнего сервиса const response = await fetch(`https://user-service/api/users/${userId}`); if (!response.ok) { throw new Error('User service error'); } return response.json(); }; const circuitBreaker = new CircuitBreaker(userService, { failureThreshold: 3, resetTimeout: 30000 }); // Использование в API endpoint app.get('/users/:id', async (req, res) => { try { const user = await circuitBreaker.call(req.params.id); res.json(user); } catch (error) { res.status(500).json({ error: 'Service unavailable' }); } }); ``` ### 2. Retry Pattern - Повторные попытки при временных сбоях - Exponential backoff - Jitter для избежания thundering herd ```javascript // Retry pattern implementation class RetryPolicy { constructor(options = {}) { this.maxAttempts = options.maxAttempts || 3; this.baseDelay = options.baseDelay || 1000; this.maxDelay = options.maxDelay || 10000; this.backoffMultiplier = options.backoffMultiplier || 2; this.jitter = options.jitter || true; } async execute(operation) { let lastError; for (let attempt = 1; attempt <= this.maxAttempts; attempt++) { try { return await operation(); } catch (error) { lastError = error; if (attempt === this.maxAttempts) { throw error; } const delay = this.calculateDelay(attempt); await this.sleep(delay); } } throw lastError; } calculateDelay(attempt) { let delay = this.baseDelay * Math.pow(this.backoffMultiplier, attempt - 1); delay = Math.min(delay, this.maxDelay); if (this.jitter) { delay = delay * (0.5 + Math.random() * 0.5); } return delay; } sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } // Использование Retry Policy const retryPolicy = new RetryPolicy({ maxAttempts: 3, baseDelay: 1000, maxDelay: 5000, backoffMultiplier: 2, jitter: true }); app.get('/external-data', async (req, res) => { try { const data = await retryPolicy.execute(async () => { const response = await fetch('https://external-api.com/data'); if (!response.ok) { throw new Error(`HTTP ${response.status}`); } return response.json(); }); res.json(data); } catch (error) { res.status(500).json({ error: 'Failed to fetch external data' }); } }); ``` ### 3. Bulkhead Pattern - Изоляция ресурсов - Предотвращение каскадных сбоев - Независимое масштабирование ```javascript // Bulkhead pattern implementation class BulkheadPool { constructor(options = {}) { this.maxConcurrency = options.maxConcurrency || 10; this.queue = []; this.active = 0; } async execute(operation) { return new Promise((resolve, reject) => { this.queue.push({ operation, resolve, reject }); this.process(); }); } async process() { if (this.active >= this.maxConcurrency || this.queue.length === 0) { return; } this.active++; const { operation, resolve, reject } = this.queue.shift(); try { const result = await operation(); resolve(result); } catch (error) { reject(error); } finally { this.active--; this.process(); } } } // Создание отдельных пулов для разных операций const databasePool = new BulkheadPool({ maxConcurrency: 5 }); const externalApiPool = new BulkheadPool({ maxConcurrency: 3 }); const fileProcessingPool = new BulkheadPool({ maxConcurrency: 2 }); // Использование в API endpoints app.post('/orders', async (req, res) => { try { // Использование database pool const order = await databasePool.execute(async () => { return await saveOrder(req.body); }); // Использование external API pool const paymentResult = await externalApiPool.execute(async () => { return await processPayment(order.id, order.total); }); res.json({ order, payment: paymentResult }); } catch (error) { res.status(500).json({ error: 'Order processing failed' }); } }); ``` ## Serverless Architecture ### 1. Function as a Service (FaaS) - Выполнение кода без управления серверами - Автоматическое масштабирование - Pay-per-execution модель ```javascript // AWS Lambda function exports.handler = async (event, context) => { const { Records } = event; for (const record of Records) { if (record.eventSource === 'aws:s3') { await processS3Event(record); } else if (record.eventSource === 'aws:sqs') { await processSQSMessage(record); } } return { statusCode: 200, body: 'Processed successfully' }; }; async function processS3Event(record) { const bucket = record.s3.bucket.name; const key = record.s3.object.key; // Обработка файла из S3 console.log(`Processing file: ${bucket}/${key}`); // Логика обработки файла // ... } async function processSQSMessage(record) { const body = JSON.parse(record.body); // Обработка сообщения из SQS console.log('Processing message:', body); // Логика обработки сообщения // ... } ``` ### 2. Event-Driven Serverless - Обработка событий через serverless функции - Интеграция с различными источниками событий - Асинхронная обработка ```yaml # Serverless Framework configuration service: my-serverless-app provider: name: aws runtime: nodejs18.x region: us-east-1 environment: DATABASE_URL: ${env:DATABASE_URL} REDIS_URL: ${env:REDIS_URL} functions: processOrder: handler: handlers/order.handler events: - http: path: /orders method: post cors: true - sqs: arn: !GetAtt OrderQueue.Arn batchSize: 10 environment: QUEUE_URL: !Ref OrderQueue processPayment: handler: handlers/payment.handler events: - sns: topic: order-created filterPolicy: status: [created] environment: PAYMENT_API_URL: ${env:PAYMENT_API_URL} sendNotification: handler: handlers/notification.handler events: - sns: topic: order-processed environment: EMAIL_SERVICE_URL: ${env:EMAIL_SERVICE_URL} resources: Resources: OrderQueue: Type: AWS::SQS::Queue Properties: QueueName: order-queue VisibilityTimeoutSeconds: 300 MessageRetentionPeriod: 1209600 OrderTopic: Type: AWS::SNS::Topic Properties: TopicName: order-created ``` ## Best Practices ### 1. Design for Failure - Предполагайте, что компоненты могут выйти из строя - Реализуйте graceful degradation - Используйте health checks и monitoring ### 2. Stateless Services - Не храните состояние в сервисах - Используйте внешние хранилища для состояния - Обеспечивайте горизонтальное масштабирование ### 3. Configuration Management - Выносите конфигурацию в переменные окружения - Используйте секреты для чувствительных данных - Применяйте принцип least privilege ### 4. Monitoring and Observability - Настройте comprehensive monitoring - Используйте structured logging - Реализуйте distributed tracing ### 5. Security - Применяйте defense in depth - Используйте service mesh для безопасности - Регулярно обновляйте зависимости