diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..d48a01a --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,62 @@ +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 diff --git a/docs/adr/001-kubernetes-deploy.md b/docs/adr/001-kubernetes-deploy.md new file mode 100644 index 0000000..d316df9 --- /dev/null +++ b/docs/adr/001-kubernetes-deploy.md @@ -0,0 +1,53 @@ +# ADR 001 — Usar K3s para deploy da aplicação + +**Status:** Aceito + +**Data:** 2026-08-07 + +## 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 +- Menor custo +- Provisionamento rápido +- Sem alta disponibilidade nativa + +### MKS (Kubernetes Gerenciado) + +- Alta disponibilidade nativa +- Gerenciamento simplificado +- Maior custo + +### Docker Compose em VM + +- Simples de configurar +- Sem orquestração +- Sem self-healing + +## Decisão + +Utilizar K3s em uma VM BV2-2-40 para executar a aplicação. + +### Critério da decisão + +Menor custo operacional, provisionamento rápido e compatibilidade com Kubernetes padrão. + +## Consequências + +### Positivas + +- Baixo custo +- Provisionamento rápido +- Facilidade de escalabilidade horizontal +- Compatibilidade com Kubernetes + +### Negativas + +- Ponto único de falha +- Sem alta disponibilidade nativa +- Recursos limitados à VM diff --git a/docs/adr/002-dbaas-postgresql.md b/docs/adr/002-dbaas-postgresql.md new file mode 100644 index 0000000..ea8641e --- /dev/null +++ b/docs/adr/002-dbaas-postgresql.md @@ -0,0 +1,45 @@ +# ADR 002 — Usar DBaaS PostgreSQL da Magalu Cloud + +**Status:** Aceito + +**Data:** 2026-08-07 + +## Contexto + +A aplicação precisa armazenar pedidos e itens de forma persistente e acessível para múltiplas réplicas da API. + +## Alternativas consideradas + +### DBaaS PostgreSQL + +- Backup gerenciado +- Alta disponibilidade +- Menor esforço operacional + +### PostgreSQL em Container + +- Menor custo inicial +- Maior responsabilidade operacional +- Backup manual + +## Decisão + +Utilizar o DBaaS PostgreSQL da Magalu Cloud. + +### Critério da decisão + +Garantir persistência dos dados com menor custo operacional e maior confiabilidade. + +## Consequências + +### Positivas + +- Backup automático +- Menor manutenção +- Alta disponibilidade + +### Negativas + +- Custo recorrente +- Dependência do provedor +- Menor controle de configuração diff --git a/docs/adr/003-github-actions-cicd.md b/docs/adr/003-github-actions-cicd.md new file mode 100644 index 0000000..a9955c1 --- /dev/null +++ b/docs/adr/003-github-actions-cicd.md @@ -0,0 +1,48 @@ +# ADR 003 — Utilizar GitHub Actions para CI/CD + +**Status:** Aceito + +**Data:** 2026-08-07 + +## Contexto + +A aplicação necessita de um processo automatizado para build, testes e deploy, reduzindo erros manuais e aumentando a rastreabilidade das entregas. + +## Alternativas consideradas + +### GitHub Actions + +- Integração nativa com GitHub +- Fácil configuração +- Sem necessidade de servidor dedicado + +### Jenkins + +- Altamente customizável +- Requer administração própria + +### Deploy Manual + +- Simples inicialmente +- Propenso a erros humanos + +## Decisão + +Utilizar GitHub Actions como ferramenta de CI/CD. + +### Critério da decisão + +Integração nativa com o repositório, facilidade de manutenção e automação do processo de entrega. + +## Consequências + +### Positivas + +- Deploy automatizado +- Rastreabilidade das versões +- Menor risco de erro manual + +### Negativas + +- Dependência do GitHub +- Curva inicial de aprendizado para workflows diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..dbbcd0d --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,175 @@ +# Arquitetura da Solução + +## Diagrama C1 - Contexto + +```mermaid +graph TD + + User[Usuário Final] + GitHub[GitHub Actions] + App[Sistema de Pedidos] + DB[(DBaaS PostgreSQL)] + + User -->|HTTP| App + GitHub -->|CI/CD| App + App -->|CRUD de Pedidos e Itens| DB +``` + +--- + +## Diagrama C2 - Containers + +```mermaid +graph TB + + Browser[Usuário / Navegador] + + GH[GitHub Actions] + + subgraph MGC [Magalu Cloud] + + Registry[Container Registry] + + DB[(DBaaS PostgreSQL)] + + subgraph VM [VM BV2-2-40 - K3s] + + LB[Klipper ServiceLB] + + API1[API Pod 1] + + API2[API Pod 2] + + end + + end + + Browser -->|Requisição HTTP Porta 80| LB + + LB -->|HTTP Interno| API1 + LB -->|HTTP Interno| API2 + + API1 -->|TCP 5432 Leitura e Escrita| DB + API2 -->|TCP 5432 Leitura e Escrita| DB + + GH -->|Publica imagem Docker via HTTPS| Registry + + GH -->|Atualiza Deployment Kubernetes| VM + + VM -->|Pull da imagem via HTTPS| Registry +``` + +--- + +## Componentes da Arquitetura + +| Componente | Serviço MGC | Função | +|------------|-------------|--------| +| API | K3s (2 réplicas) | Processar requisições HTTP | +| Banco de dados | DBaaS PostgreSQL | Persistir 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 | + +--- + +## Fluxo de uma Requisição + +1. O usuário acessa a aplicação pelo navegador. +2. A requisição HTTP chega ao Klipper ServiceLB através do IP público da VM. +3. O balanceador encaminha a requisição para uma das duas réplicas da API. +4. A API processa a requisição. +5. Caso necessário, a API consulta ou grava dados no DBaaS PostgreSQL pela porta 5432. +6. O banco retorna os dados para a API. +7. A API devolve a resposta HTTP para o usuário. + +--- + +## 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 o estilo arquitetural de **Monólito em Camadas**, composto pelas camadas de apresentação, serviço e dados. + +A aplicação é empacotada em um único container Docker e implantada em duas réplicas no cluster Kubernetes K3s. + +O banco de dados é executado externamente através do serviço DBaaS PostgreSQL da Magalu Cloud. + +--- + +## 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 | + +--- + +## Estado Atual da Solução + +- Cluster K3s executando em VM BV2-2-40. +- Duas réplicas da API. +- Banco de dados PostgreSQL gerenciado (DBaaS). +- Deploy automatizado por GitHub Actions. +- Imagens armazenadas no Container Registry. + +--- + +## Limitações da Arquitetura + +- Cluster executando em nó único (single node). +- Ponto único de falha na VM. +- Ausência de auto scaling. +- Sem HTTPS/TLS configurado. +- Dependência do DBaaS para persistência dos dados. +- Balanceamento limitado ao Klipper ServiceLB. + +--- + +## Estado-alvo + +- Migrar para um cluster com múltiplos nós para garantir alta disponibilidade. + +- Implementar escalonamento automático (HPA). + +- Configurar terminação TLS para habilitar tráfego HTTPS. + +--- + +## Pontos de Melhoria (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/* diff --git a/docs/data-model.md b/docs/data-model.md new file mode 100644 index 0000000..1d70dbd --- /dev/null +++ b/docs/data-model.md @@ -0,0 +1,72 @@ +# 📊 Documentação de Modelagem de Dados + +Este documento detalha o modelo relacional de dados da aplicação, especificando os tipos de dados compatíveis com **PostgreSQL** e as restrições de integridade. + +--- + +## 🏗️ Diagrama Entidade-Relacionamento (ERD) + +Abaixo está a representação visual do relacionamento entre as tabelas através da tecnologia Mermaid. + +```mermaid +erDiagram + ORDERS ||--o{ ITEMS : "contém (1:N)" + + ORDERS { + VARCHAR_36 id PK "UUID gerado pela aplicação" + TIMESTAMP created_at "Data e hora do pedido" + VARCHAR_50 status "Status atual (ex: PENDING, PAID, SHIPPED)" + DECIMAL_10_2 total_amount "Valor total do pedido" + } + + ITEMS { + VARCHAR_36 id PK "UUID gerado pela aplicação" + VARCHAR_36 order_id FK "Chave Estrangeira vinculada a ORDERS" + VARCHAR_255 product_name "Nome do produto" + INTEGER quantity "Quantidade comprada" + DECIMAL_10_2 unit_price "Preço unitário do item" + } +``` + +--- + +## 🗄️ Dicionário de Dados (Entidades) + +### 📦 1. Pedido (`orders`) +Armazena as informações principais e o status do pedido de compra. + +| Coluna | Tipo (SQL/PostgreSQL) | Restrições | Descrição | +| :--- | :--- | :--- | :--- | +| `id` | `VARCHAR(36)` / `UUID` | `PRIMARY KEY` | Identificador único universal do pedido. | +| `created_at` | `TIMESTAMP` | `NOT NULL`, `DEFAULT NOW()` | Carimbo de data/hora da criação do registro. | +| `status` | `VARCHAR(50)` | `NOT NULL` | Estado do pedido (ex: `PENDING`, `COMPLETED`, `CANCELED`). | +| `total_amount` | `DECIMAL(10, 2)` | `NOT NULL`, `>= 0` | Valor total financeiro somado do pedido. | + +### 🧩 2. Item (`items`) +Armazena os produtos vinculados a cada pedido (Linhas do pedido). + +| Coluna | Tipo (SQL/PostgreSQL) | Restrições | Descrição | +| :--- | :--- | :--- | :--- | +| `id` | `VARCHAR(36)` / `UUID` | `PRIMARY KEY` | Identificador único universal do item. | +| `order_id` | `VARCHAR(36)` / `UUID` | `FOREIGN KEY` | Vinculo com `orders(id)` com regra `ON DELETE CASCADE`. | +| `product_name` | `VARCHAR(255)` | `NOT NULL` | Nome comercial do produto adquirido. | +| `quantity` | `INTEGER` | `NOT NULL`, `> 0` | Quantidade de unidades compradas deste produto. | +| `unit_price` | `DECIMAL(10, 2)` | `NOT NULL`, `>= 0` | Preço cobrado por uma única unidade do produto. | + +--- + +## 🔗 Relacionamentos e Regras de Negócio + +* **Cardinalidade:** `1:N` (Um para Muitos). Um **Pedido** (`orders`) pode conter um ou vários **Itens** (`items`), mas um item pertence obrigatoriamente a apenas um pedido. +* **Integridade Referencial:** A coluna `order_id` na tabela `items` possui uma restrição de chave estrangeira apontando para `id` na tabela `orders`. +* **Cascata (`ON DELETE CASCADE`):** Caso um pedido seja deletado do sistema, todos os itens associados a ele serão removidos do banco automaticamente para evitar dados órfãos. + +--- + +## 🚀 Engenharia e Estratégia de Persistência + +As tabelas são mapeadas via **SQLAlchemy ORM (Python)** com migrações gerenciadas pelo **Alembic**. + +1. **A aplicação lê o modelo:** As classes declarativas em `app/models.py` definem a estrutura lógica. +2. **Abstração do Banco:** O SQLAlchemy traduz as classes Python em tabelas nativas `DOCKER-POSTGRESQL`. +3. **Idempotência:** A criação e evolução do banco usam rotinas que verificam se as tabelas já existem antes de executar comandos `DDL`. diff --git a/k8s/app.yaml b/k8s/app.yaml new file mode 100644 index 0000000..de15a0f --- /dev/null +++ b/k8s/app.yaml @@ -0,0 +1,56 @@ +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 diff --git a/k8s/servicemonitor.yaml b/k8s/servicemonitor.yaml new file mode 100644 index 0000000..585d169 --- /dev/null +++ b/k8s/servicemonitor.yaml @@ -0,0 +1,18 @@ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: cloud-application + namespace: default + labels: + release: monitoring # obrigatório: identifica este ServiceMonitor para o kube-prometheus-stack +spec: + namespaceSelector: + matchNames: + - default + selector: + matchLabels: + app: cloud-application + endpoints: + - port: http + path: /metrics + interval: 30s