Taller Kubernetes: Almacenamiento en Kubernetes

Introducción

En este taller, vamos a ver cómo Kubernetes gestiona el almacenamiento de los contenedores. Para ello, vamos a trabajar con recursos de almacenamiento que se pueden crear en Kubernetes. Estos recursos son PersistentVolume y PersistentVolumeClaim.

Ejercicio 1: Desplegando un servidor web persistente

En este ejercicio, vamos a desplegar un servidor web que guarde los datos en un volumen persistente. Para ello, vamos a crear un PersistentVolume y un PersistentVolumeClaim. El PersistentVolumeClaim se va a asociar al PersistentVolume para que el servidor web pueda acceder a él.

  • Creamos el fichero que va a definir el PersistentVolume. El fichero es el siguiente:
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: pvc-serverweb
    spec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 2Gi
    
  • Creamos el PersistentVolume con el siguiente comando:
    kubectl apply -f pvc-serverweb.yaml
    

Y comprobamos que se ha creado correctamente:

    kubectl get pc, pvc
    

1

  • Creamos el fichero de despliegue del servidor web. El fichero es el siguiente:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: web-php
      labels:
        app: apache
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: apache
      template:
        metadata:
          labels:
            app: apache
        spec:
          volumes:
            - name: volumen-web-php
              persistentVolumeClaim:
                claimName: pvc-webserver
          containers:
            - name: contenedor-apache-php
              image: php:7.4-apache
              ports:
                - name: http-server
                  containerPort: 80
              volumeMounts:
                - mountPath: "/var/www/html"
                  name: volumen-web-php
    
  • Creamos el despliegue con el siguiente comando:
    kubectl apply -f web-php.yaml
    

Y comprobamos que se ha creado correctamente:

    kubectl get all
    

2

  • Creamos el fichero de servicio del servidor web. El fichero es el siguiente:
    apiVersion: v1
    kind: Service
    metadata:
      name: servicio-web-php
    spec:
      type: NodePort
      ports:
      - name: service-http
        port: 80
        targetPort: http-server
      selector:
        app: apache
    
  • Creamos el servicio con el siguiente comando:
    kubectl apply -f servicio-web-php.yaml
    

3

  • Creamos el fichero info.php que vamos a guardar en el volumen persistente. Para ello, primero deberemos averiguar la ID del pod:

En la imagen del punto anterior, podemos ver que el pod se llama pod/servidorweb-778bc767f5-dvf7x

Sabido esto, crearemos el fichero info.php con el siguiente comando:

  kubectl exec pod/servidorweb-778bc767f5-dvf7x -- bash -c "echo '<?php phpinfo(); ?>' > /var/www/html/info.php"
  

Para comprobar que se ha creado correctamente, abriremos el fichero php en el navegador:

4

  • Como último paso, vamos a comprobar la persistencia, y por ello, vamos a eliminar el despliegue y lo volvemos a crear:
    kubectl delete deployment.apps/servidorweb
    kubectl apply -f web-php.yaml
    

Y comprobamos que se ha creado correctamente:

    kubectl get all
    

5

Y comprobamos que el fichero info.php sigue estando:

6

Ejercicio 2: Haciendo persistente la aplicación GuestBook

En este ejercicio vamos a desplegar nuestra aplicació Guestbook y vamos a hacer persistente la base de datos.

  • Creamos el fichero con el que vamos a definir el volumen PersistentVolumenClaim:
  apiVersion: v1
  kind: PersistentVolumeClaim
  metadata:
      name: pvc-redis
  spec:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 3Gi
  

Lo aplicamos y comprobamos que se ha creado correctamente:

  kubectl apply -f pvc-redis.yaml
  

7

  • Creamos el fichero de despliegue de la base de datos:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: redis
      labels:
        app: redis
        tier: backend
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: redis
          tier: backend
      template:
        metadata:
          labels:
            app: redis
            tier: backend
        spec:
          volumes:
            - name: volumen-redis
              persistentVolumeClaim:
                claimName: pvc-redis
          containers:
            - name: contenedor-redis
              image: redis
              command: ["redis-server"]
              args: ["--appendonly", "yes"]
              ports:
                - name: redis-server
                  containerPort: 6379
              volumeMounts:
                - mountPath: "/data"
                  name: volumen-redis
    
  • Creamos el despliegue de guestbook:
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: guestbook
      labels:
        app: guestbook
        tier: frontend
    spec:
      replicas: 3
      selector:
        matchLabels:
          app: guestbook
          tier: frontend
      template:
        metadata:
          labels:
            app: guestbook
            tier: frontend
        spec:
          containers:
          - name: contenedor-guestbook
            image: iesgn/guestbook
            ports:
              - name: http-server
                containerPort: 5000
    
  • Creamos el servicio de guestbook:
    apiVersion: v1
    kind: Service
    metadata:
      name: guestbook
      labels:
        app: guestbook
        tier: frontend
    spec:
      type: NodePort
      ports:
      - port: 80
        targetPort: http-server
      selector:
        app: guestbook
        tier: frontend
    
  • Creamos el servicio de redis:
    apiVersion: v1
    kind: Service
    metadata:
      name: redis
      labels:
        app: redis
        tier: backend
    spec:
      type: ClusterIP
      ports:
      - port: 6379
        targetPort: redis-server
      selector:
        app: redis
        tier: backend
    
  • Realizamos el despliegue de los servicios y comprobamos que se han creado correctamente:
    kubectl apply -f .
    

