Sobre nós Guias Projetos Contactos
Админка
please wait

Declaração do Problema

Precisa de conceber e implementar uma arquitetura de microservices escalável e fácil de manter, capaz de lidar com o aumento de tráfego, suportar deployments independentes e manter a consistência de dados entre serviços.

Pré-requisitos

  • Compreensão de contentorização (Docker)
  • Um cluster Kubernetes ou uma plataforma de orquestração de contentores
  • Um message broker (RabbitMQ, Apache Kafka)
  • Um API Gateway ou Ingress Controller

Visão Geral da Arquitetura

┌─────────────────────────────────────────────────────────────────┐
│ Load Balancer │
└─────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────────────────┐
│ API Gateway / Ingress │
└─────────────────────────────────────────────────────────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ Service │ │ Service │ │ Service │
│ A │ │ B │ │ C │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────────────┼────────────────────┘
│
┌──────────────▼──────────────┐
│ Message Broker │
│ (RabbitMQ / Kafka) │
└─────────────────────────────┘

Passo 1: Definir Limites de Serviço

Abordagem de Domain-Driven Design

Cada microservice deve:

  • Ser proprietário dos seus dados (padrão «database per service»)
  • Ter um limite de negócio claro
  • Ser implementável de forma independente
  • Comunicar através de APIs bem definidas

Exemplo de Estrutura de Serviço

/project
├── services/
│ ├── user-service/
│ │ ├── Dockerfile
│ │ ├── src/
│ │ └── k8s/
│ ├── order-service/
│ │ ├── Dockerfile
│ │ ├── src/
│ │ └── k8s/
│ └── notification-service/
│ ├── Dockerfile
│ ├── src/
│ └── k8s/
├── shared/
│ ├── proto/ # Definições gRPC
│ └── events/ # Esquemas de eventos
└── infrastructure/
├── docker-compose.yml
└── k8s/

Passo 2: Contentorizar Serviços com Builds Multi-Stage

Otimize as suas imagens Docker para produção:

# Fase de build
FROM node:alpine as builder
WORKDIR '/app'
COPY package.json .
RUN npm install
COPY . .
RUN npm run build
# Fase de execução: imagem mínima
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Para serviços backend com linguagens compiladas (Rust, Go):

# Etapa de build
FROM rust:1.70 as builder
WORKDIR /app
COPY . .
RUN cargo build --release
# Etapa de runtime
FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/myservice /usr/local/bin/
EXPOSE 8080
CMD ["myservice"]

Passo 3: Implementar Comunicação Orientada a Eventos

Seleção do Message Broker

  • RabbitMQ: Melhor para message queuing tradicional; fácil de utilizar
  • Apache Kafka: Melhor para event streaming de elevado throughput e cenários intensivos em dados

Definição do Esquema de Eventos

{
"eventType": "OrderCreated",
"eventId": "uuid-v4",
"timestamp": "2024-01-15T10:30:00Z",
"version": "1.0",
"payload": {
"orderId": "12345",
"userId": "user-789",
"items": [],
"total": 99.99
}
}

Padrões-Chave

  • Event Sourcing: Armazenar alterações de estado como uma sequência de eventos
  • CQRS: Separar modelos de leitura e escrita para escalabilidade
  • Saga Pattern: Gerir transações distribuídas entre serviços

Passo 4: Fazer Deploy para Kubernetes

Configuração Mínima de Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 3
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: myregistry/user-service:v1.0.0
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 80
targetPort: 8080

Configuração de Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-gateway
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: api.example.com
http:
paths:
- path: /users(/|$)(.*)
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- path: /orders(/|$)(.*)
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80

Passo 5: Implementar Service Discovery e Comunicação

Comunicação Interna entre Serviços

Os serviços dentro de Kubernetes podem comunicar usando DNS:

http://user-service.default.svc.cluster.local/api/users

Circuit Breaker Pattern

Implemente resiliência com circuit breakers para evitar falhas em cascata:

const CircuitBreaker = require('opossum');
const options = {
timeout: 3000,
errorThresholdPercentage: 50,
resetTimeout: 30000
};
const breaker = new CircuitBreaker(callExternalService, options);
breaker.fallback(() => cachedResponse);
breaker.on('open', () => console.log('Circuit opened'));
breaker.on('halfOpen', () => console.log('Circuit half-opened'));

