Indice de contenidos
- 1. Introducción
- 2. Entorno
- 3. Aplicación utilizada
- 4. Creación del despliegue con la base de datos
- 5. Creación del despliegue con la aplicación web
- 6. Conclusiones
Introducción
En este tutorial veremos un ejemplo práctico de cómo desplegar una aplicación web básica en Kubernetes que para almacenar los datos se comunica con una base de datos MySql también desplegada en el clúster.
Se puede leer una introducción a Kubernetes en este tutorial.
Para simplificar el tutorial y no hacer una instalación de Kubernetes desde cero, tarea que no es especialmente sencilla, vamos a usar Minikube, que está muy bien para hacer pruebas en entornos de desarrollo, pero no es recomendable poner en producción.
Podemos ver otro tipo de instalaciones locales de Kubernetes en
Entorno
Este tutorial está escrito usando el siguiente entorno:
- Hardware: MacBook Pro 15’ (2,5 GHz Intel Core i7, 16GB DDR3)
- Sistema operativo: macOS Mojave 10.14.1
- Versiones:
- Docker: 18.09.0
- Minikube: v0.30.0
- VirtualBox 5.2.22 r126460 (Qt5.6.3).
Aplicación utilizada
Para el tutorial voy a usar de ejemplo una aplicación propia de gestión de catálogos de cursos que desarrollé para probar un pequeño framework que implementé. La tengo alojada en GitHub y se puede obtener con:
git clone https://github.com/diyipol/framework.git
Creación del despliegue con la base de datos
Antes que nada, debemos arrancar Minikube:
minikube start --cpus 4 --memory 7680
Una vez que tengamos minikube arrancado vamos a crear el PersistentVolume con el PersistentVolumeClaim al que se conectará la base de datos. Para no perder la información cada vez que paremos minikube, debemos crear el volumen en alguno de los siguientes directorios:
- /data
- /var/lib/minikube
- /var/lib/docker
Creamos catalogocursos-mysql-pv.yaml:
kind: PersistentVolume
apiVersion: v1
metadata:
name: catalogocursos-mysql-pv-volume
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/data/catalogocursos-mysql-pv"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: catalogocursos-mysql-pv-claim
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
Seguidamente desplegamos en kubernetes:
kubectl create -f catalogocursos-mysql-pv.yaml
Mostramos la información del PersistentVolume creado:
kubectl describe pv
Name: catalogocursos-mysql-pv-volume
Labels: type=local
Annotations: pv.kubernetes.io/bound-by-controller=yes
Finalizers: [kubernetes.io/pv-protection]
StorageClass: manual
Status: Bound
Claim: default/catalogocursos-mysql-pv-claim
Reclaim Policy: Retain
Access Modes: RWO
Capacity: 5Gi
Node Affinity:
Message:
Source:
Type: HostPath (bare host directory volume)
Path: /data/catalogocursos-mysql-pv
HostPathType:
Events:
Lo siguiente que vamos a hacer es crear el servicio catalogocursos-mysql-svc.yaml:
apiVersion: v1
kind: Service
metadata:
name: catalogocursos-mysql-service
spec:
type: NodePort
selector:
app: catalogocursos-mysql-service
ports:
- name: my-sql
port: 3306
Establecemos «type: NodePort» para poder conectarnos a la base de datos desde algún cliente fuera del clúster.
Por último creamos el despliegue catalogocursos-mysql-deployment.yaml para que se conecte con el servicio y el PersistentVolumeClaim anteriormente creados:
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalogocursos-mysql-deployment
spec:
selector:
matchLabels:
app: catalogocursos-mysql-service
strategy:
type: Recreate
template:
metadata:
labels:
app: catalogocursos-mysql-service
spec:
containers:
- image: mysql:5.7.23
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: root
- name: MYSQL_DATABASE
value: cursos_autentia
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: catalogocursos-mysql-pv-claim
Los desplegamos:
kubectl create -f catalogocursos-mysql-svc.yaml
kubectl create -f catalogocursos-mysql-deployment.yaml
Como tiene que descargar la imagen, puede que tarde un poco. Podemos ver el estado con:
kubectl get pods
Cuando ya esté en estado «Running» podemos pedirle a minikube la url para conectarnos a la base de datos desde cualquier cliente externo:
minikube service catalogocursos-mysql-service --url
Ya con cualquier cliente de base de datos como DBeaver podremos acceder a la base de datos en la url y puerto que nos devuelva. En este ejemplo en particular, para que la aplicación catálogo de cursos funcione, tenemos que pasar el parche db_schema.sql que se encuentra en la carpeta sql del proyecto, teniendo en cuenta que la base de datos ya tiene que estar creada.
Creación del despliegue con la aplicación web
Lo primero que vamos a hacer es generar la imagen docker con el war desplegado en el tomcat para usar en kubernetes. Generamos primero un war con el perfil kubernetes. En este caso, en esta aplicación, el perfil cambia la url de conexión de base de datos, para que pueda conectar con la que acabamos de crear de forma que la url de conexión quede del tipo:
jdbc:mysql://catalogocursos-mysql-service:3306/cursos_autentia
Generamos el war:
mvn clean package -Pkubernetes
Ahora creamos el Dockerfile con el que tenemos que construir la imagen.
FROM tomcat:9.0.13-jre8-alpine
MAINTAINER Pablo Betancor Lugo
ADD ./cursosautentia.war /usr/local/tomcat/webapps/
CMD ["catalina.sh", "run"]
Necesitamos construir la imagen de forma que esté accesible desde minikube. Para poder trabajar con el daemon de docker desde el host mac/linux es necesario usar el comando docker-env.
eval $(minikube docker-env)
Ahora ya podemos usar docker en la línea de comandos hablando con el daemon de docker que se encuentra dentro de la minikube VM.
Construimos la imagen y queda dentro del catálogo de imágenes del Docker que corre dentro de Minikube:
docker build -t cursosautentia .
Creamos el servicio catalogocursos-webapp-svc.yaml:
apiVersion: v1
kind: Service
metadata:
name: catalogocursos-webapp-service
spec:
type: NodePort
selector:
app: catalogocursos-webapp-service
ports:
- name: tomcat
port: 8080
Creamos el despliegue catalogocursos-webapp-deployment.yaml teniendo en cuenta que en el pod spec debemos poner el mismo nombre de la imagen con el que la construimos (por ejemplo cursosautentia) y establecer «imagePullPolicy» a «Never», para que kubernetes no intente descargar la imagen.
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalogocursos-webapp-deployment
spec:
selector:
matchLabels:
app: catalogocursos-webapp-service
strategy:
type: Recreate
template:
metadata:
labels:
app: catalogocursos-webapp-service
spec:
containers:
- image: cursosautentia:latest
name: cursosautentia
imagePullPolicy: Never
ports:
- containerPort: 8080
name: cursosautentia
Desplegamos:
kubectl create -f catalogocursos-webapp-svc.yaml
kubectl create -f catalogocursos-webapp-deployment.yaml
Comprobamos que todos los objetos se han creado:
kubectl get all -l app=catalogocursos-webapp-service
NAME READY STATUS RESTARTS AGE
pod/catalogocursos-webapp-deployment-5f7997cd47-wnpp4 1/1 Running 0 5m
NAME DESIRED CURRENT READY AGE
replicaset.apps/catalogocursos-webapp-deployment-5f7997cd47 1 1 1 5m
Para ver el log de arranque de la aplicación web y comprobar si todo ha ido bien ejecutamos:
kubectl logs catalogocursos-webapp-deployment-5f7997cd47-wnpp4
Ahora vemos la url que tiene la aplicación para poder acceder a ella desde fuera del clúster con nuestro navegador.
minikube service catalogocursos-webapp-service --url
http://192.168.99.100:32261
Ya podemos acceder a la aplicación desplegada en: http://192.168.99.100:32261/cursosautentia/catalogocursos/cursos?orden=titulo&pagina=1&numElementosPagina=3
Ya con todo funcionando podemos empezar a jugar con Kubernetes todo lo que queramos. Por ejemplo podemos escalar el deployment:
kubectl scale deployments catalogocursos-webapp-deployment --replicas=2
Comprobamos que se nos ha creado un nuevo pod.
kubectl get pods
NAME READY STATUS RESTARTS AGE
catalogocursos-mysql-deployment-ccdb6fdf8-fkd8h 1/1 Running 0 1h
catalogocursos-webapp-deployment-5f7997cd47-wnpp4 1/1 Running 0 53m
catalogocursos-webapp-deployment-5f7997cd47-zxn78 1/1 Running 0 2m
Ahora haciendo diferentes peticiones a la aplicación desplegada, y mirando el log de cada uno de los dos pods de la webapp podemos comprobar como a veces nos responde uno de ellos y en otras ocasiones el otro.
kubectl logs catalogocursos-webapp-deployment-5f7997cd47-wnpp4
kubectl logs catalogocursos-webapp-deployment-5f7997cd47-zxn78
Conclusiones
A lo largo del tutorial hemos visto de una forma muy sencilla cómo podemos desplegar una base de datos en Kubernetes y exponerla como un servicio dentro del clúster. Luego hemos podido desplegar una aplicación web y conectarla a la base de datos, para finalmente acceder a la aplicación desde fuera del clúster con nuestro navegador.
Saludos.