From 94fb5e68c66accceda16f83d27b2a96bb60ee143 Mon Sep 17 00:00:00 2001 From: hoshikawa2 Date: Thu, 7 May 2026 13:02:13 -0300 Subject: [PATCH] first commit --- .idea/.gitignore | 10 + .idea/deploy_langfuse_oci.iml | 9 + .idea/misc.xml | 6 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + .oca/custom_code_review_guidelines.txt | 24 + README.md | 1382 ++++++++++++++++++++++++ create_secrets.sh | 103 ++ final.yaml | 591 ++++++++++ img.png | Bin 0 -> 102148 bytes langfuse_dest.yaml | 591 ++++++++++ mirror_images.sh | 81 ++ set_var.sh | 236 ++++ update_secret.sh | 81 ++ 14 files changed, 3128 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/deploy_langfuse_oci.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .oca/custom_code_review_guidelines.txt create mode 100644 README.md create mode 100644 create_secrets.sh create mode 100644 final.yaml create mode 100644 img.png create mode 100644 langfuse_dest.yaml create mode 100644 mirror_images.sh create mode 100644 set_var.sh create mode 100644 update_secret.sh diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7bc07ec --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Environment-dependent path to Maven home directory +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/deploy_langfuse_oci.iml b/.idea/deploy_langfuse_oci.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/deploy_langfuse_oci.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..89ee753 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a7e2e9f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.oca/custom_code_review_guidelines.txt b/.oca/custom_code_review_guidelines.txt new file mode 100644 index 0000000..a0a3b63 --- /dev/null +++ b/.oca/custom_code_review_guidelines.txt @@ -0,0 +1,24 @@ +# Sample guideline, please follow similar structure for guideline with code samples +# 1. Suggest using streams instead of simple loops for better readability. +# +# *Comment: +# Category: Minor +# Issue: Use streams instead of a loop for better readability. +# Code Block: +# +# ```java +# // Calculate squares of numbers +# List squares = new ArrayList<>(); +# for (int number : numbers) { +# squares.add(number * number); +# } +# ``` +# Recommendation: +# +# ```java +# // Calculate squares of numbers +# List squares = Arrays.stream(numbers) +# .map(n -> n * n) // Map each number to its square +# .toList(); +# ``` +# diff --git a/README.md b/README.md new file mode 100644 index 0000000..f9285ea --- /dev/null +++ b/README.md @@ -0,0 +1,1382 @@ +# 🚀 Tutorial Completo — Deploy do Langfuse no Oracle Kubernetes Engine (OKE) + +## 📌 Introdução + +Este tutorial apresenta um processo completo para implantação do Langfuse no Oracle Cloud Infrastructure (OCI), utilizando: + +- Oracle Kubernetes Engine (OKE) +- OCI Container Registry (OCIR) +- OCI Vault +- OCI Object Storage +- PostgreSQL +- Redis +- ClickHouse + +Além do deployment em si, este tutorial cobre: +- arquitetura do Langfuse +- conceitos fundamentais +- purpose de cada componente +- gerenciamento de imagens Docker +- secrets centralizados no OCI Vault +- preparação de ambiente +- deployment automatizado via Kubernetes YAML + +## 🧠 O que é o Langfuse + +Langfuse é uma plataforma de observabilidade para aplicações de IA generativa. + +Ele permite: +- rastrear chamadas de LLM +- armazenar traces +- monitorar prompts +- analisar custos +- acompanhar latência +- versionar prompts +- inspecionar tool calls +- avaliar qualidade de respostas + +## 🏗️ Arquitetura Geral + +Componentes: +- PostgreSQL +- Redis +- ClickHouse +- Langfuse API +- Langfuse Worker +- OCI Vault +- OCI Object Storage +- OCIR +- OKE + +## 🔐 OCI Vault + +Os scripts create_secrets.sh e update_secret.sh criam e atualizam secrets no OCI Vault. + +Antes de iniciar esta etapa, crie um VAULT e UMA ENCRYPTION KEY. Busque pelos OCID destes 2 componentes para setar dentro de seu script create_secrets.sh e update_secret.sh + +A ENCRYPTION_KEY precisa possuir: +- 64 caracteres +- hexadecimal +- 256 bits + +Exemplo: + +```bash +ENCRYPTION_KEY=$(openssl rand -hex 32) +``` + +## 📦 OCI Registry (OCIR) + +O script mirror_images.sh copia imagens públicas para o registry privado OCI. + +Exemplo: + +```bash +docker pull langfuse/langfuse:3.1.0 + +docker tag langfuse/langfuse:3.1.0 iad.ocir.io/namespace/langfuse:3.1.0 + +docker push iad.ocir.io/namespace/langfuse:3.1.0 +``` + +O mesmo ocorre para: +- langfuse-worker +- redis +- postgres +- clickhouse + +## ☸️ Kubernetes Deployment + +O arquivo final.yaml contém: +- StorageClass +- PVCs +- Deployments +- StatefulSets +- Services +- HPA + +## PostgreSQL + +Responsável por: +- usuários +- traces +- prompts +- configurações + +Readiness probe: + +```yaml +pg_isready -U langfuse +``` + +## Redis + +Responsável por: +- filas +- cache +- jobs + +## ClickHouse + +Responsável por: +- analytics +- métricas +- observabilidade + +## Langfuse API + +Responsável por: +- UI +- API +- autenticação + +## Langfuse Worker + +Responsável por: +- filas +- processamento assíncrono +- exports + +## initContainers + +Utilizados para aguardar: +- PostgreSQL +- Redis +- ClickHouse + +antes da inicialização dos pods. + +Exemplo: + +```yaml +until nc -z langfuse-db 5432 +``` + +## 🌐 OCI Load Balancer + +Service utilizado: + +```yaml +kind: Service +type: LoadBalancer +``` + +Healthcheck: + +```yaml +oci.oraclecloud.com/healthcheck-path: "/api/public/health" +``` + +## ☁️ OCI Object Storage + +Utilizado para: +- eventos +- batches +- exports + +Auth utilizada: + +```yaml +LANGFUSE_OCI_AUTH_TYPE=instance_principal +``` + +## ⚙️ Principais Variáveis + +| Variável | Função | +|---|---| +| DATABASE_URL | PostgreSQL | +| REDIS_AUTH | Redis | +| CLICKHOUSE_PASSWORD | ClickHouse | +| NEXTAUTH_SECRET | Auth | +| ENCRYPTION_KEY | Criptografia | +| SALT | Salt | +| NEXTAUTH_URL | URL pública | + +# 📦 Fluxo Completo — Imagens, OCI Vault e Deployment Final + +## 🎯 Objetivo + +Antes do deployment do Langfuse no Oracle Kubernetes Engine (OKE), existem algumas etapas preparatórias fundamentais. + +Essas etapas possuem dois objetivos principais: + +1. Centralizar imagens Docker em um registry privado OCI (OCIR) +2. Centralizar secrets no OCI Vault para que o deployment Kubernetes possa consumir valores seguros dinamicamente + +O fluxo completo funciona assim: + +```text +Docker Hub + ↓ +Mirror para OCI Registry (OCIR) + ↓ +Criação de Secrets no OCI Vault + ↓ +Captura dos OCIDs dos Secrets + ↓ +Leitura dinâmica via OCI CLI + ↓ +envsubst + ↓ +Geração do final.yaml + ↓ +kubectl apply +``` + +>**Nota:** Antes de iniciar o processo de deployment, certifque-se de que exista um dynamic group e as policies estejam de acordo na OCI. + +Dynamic Group oke-langfuse: +ALL {instance.compartment.id = 'ocid1.compartment.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'} +- Todos os recursos deste compartment estarão habilitados para as policies abaixo + + +Algumas policies importantes: +- Allow dynamic-group oke-langfuse to manage objects in compartment kubernetes +- Allow dynamic-group oke-langfuse to read buckets in compartment kubernetes +- Allow dynamic-group oke-langfuse to manage volume-family in compartment kubernetes + +--- + +# 🟢 Etapa 1 — Publicar Imagens no OCI Registry (OCIR) + +## 📌 Objetivo + +O Kubernetes do OKE precisa baixar imagens Docker. + +Embora seja possível utilizar imagens públicas diretamente do Docker Hub, em ambientes corporativos normalmente utilizamos um registry privado OCI por motivos de: + +* segurança +* governança +* performance +* disponibilidade +* controle de versões +* evitar rate limit do Docker Hub + +--- + +# 📦 Imagens utilizadas + +O deployment utiliza: + +| Imagem | Função | +| ---------------------------- | --------- | +| langfuse/langfuse | API/UI | +| langfuse/langfuse-worker | Worker | +| postgres | Banco | +| redis | Cache | +| clickhouse/clickhouse-server | Analytics | + +--- + +# 🔄 Processo de Mirror + +O script: + +```text +mirror_images.sh +``` + +executa: + +1. Pull da imagem pública +2. Tag para o OCIR +3. Push para o registry OCI + +--- + +# Exemplo + +## Pull + +```bash +docker pull langfuse/langfuse:3.1.0 +``` + +## Tag + +```bash +docker tag langfuse/langfuse:3.1.0 iad.ocir.io/namespace/langfuse:3.1.0 +``` + +## Push + +```bash +docker push iad.ocir.io/namespace/langfuse:3.1.0 +``` + +--- + +# 📌 Resultado Final + +Após isso, o Kubernetes utilizará: + +```text +iad.ocir.io/SEU_NAMESPACE/langfuse:3.1.0 +``` + +em vez da imagem pública. + +--- + +# 🔐 Etapa 2 — Criar Secrets no OCI Vault + +## 📌 Objetivo + +O deployment possui: + +* senhas +* tokens +* chaves de criptografia + +Esses valores NÃO devem ficar hardcoded no YAML. + +Por isso usamos: + +* OCI Vault +* OCI KMS + +--- + +# 🧠 Secrets utilizados + +| Secret | Finalidade | +| ------------------- | -------------------- | +| DB_PASSWORD | PostgreSQL | +| REDIS_AUTH | Redis | +| CLICKHOUSE_PASSWORD | ClickHouse | +| NEXTAUTH_SECRET | Auth Langfuse | +| ENCRYPTION_KEY | Criptografia interna | +| SALT | Salt interno | + +--- + +# 🔧 Script utilizado + +Arquivo: + +```text +create_secrets.sh +``` + +--- + +# 📌 Como funciona + +O script: + +1. Gera senhas +2. Gera ENCRYPTION_KEY +3. Codifica em Base64 +4. Cria os secrets no OCI Vault + +--- + +# 🔐 ENCRYPTION_KEY + +A variável: + +```bash +ENCRYPTION_KEY=$(openssl rand -hex 32) +``` + +gera: + +* 32 bytes +* 64 chars hex +* 256 bits + +Formato obrigatório do Langfuse. + +--- + +# 📤 Resultado do Script + +Ao final, o OCI Vault retorna: + +```bash +export OCI_SECRET_DB=ocid1.vaultsecret... +``` + +Esses valores são os: + +* OCIDs dos secrets + +--- + +# 🧠 O que é o OCID do Secret? + +Cada secret criado no OCI Vault recebe um identificador único: + +```text +ocid1.vaultsecret.oc1.... +``` + +Esse OCID é utilizado depois para: + +* localizar +* ler +* atualizar + +o conteúdo do secret. + +--- + +# 🔥 Etapa 3 — Capturar os OCIDs + +## 📌 Objetivo + +Os scripts seguintes precisam saber: + +* qual secret acessar +* onde buscar a senha + +Por isso os OCIDs precisam ser armazenados. + +--- + +# 📌 Exemplo + +```bash +export OCI_SECRET_DB="ocid1.vaultsecret.oc1..." +``` + +--- + +# 📌 Utilização + +Esses OCIDs serão utilizados no: + +```text +set_var.sh +``` + +--- + +# ⚙️ Etapa 4 — Carregar Secrets Dinamicamente + +## 📌 Objetivo + +Antes de gerar o YAML final: + +* precisamos buscar os valores reais no OCI Vault + +--- + +# 🔧 Função utilizada + +```bash +get_secret() { + + oci secrets secret-bundle get \ + --secret-id "$secret_ocid" \ + --query 'data."secret-bundle-content".content' \ + --raw-output | base64 --decode +} +``` + +--- + +# 📌 O que ocorre aqui + +O script: + +1. Chama OCI CLI +2. Busca o secret +3. Recebe Base64 +4. Decodifica +5. Exporta para variável de ambiente + +--- + +# 📌 Exemplo + +```bash +export DB_PASSWORD=$(get_secret "$OCI_SECRET_DB") +``` + +--- + +# 📌 Resultado + +Agora temos: + +```bash +export DB_PASSWORD="senha_real" +``` + +em memória no shell. + +--- + +# 🧩 Etapa 5 — Construção do final.yaml + +## 📌 Objetivo + +O arquivo: + +```text +langfuse_dest.yaml +``` + +possui placeholders: + +```yaml +${DB_PASSWORD} +``` +--- + +# ⚙️ Variáveis de Ambiente — Langfuse no OKE + +## 📌 Introdução + +O deployment do Langfuse depende de diversas variáveis de ambiente responsáveis por configurar: + +* banco de dados +* cache +* analytics +* autenticação +* object storage +* OCI +* imagens +* Kubernetes +* URLs +* segurança + +Essas variáveis são carregadas principalmente pelo: + +```text id="vars1" +set_var.sh +``` + +e posteriormente injetadas no: + +```text id="vars2" +final.yaml +``` + +através do: + +```bash id="vars3" +envsubst +``` + +--- + +# 🧩 Variáveis de Configuração OCI + +--- + +## OCI_PROFILE + +```bash id="vars4" +export OCI_PROFILE=DEFAULT +``` + +### Objetivo + +Define qual profile do OCI CLI será utilizado. + +### Origem + +Arquivo: + +```text id="vars5" +~/.oci/config +``` + +### Exemplo + +```ini id="vars6" +[DEFAULT] +user=... +fingerprint=... +tenancy=... +region=... +``` + +--- + +## OCI_REGION + +```bash id="vars7" +export OCI_REGION=us-ashburn-1 +``` + +### Objetivo + +Define a região OCI utilizada por: + +* Vault +* Object Storage +* Registry +* APIs OCI + +### Exemplos + +| Região | Valor | +| --------- | ------------- | +| Ashburn | us-ashburn-1 | +| São Paulo | sa-saopaulo-1 | + +--- + +## OCI_NAMESPACE + +```bash id="vars8" +export OCI_NAMESPACE="idavixsf5sbx" +``` + +### Objetivo + +Namespace do OCI Object Storage e OCIR. + +Utilizado em: + +* Registry +* Bucket endpoints + +--- + +# 📦 Variáveis do OCI Registry (OCIR) + +--- + +## REGION + +```bash id="vars9" +export REGION="iad" +``` + +### Objetivo + +Região curta utilizada pelo OCIR. + +### Exemplos + +| Região OCI | Short Name | +| ------------- | ---------- | +| us-ashburn-1 | iad | +| sa-saopaulo-1 | gru | + +--- + +## TENANCY_NAMESPACE + +```bash id="vars10" +export TENANCY_NAMESPACE=${OCI_NAMESPACE} +``` + +### Objetivo + +Namespace do tenancy no OCIR. + +--- + +## REGISTRY + +```bash id="vars11" +export REGISTRY="${REGION}.ocir.io/${TENANCY_NAMESPACE}" +``` + +### Resultado + +```text id="vars12" +iad.ocir.io/idavixsf5sbx +``` + +### Objetivo + +Registry privado OCI utilizado pelo Kubernetes. + +--- + +# 🧠 Variáveis do Deployment + +--- + +## APP_NAME + +```bash id="vars13" +export APP_NAME="langfuse" +``` + +### Objetivo + +Prefixo dos recursos Kubernetes. + +### Exemplos + +Gera: + +* langfuse-db +* langfuse-redis +* langfuse-clickhouse + +--- + +## K8S_NAMESPACE + +```bash id="vars14" +export K8S_NAMESPACE="langfuse" +``` + +### Objetivo + +Namespace Kubernetes onde os recursos serão criados. + +--- + +## IMAGE_REPOSITORY + +```bash id="vars15" +export IMAGE_REPOSITORY="${REGISTRY}" +``` + +### Objetivo + +Define o registry utilizado pelas imagens. + +### Resultado + +```text id="vars16" +iad.ocir.io/idavixsf5sbx +``` + +--- + +## IMAGE_TAG + +```bash id="vars17" +export IMAGE_TAG="3.1.0" +``` + +### Objetivo + +Tag da imagem principal Langfuse. + +--- + +## CLICKHOUSE_TAG + +```bash id="vars18" +export CLICKHOUSE_TAG="23.8" +``` + +### Objetivo + +Versão do ClickHouse. + +--- + +## REDIS_TAG + +```bash id="vars19" +export REDIS_TAG="7.2" +``` + +### Objetivo + +Versão Redis. + +--- + +## POSTGRES_TAG + +```bash id="vars20" +export POSTGRES_TAG="15" +``` + +### Objetivo + +Versão PostgreSQL. + +--- + +# 🌐 Variáveis de URL + +--- + +## NEXTAUTH_URL + +```bash id="vars21" +export NEXTAUTH_URL="https://langfuse.seudominio.com" +``` + +### Objetivo + +URL pública utilizada pelo frontend do Langfuse. + +### Impacto + +Utilizada para: + +* login +* redirects +* cookies +* OAuth + +--- + +# 🔐 Variáveis de Secrets OCI Vault + +--- + +## OCI_SECRET_DB + +```bash id="vars22" +export OCI_SECRET_DB="ocid1.vaultsecret..." +``` + +### Objetivo + +OCID do secret contendo a senha PostgreSQL. + +--- + +## OCI_SECRET_REDIS + +```bash id="vars23" +export OCI_SECRET_REDIS="ocid1.vaultsecret..." +``` + +### Objetivo + +OCID do secret Redis. + +--- + +## OCI_SECRET_CLICKHOUSE + +```bash id="vars24" +export OCI_SECRET_CLICKHOUSE="ocid1.vaultsecret..." +``` + +### Objetivo + +OCID da senha ClickHouse. + +--- + +## OCI_SECRET_NEXTAUTH + +```bash id="vars25" +export OCI_SECRET_NEXTAUTH="ocid1.vaultsecret..." +``` + +### Objetivo + +Secret de autenticação do Langfuse. + +--- + +## OCI_SECRET_ENCRYPTION + +```bash id="vars26" +export OCI_SECRET_ENCRYPTION="ocid1.vaultsecret..." +``` + +### Objetivo + +Secret utilizado para criptografia interna. + +--- + +## OCI_SECRET_SALT + +```bash id="vars27" +export OCI_SECRET_SALT="ocid1.vaultsecret..." +``` + +### Objetivo + +Salt interno do Langfuse. + +--- + +# 🔥 Variáveis Carregadas Dinamicamente + +Estas variáveis são carregadas via: + +```bash id="vars28" +get_secret() +``` + +--- + +## DB_PASSWORD + +```bash id="vars29" +export DB_PASSWORD=$(get_secret "$OCI_SECRET_DB") +``` + +### Objetivo + +Senha PostgreSQL. + +--- + +## DATABASE_URL + +```bash id="vars30" +export DATABASE_URL="postgresql://langfuse:${DB_PASSWORD}@${APP_NAME}-db:5432/langfuse" +``` + +### Objetivo + +Connection string PostgreSQL. + +### Estrutura + +```text id="vars31" +postgresql://USUARIO:SENHA@HOST:PORTA/BANCO +``` + +--- + +## REDIS_AUTH + +```bash id="vars32" +export REDIS_AUTH=$(get_secret "$OCI_SECRET_REDIS") +``` + +### Objetivo + +Senha Redis. + +--- + +## CLICKHOUSE_PASSWORD + +```bash id="vars33" +export CLICKHOUSE_PASSWORD=$(get_secret "$OCI_SECRET_CLICKHOUSE") +``` + +### Objetivo + +Senha ClickHouse. + +--- + +## NEXTAUTH_SECRET + +```bash id="vars34" +export NEXTAUTH_SECRET=$(get_secret "$OCI_SECRET_NEXTAUTH") +``` + +### Objetivo + +Secret JWT/Auth do Langfuse. + +--- + +## ENCRYPTION_KEY + +```bash id="vars35" +export ENCRYPTION_KEY=$(get_secret "$OCI_SECRET_ENCRYPTION") +``` + +### Objetivo + +Criptografia interna. + +### Requisitos + +* 64 chars +* hexadecimal +* 256 bits + +### Validação + +```bash id="vars36" +openssl rand -hex 32 +``` + +--- + +## SALT + +```bash id="vars37" +export SALT=$(get_secret "$OCI_SECRET_SALT") +``` + +### Objetivo + +Salt interno utilizado pelo Langfuse. + +--- + +# ⚡ Redis + +--- + +## REDIS_HOST + +```bash id="vars38" +export REDIS_HOST="${APP_NAME}-redis" +``` + +### Objetivo + +Hostname Redis interno Kubernetes. + +--- + +## REDIS_PORT + +```bash id="vars39" +export REDIS_PORT="6379" +``` + +### Objetivo + +Porta Redis. + +--- + +# 📊 ClickHouse + +--- + +## CLICKHOUSE_URL + +```bash id="vars40" +export CLICKHOUSE_URL="http://${APP_NAME}-clickhouse:8123" +``` + +### Objetivo + +Endpoint HTTP ClickHouse. + +--- + +## CLICKHOUSE_USER + +```bash id="vars41" +export CLICKHOUSE_USER="default" +``` + +### Objetivo + +Usuário ClickHouse. + +--- + +## CLICKHOUSE_MIGRATION_URL + +```bash id="vars42" +export CLICKHOUSE_MIGRATION_URL="clickhouse://${APP_NAME}-clickhouse:9000" +``` + +### Objetivo + +Endpoint nativo ClickHouse utilizado pelas migrations. + +--- + +## CLICKHOUSE_CLUSTER_ENABLED + +```bash id="vars43" +export CLICKHOUSE_CLUSTER_ENABLED="false" +``` + +### Objetivo + +Define se ClickHouse opera em cluster distribuído. + +--- + +# ☁️ OCI Object Storage + +--- + +## LANGFUSE_S3_EVENT_UPLOAD_BUCKET + +```bash id="vars44" +export LANGFUSE_S3_EVENT_UPLOAD_BUCKET="langfuse-events" +``` + +### Objetivo + +Bucket de eventos. + +--- + +## LANGFUSE_S3_EVENT_UPLOAD_REGION + +```bash id="vars45" +export LANGFUSE_S3_EVENT_UPLOAD_REGION="${OCI_REGION}" +``` + +### Objetivo + +Região do bucket. + +--- + +## LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT + +```bash id="vars46" +export LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT="https://${OCI_NAMESPACE}.compat.objectstorage.${OCI_REGION}.oraclecloud.com" +``` + +### Objetivo + +Endpoint S3-compatible OCI. + +--- + +## LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE + +```bash id="vars47" +export LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE="true" +``` + +### Objetivo + +Ativa integração OCI nativa. + +--- + +## LANGFUSE_OCI_AUTH_TYPE + +```bash id="vars48" +export LANGFUSE_OCI_AUTH_TYPE="instance_principal" +``` + +### Objetivo + +Utiliza Instance Principal em vez de Access Key. + +### Benefício + +Não precisamos: + +* access key +* secret key + +--- + +# 📌 Resumo Geral + +## Principais categorias + +| Categoria | Variáveis | +| -------------- | ------------------------------- | +| OCI | OCI_REGION, OCI_PROFILE | +| Registry | REGISTRY, IMAGE_REPOSITORY | +| Kubernetes | APP_NAME, K8S_NAMESPACE | +| PostgreSQL | DATABASE_URL | +| Redis | REDIS_AUTH | +| ClickHouse | CLICKHOUSE_URL | +| Security | NEXTAUTH_SECRET, ENCRYPTION_KEY | +| OCI Vault | OCI_SECRET_* | +| Object Storage | LANGFUSE_S3_* | + +--- + +# 🚀 Fluxo Completo + +```text id="vars49" +OCI Vault + ↓ +get_secret() + ↓ +export VARIAVEIS + ↓ +envsubst + ↓ +final.yaml + ↓ +kubectl apply +``` + +--- + +# ✅ Resultado Final + +Ao final: + +* todas variáveis ficam centralizadas +* secrets permanecem seguros +* deployment torna-se parametrizável +* ambiente fica reproduzível +* stack torna-se adequada para CI/CD e produção + + +--- + +# 🔧 Substituição dinâmica + +O script utiliza: + +```bash +envsubst < langfuse_dest.yaml > final.yaml +``` + +--- + +# 📌 O que acontece + +O Linux substitui: + +```yaml +${DB_PASSWORD} +``` + +por: + +```yaml +langfuse123 +``` + +--- + +# 📌 Resultado + +É gerado: + +```text +final.yaml +``` + +com: + +* todas variáveis preenchidas +* senhas reais +* URLs reais +* tags reais +* namespace real + +--- + +# ☸️ Etapa 6 — Deployment Kubernetes + +## 📌 Objetivo + +Aplicar: + +* Deployments +* Services +* StatefulSets +* PVCs +* HPAs + +no OKE. + +>**Nota:** É importante frisar que em regime produtivo, as etapas de gerar imagens, setar variaveis e executar a atualização no YAML de deployment final não são etapas necessárias. Pressupõe-se que as variáveis e as imagens já estejam preparadas para a execução de uma esteira produtiva. Estas etapas anteriores apenas servem para este tutorial como ilustração de como os recursos de OCI se comportam. + +--- + +# 🔧 Comando + +```bash +kubectl apply -f final.yaml +``` + +>**Nota:** O arquivo de deployment final.yaml publica a solução em um endpoint com DNS: https://langfuse.seu. Lembre-se de alterar + +--- + +# 📌 Resultado + +O Kubernetes: + +* cria pods +* cria services +* cria volumes +* cria load balancer +* sobe Langfuse + +--- + +# 🔒 Benefícios dessa Arquitetura + +## Segurança + +Nenhuma senha: + +* hardcoded +* commitada +* salva no YAML final do template + +--- + +## Governança + +Secrets centralizados no OCI Vault. + +--- + +## Escalabilidade + +O mesmo template pode ser usado para: + +* DEV +* QA +* PROD + +mudando apenas: + +* variáveis +* secrets +* tags + +--- + +## Reutilização + +O deployment torna-se: + +* parametrizável +* automatizável +* CI/CD friendly + +--- + +# 🚀 Fluxo Final Consolidado + +```text +1. mirror_images.sh + ↓ +2. create_secrets.sh + ↓ +3. capturar OCIDs + ↓ +4. set_var.sh + ↓ +5. envsubst + ↓ +6. final.yaml + ↓ +7. kubectl apply +``` + +--- + +# Testando a Solução + +Abra o browser para validar a UI do Langfuse. O Load-Balancer foi criado para uma subnet privada, nada impede de se criar em uma subnet pública. +Para poder acessar talvez seja necessário executar o ssh em túnel como o exemplo: + + ssh -i /mnt/d/Dropbox/ORACLE/sshkeybundle/Privatekey.pem opc@129.158.211.144 -L 8087:10.0.20.44:80 + + +E depois acessar via browser com: + + http://localhost:8087 + +![img.png](img.png) + +--- + +# ✅ Resultado Final + +Ao final: + +* imagens privadas OCI +* secrets centralizados +* deployment automatizado +* stack observável +* Langfuse operacional no OKE +* arquitetura segura e escalável + diff --git a/create_secrets.sh b/create_secrets.sh new file mode 100644 index 0000000..1ca22f7 --- /dev/null +++ b/create_secrets.sh @@ -0,0 +1,103 @@ +#!/bin/bash +set -ex + +echo "🔐 Criando secrets no OCI Vault..." + +# ========================= +# ⚙️ CONFIG +# ========================= + +export COMPARTMENT_ID="ocid1.compartment.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +export VAULT_ID="ocid1.vault.oc1.iad.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +export KEY_ID="ocid1.key.oc1.iad.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +export PREFIX="langfuse" + +# ========================= +# 🔑 SENHAS +# ========================= + +# Senha comum simples +if [ -z "$COMMON_SECRET" ]; then + echo "🔑 Gerando COMMON_SECRET..." + COMMON_SECRET=$(openssl rand -base64 32 | tr -d '\n') +fi + +# ENCRYPTION_KEY precisa: +# - 64 chars +# - hexadecimal +# - 256 bits +if [ -z "$ENCRYPTION_KEY" ]; then + echo "🔐 Gerando ENCRYPTION_KEY..." + ENCRYPTION_KEY=$(openssl rand -hex 32) +fi + +echo "👉 COMMON_SECRET: $COMMON_SECRET" +echo "👉 ENCRYPTION_KEY: $ENCRYPTION_KEY" + +# ========================= +# 🔧 FUNÇÃO +# ========================= + +create_secret() { + + local name=$1 + local value=$2 + + echo "📦 Criando secret: $name" + + local base64_value + base64_value=$(printf "%s" "$value" | base64 | tr -d '\n') + + oci vault secret create-base64 \ + --compartment-id "$COMPARTMENT_ID" \ + --vault-id "$VAULT_ID" \ + --key-id "$KEY_ID" \ + --secret-name "$name" \ + --secret-content-content "$base64_value" \ + --query 'data.id' \ + --raw-output +} + +# ========================= +# 🔥 CRIAR SECRETS +# ========================= + +DB_SECRET=$(create_secret "${PREFIX}-db-password" "$COMMON_SECRET") + +REDIS_SECRET=$(create_secret "${PREFIX}-redis" "$COMMON_SECRET") + +CLICKHOUSE_SECRET=$(create_secret "${PREFIX}-clickhouse" "$COMMON_SECRET") + +NEXTAUTH_SECRET=$(create_secret "${PREFIX}-nextauth" "$COMMON_SECRET") + +# 👇 SOMENTE ESTA KEY TEM FORMATO ESPECIAL +ENCRYPTION_SECRET=$(create_secret "${PREFIX}-encryption" "$ENCRYPTION_KEY") + +SALT_SECRET=$(create_secret "${PREFIX}-salt" "$COMMON_SECRET") + +OCI_ACCESS_SECRET=$(create_secret "${PREFIX}-oci-access" "$COMMON_SECRET") + +OCI_SECRET_SECRET=$(create_secret "${PREFIX}-oci-secret" "$COMMON_SECRET") + +# ========================= +# 📤 OUTPUT +# ========================= + +echo "" +echo "✅ Secrets criados com sucesso!" +echo "" + +echo "export OCI_SECRET_DB=$DB_SECRET" +echo "export OCI_SECRET_REDIS=$REDIS_SECRET" +echo "export OCI_SECRET_CLICKHOUSE=$CLICKHOUSE_SECRET" +echo "export OCI_SECRET_NEXTAUTH=$NEXTAUTH_SECRET" +echo "export OCI_SECRET_ENCRYPTION=$ENCRYPTION_SECRET" +echo "export OCI_SECRET_SALT=$SALT_SECRET" +echo "export OCI_SECRET_OCI_ACCESS=$OCI_ACCESS_SECRET" +echo "export OCI_SECRET_OCI_SECRET=$OCI_SECRET_SECRET" + +echo "" +echo "💡 Salve isso no seu pipeline ou env.sh" \ No newline at end of file diff --git a/final.yaml b/final.yaml new file mode 100644 index 0000000..dbcb09d --- /dev/null +++ b/final.yaml @@ -0,0 +1,591 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: oci-bv-high-performance +provisioner: blockvolume.csi.oraclecloud.com +parameters: + vpusPerGB: "20" + attachment-type: "paravirtualized" +volumeBindingMode: WaitForFirstConsumer +allowVolumeExpansion: true +reclaimPolicy: Delete +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: langfuse-db-pvc + namespace: langfuse +spec: + accessModes: ["ReadWriteOnce"] + storageClassName: oci-bv-high-performance + resources: + requests: + storage: 50Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: langfuse-db + namespace: langfuse +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: langfuse-db + template: + metadata: + labels: + app: langfuse-db + spec: + imagePullSecrets: + - name: ocirsecret + containers: + - name: langfuse-db + image: iad.ocir.io/xxxxxxxxxx/postgres:15 + ports: + - containerPort: 5432 + env: + - name: POSTGRES_USER + value: "langfuse" + - name: POSTGRES_PASSWORD + value: "langfuse123" + - name: POSTGRES_DB + value: "langfuse" + - name: PGDATA + value: "/var/lib/postgresql/data/pgdata" + resources: + requests: + cpu: "50m" + memory: "2Gi" + limits: + cpu: "512m" + memory: "3Gi" + volumeMounts: + - name: db-data + mountPath: /var/lib/postgresql/data + volumes: + - name: db-data + persistentVolumeClaim: + claimName: langfuse-db-pvc +--- +apiVersion: v1 +kind: Service +metadata: + name: langfuse-db + namespace: langfuse +spec: + type: NodePort + selector: + app: langfuse-db + ports: + - protocol: TCP + port: 5432 + targetPort: 5432 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: langfuse-redis + namespace: langfuse +spec: + replicas: 1 + selector: + matchLabels: + app: langfuse-redis + template: + metadata: + labels: + app: langfuse-redis + spec: + imagePullSecrets: + - name: ocirsecret + containers: + - name: redis + image: iad.ocir.io/xxxxxxxxxxx/redis:7.2 + command: ["redis-server", "--requirepass", "redis123"] + ports: + - containerPort: 6379 + resources: + requests: + cpu: "50m" + memory: "2Gi" + limits: + cpu: "512m" + memory: "3Gi" +--- +apiVersion: v1 +kind: Service +metadata: + name: langfuse-redis + namespace: langfuse +spec: + type: NodePort + selector: + app: langfuse-redis + ports: + - protocol: TCP + port: 6379 + targetPort: 6379 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: langfuse-clickhouse + namespace: langfuse +spec: + serviceName: langfuse-clickhouse + replicas: 1 + selector: + matchLabels: + app: langfuse-clickhouse + template: + metadata: + labels: + app: langfuse-clickhouse + spec: + imagePullSecrets: + - name: ocirsecret + containers: + - name: clickhouse + image: iad.ocir.io/xxxxxxxxxxxx/clickhouse:23.8 + ports: + - containerPort: 8123 + - containerPort: 9000 + resources: + requests: + cpu: "2" + memory: "8Gi" + limits: + cpu: "4" + memory: "16Gi" + volumeMounts: + - name: data + mountPath: /var/lib/clickhouse + env: + - name: CLICKHOUSE_DB + value: "default" + + - name: CLICKHOUSE_USER + value: "default" + + - name: CLICKHOUSE_PASSWORD + value: "clickhouse123" + + - name: CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT + value: "1" + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: oci-bv-high-performance + resources: + requests: + storage: 200Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: langfuse-clickhouse + namespace: langfuse +spec: + selector: + app: langfuse-clickhouse + ports: + - name: http + port: 8123 + - name: native + port: 9000 +--- +# ===== LANGFUSE APPLICATION ===== + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: langfuse + namespace: langfuse +spec: + replicas: 1 + selector: + matchLabels: + app: langfuse + template: + metadata: + labels: + app: langfuse + spec: + imagePullSecrets: + - name: ocirsecret + + initContainers: + + - name: wait-for-postgres + image: busybox + + command: + - sh + - -c + - | + until nc -z langfuse-db 5432; do + echo "⏳ Waiting for PostgreSQL..." + sleep 2 + done + + - name: wait-for-redis + image: busybox + + command: + - sh + - -c + - | + until nc -z langfuse-redis 6379; do + echo "⏳ Waiting for Redis..." + sleep 2 + done + + - name: wait-for-clickhouse + image: busybox + + command: + - sh + - -c + - | + until nc -z langfuse-clickhouse 9000; do + echo "⏳ Waiting for ClickHouse..." + sleep 2 + done + containers: + - name: langfuse + image: iad.ocir.io/xxxxxxxxxxxx/langfuse:3.1.0 + ports: + - containerPort: 3000 + env: + - name: DATABASE_URL + value: "postgresql://langfuse:langfuse123@langfuse-db:5432/langfuse" + - name: NEXTAUTH_SECRET + value: "nextauth-secret-123" + - name: SALT + value: "salt-secret-123" + - name: ENCRYPTION_KEY + value: "ea58fe36914e38ec5d52657c928d3e8350ff1513e1930e0fd306dce6422bf87d" + - name: NEXTAUTH_URL + value: https://langfuse.seudominio.com + - name: TELEMETRY_ENABLED + value: "false" + - name: CLICKHOUSE_URL + value: "http://langfuse-clickhouse:8123" + - name: CLICKHOUSE_USER + value: "default" + - name: CLICKHOUSE_PASSWORD + value: "clickhouse123" + - name: CLICKHOUSE_CLUSTER_ENABLED + value: "false" + - name: CLICKHOUSE_MIGRATION_URL + value: "clickhouse://langfuse-clickhouse:9000" + - name: REDIS_HOST + value: "langfuse-redis" + - name: REDIS_PORT + value: "6379" + - name: REDIS_AUTH + value: "redis123" + # ===== OCI OBJECT STORAGE (EVENTS) ===== + - name: LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE + value: "true" + - name: LANGFUSE_OCI_AUTH_TYPE + value: "instance_principal" + - name: LANGFUSE_S3_EVENT_UPLOAD_BUCKET + value: "langfuse-events" + + - name: LANGFUSE_S3_EVENT_UPLOAD_REGION + value: "us-ashburn-1" + + - name: LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT + value: "https://xxxxxxxxxxx.compat.objectstorage.us-ashburn-1.oraclecloud.com" + +# - name: LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-access-key +# +# - name: LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-secret-key + + - name: LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE + value: "true" + + # ===== EXPORT ===== + - name: LANGFUSE_S3_BATCH_EXPORT_ENABLED + value: "true" + + - name: LANGFUSE_S3_BATCH_EXPORT_BUCKET + value: "langfuse-exports" + + - name: LANGFUSE_S3_BATCH_EXPORT_REGION + value: "us-ashburn-1" + + - name: LANGFUSE_S3_BATCH_EXPORT_ENDPOINT + value: "https://xxxxxxxxxxxx.compat.objectstorage.us-ashburn-1.oraclecloud.com" + +# - name: LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-access-key +# +# - name: LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-secret-key + + - name: LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE + value: "true" + resources: + requests: + cpu: "50m" + memory: "2Gi" + limits: + cpu: "512m" + memory: "3Gi" + livenessProbe: + httpGet: + path: /api/public/health + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 15 + readinessProbe: + httpGet: + path: /api/public/health + port: 3000 + initialDelaySeconds: 15 + periodSeconds: 10 + +--- +# ===== LANGFUSE WORKER ===== + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: langfuse-worker + namespace: langfuse +spec: + replicas: 1 + selector: + matchLabels: + app: langfuse-worker + template: + metadata: + labels: + app: langfuse-worker + spec: + imagePullSecrets: + - name: ocirsecret + + initContainers: + + - name: wait-for-postgres + image: busybox + + command: + - sh + - -c + - | + until nc -z langfuse-db 5432; do + echo "⏳ Waiting for PostgreSQL..." + sleep 2 + done + + - name: wait-for-redis + image: busybox + + command: + - sh + - -c + - | + until nc -z langfuse-redis 6379; do + echo "⏳ Waiting for Redis..." + sleep 2 + done + + - name: wait-for-clickhouse + image: busybox + + command: + - sh + - -c + - | + until nc -z langfuse-clickhouse 9000; do + echo "⏳ Waiting for ClickHouse..." + sleep 2 + done + containers: + - name: worker + image: iad.ocir.io/xxxxxxxxxxxx/langfuse-worker:3.1.0 + env: + - name: DATABASE_URL + value: "postgresql://langfuse:langfuse123@langfuse-db:5432/langfuse" + - name: ENCRYPTION_KEY + value: "ea58fe36914e38ec5d52657c928d3e8350ff1513e1930e0fd306dce6422bf87d" + - name: CLICKHOUSE_URL + value: "http://langfuse-clickhouse:8123" + - name: CLICKHOUSE_USER + value: "default" + - name: CLICKHOUSE_PASSWORD + value: "clickhouse123" + - name: CLICKHOUSE_CLUSTER_ENABLED + value: "false" + - name: CLICKHOUSE_MIGRATION_URL + value: "clickhouse://langfuse-clickhouse:9000" + - name: REDIS_HOST + value: "langfuse-redis" + - name: REDIS_PORT + value: "6379" + - name: REDIS_AUTH + value: "redis123" + # ===== OCI OBJECT STORAGE (EVENTS) ===== + - name: LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE + value: "true" + - name: LANGFUSE_OCI_AUTH_TYPE + value: "instance_principal" + - name: LANGFUSE_S3_EVENT_UPLOAD_BUCKET + value: "langfuse-events" + + - name: LANGFUSE_S3_EVENT_UPLOAD_REGION + value: "us-ashburn-1" + + - name: LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT + value: "https://xxxxxxxxx.compat.objectstorage.us-ashburn-1.oraclecloud.com" + +# - name: LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-access-key +# +# - name: LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-secret-key + + - name: LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE + value: "true" + + # ===== EXPORT ===== + - name: LANGFUSE_S3_BATCH_EXPORT_ENABLED + value: "true" + + - name: LANGFUSE_S3_BATCH_EXPORT_BUCKET + value: "langfuse-exports" + + - name: LANGFUSE_S3_BATCH_EXPORT_REGION + value: "us-ashburn-1" + + - name: LANGFUSE_S3_BATCH_EXPORT_ENDPOINT + value: "https://xxxxxxxxxxx.compat.objectstorage.us-ashburn-1.oraclecloud.com" + +# - name: LANGFUSE_S3_BATCH_EXPORT_ACCESS_KEY_ID +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-access-key +# +# - name: LANGFUSE_S3_BATCH_EXPORT_SECRET_ACCESS_KEY +# valueFrom: +# secretKeyRef: +# name: langfuse-secret +# key: oci-secret-key + + - name: LANGFUSE_S3_BATCH_EXPORT_FORCE_PATH_STYLE + value: "true" + resources: + requests: + cpu: "50m" + memory: "2Gi" + limits: + cpu: "512m" + memory: "3Gi" +--- +# ===== LANGFUSE SERVICE ===== + +apiVersion: v1 +kind: Service +metadata: + name: langfuse-web + namespace: langfuse + annotations: + service.beta.kubernetes.io/oci-load-balancer-internal: "true" + oci.oraclecloud.com/healthcheck-path: "/api/public/health" + oci.oraclecloud.com/healthcheck-port: "3000" +spec: + type: LoadBalancer + selector: + app: langfuse + ports: + - protocol: TCP + port: 80 + targetPort: 3000 +--- +# ===== LANGFUSE HPA - HORIZONTAL POD AUTOSCALER ===== + +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: langfuse-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: langfuse + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +--- +# ===== WORKER - AUTOSCALER ===== +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: langfuse-worker-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: langfuse-worker + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 +--- +apiVersion: v1 +kind: Service +metadata: + name: langfuse-lb + namespace: langfuse + annotations: + service.beta.kubernetes.io/oci-load-balancer-shape: "flexible" + oci.oraclecloud.com/healthcheck-path: "/api/public/health" +spec: + type: LoadBalancer + selector: + app: langfuse + ports: + - port: 80 + targetPort: 3000 \ No newline at end of file diff --git a/img.png b/img.png new file mode 100644 index 0000000000000000000000000000000000000000..6bb69d51a11fe3584aa9fdca746c16c12ce7bc5d GIT binary patch literal 102148 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 c#^J9YZEE}2S+u`rSt}0z&2 + + oci secrets secret-bundle get \ + --secret-id "$secret_ocid" \ + --query 'data."secret-bundle-content".content' \ + --raw-output | base64 --decode +} +# ========================= + +# 🔥 CARREGAR SECRETS + +# ========================= + +echo "" +echo "🔐 Etapa 6: Carregando secrets do OCI..." + +export DB_PASSWORD=$(get_secret "$OCI_SECRET_DB") +export DATABASE_URL="postgresql://langfuse:${DB_PASSWORD}@${APP_NAME}-db:5432/langfuse" +echo "✔️ DATABASE_URL carregado" + +export REDIS_AUTH=$(get_secret "$OCI_SECRET_REDIS") +echo "✔️ REDIS_AUTH carregado" + +export CLICKHOUSE_PASSWORD=$(get_secret "$OCI_SECRET_CLICKHOUSE") +echo "✔️ CLICKHOUSE_PASSWORD carregado" + +export NEXTAUTH_SECRET=$(get_secret "$OCI_SECRET_NEXTAUTH") +echo "✔️ NEXTAUTH_SECRET carregado" + + +export ENCRYPTION_KEY=$(get_secret "$OCI_SECRET_ENCRYPTION" | tr -d '\r\n') + +# Corrige automaticamente caso a key esteja inválida +if ! [[ "$ENCRYPTION_KEY" =~ ^[a-fA-F0-9]{64}$ ]]; then + echo "⚠️ ENCRYPTION_KEY inválida no Vault. Gerando automaticamente..." + + ENCRYPTION_KEY=$(openssl rand -hex 32) + + echo "🔐 Nova ENCRYPTION_KEY:" + echo "$ENCRYPTION_KEY" +fi + +echo "✔️ ENCRYPTION_KEY carregado" + +export SALT=$(get_secret "$OCI_SECRET_SALT") +echo "✔️ SALT carregado" + +# ========================= + +# ⚡ REDIS + +# ========================= + +echo "" +echo "⚡ Etapa 7: Config Redis..." + +export REDIS_HOST="${APP_NAME}-redis" +export REDIS_PORT="6379" + +echo "✔️ Redis: $REDIS_HOST:$REDIS_PORT" + +# ========================= + +# 📊 CLICKHOUSE + +# ========================= + +echo "" +echo "📊 Etapa 8: Config ClickHouse..." + +export CLICKHOUSE_URL="http://${APP_NAME}-clickhouse:8123" +export CLICKHOUSE_USER="default" +export CLICKHOUSE_MIGRATION_URL="clickhouse://${APP_NAME}-clickhouse:9000" +export CLICKHOUSE_CLUSTER_ENABLED="false" + +echo "✔️ ClickHouse: $CLICKHOUSE_URL" + +# ========================= + +# ☁️ OBJECT STORAGE + +# ========================= + +echo "" +echo "☁️ Etapa 9: Config OCI Object Storage..." + +export LANGFUSE_S3_EVENT_UPLOAD_BUCKET="langfuse-events" +export LANGFUSE_S3_EVENT_UPLOAD_REGION="${OCI_REGION}" +export LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT="https://${OCI_NAMESPACE}.compat.objectstorage.${OCI_REGION}.oraclecloud.com" +export LANGFUSE_USE_OCI_NATIVE_OBJECT_STORAGE="true" +echo "✔️ Bucket eventos: $LANGFUSE_S3_EVENT_UPLOAD_BUCKET" + +# ========================= + +# 🚀 DEPLOY + +# ========================= + +echo "" +echo "🚀 Etapa 10: Aplicando Kubernetes..." + +echo "📄 Preview YAML (primeiras linhas):" +envsubst < langfuse_dest.yaml | head -n 20 + +echo "" +echo "📦 Aplicando..." + +echo "" +echo "📄 Gerando YAML final..." + +envsubst < langfuse_dest.yaml > final.yaml + +echo "" +echo "📄 Preview:" +head -n 20 final.yaml + +echo "" +echo "🔍 Validando YAML..." + +kubectl apply --dry-run=client -f final.yaml + +echo "" +echo "📦 Aplicando..." + +# kubectl apply -f final.yaml + +echo "" +echo "========================================" +echo "🎯 DEPLOY FINALIZADO COM SUCESSO!" +echo "========================================" diff --git a/update_secret.sh b/update_secret.sh new file mode 100644 index 0000000..c0cd5c9 --- /dev/null +++ b/update_secret.sh @@ -0,0 +1,81 @@ +#!/bin/bash +set -ex + +echo "🔐 Atualizando secrets no OCI Vault..." + +# ========================= +# ⚙️ CONFIG +# ========================= + +export OCI_SECRET_OCI_SECRET="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +export OCI_SECRET_OCI_ACCESS="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +export OCI_SECRET_SALT="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +export OCI_SECRET_ENCRYPTION="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +export OCI_SECRET_NEXTAUTH="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +export OCI_SECRET_CLICKHOUSE="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +export OCI_SECRET_REDIS="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +export OCI_SECRET_DB="ocid1.vaultsecret.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" + +# ========================= +# 🔑 VALORES +# ========================= + +DB_PASSWORD="langfuse123" + +REDIS_PASSWORD="redis123" + +CLICKHOUSE_PASSWORD="clickhouse123" + +NEXTAUTH_SECRET_VALUE="nextauth-secret-123" + +SALT_VALUE="salt-secret-123" + +# ⚠️ OBRIGATÓRIO: +# 64 chars hex / 256 bits +ENCRYPTION_KEY=$(openssl rand -hex 32) + +echo "🔐 ENCRYPTION_KEY gerada:" +echo "$ENCRYPTION_KEY" + +# ========================= +# 🔧 FUNÇÃO UPDATE +# ========================= + +update_secret() { + + local secret_ocid=$1 + local value=$2 + + echo "🔄 Atualizando secret: $secret_ocid" + + local base64_value + base64_value=$(printf "%s" "$value" | base64 | tr -d '\n') + + oci vault secret update-base64 \ + --secret-id "$secret_ocid" \ + --secret-content-content "$base64_value" +} + +# ========================= +# 🔥 UPDATE +# ========================= + +update_secret "$OCI_SECRET_DB" "$DB_PASSWORD" + +update_secret "$OCI_SECRET_REDIS" "$REDIS_PASSWORD" + +update_secret "$OCI_SECRET_CLICKHOUSE" "$CLICKHOUSE_PASSWORD" + +update_secret "$OCI_SECRET_NEXTAUTH" "$NEXTAUTH_SECRET_VALUE" + +update_secret "$OCI_SECRET_ENCRYPTION" "$ENCRYPTION_KEY" + +update_secret "$OCI_SECRET_SALT" "$SALT_VALUE" + +echo "" +echo "✅ Secrets atualizados com sucesso!" +echo "" +echo "👉 ENCRYPTION_KEY usada:" +echo "$ENCRYPTION_KEY"