8

  • Comprobamos que la aplicación funciona correctamente:

9

  • Eliminamos el despliegue de guestbook y lo volvemos a crear:
    kubectl delete -f redis-deployment.yaml
    kubectl apply -f redis-deployment.yaml
    

10

  • Accedemos a la aplicación y comprobamos que los datos siguen estando:

11

Ejercicio 3: Haciendo persistente la aplicación Nextcloud

En este ejercicio vamos a desplegar nuestra aplicación Nextcloud y vamos a hacer persistente tanto la aplicación como la base de datos.

  • Creamos los volúmenes: 1 para la base de datos y otro para la aplicación, ambos de 4GB.

  • Volumen para la base de datos:

        apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
            name: pvc-mariadb
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 4Gi
        
  • Volumen para la aplicación:
        apiVersion: v1
        kind: PersistentVolumeClaim
        metadata:
          name: pvc-nextcloud
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 4Gi
            

Y los aplicamos:

    kubectl apply -f .
    

12

  • Creamos el recurso Secret, ejecuta el siguiente comando:
    kubectl create cm bd-datos --from-literal=bd_user=nextcloud \
    --from-literal=bd_dbname=nextcloud -o yaml --dry-run=client > bd_datos_configmap.yaml

    kubectl create secret generic bd-passwords --from-literal=bd_password=nextcloud \
    --from-literal=bd_rootpassword=nextcloud -o yaml --dry-run=client > bd_passwords_secret.yaml
    

Y los desplegamos:

    kubectl apply -f bd_datos_configmap.yaml
    kubectl apply -f bd_passwords_secret.yaml
    

13

  • Creamos el fichero de despliegue de la base de datos.
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: mariadb-deployment
    labels:
      app: nextcloud
      type: database
  spec:
    replicas: 1
    selector:
      matchLabels:
        app: nextcloud
        type: database
    template:
      metadata:
        labels:
          app: nextcloud
          type: database
      spec:
        containers:
          - name: contenedor-mariadb
            image: mariadb:10.5
            ports:
              - containerPort: 3306
              name: db-port
            env:
              - name: MYSQL_USER
                valueFrom:
                  configMapKeyRef:
                    name: bd-datos
                    key: bd_user
              - name: MYSQL_DATABASE
                valueFrom:
                  configMapKeyRef:
                    name: bd-datos
                    key: bd_dbname
              - name: MYSQL_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: bd-datos
                    key: bd_password
              - name: MYSQL_ROOT_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: bd-datos
                    key: bd_root_password
        volumes:
        - name: volumen-mariadb
          persistentVolumeClaim:
            claimName: pvc-mariadb
  
  • Creamos el servicio de la base de datos:
  apiVersion: v1
  kind: Service
  metadata:
    name: mariadb-service
    labels:
      app: nextcloud
      type: database
  spec:
    selector:
      app: nextcloud
      type: database
    ports:
    - port: 3306
      targetPort: db-port
    type: ClusterIP
  
  • Creamos el despliegue de Nextcloud:
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nextcloud-deployment
    labels:
      app: nextcloud
      type: frontend
  spec:
    replicas: 1
    selector:
      matchLabels:
        app: nextcloud
        type: frontend
    template:
      metadata:
        labels:
          app: nextcloud
          type: frontend
      spec:
        containers:
          - name: contenedor-nextcloud
            image: nextcloud:latest
            ports:
              - containerPort: 80
                name: http-port
              - containerPort: 443
                name: https-port
            env:
              - name: MYSQL_HOST
                value: mariadb-service
              - name: MYSQL_USER
                valueFrom:
                  configMapKeyRef:
                    name: bd-datos
                    key: bd_user
              - name: MYSQL_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: bd-datos
                    key: bd_password
              - name: MYSQL_DATABASE
                valueFrom:
                  configMapKeyRef:
                    name: bd-datos
                    key: bd_dbname
          volumes:
            - name: volumen-nextcloud
            persistentVolumeClaim:
              claimName: pvc-nextcloud
  
  • Creamos el servicio de Nextcloud:
  apiVersion: v1
  kind: Service
  metadata:
    name: nextcloud-service
    labels:
      app: nextcloud
      type: frontend
  spec:
    selector:
      app: nextcloud
      type: frontend
    ports:
    - name: http-sv-port
      port: 80
      targetPort: http-port
    - name: https-sv-port
      port: 443
      targetPort: https-port
    type: NodePort
  
  • Creamos el fichero Ingress para Nextcloud:
  apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
    name: nextcloud-ingress
    labels:
      app: nextcloud
      type: frontend
  spec:
    rules:
    - host: www.maria-nextcloud.org
      http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: nextcloud-service
                port:
                  number: http-sv-port
  
  • Desplegamos los recursos:
  kubectl apply -f .
  

14

  • Cambiamos el fichero /etc/hosts para que apunte a nuestro cluster:
  sudo nano /etc/hosts
  

15

  • Accedemos a Nextcloud:

Entramos en el navegador y accedemos a la dirección www.maria-nextcloud.org:

16

Y nos logueamos con las siguientes credenciales:

  • Usuario: nextcloud
  • Contraseña: bmV4dGNsb3Vk

17