Compartir comentarios
Las respuestas se generan en base a la documentación.

Despliega tu aplicación Node.js

Requisitos previos

Descripción general

En esta sección, aprenderás cómo desplegar tu aplicación Node.js contenerizada en Kubernetes usando Docker Desktop. Este despliegue utiliza configuraciones listas para producción que incluyen endurecimiento de seguridad, autoescalado, almacenamiento persistente y características de alta disponibilidad.

Desplegarás una pila (stack) completa que incluye:

  • Aplicación de tareas (Todo) en Node.js con 3 réplicas.
  • Base de datos PostgreSQL con almacenamiento persistente.
  • Autoescalado basado en el uso de CPU y memoria.
  • Configuración de Ingress para el acceso externo.
  • Ajustes de seguridad.

Crea un archivo de despliegue de Kubernetes

Crea un nuevo archivo llamado nodejs-sample-kubernetes.yaml en la raíz de tu proyecto:

# ========================================
# Aplicación de tareas (Todo) en Node.js - Despliegue de Kubernetes
# ========================================

apiVersion: v1
kind: Namespace
metadata:
  name: todoapp
  labels:
    app: todoapp

---
# ========================================
# ConfigMap para la configuración de la aplicación
# ========================================
apiVersion: v1
kind: ConfigMap
metadata:
  name: todoapp-config
  namespace: todoapp
data:
  NODE_ENV: 'production'
  ALLOWED_ORIGINS: 'https://tudominio.com'
  POSTGRES_HOST: 'todoapp-postgres'
  POSTGRES_PORT: '5432'
  POSTGRES_DB: 'todoapp'
  POSTGRES_USER: 'todoapp'

---
# ========================================
# Secret para las credenciales de la base de datos
# ========================================
apiVersion: v1
kind: Secret
metadata:
  name: todoapp-secrets
  namespace: todoapp
type: Opaque
data:
  postgres-password: dG9kb2FwcF9wYXNzd29yZA== # "todoapp_password" codificado en base64

---
# ========================================
# PersistentVolumeClaim de PostgreSQL
# ========================================
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: todoapp
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: standard

---
# ========================================
# Despliegue de PostgreSQL (Deployment)
# ========================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todoapp-postgres
  namespace: todoapp
  labels:
    app: todoapp-postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: todoapp-postgres
  template:
    metadata:
      labels:
        app: todoapp-postgres
    spec:
      containers:
        - name: postgres
          image: postgres:18-alpine
          ports:
            - containerPort: 5432
              name: postgres
          env:
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: POSTGRES_DB
            - name: POSTGRES_USER
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: todoapp-secrets
                  key: postgres-password
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/postgresql
          livenessProbe:
            exec:
              command:
                - pg_isready
                - -U
                - todoapp
                - -d
                - todoapp
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            exec:
              command:
                - pg_isready
                - -U
                - todoapp
                - -d
                - todoapp
            initialDelaySeconds: 5
            periodSeconds: 5
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-pvc

---
# ========================================
# Servicio de PostgreSQL (Service)
# ========================================
apiVersion: v1
kind: Service
metadata:
  name: todoapp-postgres
  namespace: todoapp
  labels:
    app: todoapp-postgres
spec:
  type: ClusterIP
  ports:
    - port: 5432
      targetPort: 5432
      protocol: TCP
      name: postgres
  selector:
    app: todoapp-postgres

---
# ========================================
# Despliegue de la aplicación (Deployment)
# ========================================
apiVersion: apps/v1
kind: Deployment
metadata:
  name: todoapp-deployment
  namespace: todoapp
  labels:
    app: todoapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: todoapp
  template:
    metadata:
      labels:
        app: todoapp
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        fsGroup: 1001
      containers:
        - name: todoapp
          image: ghcr.io/tu-nombre-de-usuario/docker-nodejs-sample:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 3000
              name: http
              protocol: TCP
          env:
            - name: NODE_ENV
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: NODE_ENV
            - name: ALLOWED_ORIGINS
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: ALLOWED_ORIGINS
            - name: POSTGRES_HOST
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: POSTGRES_HOST
            - name: POSTGRES_PORT
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: POSTGRES_PORT
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: POSTGRES_DB
            - name: POSTGRES_USER
              valueFrom:
                configMapKeyRef:
                  name: todoapp-config
                  key: POSTGRES_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: todoapp-secrets
                  key: postgres-password
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 5
          resources:
            requests:
              memory: '256Mi'
              cpu: '250m'
            limits:
              memory: '512Mi'
              cpu: '500m'
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL

---
# ========================================
# Servicio de la aplicación (Service)
# ========================================
apiVersion: v1
kind: Service
metadata:
  name: todoapp-service
  namespace: todoapp
  labels:
    app: todoapp
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 80
      targetPort: 3000
      protocol: TCP
  selector:
    app: todoapp

---
# ========================================
# Ingress para acceso externo
# ========================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: todoapp-ingress
  namespace: todoapp
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: 'letsencrypt-prod'
spec:
  tls:
    - hosts:
        - tudominio.com
      secretName: todoapp-tls
  rules:
    - host: tudominio.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: todoapp-service
                port:
                  number: 80

---
# ========================================
# HorizontalPodAutoscaler
# ========================================
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: todoapp-hpa
  namespace: todoapp
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: todoapp-deployment
  minReplicas: 1
  maxReplicas: 5
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80

---
# ========================================
# PodDisruptionBudget
# ========================================
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: todoapp-pdb
  namespace: todoapp
spec:
  minAvailable: 1
  selector:
    matchLabels:
      app: todoapp

Configura el despliegue

Antes de realizar el despliegue, debes personalizar el archivo de despliegue para tu entorno:

  1. Referencia de la imagen: Reemplaza tu-nombre-de-usuario con tu nombre de usuario de GitHub o Docker Hub:

    image: ghcr.io/tu-nombre-de-usuario/docker-nodejs-sample:latest
  2. Nombre de dominio: Reemplaza tudominio.com por tu dominio real en dos lugares:

    # En el ConfigMap
    ALLOWED_ORIGINS: "https://tudominio.com"
    
    # En el Ingress
    - host: tudominio.com
  3. Contraseña de la base de datos (opcional): La contraseña predeterminada ya está codificada en base64. Para cambiarla:

    $ echo -n "tu-nueva-contraseña" | base64
    

    Luego actualiza el Secret:

    data:
      postgres-password: <tu-contraseña-codificada-en-base64>
  4. Clase de almacenamiento (Storage class): Ajústala según tu clúster (actualmente: standard).

Entiende el despliegue

El archivo de despliegue crea una pila de aplicación completa con múltiples componentes trabajando en conjunto.

Arquitectura

El despliegue incluye:

  • Aplicación Node.js: Ejecuta 3 réplicas de tu aplicación de tareas contenerizada.
  • Base de datos PostgreSQL: Instancia única con 10Gi de almacenamiento persistente.
  • Servicios (Services): Los servicios de Kubernetes gestionan el balanceo de carga entre las réplicas de la aplicación.
  • Ingress: Acceso externo a través de un controlador de Ingress con soporte SSL/TLS.

Seguridad

El despliegue utiliza varias características de seguridad:

  • Los contenedores se ejecutan como un usuario no root (UID 1001).
  • El sistema de archivos raíz de solo lectura (read-only root filesystem) evita escrituras no autorizadas.
  • Se eliminan las capacidades de Linux para minimizar la superficie de ataque.
  • Los datos confidenciales, como las contraseñas de la base de datos, se almacenan en Kubernetes Secrets.

Alta disponibilidad

Para mantener tu aplicación ejecutándose de forma confiable:

  • Tres réplicas de la aplicación garantizan la continuidad del servicio si falla un pod.
  • El presupuesto de interrupción de pods (Pod disruption budget) mantiene al menos un pod disponible durante las actualizaciones.
  • Las actualizaciones progresivas (rolling updates) permiten despliegues sin tiempo de inactividad.
  • Las comprobaciones de estado (health checks) en el endpoint /health aseguran que solo los pods saludables reciban tráfico.

Autoescalado

