Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Deploy
on:
workflow_dispatch:
env:
REGISTRY: container-registry.br-se1.magalu.cloud
IMAGE_NAME: cloud-application
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Instalar dependências
run: |
pip install poetry
poetry install --no-root
- name: Executar testes
run: poetry run pytest
build-and-deploy:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login no Container Registry
run: |
docker login https://${{ env.REGISTRY }} \
-u ${{ secrets.MGC_REGISTRY_USER }} \
-p ${{ secrets.MGC_REGISTRY_PASSWORD }}
- name: Build e push da imagem
run: |
IMAGE=${{ env.REGISTRY }}/${{ secrets.MGC_REGISTRY_NAME }}/${{ env.IMAGE_NAME }}:latest
docker build -t $IMAGE .
docker push $IMAGE
- name: Configurar kubectl
run: |
echo "${{ secrets.MGC_KUBECONFIG }}" > kubeconfig.yaml
- name: Criar Secret do banco
run: |
export KUBECONFIG=kubeconfig.yaml
kubectl create secret generic db-secret \
--from-literal=url=${{ secrets.DATABASE_URL }} \
--dry-run=client -o yaml | kubectl apply -f -
- name: Deploy no Kubernetes
run: |
export KUBECONFIG=kubeconfig.yaml
export MGC_REGISTRY_NAME=${{ secrets.MGC_REGISTRY_NAME }}
envsubst < k8s/app.yaml | kubectl apply -f -
kubectl rollout status deployment/cloud-application
26 changes: 26 additions & 0 deletions docs/data-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Modelagem de Dados

## Entidades

### Pedido (orders)

| Coluna | Tipo | Descrição |
|--------------|--------------------------------|----------------------------------------------------------------------------|
| id | String (UUID) | Chave primária. Gerado automaticamente (`uuid4`) na criação do pedido |
| customer | String | Nome do cliente que fez o pedido. Obrigatório |
| status | String | Status do pedido. Padrão: `"open"` |
| created_at | DateTime (com timezone, UTC) | Data/hora de criação do pedido. Preenchido automaticamente (`now(UTC)`) |

### Item (items)

| Coluna | Tipo | Descrição |
|--------------|-----------|-----------------------------------------------------------------------------|
| id | String (UUID) | Chave primária. Gerado automaticamente (`uuid4`) na criação do item |
| order_id | String | Chave estrangeira para `orders.id`. Obrigatório |
| sku | String | Código (SKU) do produto. Obrigatório |
| description | String | Descrição do item. Obrigatório |
| quantity | Integer | Quantidade do item no pedido. Obrigatório |

## Relacionamento

Relacionamento **1:N** entre `orders` e `items`: um pedido (`Order`) pode ter vários itens (`Item`), e cada item pertence a exatamente um pedido, via a chave estrangeira `items.order_id → orders.id`.
35 changes: 35 additions & 0 deletions docs/docs/adr/001-kubernetes-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# ADR 001 — Usar K3s para deploy da aplicação

**Status:** Aceito
**Data:** 2026-08-04

## Contexto

A aplicação precisa ser implantada na Magalu Cloud de forma acessível publicamente, resiliente a falhas e com capacidade de escalar.

## Alternativas consideradas

- **K3s em VM** — Kubernetes leve; cobra só a VM; provisionamento < 2 min; sem HA nativa.
- **MKS (Kubernetes Gerenciado)** — control plane e HA gerenciados; custo maior; provisionamento 5-10 min.
- **VM com Docker Compose** — mais simples de subir; sem orquestração, self-healing nem escala declarativa.

## Decisão

Usar K3s em uma VM BV2-2-40 (Ubuntu 24.04) com Klipper ServiceLB para expor a aplicação na porta 80 do IP público da VM.

O script `k3s-mgc` automatiza todo o provisionamento. Critério: menor custo e provisionamento mais rápido, com manifests idênticos a qualquer Kubernetes.

