09 - Docker files
Continuação do artigo:
08 - Diagramas Mermaid e visão geral de serviços + contextos
Segue o pacote completo ❤️
docker-compose.yml consolidado
version: "3.9"
services:
# ---------------------------------------------------------
# 1) Job de treino – gera model/model.onnx
# ---------------------------------------------------------
trainer:
container_name: trainer-product-category
build:
context: .
dockerfile: Dockerfile.ml
volumes:
- ./model:/app/model
command: ["python", "ml/train_product_category.py"]
# ---------------------------------------------------------
# 2) API de Embeddings interna (Go + OpenAI)
# ---------------------------------------------------------
embeddings-api:
container_name: embeddings-api
build:
context: .
dockerfile: Dockerfile.embeddings-api
environment:
# Observabilidade
- OTEL_EXPORTER_OTLP_ENDPOINT=tempo:4317
# OpenAI
- OPENAI_BASE_URL=${OPENAI_BASE_URL:-https://api.openai.com}
- OPENAI_API_KEY=${OPENAI_API_KEY}
- OPENAI_EMBEDDINGS_MODEL=${OPENAI_EMBEDDINGS_MODEL:-text-embedding-3-small}
# Auth da própria API de embeddings (Bearer token)
- EMBEDDINGS_API_TOKEN=${EMBEDDINGS_API_TOKEN:-changeme}
ports:
- "8080:8080"
depends_on:
- tempo
# ---------------------------------------------------------
# 3) Product Category API (Go + ONNX)
# ---------------------------------------------------------
product-category-api:
container_name: product-category-api
build:
context: .
dockerfile: Dockerfile.api-go
volumes:
- ./model:/app/model:ro
environment:
# ONNX
- ONNXRUNTIME_LIB=/usr/local/lib/libonnxruntime.so
- MODEL_PATH=/app/model/model.onnx
# Observabilidade
- OTEL_EXPORTER_OTLP_ENDPOINT=tempo:4317
# Client de embeddings (chamando embeddings-api)
- EMBEDDINGS_PROVIDER=http
- EMBEDDINGS_BASE_URL=http://embeddings-api:8080
- EMBEDDINGS_CUSTOM_PATH=/v1/embeddings/product
# Reuso do mesmo token para chamar a embeddings-api
- EMBEDDINGS_API_KEY=${EMBEDDINGS_API_TOKEN:-changeme}
ports:
- "8000:8000"
depends_on:
- embeddings-api
- tempo
# ---------------------------------------------------------
# 4) Prometheus (métricas)
# ---------------------------------------------------------
prometheus:
image: prom/prometheus:latest
container_name: prometheus
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
command:
- "--config.file=/etc/prometheus/prometheus.yml"
ports:
- "9090:9090"
depends_on:
- product-category-api
- embeddings-api
# ---------------------------------------------------------
# 5) Loki (logs)
# ---------------------------------------------------------
loki:
image: grafana/loki:3.0.0
container_name: loki
command: -config.file=/etc/loki/config.yml
volumes:
- ./loki/config.yml:/etc/loki/config.yml
- ./loki/data:/loki
ports:
- "3100:3100"
# ---------------------------------------------------------
# 6) Promtail (coletor de logs -> Loki)
# ---------------------------------------------------------
promtail:
image: grafana/promtail:3.0.0
container_name: promtail
command: -config.file=/etc/promtail/config.yml
volumes:
- ./promtail/config.yml:/etc/promtail/config.yml
# Ajustar paths conforme seu ambiente Docker/WSL
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers
depends_on:
- loki
# ---------------------------------------------------------
# 7) Tempo (traces OTEL)
# ---------------------------------------------------------
tempo:
image: grafana/tempo:2.6.0
container_name: tempo
command: [ "-config.file=/etc/tempo/tempo.yml" ]
volumes:
- ./tempo/tempo.yml:/etc/tempo/tempo.yml
- ./tempo/data:/tmp/tempo
ports:
- "3200:3200" # HTTP
- "4317:4317" # OTLP gRPC
# ---------------------------------------------------------
# 8) Grafana (dashboard)
# ---------------------------------------------------------
grafana:
image: grafana/grafana:11.0.0
container_name: grafana
environment:
- GF_SECURITY_ADMIN_USER=${GRAFANA_USER:-admin}
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASS:-admin}
ports:
- "3000:3000"
depends_on:
- prometheus
- loki
- tempo
volumes:
- ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources
- ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/dashboards:/var/lib/grafana/dashboards
README.md – padrão “Farsoft”
# Farsoft – ML Stack de Categorização de Produtos
Projeto de referência para:
- Treinar um modelo de **categorização de produto** (ex.: AUTOPEÇAS, INFORMÁTICA, MATERIAL_CONSTRUÇÃO)
- Exportar para **ONNX**
- Servir via **Product Category API** (Go + chi + ONNX Runtime)
- Usar uma **Embeddings API interna** (Go) que pode:
- chamar **OpenAI Embeddings**, ou
- chamar outro provider no futuro
- Integrar tudo com **Prometheus + Grafana + Loki + Tempo** (observabilidade completa)
Arquitetura pensada para encaixar no ecossistema Farsoft (contextos `erp`, `ml`, `platform`).
---
## Visão Geral do Fluxo
Fluxo principal:
```text
Treino (Python) -> model.onnx -> Product Category API (Go) -> ERP
Product Category API -> Embeddings API -> OpenAI (ou outro provider)
-> ONNX Runtime -> Categoria + probabilidades
APIs -> Prometheus/Loki/Tempo -> Grafana (dashboards)
1. Treino
-
Container
trainerexecutaml/train_product_category.py -
Gera
model/model.onnxna pasta compartilhada./model -
O modelo espera vetores de features de tamanho 16
2. Embeddings API (embeddings-api)
-
Serviço interno, escrito em Go, com organização:
embeddings-api/ internal/ erp/ product/ # DTOs de request/response ml/ embeddings/ # Service + Provider (OpenAI, etc.) platform/ http/ # chi, handlers, auth, rate limit observability/ # metrics + tracing -
Endpoint principal:
POST /v1/embeddings/productRequest:
{ "workspace_id": "uuid-do-workspace", "empresa_id": 1, "produto_id": 123, "tipo": "descricao_produto", "descricao": "Pastilha de freio dianteira Corolla 2018", "marca": "Bosch", "grupo_atual": "Freios", "metadata": { "origem": "erp_legacy", "usuario": "farnetani" } }Response:
{ "embedding": [0.0123, -0.0456, 0.0789], "dimensions": 3, "model": "farsoft-produto-v1", "workspace_id": "uuid-do-workspace", "empresa_id": 1, "produto_id": 123, "tipo": "descricao_produto", "metadata": { "origem": "erp_legacy", "usuario": "farnetani" } } -
Autenticação:
-
Header
Authorization: Bearer <EMBEDDINGS_API_TOKEN> -
Token configurado via
EMBEDDINGS_API_TOKENnodocker-compose
-
-
Provider OpenAI:
-
Configurado via:
-
OPENAI_BASE_URL(default:https://api.openai.com) -
OPENAI_API_KEY -
OPENAI_EMBEDDINGS_MODEL(ex.:text-embedding-3-small)
-
-
3. Product Category API (product-category-api)
-
Serviço em Go com estrutura:
product-category-api/ internal/ erp/ productcategory/ # domínio + app (CategoryService) ml/ onnx/ # OnnxCategoryPredictor embeddingsclient/ # HTTPEmbeddingsClient platform/ http/ # chi, handlers, rotas observability/ # metrics + tracing -
Fluxo do endpoint principal:
POST /predict-description-
Recebe JSON com descrição:
{ "descricao": "Pastilha de freio dianteira Corolla 2018" } -
Service (
CategoryService) chamaEmbeddingsPort:HTTPEmbeddingsClientfaz chamada paraembeddings-api(/v1/embeddings/product)
-
Recebe o vetor de
embedding(convertido para[]float32com tamanho 16). -
Chama
OnnxCategoryPredictor:-
Executa
model.onnxvia ONNX Runtime -
Retorna categoria + probabilidades
-
-
Resposta:
{ "category_code": "AUTOPECAS", "category_name": "AUTOPECAS", "probabilities": [0.98, 0.01, 0.01] }
-
-
Variáveis de ambiente importantes:
-
MODEL_PATH=/app/model/model.onnx -
ONNXRUNTIME_LIB=/usr/local/lib/libonnxruntime.so -
OTEL_EXPORTER_OTLP_ENDPOINT=tempo:4317 -
EMBEDDINGS_PROVIDER=http -
EMBEDDINGS_BASE_URL=http://embeddings-api:8080 -
EMBEDDINGS_CUSTOM_PATH=/v1/embeddings/product -
EMBEDDINGS_API_KEY=${EMBEDDINGS_API_TOKEN}
-
Observabilidade
Stack:
-
Prometheus – lê
/metricsdas APIs -
Loki + Promtail – coleta logs de containers
-
Tempo – recebe traces OTLP (OTel)
-
Grafana – dashboards prontos para:
-
Latência das rotas
-
Taxa de erro
-
Throughput das APIs
-
Endpoints de observabilidade
-
Product Category API:
-
GET /metrics -
GET /healthz
-
-
Embeddings API:
-
GET /metrics -
GET /healthz
-
-
Prometheus:
- UI em
http://localhost:9090
- UI em
-
Grafana:
-
http://localhost:3000 -
usuário/senha default:
admin / admin(pode sobrescrever por env)
-
Estrutura de Diretórios (resumida)
.
├── docker-compose.yml
├── Makefile
├── Taskfile.yml
├── model/ # ONNX gerado pelo trainer
├── ml/
│ └── train_product_category.py
├── product-category-api/
│ └── internal/
│ ├── erp/productcategory/
│ ├── ml/onnx/
│ ├── ml/embeddingsclient/
│ └── platform/
│ ├── http/
│ └── observability/
├── embeddings-api/
│ └── internal/
│ ├── erp/product/
│ ├── ml/embeddings/
│ └── platform/
│ ├── http/
│ └── observability/
├── prometheus/
│ └── prometheus.yml
├── loki/
│ └── config.yml
├── tempo/
│ └── tempo.yml
├── promtail/
│ └── config.yml
└── grafana/
├── provisioning/
│ ├── datasources/
│ │ └── datasources.yml
│ └── dashboards/
│ └── dashboards.yml
└── dashboards/
└── product-api.json
Fluxo de Uso
1. Build das imagens
make build
# ou
task build
2. Treinar o modelo (gera model/model.onnx)
make train
# ou
task train
3. Subir APIs + Observabilidade
make up
# ou
task up
Serviços principais:
-
Product Category API:
http://localhost:8000 -
Embeddings API:
http://localhost:8080 -
Grafana:
http://localhost:3000 -
Prometheus:
http://localhost:9090
4. Testar classificação por descrição
curl -X POST http://localhost:8000/predict-description \
-H "Content-Type: application/json" \
-d '{"descricao": "Pastilha de freio dianteira Corolla 2018"}'
5. Observar métricas e logs
-
Acessar Grafana em
http://localhost:3000 -
Datasources:
- Prometheus
- Loki
- Tempo
-
Dashboard: Product Category API
Variáveis de Ambiente Importantes
No seu .env ou ambiente:
# OpenAI
OPENAI_API_KEY=sk-...
OPENAI_BASE_URL=https://api.openai.com
OPENAI_EMBEDDINGS_MODEL=text-embedding-3-small
# Auth da embeddings-api
EMBEDDINGS_API_TOKEN=algum-token-forte
# Grafana
GRAFANA_USER=admin
GRAFANA_PASS=admin
Próximos Passos Naturais
-
Substituir os dados sintéticos de treino por embeddings reais do seu ERP (produtos de autopeças, informática etc.).
-
Integrar o
product-category-apidiretamente nos seus contextos ERP via:- Webhooks,
- Jobs de classificação em lote,
- Ou classificação on-the-fly na tela de produto.
-
Evoluir o modelo para:
- Subcategoria,
- Sugestão de correção de cadastro,
- Enriquecimento de dados (marca, grupo, NCM, etc.).
Farsoft – ML encaixado no ERP, observável, versionável e plugável. 🙂