Guia de turismo inteligente da Bahia — IA local rodando em Kubernetes na AWS, sem depender de APIs pagas de LLM.
🔗 Produção: https://bahiatur.mateusassis.com
| Componente | Tecnologia |
|---|---|
| LLM | Ollama + llama3.2:3b (local, sem custo de API) |
| Backend | Python 3.11 + FastAPI + SSE Streaming |
| Autenticação | Google OAuth 2.0 (GCP) + JWT + bcrypt |
| Banco de usuários | SQLite async (SQLAlchemy + aiosqlite) |
| Frontend | HTML/CSS/JS puro com streaming token a token |
| Distâncias | OSRM (OpenStreetMap) — gratuito |
| Geolocalização | Browser API + Nominatim (OpenStreetMap) — gratuito |
| Infraestrutura | MicroK8s na AWS EC2 |
| CI/CD | GitLab CI/CD com pipeline automático |
| Deploy | Helm Charts |
| Registry | Registry privado (registry.mateusassis.com) |
| TLS | cert-manager + Let's Encrypt |
| DNS | AWS Route 53 |
Internet
│
▼
Route 53 (DNS: bahiatur.mateusassis.com)
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AWS us-east-2 │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌───────────┐ │
│ │ gitlab-server │ │ k8s-server │ │ registry │ │
│ │ t3a.large │ │ t3a.xlarge │ │ -runner │ │
│ │ │ │ │ │ t3a.large │ │
│ │ │ │ │ │ │ │
│ │ GitLab CE │ │ MicroK8s │ │ │ │
│ │ Repositórios │ │ ├─bahiatur-ai │ │ Registry │ │
│ │ CI/CD Pipelines│ │ │ ├─FastAPI │ │ privado │ │
│ │ │ │ │ └─Ollama │ │ │ │
│ │ │ │ └─cert-manager │ │ GitLab │ │
│ │ │ │ nginx ingress │ │ Runner │ │
│ └─────────────────┘ └──────────────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────────┘
bahiatur-ia/
├── infrastructure/
│ ├── main.py # FastAPI + SSE streaming + auth + OSRM
│ ├── auth.py # JWT + Google OAuth + bcrypt
│ ├── database.py # SQLite async (usuários)
│ ├── destinos.py # Base de 60+ destinos baianos verificados
│ ├── settings.py # Configurações (injetado via CI/CD)
│ └── requirements.txt
├── frontend/
│ ├── index.html # Chat UI com streaming + geolocalização
│ ├── bg-login.jpg # Elevador Lacerda
│ └── bg-chat.jpg # Vista aérea Farol da Barra
├── deploy/
│ ├── chart/ # Helm chart da aplicação
│ │ ├── templates/
│ │ │ ├── deployment.yaml
│ │ │ ├── service.yaml
│ │ │ ├── ingress.yaml # timeout 300s para LLM
│ │ │ ├── pvc.yaml
│ │ │ └── secret.yaml
│ │ ├── Chart-main.yaml
│ │ └── Chart-hml.yaml
│ ├── ollama/ # Manifests do Ollama no K8s
│ │ ├── ollama-deployment.yaml
│ │ ├── ollama-service.yaml
│ │ ├── ollama-pvc.yaml
│ │ └── ollama-job-pull.yaml
│ └── values/
│ ├── main.yml # Produção
│ └── homolog.yaml # Homologação
├── .gitlab-ci.yml
├── .gitlab-production-ci.yml # 4 stages: build → ollama → deploy → restart
├── Dockerfile
└── docker-compose.yml # Para desenvolvimento local
| Instância | Tipo | IP | Função |
|---|---|---|---|
| gitlab-server | t3a.large | — | GitLab CE — repositório e pipelines CI/CD |
| k8s-server | t3a.xlarge | — | MicroK8s — cluster K8s, aplicação e Ollama |
| registry-runner | t3a.large | — | Registry Docker privado + GitLab Runner |
Todas as instâncias rodam Ubuntu 22.04 LTS na região us-east-2 (Ohio).
Nota: O k8s-server foi migrado de t3a.large para t3a.xlarge (4 vCPU, 16GB) para suportar LLMs maiores. Upgrade feito sem perda de dados via AWS Console (Stop → Change Instance Type → Start).
# Instalação
sudo snap install microk8s --classic --channel=1.29/stable
# Addons necessários
microk8s enable dns storage ingress cert-manager registry metrics-server
# Kubeconfig
microk8s config > ~/.kube/config# Verificar StorageClass disponível
kubectl get storageclass
# NAME PROVISIONER
# microk8s-hostpath (default) microk8s.io/hostpath- Acesse console.cloud.google.com
- Criar novo projeto ou usar existente
- APIs & Services → OAuth consent screen
- Tipo de usuário: Externo
- Preencher:
- Nome do app:
BahiaTUR IA - E-mail de suporte: seu e-mail
- Domínio:
bahiatur.mateusassis.com
- Nome do app:
- Salvar
- APIs & Services → Credenciais → Criar credencial → ID do cliente OAuth 2.0
- Tipo: Aplicativo da Web
- Nome:
BahiaTUR IA - Origens JavaScript autorizadas:
https://bahiatur.mateusassis.com - Copiar o Client ID gerado
No infrastructure/settings.py:
GOOGLE_CLIENT_ID = "SEU_CLIENT_ID.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET = "SEU_CLIENT_SECRET"No frontend/index.html:
const GOOGLE_CLIENT_ID = 'SEU_CLIENT_ID.apps.googleusercontent.com';| Variável | Tipo | Descrição |
|---|---|---|
APP_SETTINGS_64_PRD |
File | Conteúdo completo do settings.py de produção |
KUBECONFIG_PRD_K8S |
Variable (masked) | kubeconfig em base64 |
# Na EC2 do cluster
microk8s config | sed 's/127.0.0.1/IP_INTERNO_EC2/g' | base64 -w0
# Colar o resultado na variável KUBECONFIG_PRD_K8S no GitLab- Edite o
infrastructure/settings.pycom os valores de produção - Cole o conteúdo diretamente na variável CI/CD (tipo: File, Expand variable references: OFF)
O pipeline tem 4 stages automáticos ao fazer push na branch main:
build-and-push-main
→ Injeta settings.py via variável CI
→ Build da imagem com tag única (main-N)
→ Push para registry privado
deploy-ollama
→ Aplica PVC, Deployment e Service do Ollama
→ Verifica se modelo já existe (evita re-download)
deploy-main
→ helm upgrade --install com tag da imagem nova
→ Cria namespace se não existir
restart-pod
→ kubectl rollout restart
→ Aguarda pod ficar Ready
# Aplica os manifests
kubectl apply -f deploy/ollama/ollama-pvc.yaml
kubectl apply -f deploy/ollama/ollama-deployment.yaml
kubectl apply -f deploy/ollama/ollama-service.yaml
# Baixa o modelo (primeira vez — ~2GB)
kubectl exec -n bahiatur-ai deployment/ollama -- ollama pull llama3.2:3b
# Verifica modelos disponíveis
kubectl exec -n bahiatur-ai deployment/ollama -- ollama list| Modelo | Tamanho | Velocidade CPU | Qualidade PT |
|---|---|---|---|
| qwen2:0.5b | 352MB | Muito rápido | Básica |
| gemma2:2b | 1.6GB | Rápido | Boa |
| llama3.2:3b | 2.0GB | Rápido | Muito boa ✅ |
| mistral:7b | 4.4GB | Lento | Excelente |
# Clone
git clone git@gitlab.mateusassis.com:aws/bahiatur-ia.git
cd bahiatur-ia
# Sobe com Docker Compose
docker compose up -d --build
# Baixa o modelo
docker exec bahiatur-ollama ollama pull llama3.2:3b
# Acessa
http://localhost- ✅ Login com Google OAuth 2.0 (GCP)
- ✅ Cadastro com e-mail e senha (bcrypt)
- ✅ Streaming SSE — respostas token a token
- ✅ Geolocalização — botão 📍 detecta cidade do usuário via browser
- ✅ Distâncias reais via OSRM (OpenStreetMap) — sem custo
- ✅ Base de 60+ destinos baianos com informações verificadas
- ✅ Cobertura: Litoral Norte, Linha Verde, Chapada Diamantina, Costa do Cacau, Costa do Descobrimento, Costa das Baleias, Sertão, Recôncavo
- ✅ HTTPS com Let's Encrypt automático
- ✅ Pipeline CI/CD completo com 4 stages
kubectl top nodes
kubectl top pods -n bahiatur-ai
kubectl get pods -n bahiatur-ai# App
kubectl logs -n bahiatur-ai -l app.kubernetes.io/name=bahiatur-ai -f
# Ollama
kubectl logs -n bahiatur-ai -l app=ollama -fsudo microk8s ctr --namespace k8s.io images ls | grep bahiatur-ai | awk '{print $1}' | while read img; do
sudo microk8s ctr --namespace k8s.io images rm "$img" 2>/dev/null
done
df -h /# Remove taint manualmente
kubectl taint nodes ip-10-0-1-16 node.kubernetes.io/disk-pressure:NoSchedule- 2>/dev/null
# Expande disco após aumentar EBS no AWS Console
sudo growpart /dev/nvme0n1 1
sudo resize2fs /dev/nvme0n1p1
df -h /| Recurso | Tipo | Custo/mês |
|---|---|---|
| EC2 gitlab-server | t3a.large | ~$55 |
| EC2 k8s-server | t3a.xlarge | ~$108 |
| EC2 registry-runner | t3a.large | ~$55 |
| EBS volumes (~60GB cada) | gp3 | ~$15 |
| Route 53 | Hosted Zone | ~$0.50 |
| Total estimado | ~$233/mês |
Dica de economia: As EC2s podem ser paradas quando não estiver em uso — você paga apenas pelo tempo ligado. Para portfólio/demos, ligue somente quando for demonstrar o projeto.
#DevOps #Kubernetes #Python #IA #AWS #Bahia #OpenSource #Ollama #FastAPI #GitLab