El Horizontal Pod Autoscaler escala tu aplicación basándose en el uso de recursos:

  • Escala de manera automática entre 1 y 5 réplicas.
  • Activa el escalado cuando el uso de CPU supera el 70%.
  • Activa el escalado cuando el uso de memoria supera el 80%.
  • Límites de recursos: 256Mi-512Mi de memoria, 250m-500m de CPU por pod.

Persistencia de datos

Los datos de PostgreSQL se almacenan de manera persistente:

  • Un volumen persistente de 10Gi almacena los archivos de la base de datos.
  • La base de datos se inicializa automáticamente en el primer arranque.
  • Los datos persisten a través de reinicios y actualizaciones de los pods.

Despliega tu aplicación

Paso 1: Despliega en Kubernetes

Despliega tu aplicación en el clúster local de Kubernetes:

$ kubectl apply -f nodejs-sample-kubernetes.yaml

Deberías ver una salida que confirme que se han creado todos los recursos:

namespace/todoapp created
secret/todoapp-secrets created
configmap/todoapp-config created
persistentvolumeclaim/postgres-pvc created
deployment.apps/todoapp-postgres created
service/todoapp-postgres created
deployment.apps/todoapp-deployment created
service/todoapp-service created
ingress.networking.k8s.io/todoapp-ingress created
poddisruptionbudget.policy/todoapp-pdb created
horizontalpodautoscaler.autoscaling/todoapp-hpa created

Paso 2: Verifica el despliegue

Comprueba que tus despliegues se estén ejecutando:

$ kubectl get deployments -n todoapp

Salida esperada:

NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
todoapp-deployment   3/3     3            3           30s
todoapp-postgres     1/1     1            1           30s

Verifica que se hayan creado tus servicios:

$ kubectl get services -n todoapp

Salida esperada:

NAME               TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
todoapp-service    ClusterIP   10.111.101.229   <none>        80/TCP     45s
todoapp-postgres   ClusterIP   10.111.102.130   <none>        5432/TCP   45s

Comprueba que el almacenamiento persistente esté funcionando:

$ kubectl get pvc -n todoapp

Salida esperada:

NAME           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
postgres-pvc   Bound    pvc-12345678-1234-1234-1234-123456789012   10Gi       RWO            standard       1m

Paso 3: Accede a tu aplicación

Para realizar pruebas locales, utiliza el reenvío de puertos (port forwarding) para acceder a tu aplicación:

$ kubectl port-forward -n todoapp service/todoapp-service 8080:80

Abre tu navegador y visita http://localhost:8080 para ver tu aplicación de tareas ejecutándose en Kubernetes.

Paso 4: Prueba el despliegue

Prueba que tu aplicación esté funcionando correctamente:

  1. Agrega algunas tareas a través de la interfaz web.

  2. Comprueba los pods de la aplicación:

    $ kubectl get pods -n todoapp -l app=todoapp
    
  3. Visualiza los logs de la aplicación:

    $ kubectl logs -f deployment/todoapp-deployment -n todoapp
    
  4. Comprueba la conectividad de la base de datos:

    $ kubectl get pods -n todoapp -l app=todoapp-postgres
    
  5. Monitorea el autoescalado:

    $ kubectl describe hpa todoapp-hpa -n todoapp
    

Paso 5: Limpieza

Cuando hayas terminado con las pruebas, elimina el despliegue:

$ kubectl delete -f nodejs-sample-kubernetes.yaml

Resumen

Has desplegado tu aplicación Node.js contenerizada en Kubernetes. Aprendiste a:

  • Crear un archivo de despliegue de Kubernetes completo con endurecimiento de seguridad.
  • Desplegar una aplicación de múltiples niveles (Node.js + PostgreSQL) con almacenamiento persistente.
  • Configurar autoescalado, comprobaciones de estado y características de alta disponibilidad.
  • Probar y monitorear tu despliegue localmente utilizando el Kubernetes integrado en Docker Desktop.

Tu aplicación ahora se está ejecutando en un entorno similar al de producción con características de nivel empresarial, incluidos contextos de seguridad, gestión de recursos y escalado automático.


Recursos relacionados

Explora referencias oficiales y mejores prácticas para perfeccionar tu flujo de trabajo de despliegue en Kubernetes: