2153 lines
93 KiB
HTML
2153 lines
93 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<link rel="preconnect" href="https://d3js.org" crossorigin>
|
|
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
<link rel="alternate icon" href="/favicon.ico">
|
|
<title>Odoo Kubernetes Hosting 2026 | Odoo Expertos · EN</title>
|
|
|
|
<meta name="description" content="Guia definitiva para desplegar Odoo en Kubernetes en 2026. Helm charts, estrategias de deployment, Providers cloud con K8s gestio - Odoo Kubernetes Hosting 2026 | EN">
|
|
<meta name="keywords" content="Odoo kubernetes, Odoo k8s, Odoo helm chart, kubernetes Odoo deployment, Odoo docker kubernetes, Odoo gke, Odoo eks, Odoo aks, Odoo cloud native">
|
|
<meta name="author" content="Dr. Equipo Odoo Expertos">
|
|
<meta name="robots" content="index, follow">
|
|
<meta name="language" content="es">
|
|
|
|
<link rel="canonical" href="https://www.odoo-expertos.com/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/">
|
|
|
|
<!-- Hreflang -->
|
|
<link rel="alternate" hreflang="en" href="https://www.odoo-expertos.com/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/" />
|
|
<link rel="alternate" hreflang="en" href="https://www.odoo-expertos.com/en/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/" />
|
|
<link rel="alternate" hreflang="de" href="https://www.odoo-expertos.com/de/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/" />
|
|
<link rel="alternate" hreflang="fr" href="https://www.odoo-expertos.com/fr/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/" />
|
|
<link rel="alternate" hreflang="x-default" href="https://www.odoo-expertos.com/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/" />
|
|
|
|
<!-- Open Graph -->
|
|
<meta property="og:title" content="Odoo en Kubernetes 2026: Complete guide de Deployment y Hosting K8s">
|
|
<meta property="og:description" content="Guia definitiva para desplegar Odoo en Kubernetes. Helm charts, estrategias de deployment, Providers cloud con K8s gestionado.">
|
|
<meta property="og:type" content="article">
|
|
<meta property="og:url" content="https://www.odoo-expertos.com/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/">
|
|
<meta property="og:image" content="https://www.odoo-expertos.com/images/Odoo-kubernetes-Hosting-2026.jpg">
|
|
<meta property="og:site_name" content="Odoo Expertos">
|
|
<meta property="og:locale" content="es_ES">
|
|
|
|
<!-- Twitter Card -->
|
|
<meta name="twitter:card" content="summary_large_image">
|
|
<meta name="twitter:title" content="Odoo en Kubernetes 2026: Complete guide de Deployment">
|
|
<meta name="twitter:description" content="Guia definitiva para desplegar Odoo en Kubernetes. Helm charts y Providers cloud.">
|
|
|
|
<!-- Schema.org Article -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "Article",
|
|
"headline": "Odoo en Kubernetes 2026: Complete guide de Deployment y Hosting K8s",
|
|
"description": "Guia definitiva para desplegar Odoo en Kubernetes en 2026. Helm charts, estrategias de deployment, Providers cloud con K8s gestionado.",
|
|
"author": {
|
|
"@type": "Person",
|
|
"name": "Dr. Equipo Odoo Expertos",
|
|
"url": "https://www.odoo-expertos.com/autor/maria-elena-rodriguez/",
|
|
"jobTitle": "Senior ERP Systems Consultant y Cloud Architecture"
|
|
},
|
|
"publisher": {
|
|
"@type": "Organization",
|
|
"name": "Odoo Expertos",
|
|
"logo": {
|
|
"@type": "ImageObject",
|
|
"url": "https://www.odoo-expertos.com/logo.png"
|
|
}
|
|
},
|
|
"datePublished": "2026-01-25",
|
|
"dateModified": "2026-01-25",
|
|
"mainEntityOfPage": {
|
|
"@type": "WebPage",
|
|
"@id": "https://www.odoo-expertos.com/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/"
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<!-- FAQ Schema -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "FAQPage",
|
|
"mainEntity": [
|
|
{
|
|
"@type": "Question",
|
|
"name": "Cuando tiene sentido usar Kubernetes para Odoo?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Kubernetes es recomendable para Odoo cuando necesitas alta disponibilidad (99.99%+), escalabilidad automatica para picos de demanda, multiples entornos (dev/staging/prod), integracion con CI/CD avanzado, o cuando manejas mas de 100 usuarios concurrentes. Para PYMEs con menos de 50 usuarios, un VPS gestionado suele ser mas cost-effective."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Cual es el mejor proveedor cloud para Odoo en Kubernetes?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Para empresas en Europe, Google GKE ofrece la Best value precio/rendimiento con Autopilot. AWS EKS es ideal Yes ya usas servicios AWS. Azure AKS es la opcion preferida para empresas con infraestructura Microsoft. DigitalOcean DOKS es excellent para presupuestos ajustados con simplicidad."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Existe un Helm chart oficial para Odoo?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "No existe un Helm chart oficial de Odoo S.A. Sin embargo, Bitnami mantiene el chart mas popular y estable (bitnami/Odoo), Updated regularmente con cada version de Odoo. Tambien existen charts comunitarios en ArtifactHub con configuraciones especializadas."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Cuanto cuesta ejecutar Odoo en Kubernetes?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "El costo minimo viable es aproximadamente 150-200 EUR/month para un cluster basico (3 nodos, 2vCPU, 4GB cada uno) mas almacenamiento y networking. Para produccion con alta disponibilidad, el costo tipico es 400-800 EUR/month. Los Costs pueden reducirse significativamente con nodos spot/preemptibles."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Como manejo los volumenes persistentes de Odoo en K8s?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Odoo requiere almacenamiento persistente para el filestore y la base de datos PostgreSQL. Usa PersistentVolumeClaims con StorageClass que soporte ReadWriteMany (como NFS, EFS, o Filestore) para el filestore Yes necesitas multiples replicas. Para PostgreSQL, usa operadores como CloudNativePG o servicios gestionados (Cloud SQL, RDS)."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Puedo escalar Odoo horizontalmente en Kubernetes?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Yes, pero con consideraciones. Odoo puede escalar horizontalmente los workers de Odoo Yes configuras correctamente: sesiones en Redis/Memcached, filestore compartido (NFS/S3), y PostgreSQL con suficiente capacidad. El escalado horizontal tipico es 2-10 pods de Odoo apuntando a una base de datos PostgreSQL con alta disponibilidad."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Que estrategia de deployment es mejor para Odoo en K8s?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Para actualizaciones de Odoo, recomendamos Blue-Green deployment por su capacidad de rollback instantaneo. Rolling updates funcionan para cambios menores de codigo. Canary deployments son utiles para probar nuevos modulos con un porcentaje de trafico. Siempre haz backup de la base de datos antes de cualquier actualizacion."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Como configuro HTTPS/SSL para Odoo en Kubernetes?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "La forma mas comun es usar un Ingress Controller (nginx-ingress o traefik) combinado con cert-manager para certificados automaticos de Let's Encrypt. Configura annotations en el Ingress para SSL redirect y HSTS. Para produccion enterprise, considera certificados wildcard o de organizacion."
|
|
}
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
|
|
<!-- HowTo Schema -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "HowTo",
|
|
"name": "Como desplegar Odoo en Kubernetes con Helm",
|
|
"description": "Guia paso a paso para instalar Odoo en un cluster de Kubernetes usando Helm charts",
|
|
"step": [
|
|
{
|
|
"@type": "HowToStep",
|
|
"name": "Preparar el cluster",
|
|
"text": "Asegurate de tener un cluster Kubernetes funcionando con kubectl configurado y Helm 3 instalado"
|
|
},
|
|
{
|
|
"@type": "HowToStep",
|
|
"name": "Agregar repositorio Bitnami",
|
|
"text": "Ejecuta: helm repo add bitnami https://charts.bitnami.com/bitnami && helm repo update"
|
|
},
|
|
{
|
|
"@type": "HowToStep",
|
|
"name": "Crear archivo de valores",
|
|
"text": "Crea un archivo values.yaml con la configuracion personalizada de recursos, persistencia y PostgreSQL"
|
|
},
|
|
{
|
|
"@type": "HowToStep",
|
|
"name": "Instalar Odoo",
|
|
"text": "Ejecuta: helm install Odoo bitnami/Odoo -f values.yaml -n Odoo --create-namespace"
|
|
},
|
|
{
|
|
"@type": "HowToStep",
|
|
"name": "Configurar Ingress",
|
|
"text": "Aplica un recurso Ingress para exponer Odoo con SSL/TLS usando cert-manager"
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
|
|
<!-- BreadcrumbList Schema -->
|
|
<script type="application/ld+json">
|
|
{
|
|
"@context": "https://schema.org",
|
|
"@type": "BreadcrumbList",
|
|
"itemListElement": [
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 1,
|
|
"name": "Home",
|
|
"item": "https://www.odoo-expertos.com/"
|
|
},
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 2,
|
|
"name": "Odoo Hosting",
|
|
"item": "https://www.odoo-expertos.com/Odoo-Hosting/"
|
|
},
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 3,
|
|
"name": "Odoo Kubernetes Hosting 2026",
|
|
"item": "https://www.odoo-expertos.com/Odoo-Hosting/Odoo-kubernetes-Hosting-2026/"
|
|
}
|
|
]
|
|
}
|
|
</script>
|
|
|
|
<link rel="stylesheet" href="/css/main.min.css">
|
|
<style>
|
|
.pillar-hero {
|
|
background: linear-gradient(135deg, #0d1b2a 0%, #1b263b 50%, #0d1b2a 100%);
|
|
padding: 4rem 2rem;
|
|
text-align: center;
|
|
border-bottom: 3px solid #326ce5;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.pillar-hero::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: radial-gradient(ellipse at center, rgba(50,108,229,0.15) 0%, transparent 70%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.pillar-hero h1 {
|
|
font-size: clamp(2rem, 5vw, 3.5rem);
|
|
color: #326ce5;
|
|
margin-bottom: 1rem;
|
|
line-height: 1.2;
|
|
position: relative;
|
|
}
|
|
|
|
.pillar-subtitle {
|
|
font-size: 1.3rem;
|
|
color: #b0b0b0;
|
|
max-width: 800px;
|
|
margin: 0 auto 2rem;
|
|
}
|
|
|
|
.pillar-meta {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 2rem;
|
|
flex-wrap: wrap;
|
|
font-size: 0.9rem;
|
|
color: #888;
|
|
}
|
|
|
|
.pillar-meta span {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.quick-stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
gap: 1.5rem;
|
|
padding: 2rem;
|
|
background: #1b263b;
|
|
border-radius: 12px;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.stat-card {
|
|
text-align: center;
|
|
padding: 1.5rem;
|
|
background: rgba(50,108,229,0.1);
|
|
border-radius: 8px;
|
|
border: 1px solid rgba(50,108,229,0.2);
|
|
transition: transform 0.3s ease, border-color 0.3s ease;
|
|
}
|
|
|
|
.stat-card:hover {
|
|
transform: translateY(-5px);
|
|
border-color: #326ce5;
|
|
}
|
|
|
|
.stat-number {
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
color: #326ce5;
|
|
display: block;
|
|
}
|
|
|
|
.stat-label {
|
|
color: #b0b0b0;
|
|
font-size: 0.9rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.toc {
|
|
background: #1b263b;
|
|
border: 2px solid #326ce5;
|
|
border-radius: 12px;
|
|
padding: 2rem;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.toc h2 {
|
|
color: #326ce5;
|
|
margin-bottom: 1rem;
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.toc-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
columns: 2;
|
|
column-gap: 2rem;
|
|
}
|
|
|
|
.toc-list li {
|
|
margin-bottom: 0.75rem;
|
|
break-inside: avoid;
|
|
}
|
|
|
|
.toc-list a {
|
|
color: #e0e0e0;
|
|
text-decoration: none;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
.toc-list a:hover {
|
|
color: #326ce5;
|
|
}
|
|
|
|
.toc-list a::before {
|
|
content: '>';
|
|
color: #326ce5;
|
|
font-family: monospace;
|
|
}
|
|
|
|
.content-section {
|
|
margin: 3rem 0;
|
|
}
|
|
|
|
.content-section h2 {
|
|
color: #5b9cf4;
|
|
margin-bottom: 1.5rem;
|
|
padding-bottom: 0.5rem;
|
|
border-bottom: 2px solid rgba(50,108,229,0.3);
|
|
}
|
|
|
|
.content-section h3 {
|
|
color: #7cb3f7;
|
|
margin: 2rem 0 1rem;
|
|
}
|
|
|
|
.chart-section {
|
|
margin: 3rem 0;
|
|
padding: 2rem;
|
|
background: #1b263b;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.chart-section h3 {
|
|
color: #5b9cf4;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.chart-container {
|
|
width: 100%;
|
|
min-height: 450px;
|
|
position: relative;
|
|
}
|
|
|
|
.comparison-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin: 2rem 0;
|
|
background: #1b263b;
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.comparison-table th,
|
|
.comparison-table td {
|
|
padding: 1rem;
|
|
text-align: left;
|
|
border-bottom: 1px solid #2a3f5f;
|
|
}
|
|
|
|
.comparison-table th {
|
|
background: rgba(50,108,229,0.2);
|
|
color: #326ce5;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.comparison-table tr:hover {
|
|
background: rgba(50,108,229,0.05);
|
|
}
|
|
|
|
.comparison-table .check {
|
|
color: #4CAF50;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.comparison-table .cross {
|
|
color: #f44336;
|
|
}
|
|
|
|
.provider-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
|
|
gap: 1.5rem;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.provider-card {
|
|
background: #1b263b;
|
|
border-radius: 12px;
|
|
padding: 1.5rem;
|
|
border: 1px solid rgba(50,108,229,0.2);
|
|
transition: transform 0.3s ease, border-color 0.3s ease;
|
|
}
|
|
|
|
.provider-card:hover {
|
|
transform: translateY(-5px);
|
|
border-color: #326ce5;
|
|
}
|
|
|
|
.provider-card.featured {
|
|
border-color: #326ce5;
|
|
background: linear-gradient(135deg, rgba(50,108,229,0.15) 0%, #1b263b 100%);
|
|
position: relative;
|
|
}
|
|
|
|
.provider-card .badge {
|
|
display: inline-block;
|
|
padding: 0.25rem 0.75rem;
|
|
background: #326ce5;
|
|
color: #fff;
|
|
border-radius: 20px;
|
|
font-size: 0.8rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.provider-card h4 {
|
|
color: #fff;
|
|
margin-bottom: 0.5rem;
|
|
font-size: 1.3rem;
|
|
}
|
|
|
|
.provider-card .price {
|
|
font-size: 2rem;
|
|
color: #326ce5;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.provider-card .price-period {
|
|
color: #888;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.provider-card ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 1rem 0;
|
|
}
|
|
|
|
.provider-card li {
|
|
padding: 0.5rem 0;
|
|
border-bottom: 1px solid #2a3f5f;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
color: #b0b0b0;
|
|
}
|
|
|
|
.provider-card li::before {
|
|
content: '>';
|
|
color: #4CAF50;
|
|
font-family: monospace;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.provider-card .cta-btn {
|
|
display: block;
|
|
text-align: center;
|
|
padding: 0.75rem 1.5rem;
|
|
background: #326ce5;
|
|
color: #fff;
|
|
border-radius: 8px;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
transition: background 0.3s ease, transform 0.3s ease;
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
.provider-card .cta-btn:hover {
|
|
background: #5b9cf4;
|
|
transform: scale(1.02);
|
|
}
|
|
|
|
.code-block {
|
|
background: #0d1b2a;
|
|
border: 1px solid #2a3f5f;
|
|
border-radius: 8px;
|
|
padding: 1.5rem;
|
|
overflow-x: auto;
|
|
margin: 1.5rem 0;
|
|
font-family: 'Monaco', 'Consolas', monospace;
|
|
font-size: 0.9rem;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
.code-block code {
|
|
color: #e0e0e0;
|
|
}
|
|
|
|
.code-block .comment {
|
|
color: #6a9955;
|
|
}
|
|
|
|
.code-block .keyword {
|
|
color: #569cd6;
|
|
}
|
|
|
|
.code-block .string {
|
|
color: #ce9178;
|
|
}
|
|
|
|
.code-block .number {
|
|
color: #b5cea8;
|
|
}
|
|
|
|
.faq-section {
|
|
margin: 3rem 0;
|
|
}
|
|
|
|
.faq-item {
|
|
background: #1b263b;
|
|
border-radius: 8px;
|
|
margin-bottom: 1rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.faq-question {
|
|
padding: 1.25rem;
|
|
cursor: pointer;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
color: #fff;
|
|
font-weight: 500;
|
|
transition: background 0.3s ease;
|
|
}
|
|
|
|
.faq-question:hover {
|
|
background: rgba(50,108,229,0.1);
|
|
}
|
|
|
|
.faq-question::after {
|
|
content: '+';
|
|
font-size: 1.5rem;
|
|
color: #326ce5;
|
|
transition: transform 0.3s ease;
|
|
}
|
|
|
|
.faq-item.open .faq-question::after {
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.faq-answer {
|
|
padding: 0 1.25rem;
|
|
max-height: 0;
|
|
overflow: hidden;
|
|
transition: max-height 0.3s ease, padding 0.3s ease;
|
|
color: #b0b0b0;
|
|
}
|
|
|
|
.faq-item.open .faq-answer {
|
|
padding: 0 1.25rem 1.25rem;
|
|
max-height: 500px;
|
|
}
|
|
|
|
.cta-banner {
|
|
background: linear-gradient(135deg, rgba(50,108,229,0.2) 0%, rgba(91,156,244,0.1) 100%);
|
|
border: 2px solid rgba(50,108,229,0.5);
|
|
border-radius: 12px;
|
|
padding: 2.5rem;
|
|
text-align: center;
|
|
margin: 3rem 0;
|
|
}
|
|
|
|
.cta-banner h3 {
|
|
color: #326ce5;
|
|
margin-bottom: 1rem;
|
|
font-size: 1.8rem;
|
|
}
|
|
|
|
.cta-banner p {
|
|
margin-bottom: 1.5rem;
|
|
color: #b0b0b0;
|
|
font-size: 1.1rem;
|
|
}
|
|
|
|
.cta-banner .cta-btn {
|
|
display: inline-block;
|
|
padding: 1rem 2.5rem;
|
|
background: #326ce5;
|
|
color: #fff;
|
|
border-radius: 8px;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
font-size: 1.1rem;
|
|
transition: background 0.3s ease, transform 0.3s ease;
|
|
}
|
|
|
|
.cta-banner .cta-btn:hover {
|
|
background: #5b9cf4;
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.highlight-box {
|
|
background: linear-gradient(135deg, rgba(50,108,229,0.1) 0%, rgba(91,156,244,0.05) 100%);
|
|
border-left: 4px solid #326ce5;
|
|
padding: 1.5rem;
|
|
margin: 2rem 0;
|
|
border-radius: 0 8px 8px 0;
|
|
}
|
|
|
|
.highlight-box h4 {
|
|
color: #326ce5;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.warning-box {
|
|
background: linear-gradient(135deg, rgba(255,152,0,0.1) 0%, rgba(255,193,7,0.05) 100%);
|
|
border-left: 4px solid #ff9800;
|
|
padding: 1.5rem;
|
|
margin: 2rem 0;
|
|
border-radius: 0 8px 8px 0;
|
|
}
|
|
|
|
.warning-box h4 {
|
|
color: #ff9800;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.pros-cons {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 1.5rem;
|
|
margin: 1.5rem 0;
|
|
}
|
|
|
|
.pros, .cons {
|
|
padding: 1.5rem;
|
|
border-radius: 8px;
|
|
}
|
|
|
|
.pros {
|
|
background: rgba(76, 175, 80, 0.1);
|
|
border: 1px solid rgba(76, 175, 80, 0.3);
|
|
}
|
|
|
|
.cons {
|
|
background: rgba(244, 67, 54, 0.1);
|
|
border: 1px solid rgba(244, 67, 54, 0.3);
|
|
}
|
|
|
|
.pros h4 {
|
|
color: #4CAF50;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.cons h4 {
|
|
color: #f44336;
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.pros ul, .cons ul {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
|
|
.pros li::before {
|
|
content: '> ';
|
|
color: #4CAF50;
|
|
font-family: monospace;
|
|
}
|
|
|
|
.cons li::before {
|
|
content: 'x ';
|
|
color: #f44336;
|
|
font-family: monospace;
|
|
}
|
|
|
|
.related-articles {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: 1.5rem;
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
.related-card {
|
|
background: #1b263b;
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
transition: transform 0.3s ease;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.related-card:hover {
|
|
transform: translateY(-5px);
|
|
}
|
|
|
|
.related-card-image {
|
|
height: 150px;
|
|
background: linear-gradient(135deg, rgba(50,108,229,0.3), rgba(91,156,244,0.3));
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 3rem;
|
|
}
|
|
|
|
.related-card-content {
|
|
padding: 1.25rem;
|
|
}
|
|
|
|
.related-card h4 {
|
|
color: #fff;
|
|
margin-bottom: 0.5rem;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.related-card p {
|
|
color: #888;
|
|
font-size: 0.9rem;
|
|
margin: 0;
|
|
}
|
|
|
|
.d3-tooltip {
|
|
position: absolute;
|
|
background: rgba(13,27,42,0.98);
|
|
color: #fff;
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 8px;
|
|
font-size: 0.9rem;
|
|
pointer-events: none;
|
|
z-index: 100;
|
|
max-width: 300px;
|
|
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
|
border: 1px solid rgba(50,108,229,0.3);
|
|
}
|
|
|
|
.d3-tooltip strong {
|
|
color: #326ce5;
|
|
}
|
|
|
|
.fade-in {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
transition: opacity 0.6s ease, transform 0.6s ease;
|
|
}
|
|
|
|
.fade-in.visible {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.k8s-icon {
|
|
display: inline-block;
|
|
width: 24px;
|
|
height: 24px;
|
|
background: #326ce5;
|
|
border-radius: 50%;
|
|
margin-right: 8px;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
.architecture-legend {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 1rem;
|
|
margin-top: 1rem;
|
|
justify-content: center;
|
|
}
|
|
|
|
.legend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
font-size: 0.85rem;
|
|
color: #b0b0b0;
|
|
}
|
|
|
|
.legend-color {
|
|
width: 16px;
|
|
height: 16px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.toc-list {
|
|
columns: 1;
|
|
}
|
|
|
|
.pillar-meta {
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.chart-container {
|
|
min-height: 350px;
|
|
}
|
|
|
|
.pros-cons {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.provider-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.code-block {
|
|
font-size: 0.8rem;
|
|
padding: 1rem;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="header"><header class="site-header"><nav class="navbar"><div class="container"><div class="nav-brand"><a href="/en/" class="logo"><span class="logo-text">Odoo Expertos</span></a></div><button class="nav-toggle" aria-label="Toggle navigation"><span></span><span></span><span></span></button><div class="nav-menu"><ul class="nav-list"><li><a href="/en/">Home</a></li><li><a href="/en/odoo/">Odoo</a></li><li><a href="/en/odoo-ia/">Odoo AI</a></li><li><a href="/en/odoo-hosting/">Odoo Hosting</a></li><li><a href="/en/mapa-del-sitio/">Sitemap</a></li></ul></div></div></nav></header></div><button class="nav-toggle" aria-label="Toggle navigation"><span></span><span></span><span></span></button><div class="nav-menu"><ul class="nav-list"><li><a href="/en/">Home</a></li><li><a href="/en/odoo/">Odoo</a></li><li><a href="/en/odoo-ia/">Odoo AI</a></li><li><a href="/en/odoo-hosting/">Odoo Hosting</a></li><li><a href="/en/mapa-del-sitio/">Sitemap</a></li></ul></div></div></nav></header></div>
|
|
|
|
<!-- Breadcrumbs -->
|
|
<nav class="breadcrumbs">
|
|
<div class="container">
|
|
<ol class="breadcrumb-list" itemscope itemtype="https://schema.org/BreadcrumbList">
|
|
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
|
<a itemprop="item" href="/"><span itemprop="name">Home</span></a>
|
|
<span itemprop="position" style="display:none">1</span>
|
|
</li>
|
|
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
|
<a itemprop="item" href="/Odoo-Hosting/"><span itemprop="name">Odoo Hosting</span></a>
|
|
<span itemprop="position" style="display:none">2</span>
|
|
</li>
|
|
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
|
|
<span itemprop="name">Odoo Kubernetes Hosting 2026</span>
|
|
<span itemprop="position" style="display:none">3</span>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Hero Section -->
|
|
<section class="pillar-hero">
|
|
<div class="container">
|
|
<h1>Odoo en Kubernetes 2026: Complete guide de Deployment y Hosting K8s</h1>
|
|
<p class="pillar-subtitle">Domina el Deployment de Odoo en Kubernetes. Desde conceptos basicos hasta arquitecturas de alta disponibilidad con escalado automatico, Helm charts y mejores practicas para produccion.</p>
|
|
<div class="pillar-meta">
|
|
<span>Published: 25 January 2026</span>
|
|
<span>Updated: 25 January 2026</span>
|
|
<span>30 min read</span>
|
|
<span>Dr. Equipo Odoo Expertos</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<main class="container">
|
|
<!-- Quick Stats -->
|
|
<section class="quick-stats fade-in">
|
|
<div class="stat-card">
|
|
<span class="stat-number">99.99%</span>
|
|
<span class="stat-label">Uptime Alcanzable</span>
|
|
</div>
|
|
<div class="stat-card">
|
|
<span class="stat-number">4</span>
|
|
<span class="stat-label">Providers K8s Analizados</span>
|
|
</div>
|
|
<div class="stat-card">
|
|
<span class="stat-number">150EUR</span>
|
|
<span class="stat-label">Costo Minimo/month</span>
|
|
</div>
|
|
<div class="stat-card">
|
|
<span class="stat-number">10x</span>
|
|
<span class="stat-label">Escalabilidad Potencial</span>
|
|
</div>
|
|
<div class="stat-card">
|
|
<span class="stat-number">3</span>
|
|
<span class="stat-label">Estrategias de Deploy</span>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Table of Contents -->
|
|
<nav class="toc fade-in">
|
|
<h2>Contents of This Guide</h2>
|
|
<ol class="toc-list">
|
|
<li><a href="#introduccion">Introduccion: Por que Kubernetes para Odoo</a></li>
|
|
<li><a href="#cuando-k8s">Cuando Tiene Sentido Usar K8s</a></li>
|
|
<li><a href="#arquitectura">Arquitectura de Odoo en Kubernetes</a></li>
|
|
<li><a href="#helm-charts">Helm Charts para Odoo</a></li>
|
|
<li><a href="#estrategias-deployment">Estrategias de Deployment</a></li>
|
|
<li><a href="#Providers-cloud">Providers Cloud con K8s Gestionado</a></li>
|
|
<li><a href="#configuracion-produccion">Configuracion para Produccion</a></li>
|
|
<li><a href="#escalabilidad">Escalabilidad y Alta Disponibilidad</a></li>
|
|
<li><a href="#Costs">Analysis de Costs</a></li>
|
|
<li><a href="#faq">Preguntas Frecuentes (8+)</a></li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<!-- Introduction -->
|
|
<section id="introduccion" class="content-section fade-in">
|
|
<h2>Introduccion: Por que Kubernetes para Odoo en 2026</h2>
|
|
|
|
<p>Kubernetes se ha consolidado como el estandar de facto para la orquestacion de contenedores en entornos empresariales. Para Odoo, esto representa una oportunidad unica de lograr niveles de disponibilidad, escalabilidad y automatizacion que eran impensables hace pocos anos. Sin embargo, No todas las implementaciones de Odoo se benefician de la complejidad adicional que Kubernetes introduce.</p>
|
|
|
|
<p>En esta guia exhaustiva, analizaremos cuando tiene sentido migrar tu instalacion de Odoo a Kubernetes, como disenar una arquitectura robusta, que herramientas utilizar (Helm charts, operadores), y como elegir entre los principales Providers de Kubernetes gestionado. Tambien cubriremos estrategias de deployment, configuracion de persistencia, y optimizacion de Costs.</p>
|
|
|
|
<div class="highlight-box">
|
|
<h4>Resumen Ejecutivo</h4>
|
|
<p>Kubernetes es ideal para Odoo cuando necesitas <strong>alta disponibilidad (99.99%+)</strong>, <strong>escalado automatico</strong> para picos de demanda, o <strong>multiples entornos</strong> (desarrollo, staging, produccion) con Deployment continuo. Para PYMEs con menos de 50 usuarios y requisitos simples, un VPS gestionado sigue siendo mas cost-effective y facil de mantener.</p>
|
|
</div>
|
|
|
|
<h3>Que Aprenderas en Esta Guia</h3>
|
|
<ul>
|
|
<li>Criterios claros para decidir Yes Kubernetes es adecuado para tu caso de uso</li>
|
|
<li>Arquitectura de referencia para Odoo en K8s con diagramas interactivos</li>
|
|
<li>Configuracion completa de Helm charts con ejemplos de codigo</li>
|
|
<li>Comparativa de GKE, EKS, AKS y DOKS para Odoo Hosting</li>
|
|
<li>Estrategias de deployment: Rolling, Blue-Green, y Canary</li>
|
|
<li>Optimizacion de Costs con nodos spot y escalado automatico</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<!-- When K8s Makes Sense -->
|
|
<section id="cuando-k8s" class="content-section fade-in">
|
|
<h2>Cuando Tiene Sentido Usar Kubernetes para Odoo</h2>
|
|
|
|
<p>La decision de usar Kubernetes No debe tomarse a la ligera. Aunque ofrece beneficios significativos, tambien introduce complejidad operativa que requiere conocimientos especializados. Aqui te presentamos los criterios clave para tomar esta decision.</p>
|
|
|
|
<h3>Kubernetes ES Recomendable Cuando:</h3>
|
|
<ul>
|
|
<li><strong>Mas de 100 usuarios concurrentes:</strong> El escalado horizontal de Odoo workers se vuelve critico</li>
|
|
<li><strong>Requisitos de uptime 99.99%+:</strong> Necesitas auto-healing y redundancia automatica</li>
|
|
<li><strong>Multiples entornos:</strong> Dev, staging, QA, produccion con Deployment frecuente</li>
|
|
<li><strong>CI/CD avanzado:</strong> Integracion con GitOps (ArgoCD, Flux) para deployments automaticos</li>
|
|
<li><strong>Microservicios:</strong> Odoo coexiste con otros servicios (APIs, workers de background)</li>
|
|
<li><strong>Picos de demanda predecibles:</strong> Black Friday, campanas de marketing, fin de mes contable</li>
|
|
<li><strong>Equipo DevOps dedicado:</strong> Tienes personal con experiencia en contenedores y K8s</li>
|
|
</ul>
|
|
|
|
<h3>Kubernetes No Es Necesario Cuando:</h3>
|
|
<ul>
|
|
<li><strong>Menos de 50 usuarios:</strong> Un VPS bien dimensionado es suficiente y mas simple</li>
|
|
<li><strong>Presupuesto Limited:</strong> K8s tiene un costo base significativo (150-200 EUR/month minimo)</li>
|
|
<li><strong>Sin equipo tecnico:</strong> La curva de aprendizaje es pronunciada</li>
|
|
<li><strong>Instalacion estatica:</strong> Raramente necesitas escalar o actualizar</li>
|
|
<li><strong>Requisitos de compliance simples:</strong> Un servidor dedicado puede ser mas facil de auditar</li>
|
|
</ul>
|
|
|
|
<div class="pros-cons">
|
|
<div class="pros">
|
|
<h4>Ventajas de K8s para Odoo</h4>
|
|
<ul>
|
|
<li>Auto-healing: pods se reinician automaticamente</li>
|
|
<li>Escalado horizontal y vertical automatico</li>
|
|
<li>Deployments sin downtime (rolling updates)</li>
|
|
<li>Rollback instantaneo a versiones anteriores</li>
|
|
<li>Infraestructura como codigo (IaC)</li>
|
|
<li>Portabilidad entre nubes</li>
|
|
</ul>
|
|
</div>
|
|
<div class="cons">
|
|
<h4>Desventajas de K8s para Odoo</h4>
|
|
<ul>
|
|
<li>Complejidad operativa significativa</li>
|
|
<li>Costo base mas alto que VPS</li>
|
|
<li>Curva de aprendizaje pronunciada</li>
|
|
<li>Debugging mas complejo</li>
|
|
<li>Requiere gestion de volumenes persistentes</li>
|
|
<li>Networking mas complejo</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Architecture Diagram -->
|
|
<section class="chart-section fade-in">
|
|
<h3 id="arquitectura">Arquitectura de Odoo en Kubernetes</h3>
|
|
<p>Este diagrama interactivo muestra una arquitectura de referencia para Odoo en Kubernetes con alta disponibilidad. Pasa el cursor sobre cada componente para ver detalles.</p>
|
|
<div class="chart-container" id="architecture-diagram"></div>
|
|
<div class="architecture-legend">
|
|
<div class="legend-item"><div class="legend-color" style="background: #326ce5;"></div> Kubernetes Components</div>
|
|
<div class="legend-item"><div class="legend-color" style="background: #4CAF50;"></div> Odoo Application</div>
|
|
<div class="legend-item"><div class="legend-color" style="background: #ff9800;"></div> Database Layer</div>
|
|
<div class="legend-item"><div class="legend-color" style="background: #9c27b0;"></div> Storage/Cache</div>
|
|
<div class="legend-item"><div class="legend-color" style="background: #00bcd4;"></div> External Services</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Helm Charts -->
|
|
<section id="helm-charts" class="content-section fade-in">
|
|
<h2>Helm Charts para Odoo: Complete guide</h2>
|
|
|
|
<p>Helm es el gestor de paquetes estandar para Kubernetes, y simplifica enormemente el Deployment de aplicaciones complejas como Odoo. Aunque Odoo S.A. No proporciona un chart oficial, la Community ha desarrollado varias opciones robustas.</p>
|
|
|
|
<h3>Chart Recommended: Bitnami Odoo</h3>
|
|
<p>El chart de Bitnami es el mas mantenido y utilizado, con actualizaciones regulares para cada version de Odoo. Incluye PostgreSQL como dependencia y soporta configuracion Advanced.</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># Agregar repositorio de Bitnami</span>
|
|
helm repo add bitnami https://charts.bitnami.com/bitnami
|
|
helm repo update
|
|
|
|
<span class="comment"># Buscar versiones disponibles</span>
|
|
helm search repo bitnami/Odoo --versions
|
|
|
|
<span class="comment"># Instalar Odoo con configuracion Basic</span>
|
|
helm install Odoo bitnami/Odoo \
|
|
--namespace Odoo \
|
|
--create-namespace \
|
|
--set odooEmail=admin@ejemplo.com \
|
|
--set odooPassword=<span class="string">"SecurePassword123!"</span></code>
|
|
</div>
|
|
|
|
<h3>Archivo values.yaml Personalizado para Produccion</h3>
|
|
<p>Para entornos de produccion, es esencial personalizar la configuracion. Aqui un ejemplo completo:</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># values-produccion.yaml</span>
|
|
<span class="keyword">replicaCount</span>: <span class="number">3</span>
|
|
|
|
<span class="keyword">image</span>:
|
|
<span class="keyword">tag</span>: <span class="string">"18.0"</span>
|
|
<span class="keyword">pullPolicy</span>: IfNotPresent
|
|
|
|
<span class="keyword">resources</span>:
|
|
<span class="keyword">requests</span>:
|
|
<span class="keyword">memory</span>: <span class="string">"2Gi"</span>
|
|
<span class="keyword">cpu</span>: <span class="string">"1000m"</span>
|
|
<span class="keyword">limits</span>:
|
|
<span class="keyword">memory</span>: <span class="string">"4Gi"</span>
|
|
<span class="keyword">cpu</span>: <span class="string">"2000m"</span>
|
|
|
|
<span class="keyword">persistence</span>:
|
|
<span class="keyword">enabled</span>: <span class="keyword">true</span>
|
|
<span class="keyword">storageClass</span>: <span class="string">"premium-rwo"</span>
|
|
<span class="keyword">size</span>: <span class="string">"100Gi"</span>
|
|
|
|
<span class="keyword">postgresql</span>:
|
|
<span class="keyword">enabled</span>: <span class="keyword">true</span>
|
|
<span class="keyword">auth</span>:
|
|
<span class="keyword">postgresPassword</span>: <span class="string">"PostgresSecure123!"</span>
|
|
<span class="keyword">database</span>: <span class="string">"Odoo"</span>
|
|
<span class="keyword">primary</span>:
|
|
<span class="keyword">persistence</span>:
|
|
<span class="keyword">size</span>: <span class="string">"50Gi"</span>
|
|
<span class="keyword">resources</span>:
|
|
<span class="keyword">requests</span>:
|
|
<span class="keyword">memory</span>: <span class="string">"1Gi"</span>
|
|
<span class="keyword">cpu</span>: <span class="string">"500m"</span>
|
|
|
|
<span class="keyword">ingress</span>:
|
|
<span class="keyword">enabled</span>: <span class="keyword">true</span>
|
|
<span class="keyword">hostname</span>: <span class="string">"Odoo.tudominio.com"</span>
|
|
<span class="keyword">tls</span>: <span class="keyword">true</span>
|
|
<span class="keyword">annotations</span>:
|
|
<span class="keyword">kubernetes.io/ingress.class</span>: <span class="string">"nginx"</span>
|
|
<span class="keyword">cert-manager.io/cluster-issuer</span>: <span class="string">"letsencrypt-prod"</span>
|
|
|
|
<span class="keyword">autoscaling</span>:
|
|
<span class="keyword">enabled</span>: <span class="keyword">true</span>
|
|
<span class="keyword">minReplicas</span>: <span class="number">2</span>
|
|
<span class="keyword">maxReplicas</span>: <span class="number">10</span>
|
|
<span class="keyword">targetCPU</span>: <span class="number">70</span>
|
|
<span class="keyword">targetMemory</span>: <span class="number">80</span></code>
|
|
</div>
|
|
|
|
<h3>Instalacion con Valores Personalizados</h3>
|
|
<div class="code-block">
|
|
<code><span class="comment"># Instalar con archivo de valores personalizado</span>
|
|
helm install Odoo bitnami/Odoo \
|
|
-f values-produccion.yaml \
|
|
-n Odoo \
|
|
--create-namespace
|
|
|
|
<span class="comment"># Verificar el estado del Deployment</span>
|
|
kubectl get pods -n Odoo -w
|
|
|
|
<span class="comment"># Obtener la URL de acceso</span>
|
|
kubectl get ingress -n Odoo</code>
|
|
</div>
|
|
|
|
<div class="warning-box">
|
|
<h4>Importante: Gestion de Secretos</h4>
|
|
<p>Nunca almacenes contrasenas en texto plano en archivos YAML versionados. Usa <strong>Kubernetes Secrets</strong>, <strong>Sealed Secrets</strong>, o integraciones con gestores de secretos como <strong>HashiCorp Vault</strong>, <strong>AWS Secrets Manager</strong>, o <strong>Google Secret Manager</strong>.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Deployment Strategies -->
|
|
<section id="estrategias-deployment" class="content-section fade-in">
|
|
<h2>Estrategias de Deployment para Odoo en Kubernetes</h2>
|
|
|
|
<p>La eleccion de la estrategia de deployment correcta es crucial para minimizar el downtime y los riesgos durante actualizaciones. Kubernetes ofrece varias opciones, cada una con sus ventajas y casos de uso.</p>
|
|
|
|
<h3>1. Rolling Update (Por Defecto)</h3>
|
|
<p>Actualiza los pods gradualmente, reemplazando instancias antiguas por nuevas de forma progresiva. Es la estrategia por defecto en Kubernetes y funciona bien para cambios menores.</p>
|
|
|
|
<div class="pros-cons">
|
|
<div class="pros">
|
|
<h4>Ventajas</h4>
|
|
<ul>
|
|
<li>Zero downtime durante actualizaciones</li>
|
|
<li>Uso eficiente de recursos</li>
|
|
<li>Configuracion simple por defecto</li>
|
|
</ul>
|
|
</div>
|
|
<div class="cons">
|
|
<h4>Limitations</h4>
|
|
<ul>
|
|
<li>Rollback No es instantaneo</li>
|
|
<li>Versiones mixtas durante el proceso</li>
|
|
<li>Problemas Yes hay migraciones de BD</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<h3>2. Blue-Green Deployment (Recommended para Odoo)</h3>
|
|
<p>Mantiene dos entornos identicos (blue y green). El trafico se dirige a uno mientras se actualiza el otro. Una vez validado, se cambia el trafico al nuevo entorno.</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># Ejemplo de Service que apunta a la version activa</span>
|
|
<span class="keyword">apiVersion</span>: v1
|
|
<span class="keyword">kind</span>: Service
|
|
<span class="keyword">metadata</span>:
|
|
<span class="keyword">name</span>: Odoo
|
|
<span class="keyword">spec</span>:
|
|
<span class="keyword">selector</span>:
|
|
<span class="keyword">app</span>: Odoo
|
|
<span class="keyword">version</span>: blue <span class="comment"># Cambiar a 'green' para switch</span>
|
|
<span class="keyword">ports</span>:
|
|
- <span class="keyword">port</span>: <span class="number">80</span>
|
|
<span class="keyword">targetPort</span>: <span class="number">8069</span></code>
|
|
</div>
|
|
|
|
<h3>3. Canary Deployment</h3>
|
|
<p>Despliega la nueva version a un pequeno porcentaje de usuarios primero. Util para probar nuevos modulos o cambios significativos con riesgo controlado.</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># Con Istio o ingress-nginx puedes controlar el porcentaje</span>
|
|
<span class="keyword">apiVersion</span>: networking.k8s.io/v1
|
|
<span class="keyword">kind</span>: Ingress
|
|
<span class="keyword">metadata</span>:
|
|
<span class="keyword">annotations</span>:
|
|
<span class="keyword">nginx.ingress.kubernetes.io/canary</span>: <span class="string">"true"</span>
|
|
<span class="keyword">nginx.ingress.kubernetes.io/canary-weight</span>: <span class="string">"10"</span> <span class="comment"># 10% del trafico</span></code>
|
|
</div>
|
|
|
|
<div class="highlight-box">
|
|
<h4>Recomendacion para Actualizaciones de Odoo</h4>
|
|
<p>Para actualizaciones mayores de Odoo (ej: 17.0 a 18.0), recomendamos <strong>Blue-Green deployment</strong> con backup completo de base de datos. Ejecuta las migraciones en el entorno "green" inactivo, valida, y luego cambia el trafico. Esto permite rollback instantaneo Yes algo falla.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Cloud Providers Comparison -->
|
|
<section class="chart-section fade-in">
|
|
<h3 id="comparacion-Providers">Comparativa de Providers K8s Gestionado</h3>
|
|
<p>Este grafico radar compara los principales Providers de Kubernetes gestionado en dimensiones clave para Odoo Hosting.</p>
|
|
<div class="chart-container" id="radar-chart"></div>
|
|
</section>
|
|
|
|
<!-- Cloud Providers -->
|
|
<section id="Providers-cloud" class="content-section fade-in">
|
|
<h2>Providers Cloud con Kubernetes Gestionado</h2>
|
|
|
|
<p>Elegir el proveedor adecuado de Kubernetes gestionado impacta directamente en Costs, rendimiento y facilidad de operacion. Analizamos los cuatro principales para Odoo Hosting.</p>
|
|
|
|
<div class="provider-grid">
|
|
<!-- GKE -->
|
|
<div class="provider-card featured">
|
|
<span class="badge">Recommended</span>
|
|
<h4>Google GKE</h4>
|
|
<p class="price">~180EUR<span class="price-period">/mes (Autopilot)</span></p>
|
|
<ul>
|
|
<li>Autopilot: K8s sin gestion de nodos</li>
|
|
<li>Integracion Native con Cloud SQL</li>
|
|
<li>Networking avanzado (VPC-native)</li>
|
|
<li>Auto-upgrade y auto-repair</li>
|
|
<li>excellent documentacion</li>
|
|
<li>Precios competitivos en Europe</li>
|
|
</ul>
|
|
<a href="https://cloud.google.com/kubernetes-engine" class="cta-btn" target="_blank" rel="noopener">Ver GKE</a>
|
|
</div>
|
|
|
|
<!-- EKS -->
|
|
<div class="provider-card">
|
|
<span class="badge">Enterprise</span>
|
|
<h4>Amazon EKS</h4>
|
|
<p class="price">~220EUR<span class="price-period">/mes</span></p>
|
|
<ul>
|
|
<li>Integracion con ecosistema AWS</li>
|
|
<li>EKS Fargate para serverless</li>
|
|
<li>RDS PostgreSQL gestionado</li>
|
|
<li>IAM para autenticacion</li>
|
|
<li>Mayor adopcion enterprise</li>
|
|
<li>Soporte 24/7 disponible</li>
|
|
</ul>
|
|
<a href="https://aws.amazon.com/eks/" class="cta-btn" target="_blank" rel="noopener">Ver EKS</a>
|
|
</div>
|
|
|
|
<!-- AKS -->
|
|
<div class="provider-card">
|
|
<span class="badge">Microsoft</span>
|
|
<h4>Azure AKS</h4>
|
|
<p class="price">~200EUR<span class="price-period">/mes</span></p>
|
|
<ul>
|
|
<li>Control plane gratuito</li>
|
|
<li>Integracion con Azure AD</li>
|
|
<li>Azure Database for PostgreSQL</li>
|
|
<li>Dev Spaces para desarrollo</li>
|
|
<li>Ideal Yes usas Office 365</li>
|
|
<li>Creditos para startups</li>
|
|
</ul>
|
|
<a href="https://azure.microsoft.com/services/kubernetes-service/" class="cta-btn" target="_blank" rel="noopener">Ver AKS</a>
|
|
</div>
|
|
|
|
<!-- DOKS -->
|
|
<div class="provider-card">
|
|
<span class="badge">Economico</span>
|
|
<h4>DigitalOcean DOKS</h4>
|
|
<p class="price">~150EUR<span class="price-period">/mes</span></p>
|
|
<ul>
|
|
<li>Simplicidad de uso</li>
|
|
<li>Control plane gratuito</li>
|
|
<li>Precios predecibles</li>
|
|
<li>Managed PostgreSQL</li>
|
|
<li>UI intuitiva</li>
|
|
<li>Ideal para startups</li>
|
|
</ul>
|
|
<a href="https://www.digitalocean.com/products/kubernetes" class="cta-btn" target="_blank" rel="noopener">Ver DOKS</a>
|
|
</div>
|
|
</div>
|
|
|
|
<h3>Tabla Comparativa Detallada</h3>
|
|
<div style="overflow-x: auto;">
|
|
<table class="comparison-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Feature</th>
|
|
<th>GKE</th>
|
|
<th>EKS</th>
|
|
<th>AKS</th>
|
|
<th>DOKS</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td><strong>Control Plane</strong></td>
|
|
<td>$0.10/hr (Autopilot gratis)</td>
|
|
<td>$0.10/hr</td>
|
|
<td>Gratis</td>
|
|
<td>Gratis</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Nodo Minimo</strong></td>
|
|
<td>~$25/month (e2-small)</td>
|
|
<td>~$30/month (t3.small)</td>
|
|
<td>~$28/month (B2s)</td>
|
|
<td>~$24/month (s-2vcpu-4gb)</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>PostgreSQL Gestionado</strong></td>
|
|
<td class="check">Cloud SQL</td>
|
|
<td class="check">RDS</td>
|
|
<td class="check">Azure Database</td>
|
|
<td class="check">Managed DB</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Serverless Pods</strong></td>
|
|
<td class="check">Autopilot/Cloud Run</td>
|
|
<td class="check">Fargate</td>
|
|
<td class="check">ACI Integration</td>
|
|
<td class="cross">No disponible</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Regiones en Europe</strong></td>
|
|
<td>6+ regiones</td>
|
|
<td>5+ regiones</td>
|
|
<td>8+ regiones</td>
|
|
<td>3 regiones</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Soporte SLA</strong></td>
|
|
<td>99.95%</td>
|
|
<td>99.95%</td>
|
|
<td>99.95%</td>
|
|
<td>99.99%</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Facilidad de Uso</strong></td>
|
|
<td>Alta</td>
|
|
<td>Media</td>
|
|
<td>Alta</td>
|
|
<td>Muy Alta</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Production Configuration -->
|
|
<section id="configuracion-produccion" class="content-section fade-in">
|
|
<h2>Configuracion para Produccion</h2>
|
|
|
|
<p>Llevar Odoo a produccion en Kubernetes requiere atencion a varios aspectos criticos: persistencia de datos, networking, seguridad, y observabilidad. Aqui cubrimos las configuraciones esenciales.</p>
|
|
|
|
<h3>1. Persistencia de Datos</h3>
|
|
<p>Odoo almacena archivos adjuntos en el filestore y requiere acceso a PostgreSQL. En K8s, esto se gestiona con PersistentVolumeClaims.</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># PVC para filestore de Odoo</span>
|
|
<span class="keyword">apiVersion</span>: v1
|
|
<span class="keyword">kind</span>: PersistentVolumeClaim
|
|
<span class="keyword">metadata</span>:
|
|
<span class="keyword">name</span>: Odoo-filestore
|
|
<span class="keyword">namespace</span>: Odoo
|
|
<span class="keyword">spec</span>:
|
|
<span class="keyword">accessModes</span>:
|
|
- ReadWriteMany <span class="comment"># Requerido para multiples replicas</span>
|
|
<span class="keyword">storageClassName</span>: <span class="string">"filestore-nfs"</span> <span class="comment"># O EFS, Filestore, etc.</span>
|
|
<span class="keyword">resources</span>:
|
|
<span class="keyword">requests</span>:
|
|
<span class="keyword">storage</span>: <span class="string">"100Gi"</span></code>
|
|
</div>
|
|
|
|
<h3>2. Base de Datos PostgreSQL</h3>
|
|
<p>Para produccion, recomendamos usar un servicio de PostgreSQL gestionado (Cloud SQL, RDS, Azure Database) en lugar de ejecutar PostgreSQL dentro del cluster.</p>
|
|
|
|
<div class="highlight-box">
|
|
<h4>Recomendacion: PostgreSQL Gestionado</h4>
|
|
<p>Los servicios gestionados ofrecen backups automaticos, alta disponibilidad, actualizaciones de seguridad, y escalado simplificado. El costo adicional (~50-100 EUR/month) se compensa con la reduccion de carga operativa.</p>
|
|
</div>
|
|
|
|
<h3>3. Ingress y SSL/TLS</h3>
|
|
<p>Configura un Ingress Controller (nginx-ingress o traefik) con cert-manager para certificados automaticos.</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># Ingress con SSL automatico via cert-manager</span>
|
|
<span class="keyword">apiVersion</span>: networking.k8s.io/v1
|
|
<span class="keyword">kind</span>: Ingress
|
|
<span class="keyword">metadata</span>:
|
|
<span class="keyword">name</span>: Odoo-ingress
|
|
<span class="keyword">namespace</span>: Odoo
|
|
<span class="keyword">annotations</span>:
|
|
<span class="keyword">kubernetes.io/ingress.class</span>: <span class="string">"nginx"</span>
|
|
<span class="keyword">cert-manager.io/cluster-issuer</span>: <span class="string">"letsencrypt-prod"</span>
|
|
<span class="keyword">nginx.ingress.kubernetes.io/proxy-body-size</span>: <span class="string">"100m"</span>
|
|
<span class="keyword">nginx.ingress.kubernetes.io/proxy-read-timeout</span>: <span class="string">"600"</span>
|
|
<span class="keyword">spec</span>:
|
|
<span class="keyword">tls</span>:
|
|
- <span class="keyword">hosts</span>:
|
|
- <span class="string">"Odoo.tudominio.com"</span>
|
|
<span class="keyword">secretName</span>: Odoo-tls
|
|
<span class="keyword">rules</span>:
|
|
- <span class="keyword">host</span>: <span class="string">"Odoo.tudominio.com"</span>
|
|
<span class="keyword">http</span>:
|
|
<span class="keyword">paths</span>:
|
|
- <span class="keyword">path</span>: /
|
|
<span class="keyword">pathType</span>: Prefix
|
|
<span class="keyword">backend</span>:
|
|
<span class="keyword">service</span>:
|
|
<span class="keyword">name</span>: Odoo
|
|
<span class="keyword">port</span>:
|
|
<span class="keyword">number</span>: <span class="number">80</span></code>
|
|
</div>
|
|
|
|
<h3>4. Configuracion de Workers de Odoo</h3>
|
|
<p>Para multiples replicas, configura Odoo en modo multi-worker con sesiones compartidas:</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># Configuracion de Odoo.conf para K8s</span>
|
|
[options]
|
|
workers = <span class="number">4</span>
|
|
max_cron_threads = <span class="number">1</span>
|
|
limit_memory_hard = <span class="number">2684354560</span>
|
|
limit_memory_soft = <span class="number">2147483648</span>
|
|
limit_time_cpu = <span class="number">600</span>
|
|
limit_time_real = <span class="number">1200</span>
|
|
proxy_mode = True
|
|
db_host = postgresql-service
|
|
db_port = <span class="number">5432</span>
|
|
db_user = Odoo
|
|
db_password = <span class="string">${DB_PASSWORD}</span> <span class="comment"># Desde Secret</span></code>
|
|
</div>
|
|
|
|
<h3>5. Sesiones con Redis</h3>
|
|
<p>Para escalado horizontal, las sesiones deben almacenarse en Redis en lugar del filesystem local:</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="comment"># Instalar Redis con Helm</span>
|
|
helm install redis bitnami/redis \
|
|
--namespace Odoo \
|
|
--set auth.password=<span class="string">"RedisSecure123!"</span> \
|
|
--set replica.replicaCount=<span class="number">2</span>
|
|
|
|
<span class="comment"># Configurar Odoo para usar Redis (requiere modulo)</span>
|
|
<span class="comment"># O usar nginx sticky sessions como alternativa</span></code>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Scalability -->
|
|
<section id="escalabilidad" class="content-section fade-in">
|
|
<h2>Escalabilidad y Alta Disponibilidad</h2>
|
|
|
|
<p>Una de las principales ventajas de Kubernetes es la capacidad de escalar automaticamente segun la demanda. Para Odoo, esto requiere configuracion especifica debido a la naturaleza stateful de la aplicacion.</p>
|
|
|
|
<h3>Horizontal Pod Autoscaler (HPA)</h3>
|
|
<p>Configura el HPA para escalar automaticamente los pods de Odoo basandose en CPU o memoria:</p>
|
|
|
|
<div class="code-block">
|
|
<code><span class="keyword">apiVersion</span>: autoscaling/v2
|
|
<span class="keyword">kind</span>: HorizontalPodAutoscaler
|
|
<span class="keyword">metadata</span>:
|
|
<span class="keyword">name</span>: Odoo-hpa
|
|
<span class="keyword">namespace</span>: Odoo
|
|
<span class="keyword">spec</span>:
|
|
<span class="keyword">scaleTargetRef</span>:
|
|
<span class="keyword">apiVersion</span>: apps/v1
|
|
<span class="keyword">kind</span>: Deployment
|
|
<span class="keyword">name</span>: Odoo
|
|
<span class="keyword">minReplicas</span>: <span class="number">2</span>
|
|
<span class="keyword">maxReplicas</span>: <span class="number">10</span>
|
|
<span class="keyword">metrics</span>:
|
|
- <span class="keyword">type</span>: Resource
|
|
<span class="keyword">resource</span>:
|
|
<span class="keyword">name</span>: cpu
|
|
<span class="keyword">target</span>:
|
|
<span class="keyword">type</span>: Utilization
|
|
<span class="keyword">averageUtilization</span>: <span class="number">70</span>
|
|
- <span class="keyword">type</span>: Resource
|
|
<span class="keyword">resource</span>:
|
|
<span class="keyword">name</span>: memory
|
|
<span class="keyword">target</span>:
|
|
<span class="keyword">type</span>: Utilization
|
|
<span class="keyword">averageUtilization</span>: <span class="number">80</span>
|
|
<span class="keyword">behavior</span>:
|
|
<span class="keyword">scaleDown</span>:
|
|
<span class="keyword">stabilizationWindowSeconds</span>: <span class="number">300</span> <span class="comment"># Evita scale-down agresivo</span>
|
|
<span class="keyword">scaleUp</span>:
|
|
<span class="keyword">stabilizationWindowSeconds</span>: <span class="number">60</span></code>
|
|
</div>
|
|
|
|
<h3>Alta Disponibilidad de PostgreSQL</h3>
|
|
<p>Para alta disponibilidad de la base de datos, considera:</p>
|
|
<ul>
|
|
<li><strong>Servicios gestionados:</strong> Cloud SQL HA, RDS Multi-AZ, Azure Database HA</li>
|
|
<li><strong>Operadores K8s:</strong> CloudNativePG, Zalando Postgres Operator, CrunchyData PGO</li>
|
|
<li><strong>Patroni:</strong> Solucion open source para PostgreSQL HA</li>
|
|
</ul>
|
|
|
|
<h3>Arquitectura Multi-Region (Disaster Recovery)</h3>
|
|
<p>Para requisitos criticos de disponibilidad, considera una arquitectura multi-region con replicacion de base de datos y failover automatico:</p>
|
|
|
|
<div class="warning-box">
|
|
<h4>Complejidad vs Beneficio</h4>
|
|
<p>Una arquitectura multi-region anade complejidad significativa y Costs (2-3x). Solo es necesaria Yes tu SLA requiere recuperacion en minutos ante desastres regionales. Para la mayoria de casos, alta disponibilidad dentro de una region (multi-zona) es suficiente.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Cost Analysis Chart -->
|
|
<section class="chart-section fade-in">
|
|
<h3 id="Costs">Analysis de Costs: K8s vs VPS vs Managed Hosting</h3>
|
|
<p>Este grafico compara los Costs mensuales de diferentes opciones de Hosting para Odoo segun el numero de usuarios.</p>
|
|
<div class="chart-container" id="cost-chart"></div>
|
|
</section>
|
|
|
|
<!-- Cost Section -->
|
|
<section id="Costs-detalle" class="content-section fade-in">
|
|
<h2>Analysis Detallado de Costs</h2>
|
|
|
|
<p>Kubernetes tiene un costo base significativo que debe justificarse con los beneficios obtenidos. Aqui desglosamos los Costs tipicos para diferentes escalas de uso.</p>
|
|
|
|
<h3>Costo Minimo Viable (Desarrollo/Test)</h3>
|
|
<table class="comparison-table">
|
|
<tr><th>Componente</th><th>GKE</th><th>EKS</th><th>AKS</th><th>DOKS</th></tr>
|
|
<tr><td>Control Plane</td><td>$0 (Autopilot)</td><td>$73/month</td><td>$0</td><td>$0</td></tr>
|
|
<tr><td>Nodos (2x small)</td><td>~$50/month</td><td>~$60/month</td><td>~$56/month</td><td>~$48/month</td></tr>
|
|
<tr><td>Load Balancer</td><td>~$20/month</td><td>~$20/month</td><td>~$20/month</td><td>~$12/month</td></tr>
|
|
<tr><td>Almacenamiento 50GB</td><td>~$10/month</td><td>~$12/month</td><td>~$10/month</td><td>~$10/month</td></tr>
|
|
<tr><th>Full Minimo</th><th>~$80/month</th><th>~$165/month</th><th>~$86/month</th><th>~$70/month</th></tr>
|
|
</table>
|
|
|
|
<h3>Costo Produccion (10-50 usuarios)</h3>
|
|
<table class="comparison-table">
|
|
<tr><th>Componente</th><th>Cantidad</th><th>Costo Mensual</th></tr>
|
|
<tr><td>Nodos K8s (3x 4vCPU, 8GB)</td><td>3</td><td>~$180-250</td></tr>
|
|
<tr><td>PostgreSQL Gestionado</td><td>1 (db-f1-micro a db-n1-standard-1)</td><td>~$50-100</td></tr>
|
|
<tr><td>Load Balancer</td><td>1</td><td>~$20</td></tr>
|
|
<tr><td>Almacenamiento (100GB SSD)</td><td>100GB</td><td>~$20</td></tr>
|
|
<tr><td>Networking (egress)</td><td>Variable</td><td>~$20-50</td></tr>
|
|
<tr><th>Full Produccion</th><th colspan="2">~$290-440/month (250-380 EUR)</th></tr>
|
|
</table>
|
|
|
|
<h3>Optimizacion de Costs</h3>
|
|
<ul>
|
|
<li><strong>Nodos Spot/Preemptibles:</strong> Ahorra 60-90% en nodos No criticos (workers de Odoo son buenos candidatos)</li>
|
|
<li><strong>Committed Use Discounts:</strong> 1-3 anos de compromiso = 30-60% descuento</li>
|
|
<li><strong>Right-sizing:</strong> Monitorea y ajusta recursos basandote en uso real</li>
|
|
<li><strong>Cluster Autoscaler:</strong> Reduce nodos en horarios de baja demanda</li>
|
|
<li><strong>GKE Autopilot:</strong> Paga solo por pods, No por nodos subutilizados</li>
|
|
</ul>
|
|
|
|
<div class="highlight-box">
|
|
<h4>Comparacion: K8s vs VPS Gestionado</h4>
|
|
<p>Para 20 usuarios, un <strong>VPS gestionado</strong> cuesta ~$50-80/month (ej: Odoo4projects a 29EUR). Kubernetes para el mismo caso cuesta ~$200-300/month. K8s se justifica cuando necesitas escalabilidad, alta disponibilidad, o tienes >50 usuarios con picos de demanda.</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- CTA Banner -->
|
|
<section class="cta-banner fade-in">
|
|
<h3>Necesitas Ayuda con tu Deployment de Odoo en Kubernetes?</h3>
|
|
<p>Nuestro equipo de expertos puede disenar e implementar tu arquitectura K8s para Odoo. Desde consultorias hasta implementaciones llave en mano.</p>
|
|
<a href="/contacto/" class="cta-btn">Solicitar Consultoria</a>
|
|
</section>
|
|
|
|
<!-- FAQ Section -->
|
|
<section id="faq" class="faq-section fade-in">
|
|
<h2>Preguntas Frecuentes sobre Odoo en Kubernetes</h2>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Cuando tiene sentido usar Kubernetes para Odoo?</div>
|
|
<div class="faq-answer">Kubernetes es recomendable para Odoo cuando necesitas alta disponibilidad (99.99%+), escalabilidad automatica para picos de demanda, multiples entornos (dev/staging/prod), integracion con CI/CD avanzado, o cuando manejas mas de 100 usuarios concurrentes. Para PYMEs con menos de 50 usuarios, un VPS gestionado suele ser mas cost-effective.</div>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Cual es el mejor proveedor cloud para Odoo en Kubernetes?</div>
|
|
<div class="faq-answer">Para empresas en Europe, Google GKE ofrece la Best value precio/rendimiento con Autopilot. AWS EKS es ideal Yes ya usas servicios AWS. Azure AKS es la opcion preferida para empresas con infraestructura Microsoft. DigitalOcean DOKS es excellent para presupuestos ajustados con simplicidad.</div>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Existe un Helm chart oficial para Odoo?</div>
|
|
<div class="faq-answer">No existe un Helm chart oficial de Odoo S.A. Sin embargo, Bitnami mantiene el chart mas popular y estable (bitnami/Odoo), Updated regularmente con cada version de Odoo. Tambien existen charts comunitarios en ArtifactHub con configuraciones especializadas.</div>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Cuanto cuesta ejecutar Odoo en Kubernetes?</div>
|
|
<div class="faq-answer">El costo minimo viable es aproximadamente 150-200 EUR/month para un cluster basico (3 nodos, 2vCPU, 4GB cada uno) mas almacenamiento y networking. Para produccion con alta disponibilidad, el costo tipico es 400-800 EUR/month. Los Costs pueden reducirse significativamente con nodos spot/preemptibles.</div>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Como manejo los volumenes persistentes de Odoo en K8s?</div>
|
|
<div class="faq-answer">Odoo requiere almacenamiento persistente para el filestore y la base de datos PostgreSQL. Usa PersistentVolumeClaims con StorageClass que soporte ReadWriteMany (como NFS, EFS, o Filestore) para el filestore Yes necesitas multiples replicas. Para PostgreSQL, usa operadores como CloudNativePG o servicios gestionados (Cloud SQL, RDS).</div>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Puedo escalar Odoo horizontalmente en Kubernetes?</div>
|
|
<div class="faq-answer">Yes, pero con consideraciones. Odoo puede escalar horizontalmente los workers de Odoo Yes configuras correctamente: sesiones en Redis/Memcached, filestore compartido (NFS/S3), y PostgreSQL con suficiente capacidad. El escalado horizontal tipico es 2-10 pods de Odoo apuntando a una base de datos PostgreSQL con alta disponibilidad.</div>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Que estrategia de deployment es mejor para Odoo en K8s?</div>
|
|
<div class="faq-answer">Para actualizaciones de Odoo, recomendamos Blue-Green deployment por su capacidad de rollback instantaneo. Rolling updates funcionan para cambios menores de codigo. Canary deployments son utiles para probar nuevos modulos con un porcentaje de trafico. Siempre haz backup de la base de datos antes de cualquier actualizacion.</div>
|
|
</div>
|
|
|
|
<div class="faq-item">
|
|
<div class="faq-question">Como configuro HTTPS/SSL para Odoo en Kubernetes?</div>
|
|
<div class="faq-answer">La forma mas comun es usar un Ingress Controller (nginx-ingress o traefik) combinado con cert-manager para certificados automaticos de Lets Encrypt. Configura annotations en el Ingress para SSL redirect y HSTS. Para produccion enterprise, considera certificados wildcard o de organizacion.</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Related Articles -->
|
|
<section class="content-section fade-in">
|
|
<h2>Related Articles</h2>
|
|
|
|
<div class="related-articles">
|
|
<a href="/Odoo-Hosting/mejor-Hosting-Odoo-2026/" class="related-card">
|
|
<div class="related-card-image">Hosting</div>
|
|
<div class="related-card-content">
|
|
<h4>Mejor Odoo Hosting 2026: Comparativa</h4>
|
|
<p>Analysis de 15+ Providers de Hosting para Odoo</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="/Odoo-Hosting/vps-Hosting-Odoo-servidor-virtual-2025/" class="related-card">
|
|
<div class="related-card-image">VPS</div>
|
|
<div class="related-card-content">
|
|
<h4>VPS para Odoo: Complete guide</h4>
|
|
<p>Configura tu propio servidor virtual paso a paso</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="/Odoo-Hosting/Odoo-docker-contenedores-2025/" class="related-card">
|
|
<div class="related-card-image">Docker</div>
|
|
<div class="related-card-content">
|
|
<h4>Odoo con Docker: Guia Definitiva</h4>
|
|
<p>Contenedores Docker para desarrollo y produccion</p>
|
|
</div>
|
|
</a>
|
|
|
|
<a href="/Odoo-Hosting/requisitos-Hosting-Odoo-servidor-2025/" class="related-card">
|
|
<div class="related-card-image">Req</div>
|
|
<div class="related-card-content">
|
|
<h4>Requisitos Tecnicos para Odoo</h4>
|
|
<p>Especificaciones de servidor recomendadas</p>
|
|
</div>
|
|
</a>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<!-- EEAT Author Section -->
|
|
<div id="eeat-section"></div>
|
|
|
|
<div id="footer"><footer class="site-footer"><div class="container"><div class="footer-cols"><div><strong>Odoo Expertos</strong></div><nav class="footer-nav"><a href="/en/">Home</a><a href="/en/odoo/">Odoo</a><a href="/en/odoo-ia/">Odoo AI</a><a href="/en/odoo-hosting/">Odoo Hosting</a><a href="/en/mapa-del-sitio/">Sitemap</a></nav></div><div class="footer-bottom"><small>© 2026 Odoo Expertos</small></div></div></footer></div><nav class="footer-nav"><a href="/en/">Home</a><a href="/en/odoo/">Odoo</a><a href="/en/odoo-ia/">Odoo AI</a><a href="/en/odoo-hosting/">Odoo Hosting</a><a href="/en/mapa-del-sitio/">Sitemap</a></nav></div><div class="footer-bottom"><small>© 2026 Odoo Expertos</small></div></div></footer></div>
|
|
|
|
<!-- Scripts -->
|
|
<script src="/js/language-router.js"></script>
|
|
<script src="/components/header.min.js"></script>
|
|
<script src="/components/footer.min.js"></script>
|
|
<script src="/components/eeat.min.js"></script>
|
|
<script src="/js/cookie-banner.min.js"></script>
|
|
|
|
<!-- D3.js -->
|
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
|
|
|
<script>
|
|
// Initialize components
|
|
// Scroll animation observer
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('visible');
|
|
}
|
|
});
|
|
}, { threshold: 0.1 });
|
|
|
|
document.querySelectorAll('.fade-in').forEach(el => observer.observe(el));
|
|
|
|
// FAQ Toggle
|
|
document.querySelectorAll('.faq-question').forEach(question => {
|
|
question.addEventListener('click', () => {
|
|
const item = question.parentElement;
|
|
item.classList.toggle('open');
|
|
});
|
|
});
|
|
|
|
// Tooltip
|
|
const tooltip = d3.select('body').append('div')
|
|
.attr('class', 'd3-tooltip')
|
|
.style('opacity', 0);
|
|
|
|
// Color scales
|
|
const k8sColors = {
|
|
kubernetes: '#326ce5',
|
|
odoo: '#4CAF50',
|
|
database: '#ff9800',
|
|
storage: '#9c27b0',
|
|
external: '#00bcd4'
|
|
};
|
|
|
|
// Architecture Diagram
|
|
function renderArchitectureDiagram() {
|
|
const container = d3.select('#architecture-diagram');
|
|
if (container.empty()) return;
|
|
|
|
const width = container.node().getBoundingClientRect().width;
|
|
const height = 450;
|
|
|
|
container.html('');
|
|
|
|
const svg = container.append('svg')
|
|
.attr('width', width)
|
|
.attr('height', height);
|
|
|
|
// Define architecture components
|
|
const components = [
|
|
// External Layer
|
|
{ id: 'users', label: 'Usuarios', x: width/2, y: 30, color: k8sColors.external, type: 'external', description: 'Clientes web y movil accediendo a Odoo' },
|
|
{ id: 'dns', label: 'DNS/CDN', x: width/2, y: 80, color: k8sColors.external, type: 'external', description: 'Cloudflare, Route53 o similar para DNS y cache' },
|
|
|
|
// Kubernetes Cluster
|
|
{ id: 'ingress', label: 'Ingress Controller', x: width/2, y: 140, color: k8sColors.kubernetes, type: 'k8s', description: 'nginx-ingress o traefik con cert-manager para SSL' },
|
|
{ id: 'service', label: 'Odoo Service (ClusterIP)', x: width/2, y: 200, color: k8sColors.kubernetes, type: 'k8s', description: 'Load balancing interno entre pods de Odoo' },
|
|
|
|
// Odoo Pods
|
|
{ id: 'odoo1', label: 'Odoo Pod 1', x: width/4, y: 270, color: k8sColors.odoo, type: 'odoo', description: 'Replica 1 de Odoo con 4 workers' },
|
|
{ id: 'odoo2', label: 'Odoo Pod 2', x: width/2, y: 270, color: k8sColors.odoo, type: 'odoo', description: 'Replica 2 de Odoo con 4 workers' },
|
|
{ id: 'odoo3', label: 'Odoo Pod 3', x: 3*width/4, y: 270, color: k8sColors.odoo, type: 'odoo', description: 'Replica 3 de Odoo con 4 workers (escalable a N)' },
|
|
|
|
// Data Layer
|
|
{ id: 'redis', label: 'Redis (Sessions)', x: width/4, y: 350, color: k8sColors.storage, type: 'storage', description: 'Cache de sesiones compartido entre pods' },
|
|
{ id: 'postgres', label: 'PostgreSQL HA', x: width/2, y: 350, color: k8sColors.database, type: 'db', description: 'Base de datos con alta disponibilidad (Cloud SQL, RDS, o operador)' },
|
|
{ id: 'filestore', label: 'Filestore (NFS/S3)', x: 3*width/4, y: 350, color: k8sColors.storage, type: 'storage', description: 'Almacenamiento compartido para archivos adjuntos' },
|
|
|
|
// HPA
|
|
{ id: 'hpa', label: 'HPA (Autoscaler)', x: width - 80, y: 200, color: k8sColors.kubernetes, type: 'k8s', description: 'Horizontal Pod Autoscaler para escalar segun CPU/memoria' }
|
|
];
|
|
|
|
// Define connections
|
|
const connections = [
|
|
{ source: 'users', target: 'dns' },
|
|
{ source: 'dns', target: 'ingress' },
|
|
{ source: 'ingress', target: 'service' },
|
|
{ source: 'service', target: 'odoo1' },
|
|
{ source: 'service', target: 'odoo2' },
|
|
{ source: 'service', target: 'odoo3' },
|
|
{ source: 'odoo1', target: 'redis' },
|
|
{ source: 'odoo2', target: 'redis' },
|
|
{ source: 'odoo3', target: 'redis' },
|
|
{ source: 'odoo1', target: 'postgres' },
|
|
{ source: 'odoo2', target: 'postgres' },
|
|
{ source: 'odoo3', target: 'postgres' },
|
|
{ source: 'odoo1', target: 'filestore' },
|
|
{ source: 'odoo2', target: 'filestore' },
|
|
{ source: 'odoo3', target: 'filestore' },
|
|
{ source: 'hpa', target: 'odoo2', dashed: true }
|
|
];
|
|
|
|
// Draw cluster boundary
|
|
svg.append('rect')
|
|
.attr('x', 40)
|
|
.attr('y', 115)
|
|
.attr('width', width - 80)
|
|
.attr('height', 280)
|
|
.attr('fill', 'none')
|
|
.attr('stroke', k8sColors.kubernetes)
|
|
.attr('stroke-width', 2)
|
|
.attr('stroke-dasharray', '8,4')
|
|
.attr('rx', 12);
|
|
|
|
svg.append('text')
|
|
.attr('x', 55)
|
|
.attr('y', 135)
|
|
.attr('fill', k8sColors.kubernetes)
|
|
.attr('font-size', '12px')
|
|
.attr('font-weight', '600')
|
|
.text('Kubernetes Cluster');
|
|
|
|
// Create component map for connections
|
|
const componentMap = {};
|
|
components.forEach(c => componentMap[c.id] = c);
|
|
|
|
// Draw connections
|
|
connections.forEach((conn, i) => {
|
|
const source = componentMap[conn.source];
|
|
const target = componentMap[conn.target];
|
|
|
|
const line = svg.append('line')
|
|
.attr('x1', source.x)
|
|
.attr('y1', source.y + 15)
|
|
.attr('x2', target.x)
|
|
.attr('y2', target.y - 15)
|
|
.attr('stroke', '#555')
|
|
.attr('stroke-width', conn.dashed ? 1.5 : 1)
|
|
.attr('stroke-dasharray', conn.dashed ? '5,3' : 'none')
|
|
.attr('opacity', 0);
|
|
|
|
line.transition()
|
|
.duration(500)
|
|
.delay(i * 30)
|
|
.attr('opacity', 0.6);
|
|
});
|
|
|
|
// Draw components
|
|
components.forEach((comp, i) => {
|
|
const g = svg.append('g')
|
|
.attr('transform', `translate(${comp.x}, ${comp.y})`)
|
|
.style('cursor', 'pointer')
|
|
.style('opacity', 0);
|
|
|
|
// Background rect
|
|
const rectWidth = comp.type === 'k8s' || comp.type === 'db' ? 140 : 100;
|
|
g.append('rect')
|
|
.attr('x', -rectWidth/2)
|
|
.attr('y', -15)
|
|
.attr('width', rectWidth)
|
|
.attr('height', 30)
|
|
.attr('fill', comp.color)
|
|
.attr('fill-opacity', 0.2)
|
|
.attr('stroke', comp.color)
|
|
.attr('stroke-width', 2)
|
|
.attr('rx', 6);
|
|
|
|
// Label
|
|
g.append('text')
|
|
.attr('text-anchor', 'middle')
|
|
.attr('dominant-baseline', 'middle')
|
|
.attr('fill', '#e0e0e0')
|
|
.attr('font-size', '11px')
|
|
.attr('font-weight', '500')
|
|
.text(comp.label);
|
|
|
|
// Hover events
|
|
g.on('mouseover', function(event) {
|
|
d3.select(this).select('rect')
|
|
.attr('fill-opacity', 0.4)
|
|
.attr('stroke-width', 3);
|
|
|
|
tooltip.transition().duration(200).style('opacity', 1);
|
|
tooltip.html(`<strong>${comp.label}</strong><br>${comp.description}`)
|
|
.style('left', (event.pageX + 15) + 'px')
|
|
.style('top', (event.pageY - 28) + 'px');
|
|
})
|
|
.on('mouseout', function() {
|
|
d3.select(this).select('rect')
|
|
.attr('fill-opacity', 0.2)
|
|
.attr('stroke-width', 2);
|
|
tooltip.transition().duration(200).style('opacity', 0);
|
|
});
|
|
|
|
// Animate in
|
|
g.transition()
|
|
.duration(600)
|
|
.delay(i * 80)
|
|
.style('opacity', 1);
|
|
});
|
|
}
|
|
|
|
// Radar Chart for Cloud Providers
|
|
function renderRadarChart() {
|
|
const container = d3.select('#radar-chart');
|
|
if (container.empty()) return;
|
|
|
|
const width = container.node().getBoundingClientRect().width;
|
|
const height = 400;
|
|
const margin = 100;
|
|
const radius = Math.min(width, height) / 2 - margin;
|
|
|
|
container.html('');
|
|
|
|
const svg = container.append('svg')
|
|
.attr('width', width)
|
|
.attr('height', height)
|
|
.append('g')
|
|
.attr('transform', `translate(${width/2},${height/2})`);
|
|
|
|
const categories = ['Precio', 'Rendimiento', 'Facilidad', 'Soporte', 'Escalabilidad', 'Integraciones'];
|
|
const angleSlice = Math.PI * 2 / categories.length;
|
|
|
|
const providers = [
|
|
{ name: 'GKE', values: [85, 95, 90, 85, 95, 90], color: '#4285f4' },
|
|
{ name: 'EKS', values: [70, 90, 70, 90, 95, 95], color: '#ff9900' },
|
|
{ name: 'AKS', values: [80, 85, 85, 80, 90, 85], color: '#0078d4' },
|
|
{ name: 'DOKS', values: [95, 75, 95, 70, 70, 60], color: '#0080ff' }
|
|
];
|
|
|
|
const rScale = d3.scaleLinear()
|
|
.domain([0, 100])
|
|
.range([0, radius]);
|
|
|
|
// Grid circles
|
|
const levels = 5;
|
|
for (let i = 1; i <= levels; i++) {
|
|
svg.append('circle')
|
|
.attr('r', radius * i / levels)
|
|
.attr('fill', 'none')
|
|
.attr('stroke', '#2a3f5f')
|
|
.attr('stroke-dasharray', '4,4');
|
|
|
|
svg.append('text')
|
|
.attr('x', 5)
|
|
.attr('y', -radius * i / levels)
|
|
.attr('fill', '#666')
|
|
.attr('font-size', '10px')
|
|
.text(i * 20);
|
|
}
|
|
|
|
// Axis lines and labels
|
|
categories.forEach((cat, i) => {
|
|
const angle = angleSlice * i - Math.PI / 2;
|
|
svg.append('line')
|
|
.attr('x1', 0)
|
|
.attr('y1', 0)
|
|
.attr('x2', radius * Math.cos(angle))
|
|
.attr('y2', radius * Math.sin(angle))
|
|
.attr('stroke', '#3a5070');
|
|
|
|
const labelRadius = radius + 25;
|
|
svg.append('text')
|
|
.attr('x', labelRadius * Math.cos(angle))
|
|
.attr('y', labelRadius * Math.sin(angle))
|
|
.attr('text-anchor', 'middle')
|
|
.attr('dominant-baseline', 'middle')
|
|
.attr('fill', '#b0b0b0')
|
|
.attr('font-size', '12px')
|
|
.text(cat);
|
|
});
|
|
|
|
// Radar areas
|
|
const radarLine = d3.lineRadial()
|
|
.radius(d => rScale(d))
|
|
.angle((d, i) => angleSlice * i)
|
|
.curve(d3.curveLinearClosed);
|
|
|
|
providers.forEach((provider, idx) => {
|
|
const path = svg.append('path')
|
|
.datum(provider.values)
|
|
.attr('d', radarLine)
|
|
.attr('fill', provider.color)
|
|
.attr('fill-opacity', 0)
|
|
.attr('stroke', provider.color)
|
|
.attr('stroke-width', 2)
|
|
.attr('stroke-opacity', 0);
|
|
|
|
path.transition()
|
|
.duration(800)
|
|
.delay(idx * 200)
|
|
.attr('fill-opacity', 0.15)
|
|
.attr('stroke-opacity', 1);
|
|
|
|
// Data points
|
|
provider.values.forEach((val, i) => {
|
|
const angle = angleSlice * i - Math.PI / 2;
|
|
svg.append('circle')
|
|
.attr('cx', rScale(val) * Math.cos(angle))
|
|
.attr('cy', rScale(val) * Math.sin(angle))
|
|
.attr('r', 0)
|
|
.attr('fill', provider.color)
|
|
.transition()
|
|
.duration(500)
|
|
.delay(idx * 200 + i * 50)
|
|
.attr('r', 4);
|
|
});
|
|
});
|
|
|
|
// Legend
|
|
const legendX = width > 600 ? radius + 40 : -radius;
|
|
const legendY = width > 600 ? -radius + 20 : radius + 30;
|
|
|
|
providers.forEach((provider, idx) => {
|
|
const g = svg.append('g')
|
|
.attr('transform', `translate(${legendX}, ${legendY + idx * 25})`);
|
|
|
|
g.append('rect')
|
|
.attr('width', 15)
|
|
.attr('height', 15)
|
|
.attr('fill', provider.color)
|
|
.attr('rx', 3);
|
|
|
|
g.append('text')
|
|
.attr('x', 22)
|
|
.attr('y', 12)
|
|
.attr('fill', '#b0b0b0')
|
|
.attr('font-size', '12px')
|
|
.text(provider.name);
|
|
});
|
|
}
|
|
|
|
// Cost Comparison Chart
|
|
function renderCostChart() {
|
|
const container = d3.select('#cost-chart');
|
|
if (container.empty()) return;
|
|
|
|
const width = container.node().getBoundingClientRect().width;
|
|
const height = 400;
|
|
const margin = { top: 40, right: 120, bottom: 60, left: 70 };
|
|
|
|
container.html('');
|
|
|
|
const svg = container.append('svg')
|
|
.attr('width', width)
|
|
.attr('height', height);
|
|
|
|
// Data: costs at different user counts
|
|
const users = [10, 25, 50, 100, 200, 500];
|
|
const solutions = [
|
|
{ name: 'VPS Gestionado', color: '#4CAF50', values: [50, 60, 80, 150, 300, 600] },
|
|
{ name: 'Kubernetes (DOKS)', color: '#0080ff', values: [150, 160, 200, 280, 400, 700] },
|
|
{ name: 'Kubernetes (GKE)', color: '#4285f4', values: [180, 200, 250, 350, 500, 900] },
|
|
{ name: 'Kubernetes (EKS)', color: '#ff9900', values: [220, 250, 320, 450, 650, 1100] }
|
|
];
|
|
|
|
const xScale = d3.scaleLinear()
|
|
.domain([0, 500])
|
|
.range([margin.left, width - margin.right]);
|
|
|
|
const yScale = d3.scaleLinear()
|
|
.domain([0, 1200])
|
|
.range([height - margin.bottom, margin.top]);
|
|
|
|
// Grid lines
|
|
svg.append('g')
|
|
.attr('class', 'grid')
|
|
.attr('transform', `translate(0,${height - margin.bottom})`)
|
|
.call(d3.axisBottom(xScale).tickSize(-(height - margin.top - margin.bottom)).tickFormat(''))
|
|
.selectAll('line').attr('stroke', '#2a3f5f');
|
|
|
|
svg.append('g')
|
|
.attr('class', 'grid')
|
|
.attr('transform', `translate(${margin.left},0)`)
|
|
.call(d3.axisLeft(yScale).tickSize(-(width - margin.left - margin.right)).tickFormat(''))
|
|
.selectAll('line').attr('stroke', '#2a3f5f');
|
|
|
|
// Axes
|
|
svg.append('g')
|
|
.attr('transform', `translate(0,${height - margin.bottom})`)
|
|
.call(d3.axisBottom(xScale).tickFormat(d => d))
|
|
.attr('color', '#888');
|
|
|
|
svg.append('g')
|
|
.attr('transform', `translate(${margin.left},0)`)
|
|
.call(d3.axisLeft(yScale).tickFormat(d => `${d} EUR`))
|
|
.attr('color', '#888');
|
|
|
|
// Axis labels
|
|
svg.append('text')
|
|
.attr('x', width / 2)
|
|
.attr('y', height - 10)
|
|
.attr('text-anchor', 'middle')
|
|
.attr('fill', '#888')
|
|
.attr('font-size', '14px')
|
|
.text('Numero de Usuarios');
|
|
|
|
svg.append('text')
|
|
.attr('transform', 'rotate(-90)')
|
|
.attr('x', -height / 2)
|
|
.attr('y', 20)
|
|
.attr('text-anchor', 'middle')
|
|
.attr('fill', '#888')
|
|
.attr('font-size', '14px')
|
|
.text('Costo Mensual (EUR)');
|
|
|
|
// Line generator
|
|
const line = d3.line()
|
|
.x((d, i) => xScale(users[i]))
|
|
.y(d => yScale(d))
|
|
.curve(d3.curveMonotoneX);
|
|
|
|
// Draw lines
|
|
solutions.forEach((sol, idx) => {
|
|
const path = svg.append('path')
|
|
.datum(sol.values)
|
|
.attr('fill', 'none')
|
|
.attr('stroke', sol.color)
|
|
.attr('stroke-width', 3)
|
|
.attr('d', line);
|
|
|
|
const totalLength = path.node().getTotalLength();
|
|
path.attr('stroke-dasharray', totalLength + ' ' + totalLength)
|
|
.attr('stroke-dashoffset', totalLength)
|
|
.transition()
|
|
.duration(1500)
|
|
.delay(idx * 200)
|
|
.ease(d3.easeLinear)
|
|
.attr('stroke-dashoffset', 0);
|
|
|
|
// Data points
|
|
sol.values.forEach((val, i) => {
|
|
svg.append('circle')
|
|
.attr('cx', xScale(users[i]))
|
|
.attr('cy', yScale(val))
|
|
.attr('r', 0)
|
|
.attr('fill', sol.color)
|
|
.attr('stroke', '#fff')
|
|
.attr('stroke-width', 2)
|
|
.style('cursor', 'pointer')
|
|
.on('mouseover', function(event) {
|
|
d3.select(this).attr('r', 8);
|
|
tooltip.transition().duration(200).style('opacity', 1);
|
|
tooltip.html(`<strong>${sol.name}</strong><br>${users[i]} usuarios: ${val} EUR/mes`)
|
|
.style('left', (event.pageX + 15) + 'px')
|
|
.style('top', (event.pageY - 28) + 'px');
|
|
})
|
|
.on('mouseout', function() {
|
|
d3.select(this).attr('r', 5);
|
|
tooltip.transition().duration(200).style('opacity', 0);
|
|
})
|
|
.transition()
|
|
.duration(400)
|
|
.delay(idx * 200 + i * 100 + 500)
|
|
.attr('r', 5);
|
|
});
|
|
});
|
|
|
|
// Legend
|
|
const legend = svg.append('g')
|
|
.attr('transform', `translate(${width - margin.right + 20}, ${margin.top})`);
|
|
|
|
solutions.forEach((sol, idx) => {
|
|
const g = legend.append('g')
|
|
.attr('transform', `translate(0, ${idx * 25})`);
|
|
|
|
g.append('line')
|
|
.attr('x1', 0)
|
|
.attr('x2', 20)
|
|
.attr('y1', 8)
|
|
.attr('y2', 8)
|
|
.attr('stroke', sol.color)
|
|
.attr('stroke-width', 3);
|
|
|
|
g.append('text')
|
|
.attr('x', 28)
|
|
.attr('y', 12)
|
|
.attr('fill', '#b0b0b0')
|
|
.attr('font-size', '11px')
|
|
.text(sol.name);
|
|
});
|
|
|
|
// Breakeven annotation
|
|
svg.append('line')
|
|
.attr('x1', xScale(75))
|
|
.attr('x2', xScale(75))
|
|
.attr('y1', margin.top)
|
|
.attr('y2', height - margin.bottom)
|
|
.attr('stroke', '#ff5722')
|
|
.attr('stroke-width', 1.5)
|
|
.attr('stroke-dasharray', '6,4')
|
|
.attr('opacity', 0.7);
|
|
|
|
svg.append('text')
|
|
.attr('x', xScale(75) + 5)
|
|
.attr('y', margin.top + 20)
|
|
.attr('fill', '#ff5722')
|
|
.attr('font-size', '11px')
|
|
.text('Punto de equilibrio K8s')
|
|
.attr('transform', `rotate(90, ${xScale(75) + 5}, ${margin.top + 20})`);
|
|
}
|
|
|
|
// Initialize charts when visible
|
|
const chartObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const id = entry.target.id;
|
|
setTimeout(() => {
|
|
if (id === 'architecture-diagram') renderArchitectureDiagram();
|
|
if (id === 'radar-chart') renderRadarChart();
|
|
if (id === 'cost-chart') renderCostChart();
|
|
}, 100);
|
|
chartObserver.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, { threshold: 0.15 });
|
|
|
|
document.querySelectorAll('.chart-container').forEach(el => chartObserver.observe(el));
|
|
|
|
// Resize handler
|
|
let resizeTimeout;
|
|
window.addEventListener('resize', () => {
|
|
clearTimeout(resizeTimeout);
|
|
resizeTimeout = setTimeout(() => {
|
|
renderArchitectureDiagram();
|
|
renderRadarChart();
|
|
renderCostChart();
|
|
}, 300);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|