## Consequências

**Positivas:**
- Custo menor que o MKS (cobra apenas pela VM e não pelo control plane)
- Provisionamento em menos de 2 minutos
- Manifests YAML idênticos a qualquer Kubernetes padrão (sem lock-in)
- Restart automático em caso de falha (liveness probe)
- Escalabilidade horizontal simples (basta aumentar o número de réplicas)

**Negativas:**
- Single point of failure: sem alta disponibilidade nativa (tudo em uma VM)
- Armazenamento efêmero: volumes locais desaparecem se a VM for recriada
- Sem auto-scaling de nós: capacidade fixa (2 vCPU, 2 GB)
- IP público muda se a VM for substituída
30 changes: 30 additions & 0 deletions docs/docs/adr/002-dbaas-postgresql.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# ADR 002 — Usar DBaaS PostgreSQL da Magalu Cloud

**Status:** Aceito
**Data:** 2026-08-04

## Contexto

A aplicação precisa de persistência de dados. O banco precisa sobreviver a reinicializações de containers e estar disponível para múltiplas réplicas da API simultaneamente.

## Alternativas consideradas

- **DBaaS PostgreSQL gerenciado (externo)** — backup, patch e HA pelo provedor; custo maior; menos controle fino.
- **PostgreSQL em pod com PVC** — custo baixo, tudo em um lugar; volume, backup e recuperação por nossa conta; o dado morre junto com o cluster.

## Decisão

Usar o serviço DBaaS PostgreSQL da Magalu Cloud — banco gerenciado, sem necessidade de operar o servidor de banco de dados. Critério: disponibilidade e custo de operação (estado é caro de operar manualmente).

## Consequências

**Positivas:**
- Backup automático gerenciado pelo provedor
- Sem custo operacional de administração do banco
- Conexões simultâneas de múltiplos pods sem conflito
- Alta disponibilidade incluída no serviço

**Negativas:**
- Custo por hora de uso, mesmo com pouco tráfego
- Menor controle sobre configurações avançadas do PostgreSQL
- Dependência do provedor para upgrades de versão
97 changes: 97 additions & 0 deletions docs/docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Arquitetura da Solução

## Diagrama de arquitetura (C4 — Nível de Container)

```mermaid
graph LR
User["Usuário / Cliente HTTP"]

subgraph GH["GitHub"]
GA["GitHub Actions<br/>(CI/CD)"]
end

subgraph MGC["Magalu Cloud"]
CR[("Container Registry")]

subgraph VM["VM BV2-2-40 · K3s"]
LB["Klipper ServiceLB<br/>IP público · porta 80"]
API1["Pod API<br/>réplica 1 · :8000"]
API2["Pod API<br/>réplica 2 · :8000"]
end

DB[("DBaaS PostgreSQL<br/>IP privado · :5432")]
end

User -->|"HTTP :80"| LB
LB -->|"HTTP :8000"| API1
LB -->|"HTTP :8000"| API2
API1 -->|"SQL/TCP :5432 (rede privada)"| DB
API2 -->|"SQL/TCP :5432 (rede privada)"| DB
GA -->|"docker push (HTTPS)"| CR
GA -->|"kubectl apply (via kubeconfig)"| VM
VM -->|"docker pull (HTTPS)"| CR
```

## Componentes da arquitetura

| Componente | Serviço MGC | Função |
|---|---|---|
| API | K3s (VM single node) — 2 réplicas | Processa as requisições HTTP |
| Banco de dados | DBaaS PostgreSQL | Persiste pedidos e itens |
| Imagens | Container Registry | Armazena versões da aplicação |
| Tráfego externo | Klipper ServiceLB (IP da VM, porta 80) | Distribui entre as réplicas e fornece acesso externo |
| CI/CD | GitHub Actions | Automatiza testes, build e deploy |

## Requisitos não-funcionais

| Requisito | Como medir | Alvo |
|---|---|---|
| Disponibilidade | Erros 5xx e uptime das probes no Grafana | 99,5% mensal |
| Latência | `histogram_quantile(0.95, ...)` do `/metrics` | P95 < 500 ms |
| Escalabilidade | Teste de carga (k6) + `rate(http_requests_total)` | 300 req/s sem degradar |
| Custo | VM + DBaaS + IP na calculadora MGC | Teto definido em ADR |

## Estilo arquitetural

A solução segue um **monolito em camadas** (apresentação → serviço → dados), implantado como container único com duas réplicas atrás de um LoadBalancer. Não há separação de bounded contexts nem comunicação assíncrona entre serviços — toda a lógica de pedidos e itens vive num único processo FastAPI.

Estilo-alvo, caso o domínio de notificações cresça: extrair um segundo serviço (ex.: um serviço de notificações desacoplado, comunicando-se via fila ou evento), evoluindo de monolito para um conjunto pequeno de serviços.

## Trade-offs

| Aspecto | Decisão tomada | Alternativa não escolhida | Motivo da escolha |
|---|---|---|---|
| Deploy | K3s em VM | MKS (Kubernetes Gerenciado) | Custo menor, provisionamento < 2 min, manifests idênticos |
| Banco | DBaaS gerenciado | PostgreSQL em container | Backup automático, sem administração |
| CI/CD | GitHub Actions | Deploy manual | Consistência e rastreabilidade |
| Réplicas | 2 pods | 1 pod | Disponibilidade mínima sem custo excessivo |
| API | FastAPI (Python) | Node.js, Go, Java | Curva de aprendizado baixa, alta produtividade |

## Pontos de melhoria

### Escalabilidade

A aplicação é stateless, então escala na horizontal — mais réplicas atrás do balanceador. Hoje são 2 réplicas fixas; o próximo passo natural é o HPA (Horizontal Pod Autoscaler), que ajusta esse número automaticamente pela utilização de CPU (ex.: mínimo 2, máximo 6, alvo de 70%). Vale registrar também que mais réplicas não resolvem um gargalo de banco — o PostgreSQL escala na vertical e costuma saturar primeiro.

### Próximos passos naturais

| Melhoria | Por quê |
|---|---|
| HTTPS / TLS | Toda API em produção deve ser acessada por HTTPS |
| Autoscaler (HPA) | Escala automaticamente conforme a carga |
| Versionamento de API | `/v1/orders` permite evoluir sem quebrar clientes |
| Rate limiting | Evita abuso e protege o banco de sobrecargas |
| Cache (Redis) | Reduz consultas repetidas ao banco |
| Migrações de schema (Alembic) | Controle de versão das mudanças no banco |
| Testes de carga | Valida o comportamento sob alto tráfego |
| Migrar para MKS | Quando precisar de HA real: basta trocar o kubeconfig — os manifests YAML são idênticos |

### Custo estimado na Magalu Cloud

| Recurso | Especificação | Observação |
|---|---|---|
| VM K3s | BV2-2-40 (2 vCPU, 2 GB) | Cobrada por hora de uso |
| DBaaS PostgreSQL | Instância pequena | Cobrado por hora de uso |
| Container Registry | Por armazenamento | Baixo para imagens < 500 MB |

Consulte os preços atualizados em: https://magalu.cloud/precos/
54 changes: 54 additions & 0 deletions k8s/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloud-application
labels:
app: cloud-application
spec:
replicas: 2
selector:
matchLabels:
app: cloud-application
template:
metadata:
labels:
app: cloud-application
spec:
containers:
- name: cloud-application
image: container-registry.br-se1.magalu.cloud/${MGC_REGISTRY_NAME}/cloud-application:latest
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
name: cloud-application
labels:
app: cloud-application
spec:
type: LoadBalancer
selector:
app: cloud-application
ports:
- name: http
port: 80
targetPort: 8000