martes, 7 de agosto de 2018

Laboratorio - Integración Contínua (V)

Quinta entrada en la creación de nuestro laboratorio CI. Recuerdo los pasos:

  1. Instalación de Ubuntu en VMWare Player en Windows 7
  2. Instalación de Jenkins en Ubuntu
  3. Instalación GitLab en Ubuntu
  4. Instalación de Docker y Minikube en Ubuntu
  5. Desarrollo de un microservicio en Eclipse Windows (1) y (2)
    1. Angular 6 frontend
    2. SpringBoot 2.x backend
    3. Mongo Replicaset (3 replicas)
  6. Despliegue CI en Minikube utilizando la infraestructura configurada
    1. Pipeline - Jenkinsfile
    2. GitLab webhook
    3. Dockerfile
    4. Deploy y Service yaml
    5. Secret
    6. Ingress

Vayamos pues con la instalación de Minikube en nuestro Ubuntu.



Qué es Minikube
En entornos cloud de producción reales tendremos una arquitectura de varios nodos, uno o varios de de ellos de tipo "Master" y el resto de tipo "Slave" o "Worker". No es nuestro objetivo. Lo que queremos es montar un laboratorio de prueba que nos permita testear nuestras aplicaciones angular, java,..., en un entorno pre-productivo.

Queremos ver si una aplicación dada, más todos los ficheros de configuración (deployment, service, ingress, etc...) funcionan antes de llevarlos a un entorno real.

En este contexto, Minikube nos proporciona lo que queremos. Es un cluster Kubernetes de un sólo nodo. Es a la vez master y slave. Permite usar todos los conceptos cloud kubernetes sin las necesidades de infraestructura que un entorno real requiere.

Otra opción para montar un laboratorio cloud sería Kubeadm, que ya desarrollamos en otra entrada. Sin embargo, en esta serie de entradas hemos sido más ambiciosos, de forma que lo que estamos montando es un laboratorio completo, donde probar un sistema de integración contínua (CI). Hemos escogido Minikube por su sencillez de uso y por los "addons" que incorpora y que de forma sencilla y rápida podemos usar.


Gráficamente, lo que queremos es un entorno como el siguiente:

Con un push de código se han de disparar todas las acciones que permitan que nuestra aplicación esté operativa en un cloud de prueba, en nuestro caso Minikube. Estamos ya cerca de conseguirlo.



Minikube
Seguimos la documentación oficial. Desde el enlace anterior se nos dirige a un enlace más específico. Veamos todos los pasos en detalle.


Instalación Kubectl
Instalamos el cliente kubernetes, kubectl. Nos permitirá acceder al nodo minikube vía línea de comandos. Para ello deberemos crearnos un repositorio ya que los paquetes de instalación no están en los repositorios oficiales de Ubuntu.

Primero instalamos dependencias si no las tuviesemos ya instaladas previamente.
    sudo apt-get update && sudo apt-get install -y apt-transport-https

Descargamos las claves de acceso al repositorio kubernetes
    curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -

Creamos un nuevo respositorio local de paquetes, específico para kubernetes
    sudo touch /etc/apt/sources.list.d/kubernetes.list

Incluimos la ruta del repo en nuestro repo local, previamente creado
    echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list.d/kubernetes.list

Update para actualizar la lista de paquetes tras la adición del nuevo repo
    sudo apt-get update

Instalamos
    sudo apt-get install -y kubectl



Instalación Minikube
La instalación es bien simple, a diferencia de kubectl, al no haber ningún repositorio con los paquetes adecuados Instalamos directamente el binario. Lo haremos en el directorio "/usr/local/bin/". 

Hemos de estar siempre desde la home del usuario de sistema con el que accedemos a Ubuntu, o en su defecto en un directorio con permisos de escritura.

Lanzamos el siguiente comando:
    sudo curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.28.2/minikube-linux-amd64 && sudo chmod +x minikube && sudo mv minikube /usr/local/bin/

Antes de arrancar minikube hemos de seguir el siguiente apartado


Instalación VirtualBox
Cuando arranquemos Minikube se creará un nodo kubernetes. Por defecto, este nodo será generado de forma virtual, sobre el driver virtualbox. Es decir, nuestro minikube correrá en forma de VM, creada partir del driver virtualbox. Esta es la forma habitual de arrancar minikube y así lo haremos en este manual.

Se podría hacer de una forma más "nativa", sin ninguna VM añadida, pero por mi experiencia, prefiero arrancar Minikube sobre una VM. Si lo hacemos sin VM, nada más arrancar minikube nos aparecerá un log como el siguiente:

WARNING: IT IS RECOMMENDED NOT TO RUN THE NONE DRIVER ON PERSONAL WORKSTATIONS
The 'none' driver will run an insecure kubernetes apiserver as root that may leave the host vulnerable to CSRF attacks
....

que nos obliga a realizar una serie de configuraciones extra. Además, al no utilizar ninguna VM, adopta kubeadm como soporte al nodo minikube. El uso de kubeadm ya lo vimos en otra entrada. En el arranque de dicho kubeadm aparecen algunos warnings sobre memoria swap y versión de docker superior a la soportada, lo cual nos obliga a arrancarlo con nuevas configuraciones extras. No es lo que queremos para nuestro laboratorio.

En definitiva, instalemos virtual box de la siguiente forma:
    sudo apt-get install virtualbox

La necesidad de instalar el driver virtualbox como soporte para minikube es lo que hizo que eligiésemos VMWare como herramienta de virtualización para la instalación de Ubuntu. En otro caso, no es posible instalar el driver VirtualBox sobre un Ubuntu corriendo sobre VirtualBox... y llegados a este punto hubiésemos tenido que tirar todo el trabajo a la basura... o bien haber escogido la otra opción de arranque de minikube (sin driver) descrita más arriba, que como digo, desde mi punto de vista, no es aconsejable.


Arranque
Ahora ya podemos arrancarlo
    minikube start

Veremos el siguiente log:
    Starting local Kubernetes v1.10.0 cluster...

    Starting VM...

    Downloading Minikube ISO

     160.27 MB / 160.27 MB [============================================] 100.00% 0s

    Getting VM IP address...

    Moving files into cluster...

    Downloading kubeadm v1.10.0

    Downloading kubelet v1.10.0
    Finished Downloading kubelet v1.10.0
    Finished Downloading kubeadm v1.10.0
    Setting up certs...
    Connecting to cluster...
    Setting up kubeconfig...
    Starting cluster components...
    Kubectl is now configured to use the cluster.
    Loading cached images from config file.


Si vemos los directorios de la home del usuario con el que hemos lanzado el "start" veremos dos nuevos directorios ocultos

    .kube 
    .minikube

Se han creado en el arranque y guardan certificados, configuraciones, etc... que en principio no deberemos tocar.

Verificamos la instalación y arranque mediante el cliente kubernetes
    kubectl cluster-info

    Salida:
        Kubernetes master is running at https://192.168.99.100:8443

        KubeDNS is running at https://192.168.99.100:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy



        To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

y ahora desde minikube
    minikube ip

    Salida:
        192.168.99.100

Es decir, minikube, con ip 192.168.99.100 (es la habitual) es el único nodo kubernetes instalado y accesible para desplegar nuestras imágenes y hacerlas correr en pods (más o menos equivalente de container en docker).

Para hacerlo más patente, ejecutemos "kubectl get nodes"


Enlace docker-minikube
Si ahora lanzamos comandos docker (docker ps, docker images....) veremos que no aparece nada, a no ser alguna imagen o contenedor de prueba de entradas anteriores. Lo que es seguro es que no aparece nada que aluda a kubernetes.

Para "conectar" docker con nuestro nodo kubernetes (minikube) hemos de hacer como se explicó en otras entradas, cargar las variables de contexto necesarias para ello. Lo habitual es hacerlo, en un entorno linux, como sigue:
    eval ($docker-machine env nombre_machine)

En el caso actual con minikube lo haremos de esta otra forma:
    eval $(minikube docker-env)

En realidad lo que estamos haciendo es cargar las variables del nodo minikube. Si ejecutamos sólo "minikube docker-env" veremos las variables que estamos cargando
    userblog@ubuntu:~$ minikube docker-env

    export DOCKER_TLS_VERIFY="1"

    export DOCKER_HOST="tcp://192.168.99.100:2376"

    export DOCKER_CERT_PATH="/home/userblog/.minikube/certs"

    export DOCKER_API_VERSION="1.35"

    # Run this command to configure your shell:

    # eval $(minikube docker-env)


La misma salida nos dice que lancemos "eval $(minikube docker-env)", y así hemos hecho. A partir de ese momento, cualquier comando docker se hará sobre minikube. Si enlazamos estos comentarios con entradas anteriores, minikube "viene a ser" como una docker-machine, aunque en realidad se trata de un nodo kubernetes.

Probad ahora a lanzar "docker ps" o "docker images", veréis una serie de contenedores e imágenes que acompañan a minikube a modo de soporte para su correcto funcionamiento.


Para desenlazar docker de minikube
    eval $(minikube docker-env -u)


Prueba - Kubectl client
Una vez levantado minikube veamos algunos comandos básicos. Lo primero que haremos será arrancar una imagen nginx de prueba. Para que una aplicación, servidor, etc... esté disponible es necesario configurar al menos dos elementos: deployment y service

El deployment se realiza a partir de una imagen docker, mientras que el service expone al exterior el deployment realizado. Al final lo que se genera es un pod activo y accesible vía url si contiene una aplicación web.

El pod es en Kubernetes el "equivalente" al container en docker. No queremos decir que sean exactamente iguales, pero a nuestros efectos como si lo fueran. Un pod puede contener varios contenedores, pero en general un pod sólo contiene un contenedor, y por tanto, la equivalencia puede decirse que es real. Veamos un ejemplo con el servidor web nginx. Habitualmente se hará con ficheros ".yaml", aunque para esta prueba lanzamos los comandos directamente.

Creamos el deploy con el puerto real por defecto de nginx
    kubectl run mynginx --image=nginx --port=80

Exponemos (al exterior) el servicio a partir del deploy anterior
    kubectl expose deployment mynginx --type=NodePort

Podríamos pensar que ya podríamos acceder a la página principal del nginx a partir de "http://minikube_ip:80". Pero no, en un entorno kubernetes no funciona de esta forma. Es el propio kubernetes el que asigna el puerto de forma dinámica. Cuando veamos Ingress veremos como construir una url de acceso a nuestras aplicaciones "estable". De momento, hemos de preguntar a minikube cual es la url de acceso al nginx desplegado
    minikube service mynginx --url

    en mi caso responde
    http://192.168.99.100:31807

Si ahora ponemos esa url en el navegador de ubuntu vemos el resultado:


Podemos ver los diferentes elementos

Vemos el deployment
    kubectl get deployments (se puede usar "deploy" o "deployment" en lugar de deployments)



Vemos el servicio
    kubectl get service (se puede usar "services" o "svc" en lugar de services)


    
Vemos el pod
    kubectl get pods (se puede usar "pod" en lugar de pods).


Si queremos eliminar el pod  ("kubectl delete pod NAME_POD") veremos que de forma inmediata minikube lo vuelve a levantar, dándole otro nombre

Para eliminar haremos (no lo hagáis, así lo veremos en el siguiente apartado)
    kubectl delete svc mynginx
    kubectl delete deploy mynginx

El pod no es necesario eliminarlo, minikube lo hace de forma automática a partir de los comandos anteriores.

Si queremos ver los detalles de un pod
    kubectl describe pod NOMBRE_POD

    igual para un servicio, deploy, etc...


Como vemos, en ninguno de los los comandos "kubectl" indicamos el namespace. Como comentaremos más adelante, si no se especifica lo contrario, por defecto se usa el namespace "default". Si queremos hacer algo sobre otro namespace se debe indicar explicitamente, por ejemplo para ver los svc del namepace kube-system
    kubectl get svc -n kube-sytem

para ver todo de todos los namespaces
    kubectl get all --all-namespaces

etc...

Para crear un namespace 
    kubectl create namespace NOMBRE

para elminarlo
    kubectl delete namespace NOMBRE

Para parar minikube
    minikube stop

Para eliminar minikube (se elimina cualquier pod, imagen, services, deploys, etc....)
    minikube delete
    En el nuevo start se volverá a generar de nuevo en su estado original



Minikube Dashboard
Minikube viene equipado con un dashboard que nos facilita la visión de los diferentes elementos que lo componen. Se podrán ver asimismo los pods, services, secrets, etc... que vayamos creando en las siguientes entradas. 

Se accede a partir de la url "http://192.168.99.100:30000". Este es su aspecto:


Vemos que minikube se organiza mediante namespaces. Se crean 3 por defecto (default, kube-public y kube-system). Cuando instalemos aplicaciones y levantamos pods, services, etc... veremos que si no se especifica lo contrario se hace sobre el namespace "default". No obstante, podríamos crear uno nuevo (como hemos visto más arriba) y trabajar con él sin ningún problema. 

En la captura anterior podemos ver el deployment de nginx (mynginx) y el pod generado a partir de dicho deployment y service.


Minikube addons
Minikube viene configurado por defecto con una configuración base. Podemos ver los addons que vienen activos
    minikube addons list

    Salida:
    - addon-manager: enabled
    - coredns: disabled
    - dashboard: enabled
    - default-storageclass: enabled
    - efk: disabled
    - freshpod: disabled
    - heapster: disabled
    - ingress: disabled
    - kube-dns: enabled
    - metrics-server: disabled
    - nvidia-driver-installer: disabled
    - nvidia-gpu-device-plugin: disabled
    - registry: disabled
    - registry-creds: disabled
    - storage-provisioner: enabled

Como vemos, sólo algunos vienen habilitados. Para habilitar cualquiera de los que vienen "disabled" haremos:
    minkube addons enable NOMBRE

    Para deshabilitar se ejecuta el mismo comando cambiando "enable" por "disable"

Veremos en siguientes entradas como deberemos activar el addon ingress para poder dar estabilidad a las url's de accesos a nuestras aplicaciones.

Un addon interesante es el "heapster", que activará Grafana, de forma que podremos acceder a la monitorización de pods que dicha herramienta provee. Podemos hacer la prueba
    minikube addons enable heapster

Veremos algunos elementos nuevos en el namespace kube-system
    kubectl get svc -n kube-system


Según la captura anterior podemos acceder ya podemos ver que el puerto de acceso a la gui de grafana es el 30002, no obstante tenemos dos procedimiento para corroborarlo. Uno ya lo hemos visto

    minikube service monitoring-grafana --url -n kube-system 

    que nos devuelve "http://192.168.99.100:30002"

Otro con
    kubectl describe svc monitoring-grafana -n kube-system

    lo cual nos arroja el resultado siguiente:

Sea como fuere accedemos a "http://192.168.99.100:30002""

Seleccionamos "Pods / namespace default / podname  mynginx-xxxxx" podemos ver algunas métricas:




Minikube node access 
En principio no será necesario, pero podríamos acceder vía ssh a minikube. Recordad que lo hemos configurado como una virtual machine

Para acceder ejecutamos
    minikube ssh


Una vez dentro podremos acceder a los diferentes directorios y ver

En algunos casos puede ser necesario si vemos que algún pod no está gestionando de forma correcta los datos que genera si los hemos configurado como "volúmenes".

Como prueba, tras acceder vía url al pod nginx desplegado, accedemos a "/var/log/pods". Si listamos los directorios veremos varios directorios, accedemos al de fecha de modificación más reciente, en mi caso "84e8b681-9a61-11e8-b943-0800277658c0" y dentro veremos un directorio con el nombre del deploy, en nuestro caso "mynginx". Si accedemos dentro vemos un softling llamado "0.log" si hacemos un "cat" del fichero veremos el acceso realizado, en mi caso:

{"log":"172.17.0.1 - - [07/Aug/2018:16:50:07 +0000] \"GET / HTTP/1.1\" 304 0 \"-\" \"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0\" \"-\"\n","stream":"stdout","time":"2018-08-07T16:50:07.638666381Z"}

Si a la vez hemos lanzado desde fuera 
    kubectl logs -f NODE_NAME

vemos el mismo acceso pero con un log algo diferente:

172.17.0.1 - - [07/Aug/2018:16:50:07 +0000] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0" "-"

Es decir, el acceso al nginx ha dejado constancia en un fichero de logs dentro de minikube, a la vez que podemos acceder a ellos desde fuera, con el habitual "kubectl logs......".

Para salir con "exit".



Conclusión
Ya tenemos el entorno configurado. El trabajo ha sido arduo, muchos elementos, mucha configuración, muchos detalles, muchos comandos... es lo que tiene el mundo DevOps. Si queremos crear y usar un entorno CI para cloud se han de tocar muchos "palos".

No obstante, una vez creado, no sabéis el placer que supondrá hacer un "git push" y sin tocar nada más, ver la aplicación desplegada y accesible.




Seguro que nos hemos dejado detalles por el camino, de lo contrario si hubiésemos querido realizar una explicación exhaustiva de todos estos elementos las entradas se hubiesen hecho aún más largas de lo que ya han sido.

Ojo, aún nos faltarán más elementos de configuración, pero ya más cercanos a desarrollo, más cercanos a aplicación. Y digo bien, "mas cercanos", no "pegados a", porque en DevOps, la mayoría de elementos están en algún punto entre Dev y Ops.


Más info
Kubectl:

Minikube:




No hay comentarios:

Publicar un comentario