Passo 6: Configurar Segurança da Comunicação entre Pods

Network Policies

Limite quais os pods que podem comunicar:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: user-service-policy
spec:
podSelector:
matchLabels:
app: user-service
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: api-gateway
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: user-database

Service Mesh (Opcional)

Para gestão avançada de tráfego, utilize Istio:

  • Encriptação Mutual TLS (mTLS) entre pods
  • Traffic shaping e canary deployments
  • Observability e tracing

Definir Limites de Serviço: A Parte Difícil

A parte mais difícil de microservices é decidir onde traçar os limites. Limites mal definidos criam monólitos distribuídos — toda a complexidade de microservices sem nenhum dos benefícios.

Diretrizes para Bons Limites

  1. Alinhar com capacidades de negócio: Os serviços devem mapear para funções de negócio, não para camadas técnicas. Não crie um «database service» — crie um «order service» que seja proprietário dos dados de encomendas.
  1. Minimizar transações entre serviços: Se as operações frequentemente abrangem vários serviços, os seus limites podem estar errados.
  1. Considerar a estrutura da equipa: A Lei de Conway sugere que a arquitetura do sistema espelha a estrutura organizacional. Os serviços devem ser propriedade de equipas únicas.
  1. Começar de forma mais abrangente, dividir mais tarde: Comece com serviços maiores e decomponha quando a complexidade o justificar. A decomposição prematura cria overhead desnecessário.

Estratégias de Gestão de Dados

Cada microservice deve ser proprietário dos seus dados, mas isto cria desafios:

Database per Service: Cada serviço tem a sua própria instância de base de dados. Isolamento máximo, mas aumenta o overhead operacional.

Schema per Service: Os serviços partilham um cluster de base de dados, mas têm schemas isolados. Mais fácil de operar, mas exige disciplina para evitar queries entre schemas.

Data Replication: Serviços que precisam de dados de outros serviços mantêm cópias locais, sincronizadas através de eventos. Melhora o desempenho de leitura e a resiliência, ao custo de consistência eventual.

Modos de Falha e Resiliência

Sistemas distribuídos falham de formas que monólitos não conseguem. Conceba a pensar na falha:

Circuit Breakers: Pare de chamar serviços em falha para evitar falhas em cascata. Após um limiar de falhas, o circuito abre e as chamadas falham rapidamente.

Timeouts: Todas as chamadas externas precisam de um timeout. Sem eles, serviços lentos consomem recursos indefinidamente.

Retries with Backoff: Falhas transitórias devem desencadear retries, mas com backoff exponencial para evitar «thundering herds».

Bulkheads: Isole falhas para evitar que um serviço com mau comportamento consuma todos os recursos. Os limites de recursos de Kubernetes fornecem bulkheading básico.

A Mentalidade de Microservices de um Senior

Um monólito bem desenhado frequentemente supera uma arquitetura de microservices mal desenhada. Escolha microservices quando os benefícios de deployment independente, scaling e autonomia das equipas superarem a complexidade operacional que introduzem.

Comece com limites de serviço claros alinhados com domínios de negócio. Use comunicação assíncrona através de message brokers sempre que possível. Implemente health checks abrangentes e limites de recursos. Automatize tudo através de pipelines de CI/CD.

Deixe a sua arquitetura evoluir com base em pontos de dor reais, em vez de problemas antecipados.

Checklist de Boas Práticas

  • [ ] Cada serviço tem a sua própria base de dados
  • [ ] Os serviços comunicam via eventos para baixo acoplamento
  • [ ] Todos os serviços têm endpoints de health e readiness
  • [ ] Estão definidos limites de recursos para todos os contentores
  • [ ] As network policies restringem comunicação desnecessária
  • [ ] Estão configurados logging e monitoring centralizados
  • [ ] Os secrets são geridos de forma segura (não no código)
  • [ ] Pipelines de CI/CD permitem deployments independentes
  • [ ] Está definida uma estratégia de versionamento de API
  • [ ] Está implementado distributed tracing
  • [ ] Circuit breakers e timeouts estão configurados para todas as chamadas externas
  • [ ] Os limites de serviço estão alinhados com capacidades de negócio

Artigos Relacionados na Wiki

 
 
 
Языки
Темы
Copyright © 1999 — 2026
ZK Interactive