Introducción
En el vertiginoso mundo de la gestión de contenedores, Kubernetes ha emergido como la plataforma líder para la orquestación de aplicaciones en entornos de producción. Dentro del vasto ecosistema de Kubernetes, la gestión de bases de datos es una consideración crucial para cualquier aplicación que busque escalabilidad, confiabilidad y persistencia. En esta guía, exploraremos cómo desplegar WordPress, uno de los sistemas de gestión de contenido más populares, junto con MariaDB, una potente base de datos relacional, todo ello en un entorno Kubernetes. Además, utilizaremos Longhorn para gestionar el almacenamiento persistente, asegurando así la integridad de nuestros datos incluso en caso de fallos en los nodos del clúster.
Pasos a seguir:
- Despliegue de MariaDB utilizando un PVC Longhorn: Configuraremos una instancia de MariaDB en nuestro clúster Kubernetes, utilizando Longhorn para gestionar el almacenamiento persistente de la base de datos.
- Configuración de WordPress para usar MariaDB: Una vez que MariaDB esté en funcionamiento, procederemos a configurar WordPress para que utilice esta base de datos en lugar de la base de datos integrada.
- Despliegue de WordPress: Finalmente, desplegaremos una instancia de WordPress en nuestro clúster Kubernetes, asegurándonos de que esté configurada correctamente para interactuar con la base de datos MariaDB que acabamos de configurar.
Acerca de Longhorn:
Longhorn es una solución de almacenamiento distribuido y persistente para Kubernetes, diseñada para ser fácil de usar y altamente confiable. Proporciona funciones avanzadas de replicación y recuperación ante desastres, lo que lo convierte en una opción ideal para entornos de producción que requieren almacenamiento persistente para sus aplicaciones.
Persistencia de datos para mariadb en longhorn
La persistencia de los datos es fundamental para garantizar la integridad y disponibilidad de las aplicaciones. El siguiente manifiesto crea un PersistentVolumeClaim (PVC), el cual esta asociado a un StorageClass llamado longhorn. A continuación vamos a crear los dos PVC uno para mariadb y otro para wordpress.
Manifiesto PVC mariadb
Creamos un archivo llamado: 01-mariadb-pvc.yaml
kind: PersistentVolumeClaim metadata: name: mariadb-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi storageClassName: longhorn
Variables del manifiesto
apiVersion: v1
: Indica que este manifiesto sigue la especificación de la API v1 de Kubernetes.kind: PersistentVolumeClaim
: Define que este objeto es un PersistentVolumeClaim, que se utiliza para solicitar almacenamiento persistente en Kubernetes.metadata
: Contiene metadatos asociados con el PVC, como el nombre.name: mariadb-pvc
: Es el nombre asignado al PersistentVolumeClaim.
spec
: Define las especificaciones del PVC, incluidos los requisitos de acceso y recursos.accessModes
: Especifica los modos de acceso al volumen. En este caso, «ReadWriteOnce» significa que el volumen solo puede ser montado en un único nodo a la vez y puede ser leído y escrito.resources
: Define los recursos solicitados para el volumen.requests
: Especifica los recursos mínimos necesarios para el volumen.storage: 5Gi
: Solicita al menos 5 gigabytes de almacenamiento para el PVC.
storageClassName: longhorn
: Indica la clase de almacenamiento que se utilizará para provisionar este volumen. En este caso, se utiliza la clase de almacenamiento «longhorn».
Manifieso PVC wordpress
Vamos a crear un archivo (manifiesto) llamado 02-wordpress-pvc.yaml para crear el PVC llamado wordpress-pvc en el StorageClass longhorn.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wordpress-pvc-protege
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
storageClassName: longhorn
Variables del manifiesto
apiVersion: v1
: Indica que este manifiesto sigue la especificación de la API v1 de Kubernetes.kind: PersistentVolumeClaim
: Define que este objeto es un PersistentVolumeClaim, que se utiliza para solicitar almacenamiento persistente en Kubernetes.metadata
: Contiene metadatos asociados con el PVC, como el nombre.name: wordpress-pvc
: Es el nombre asignado al PersistentVolumeClaim.
spec
: Define las especificaciones del PVC, incluidos los requisitos de acceso y recursos.accessModes
: Especifica los modos de acceso al volumen. En este caso, «ReadWriteOnce» significa que el volumen solo puede ser montado en un único nodo a la vez y puede ser leído y escrito.resources
: Define los recursos solicitados para el volumen.requests
: Especifica los recursos mínimos necesarios para el volumen.storage: 2Gi
: Solicita al menos 5 gigabytes de almacenamiento para el PVC.
storageClassName: longhorn
: Indica la clase de almacenamiento que se utilizará para provisionar este volumen. En este caso, se utiliza la clase de almacenamiento «longhorn».
Comandos:
- Verificar que el StorageClass longhorn este creado
# k get storageclasses.storage.k8s.io NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE longhorn (default) driver.longhorn.io Delete Immediate true 9d
Con este comando verificamos que el StorageClass longhorn esta creado y podemos usarlo para aprovicionar los PVC.
- Comando para aplicar el manifiesto 01-mariadb-pvc.yaml
# k apply -f 01-mariadb-pvc.yaml persistentvolumeclaim/mariadb-pv-protege created
- Verificación:
# k get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mariadb-pvc Bound pvc-4597d384-bfb6-4c7b-8aee-444227f06051 5Gi RWO longhorn 18s
- Comando para aplicar el manifiesto 02-wordpress-pvc.yaml
# k apply -f 02-wordpress-pvc.yaml persistentvolumeclaim/wordpres-pvc created
El comando anterior aplica el manifiesto.
- Verificación:
# k get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mariadb-pvc Bound pvc-4597d384-bfb6-4c7b-8aee-444227f06051 5Gi RWO longhorn 5m14s wordpress-pvc Bound pvc-51b45fef-3fd2-4391-b735-09f3570865ff 2Gi RWO longhorn 6s
La verificación nos muestra que tenemos los dos PVC creados.
Creación de un configmap para mariadb
¿Qué son ConfigMaps?
Antes de profundizar en cómo utilizar ConfigMaps con MariaDB, es importante comprender qué son exactamente.
Los objetos ConfigMaps nos permiten almacenar datos de configuración en forma de pares clave-valor. Estos datos pueden incluir variables de entorno, archivos de configuración o cualquier otro tipo de información necesaria para nuestras aplicaciones.
En nuestro despliegue, utilizamos un ConfigMap para almacenar la URL de la base de datos MariaDB. Esto nos permite separar la configuración de la aplicación del código fuente, lo que facilita la gestión y la portabilidad de nuestras implementaciones, vamos a crear un manifiesto llamado: 03-mariadb-configmap.yaml, con el siguiente contenido.
apiVersion: v1 kind: ConfigMap metadata: name: mariadb-configmap data: database_url: mariadb-internal-service
Variables del manifiesto
- apiVersion: Especifica la versión de la API de Kubernetes que estás utilizando.
- kind: Define el tipo de recurso de Kubernetes, en este caso, un ConfigMap (Mapa de Configuración).
- metadata: Contiene metadatos sobre el ConfigMap, incluyendo su nombre.
- data: Esta sección contiene pares de clave-valor que representan los datos de configuración. En este caso, hay un par de clave-valor:
- database_url: Esta clave almacena la URL o el nombre de host de tu servicio MariaDB (mariadb-internal-service).
Comandos:
- Aplicamos el manifiesto 03-mariadb-configmap.yaml
# k apply -f 03-mariadb-configmap.yaml configmap/mariadb-configmap created
- Verificamos:
k get configmaps -n blog NAME DATA AGE kube-root-ca.crt 1 3d16h mariadb-configmap 1 50s
Secrets
Los Secrets son objetos en Kubernetes diseñados para almacenar datos sensibles, como contraseñas, tokens de acceso o claves de cifrado, de forma segura. Los Secrets están codificados en base64 para garantizar la confidencialidad de los datos almacenados.
Descripción del archivo de Secret:
El archivo de Secret que estamos analizando se utiliza para almacenar la contraseña de root de MariaDB de forma segura. El nombre del Secret es mariadb-secret y su tipo es Opaque, lo que indica que se trata de un secreto genérico sin estructura específica.
El valor de la contraseña de root de MariaDB se encuentra en la sección data del Secret y está codificado en base64. Aunque la contraseña está codificada, es importante recordar que la codificación base64 no es un método seguro para almacenar contraseñas en producción. En un entorno de producción real, se recomienda utilizar herramientas de gestión de secretos más robustas, como Kubernetes Vault o herramientas externas de gestión de secrets.
Vamos a crear ahora el manifiesto 04-mariadb-secret.yaml
apiVersion: v1 kind: Secret metadata: name: mariadb-secret type: Opaque data: # Valor obtenido: echo -n 'secret'|base64 mariadb-root-password: c2VjcmV0
- apiVersion: v1: Indica que este recurso Secret pertenece a la versión 1 de la API de Kubernetes.
- kind: Secret: Define el tipo de recurso como un Secret, que se utiliza para almacenar datos sensibles de forma segura en un clúster de Kubernetes.
- metadata: Contiene metadatos sobre el Secret, incluyendo su nombre (mariadb-secret).
- type: Opaque: Especifica el tipo de Secret. Opaque se refiere a un secreto genérico sin una estructura específica de datos.
- data: Esta sección contiene los datos sensibles codificados en base64. En este caso, hay un solo campo:
mariadb-root-password: Contiene la contraseña de root de MariaDB, que ha sido codificada en base64.
Comandos:
- Aplicamos el manifiesto 04-mariadb-secret.yaml
# k apply -f 04-mariadb-secret.yaml secret/mariadb-secret created
- Verificamos:
# k get secrets NAME TYPE DATA AGE mariadb-secret Opaque 1 31s
- Vamos a ir un paso mas alla y ver toda la info del manifiesto aplicado utilizando el modificador describe
# k describe secret Name: mariadb-secret Namespace: blog Labels: Annotations: Type: Opaque Data ==== mariadb-root-password: 15 bytes
Vamos ahora a crear un service para mariadb
Los Service son un recurso fundamental que actúa como un balanceador de carga y un punto de acceso estable para los Pods que ejecutan una aplicación en un clúster. Básicamente, un Service expone una aplicación que se ejecuta en uno o más Pods y permite que otras partes de la aplicación o servicios externos se comuniquen con ella de manera predecible y confiable, independientemente de en qué nodo del clúster se estén ejecutando los Pods.
Vamos a crear el manifiesto 05-mariadb-service.yaml
apiVersion: v1 kind: Service metadata: name: mariadb-internal-service spec: selector: app: mariadb ports: - protocol: TCP port: 3306 targetPort: 3306 clusterIP: None
Este manifiesto de Kubernetes define un servicio (Service) llamado mariadb-internal-service que proporciona acceso interno a un servicio de base de datos MariaDB dentro de un clúster de Kubernetes. Aquí está la explicación detallada:
- apiVersion: v1: Indica que este recurso Service pertenece a la versión 1 de la API de Kubernetes.
- kind: Service: Define el tipo de recurso como un Service, que se utiliza para exponer aplicaciones dentro de un clúster de Kubernetes para que otras aplicaciones puedan comunicarse con ellas.
- metadata: Contiene metadatos sobre el Service, incluyendo su nombre (mariadb-internal-service).
- spec: Especifica las características del Service.
- selector: Define qué Pods se van a seleccionar para el servicio. En este caso, el Service seleccionará los Pods que tienen el label app: mariadb.
- ports: Lista los puertos que el Service expone.
protocol: TCP: Indica que se utiliza el protocolo TCP para la comunicación. - port: 3306: Es el puerto en el que escucha el Service.
- targetPort: 3306: Es el puerto en el que los contenedores de los Pods seleccionados están escuchando.
- clusterIP: None: Esto especifica que el Service no tiene una dirección IP interna en el clúster y solo se puede acceder internamente desde el clúster.
Este Service se puede utilizar para permitir que otras aplicaciones dentro del clúster de Kubernetes se comuniquen con el servicio de base de datos MariaDB utilizando el nombre del servicio (definido previamente en configmap) mariadb-internal-service y el puerto especificado (3306).
Comandos
- Aplicamos el manifiesto:
# k apply -f 05-mariadb-service.yaml service/mariadb-internal-service created
- Verificamos:
# k get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE mariadb-internal-service ClusterIP None <none> 3306/TCP 4s
Vamos a definir ahora un service para wordpress.
Creamos el archivo 06-wordpress-service.yaml
apiVersion: v1 kind: Service metadata: name: wordpress spec: selector: app: wordpress ports: - port: 80 targetPort: 80 protocol: TCP #default nodePort: 30007 type: NodePort
Este manifiesto de Kubernetes define un servicio (Service) llamado protege que expone una aplicación de WordPress en un clúster de Kubernetes utilizando el tipo de servicio NodePort. Aquí está la explicación detallada:
apiVersion: v1: Indica que este recurso Service pertenece a la versión 1 de la API de Kubernetes.
kind: Service: Define el tipo de recurso como un Service, que se utiliza para exponer aplicaciones dentro de un clúster de Kubernetes para que otras aplicaciones puedan comunicarse con ellas.
metadata: Contiene metadatos sobre el Service, incluyendo su nombre (protege).
spec: Especifica las características del Service.
selector: Define qué Pods se van a seleccionar para el servicio. En este caso, el Service seleccionará los Pods que tienen el label app: wordpress.
ports: Lista los puertos que el Service expone.
port: 80: Es el puerto en el que el Service escuchará las solicitudes de red.
targetPort: 80: Es el puerto en el que los contenedores de los Pods seleccionados están escuchando.
protocol: TCP: Especifica el protocolo utilizado para la comunicación. En este caso, TCP es el valor predeterminado.
nodePort: 30007: Este es el puerto de nodo que se asignará al Service. Los servicios NodePort hacen que el Service sea accesible en cada nodo del clúster en la dirección IP del nodo y el puerto especificado.
Comandos:
- Aplicamos el manifiesto:
k apply -f 06-wordpress-service.yaml service/protege created
- Verificamos:
# k get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE mariadb-internal-service ClusterIP None <none> 3306/TCP 6m29s wordpress NodePort 10.99.169.96 <none> 80:30007/TCP 76s
Vamos a crear ahora el deployment para mariadb
Este manifiesto de Kubernetes define un Deployment para crear y gestionar un conjunto de Pods que ejecutan una instancia de MariaDB en un clúster de Kubernetes.
Creamos ahora el manifiesto 07-mariadb-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: mariadb-deployment spec: # specification for deployment resource replicas: 1 selector: matchLabels: app: mariadb template: # blueprint for Pod metadata: labels: app: mariadb # service will look for this label spec: # specification for Pod containers: - name: mariadb image: mariadb ports: - containerPort: 3306 #default one env: - name: MARIADB_ROOT_PASSWORD valueFrom: secretKeyRef: name: mariadb-secret key: mariadb-root-password - name: MARIADB_DATABASE value: wordpress volumeMounts: - name: mariadb-pvc mountPath: /var/lib/mysql volumes: - name: mariadb-pvc persistentVolumeClaim: claimName: mariadb-pvc
apiVersion: apps/v1
: Indica que este recurso Deployment pertenece a la versión 1 de la API de aplicaciones de Kubernetes.kind: Deployment
: Define el tipo de recurso como un Deployment, que se utiliza para gestionar la creación y escalado de aplicaciones en Kubernetes.metadata
: Contiene metadatos sobre el Deployment, incluyendo su nombre (mariadb-deployment
).spec
: Especifica las características del Deployment.replicas: 1
: Indica que se debe crear una sola réplica del Pod de MariaDB. Esto significa que habrá un solo Pod de MariaDB ejecutándose.selector
: Define cómo se seleccionarán los Pods que serán gestionados por este Deployment. En este caso, selecciona los Pods que tienen el labelapp: mariadb
.template
: Define el modelo para crear los Pods que serán gestionados por este Deployment.metadata
: Contiene metadatos sobre el Pod template, incluyendo sus labels.spec
: Especifica las características del Pod.containers
: Lista los contenedores que se ejecutarán en el Pod.name: mariadb
: Define el nombre del contenedor como «mariadb».image: mariadb
: Especifica la imagen del contenedor que se utilizará para ejecutar la instancia de MariaDB.ports
: Lista los puertos que el contenedor expone.containerPort: 3306
: Es el puerto en el que la instancia de MariaDB dentro del contenedor está escuchando.
env
: Lista las variables de entorno que se pasarán al contenedor.name: MARIADB_ROOT_PASSWORD
: Nombre de la variable de entorno que representa la contraseña de root de MariaDB.valueFrom
: Indica que el valor de la variable de entorno se obtendrá de una referencia a un Secret.secretKeyRef
: Especifica el nombre del Secret (mariadb-secret
) y la clave (mariadb-root-password
) de donde se obtendrá el valor.
name: MARIADB_DATABASE
: Nombre de la variable de entorno que representa el nombre de la base de datos de MariaDB.value: wordpress
: Valor de la variable de entorno, que en este caso es «wordpress».
volumeMounts
: Lista los puntos de montaje que se utilizarán para montar volúmenes en el contenedor.name: mariadb-pvc
: Nombre del volumen que se montará en el contenedor.mountPath: /var/lib/mysql
: Ruta dentro del contenedor donde se montará el volumen.
volumes
: Lista los volúmenes que se utilizarán en el Pod.name: mariadb-pvc
: Nombre del volumen.persistentVolumeClaim
: Especifica el nombre de la reclamación de volumen persistente (mariadb-pvc
) que se utilizará para este volumen.
Este Deployment creará un solo Pod de MariaDB, utilizando la imagen de MariaDB especificada, con la contraseña de root y el nombre de la base de datos configurados a través de variables de entorno, y montará un volumen persistente para almacenar los datos de la base de datos en el directorio /var/lib/mysql
.
Comandos:
- Aplicamos el manifiesto 07-mariadb-deployment.yaml
# k apply -f 07-mariadb-deployment.yaml deployment.apps/mariadb-deployment created
- Verificamos:
# k apply -f 07-mariadb-deployment.yaml deployment.apps/mariadb-deployment created
Vamos a crear ahora un despliegue para wordpress
apiVersion: apps/v1 kind: Deployment metadata: name: wordpress-deployment spec: # specification for deployment resource replicas: 1 selector: matchLabels: app: wordpress template: # blueprint for Pod metadata: labels: app: wordpress spec: # specification for Pod containers: - name: wordpress image: wordpress:latest ports: - containerPort: 80 env: - name: WORDPRESS_DB_HOST valueFrom: configMapKeyRef: name: mariadb-configmap key: database_url - name: WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mariadb-secret key: mariadb-root-password - name: WORDPRESS_DB_USER value: root - name: WORDPRESS_DEBUG value: "1" volumeMounts: - name: wordpress-pvc mountPath: /var/www/html/ volumes: - name: wordpress-pvc persistentVolumeClaim: claimName: wordpress-pvc
apiVersion: apps/v1
: Indica que este recurso Deployment pertenece a la versión 1 de la API de aplicaciones de Kubernetes.kind: Deployment
: Define el tipo de recurso como un Deployment, que se utiliza para gestionar la creación y escalado de aplicaciones en Kubernetes.metadata
: Contiene metadatos sobre el Deployment, incluyendo su nombre (wordpress-deployment
).spec
: Especifica las características del Deployment.replicas: 1
: Indica que se debe crear una sola réplica del Pod de WordPress. Esto significa que habrá un solo Pod de WordPress ejecutándose.selector
: Define cómo se seleccionarán los Pods que serán gestionados por este Deployment. En este caso, selecciona los Pods que tienen el labelapp: wordpress
.template
: Define el modelo para crear los Pods que serán gestionados por este Deployment.metadata
: Contiene metadatos sobre el Pod template, incluyendo sus labels.spec
: Especifica las características del Pod.containers
: Lista los contenedores que se ejecutarán en el Pod.name: wordpress
: Define el nombre del contenedor como «wordpress».image: wordpress:latest
: Especifica la imagen del contenedor que se utilizará para ejecutar la aplicación de WordPress. En este caso, utiliza la última versión de la imagen de WordPress disponible en el repositorio Docker oficial.ports
: Lista los puertos que el contenedor expone.containerPort: 80
: Es el puerto en el que el contenedor de WordPress escuchará las solicitudes de red.
env
: Lista las variables de entorno que se pasarán al contenedor.name: WORDPRESS_DB_HOST
: Nombre de la variable de entorno que representa la dirección del host de la base de datos de WordPress.valueFrom
: Indica que el valor de la variable de entorno se obtendrá de una referencia a un ConfigMap.configMapKeyRef
: Especifica el nombre del ConfigMap (mariadb-configmap
) y la clave (database_url
) de donde se obtendrá el valor.
name: WORDPRESS_DB_PASSWORD
: Nombre de la variable de entorno que representa la contraseña de la base de datos de WordPress.valueFrom
: Indica que el valor de la variable de entorno se obtendrá de una referencia a un Secret.secretKeyRef
: Especifica el nombre del Secret (mariadb-secret
) y la clave (mariadb-root-password
) de donde se obtendrá el valor.
name: WORDPRESS_DB_USER
: Nombre de la variable de entorno que representa el usuario de la base de datos de WordPress.value: root
: Valor de la variable de entorno, que en este caso es «root».
name: WORDPRESS_DEBUG
: Nombre de la variable de entorno que activa el modo de depuración de WordPress.value: "1"
: Valor de la variable de entorno, que en este caso es «1» para activar el modo de depuración.
volumeMounts
: Lista los puntos de montaje que se utilizarán para montar volúmenes en el contenedor.name: wordpress-pvc
: Nombre del volumen que se montará en el contenedor.mountPath: /var/www/html/
: Ruta dentro del contenedor donde se montará el volumen.
volumes
: Lista los volúmenes que se utilizarán en el Pod.name: wordpress-pvc
: Nombre del volumen.persistentVolumeClaim
: Especifica el nombre de la reclamación de volumen persistente (wordpress-pvc
) que se utilizará para este volumen.
Comandos
- Aplicamos el manifiesto 07-wordpress-deployment.yaml
# k apply -f 07-mariadb-deployment.yaml deployment.apps/mariadb-deployment created
- Verificamos:
# k get deployments.apps mariadb-deployment NAME READY UP-TO-DATE AVAILABLE AGE mariadb-deployment 1/1 1 0 3m22s
Por fin podemos ir a wordpress…
Para esto abrimos el navegador y tipeamos http://IP_NODO_CUSTER:30007