Entorno mínimo para demos de Kubernetes

Cada día más los entornos de contenedores no cumplen los requisitos de los clientes para la realización de demostraciones en clientes, ya que los mismos plantean una migración/utilización Cloud o entornos Kubernetes/Openshift en la propia infraestructura del cliente.

Kubernetes no es un entorno amigable para llevarlo en un portatil de media capacidad (8GB a 16 GB de RAM) y menos con una demo que requiera ciertos recursos.

Este primer caso se basa en el despliegue basado en Kubeadm (https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/) para el despliegue de Kubernetes, usando containerd (https://containerd.io/) como gestor del ciclo de vida de los contenedores y para poder disponer de una gestión de red mínima usaremos metallb (https://metallb.universe.tf/) que nos permitirá emular la potencia de los balanceadores de los entornos Cloud y Weave (https://www.weave.works/blog/weave-net-kubernetes-integration/) que nos permite la gestión de red de los contenedores y se integra sin problemas con metallb.

Por último y aprovechando la infraestructura desplegamos el gestor en tiempo real de los recursos K8dash (https://github.com/herbrandson/k8dash) que nos permitirá un seguimiento de estado de la infraestructura y de las aplicaciones que desplegamos en la misma.

Aunque los roles de Ansible que hemos utilizado con anterioridad (ver https://github.com/aescanero/disasterproject) nos permiten desplegar el entorno con facilidad y limpieza vamos a examinar el mismo para entender como los cambios que usaremos en siguientes capítulos (usar k3s y microk8s) repercuten de manera importante en la disponibilidad y rendimiento del entorno de demo/desarrollo.

El primer paso en la instalación son las dependencias que tiene Kubernetes y una muy buena referencia sobre las mismas es la documentación que Kelsey Hightower pone a disposición de aquellos que necesitan conocer a fondo Kubernetes (https://github.com/kelseyhightower/kubernetes-the-hard-way), especialmente de todos aquellos que están interesados en certificaciones Kubernetes como CKA (https://www.cncf.io/certification/cka/).

Empezamos por una serie de paquetes de red

#Debian
sudo apt-get install -y ebtables ethtool socat libseccomp2 conntrack ipvsadm
#Centos
sudo yum install -y ebtables ethtool socat libseccomp conntrack-tools ipvsadm

Instalamos el gestor de vida de los contenedores (Containerd que incluye CRI y CNI) y aprovechamos el paquetes que viene con los interfaces de red de Kubernetes (CNI o Container Network Interface)

sudo sh -c "curl -LSs https://storage.googleapis.com/cri-containerd-release/cri-containerd-cni-1.2.7.linux-amd64.tar.gz |tar --no-overwrite-dir -C / -xz"

El paquete incluye el servicio para systemd por lo que basta con iniciar el servicio:

sudo systemctl enable containerd
sudo systemctl start containerd

Ahora descargamos los ejecutables de kubernetes, en el caso de la primera máquina a configurar será el master y tenemos que descargar los binarios kubeadm (el instalador de kubernetes), kubelet (el agente que se conectará con containerd en cada máquina, para saber cual es la versión estable de kubernetes ejecutamos:

VERSION=`curl -sSL https://dl.k8s.io/release/stable.txt`

Y descargamos los binarios (en el master todos y en los nodos solo es necesario kubelet)

sudo curl -Ol https://storage.googleapis.com/kubernetes-release/release/$VERSION/bin/linux/amd64/{"kubectl","kubelet","kubeadm"} -o /usr/bin/"#1"
sudo chmod u+x /usr/bin/{"kubectl","kubelet","kubeadm"}

Configuramos un servicio para kubelet en cada máquina creando el archivo /etc/systemd/system/kubelet.service que dependerá de si la máquina tiene la función de master o nodo. Para el master tenemos:

[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=http://kubernetes.io/docs/

[Service]
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
Environment="KUBELET_EXTRA_ARGS='--network-plugin=cni --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-ip={{ vm_ip }}'"
Environment="KUBELET_KUBECONFIG_ARGS='--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf'"
Environment="KUBELET_CONFIG_ARGS='--config=/var/lib/kubelet/config.yaml'"
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
Restart=always
StartLimitInterval=0
RestartSec=10

[Install]
WantedBy=multi-user.target

Para el resto de nodos:

[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=http://kubernetes.io/docs/

[Service]
Environment="KUBELET_EXTRA_ARGS='--network-plugin=cni --container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --node-ip={{ vm_ip }}'"
Environment="KUBELET_KUBECONFIG_ARGS='--bootstrap-kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig --kubeconfig=/var/lib/kubelet/kubeconfig'"
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_EXTRA_ARGS
Restart=always
StartLimitInterval=0
RestartSec=10

[Install]
WantedBy=multi-user.target

Una vez cargada la configuración procedemos a utilizar kubeadm en el primer node (master), indicando la ip que se va a publicar, la red interna de los pods (un /16, aunque no olvidemos que es un entorno de demo/desarrollo por lo que no debe ser un problema). No vamos a utilizar kubeadm en el resto de nodos, así que no necesitamos recoger información de la ejecución de este comando.

$ sudo kubeadm init --apiserver-advertise-address {{ vm_ip }} --pod-network-cidr=10.244.0.0/16 --cri-socket /run/containerd/containerd.sock

Antes de preparar los nodos procedemos a cargar dos elementos, el primero «metallb» nos permitirá tener servicios «balanceables por carga: loadbalancer» accesibles en el mismo rango que las máquinas virtuales y el segundo «weave» es un gestor de red que permitirá la comunicación entre pods que se ejecutan entre máquinas diferentes dentro de la red que hemos definido anteriormente. Ambos servicios se cargan con los siguientes comandos ejecutados en master:

$ sudo mkdir ~/.kube
$ sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config
$ sudo kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(sudo kubectl version | base64 | tr -d '\n')"
$ sudo kubectl apply -f "https://raw.githubusercontent.com/danderson/metallb/master/manifests/metallb.yaml"

Para añadir los nodos hemos de crear un usuario, como no disponemos de integración con ldap o directorio activo, usaremos las cuentas de sistema o sa, generamos la cuenta initnode para añadir los nodos con el siguiente yaml:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: null
  name: initnode
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: null
  name: initauth
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:node-bootstrapper
subjects:
- kind: ServiceAccount
  name: initnode
  namespace: kube-system

Una vez generada la cuenta de servicio ejecutamos kubectl get serviceaccount -n kube-system initnode -o=jsonpath="{.secrets[0].name}"
que nos dará el nombre del token y kubectl get secrets "NOMBRE_TOKEN" -n kube-system -o=jsonpath="{.data.token}"|base64 -d nos dará el valor del mismo que hemos de guardar.

Además de crear el usuario hay que crear una configuración de inicio para los nodos, para ello ejecutamos los siguientes comandos:

$ sudo kubectl config set-cluster kubernetes --kubeconfig=~/bootstrap-kubeconfig --certificate-authority=/etc/kubernetes/pki/ca.crt --embed-certs --server
$ sudo kubectl config set-credentials initnode --kubeconfig=~/bootstrap-kubeconfig --token=VALOR_TOKEN
$ sudo kubectl config set-context initnode@kubernetes --cluster=kubernetes --user=initnode --kubeconfig=~/bootstrap-kubeconfig
$ sudo kubectl config use-context initnode@kubernetes --kubeconfig=~/bootstrap-kubeconfig

El archivo generado hemos de copiarlo en todos los nodos en /var/lib/kubelet/bootstrap-kubeconfig y procedemos a iniciar el servicio kubelet en cada nodo que se encargará de contactar con el master y pedir acceso.

sudo systemctl enable kubelet
sudo systemctl start kubelet

Para autorizar el o los nodos en el master hemos de ir al mismo y consultar por las peticiones con el comando kubectl get certificatesigningrequests.certificates.k8s.io
, y autorizarlas con el comando kubectl certificate approve

Elegir entre Docker o Podman para entornos de pruebas y desarrollo

Muchas veces nos encontramos que tenemos muy pocos recursos y necesitamos un entorno para realizar una completa demostración de producto a un cliente.

En esos casos vamos a necesitar simular un entorno de la manera más sencilla y con menos recursos posibles y para ello usaremos contenedores, pero ¿cual es la mejor solución para esos pequeños entornos?

Docker es el entorno de contedores estandar en el mercado, es el más extendido y conforma un conjunto de herramientas extraordinariamente potentes como son un cliente desde linea de comandos, un API server, un gestor del ciclo de vida de los contenedores (containerd), un lanzador de contenedores (runc).

Podman es un entorno de contenedores que no utiliza un servicio y por lo tanto no dispone de un API server, las peticiones se hacen únicamente desde la linea de comandos, lo que tiene ventajas y desventajas que explicaremos al final.

Instalar docker es realmente fácil, ya que docker suministra un script que realiza el proceso de preparar y configurar los requisitos y los repositorios necesario y por último instala y configura docker dejando el servicio listo para usar.

Instalar podman es fácil en un entorno Centos (yum install -y podman para Centos 7 y yum install -y container-tools para Centos 8) pero necesita cierto trabajo en un entorno Debian:

# sudo apt update && sudo apt install -y software-properties-common dirmngr
# sudo apt-key adv --keyserver ha.pool.sks-keyservers.net --recv-keys 0x018BA5AD9DF57A4448F0E6CF8BECF1637AD8C79D
# sudo sh -c "echo 'deb http://ppa.launchpad.net/projectatomic/ppa/ubuntu bionic main' > /etc/apt/sources.list.d/container.list"
# sudo apt update && sudo apt install -y podman skopeo buildah uidmap debootstrap

En nuestro caso hemos utilizado los roles Ansible desarrollados en https://github.com/aescanero/disasterproject, para desplegar dos máquinas virtuales, una con podman y la otra con docker.

En el caso de usar una distribución basada en Debian instalamos Ansible:

$ sudo sh -c 'echo "deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main" >>/etc/apt/sources.list'
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
$ sudo apt-get update && sudo apt-get install -y ansible 

Procedemos a descargar el entorno y configurar Ansible:

$ git clone https://github.com/aescanero/disasterproject
$ cd disasterproject/ansible
$ chmod 600 files/insecure_private_key

Editamos el archivo inventory.yml y le ponemos el siguiente formato:

all:
  children:
    vms:
      hosts:
        MACHINE_NAME1:
          memory: MEMORY_IN_MB
          vcpus: vCPUS_FOR_VM
          vm_ip: "IP_VM_MACHINA_NAME1"
          linux_flavor: "debian|centos"
          container_engine: "docker|podman"
        MACHINE_NAME2:
          memory: MEMORY_IN_MB
          vcpus: vCPUS_FOR_VM
          vm_ip: "IP_VM_MACHINA_NAME2"
          linux_flavor: "debian|centos"
          container_engine: "docker|podman"
      vars:
        network_name: NETWORK_NAME
        network: "VM_NETWORK"

Existen algunas variables globales que cuelgan de «vars:», que son:

  • network_name: Nombre descriptivo de la red de libvirt que crearemos y que además será el nombre del interface que se configurará en el anfitrión KVM y que servirá de puerta de enlace de las máquinas virtuales
  • network: los tres primeros campos de la dirección IPv4 para confirmar una red con máscara 255.255.255.0, las máquinas virtuales deberán tener una IP de dicho rango (menos 0,1 y 255)

El formato de cada equipo cuelga se define por los siguientes atributos:

  • nombre: Nombre descriptivo de la máquina virtual a desplegar, será además el hostname del equipo
  • memory: Memoria de la máquina virtual en MB
  • vcpus: Número de CPUs virtuales en la máquina virtual
  • vm_ip: IP de la máquina virtual, debe pertenecer al rango definido en la variable general «network»
  • linux_flavor: Es la distribución de la máquina virtual, se permiten dos opciones: debian y centos
  • container_engine: (Opcional) Es el motor de contenedores que se puede desplegar en la máquina virtual, se permiten dos opciones: docker y podman

Desplegamos el entorno con:

$ ansible-playbook -i inventory.yml create.yml --ask-become-pass

Como resultado obtenemos dos máquinas virtuales a las cuales podemos acceder vía ssh con ssh -i files/insecure_private_key vagrant@IP_MÁQUINA_VIRTUAL, en cada una de ellas procedemos a lanzar un contenedor, docker run -td nginx y podman run -dt nginx. Una vez desplegados los contenedor nginx, analizamos que servicios se ven ejecutados en cada caso.

  • Docker, arranca 32 procesos para lanzar nginx:
containerd-+-containerd-shim-+-nginx---nginx
                     |                 `-10*[{containerd-shim}]
                     `-8*[{containerd}]
dockerd---9*[{dockerd}]
3194 root      20   0  546332  92220  39252 S  0.0  9.5   0:07.23 dockerd
 2314 root      20   0  463200  41696  25768 S  0.0  4.3   0:00.57 containerd
 3621 root      20   0   32648   5244   4564 S  0.0  0.5   0:00.08 nginx
 3603 root      20   0   11720   4612   3856 S  0.0  0.5   0:00.03 containerd-shim
 3500 root      20   0   20472   2692   1656 S  0.0  0.3   0:00.00 dhclient
 3479 root      20   0   20352   2676   1636 S  0.0  0.3   0:00.00 dhclient
 3656 systemd+  20   0   33100   2508   1468 S  0.0  0.3   0:00.00 nginx
  • Podman, arranca un proceso para lanzar nginx:
conmon-+-nginx---nginx
                 `-{gmain}
 3471 root      20   0   32648   5180   4500 S  0.0  0.5   0:00.06 nginx
 3482 systemd+  20   0   33100   2420   1380 S  0.0  0.2   0:00.00 nginx
 3461 root      20   0   85800   1924   1768 S  0.0  0.2   0:00.00 conmon

Entre las dos máquinas virtuales hay solo 100 MB de diferencia entre ambas:

1,5G docker.qcow2
1,4G podman.qcow2

Como conclusión tenemos los siguientes puntos:

DockerPodman
Gestión de ciclo de vida, por ejemplo reinicio de contenedores que fallan automáticamente, iniciar contenedores automáticamente cuando el equipo se reinicia, ejecuta chequeos sobre los contenedores, iniciar contenedores en un orden determinado, etc.Es compatible con Docker a nivel de CLI, imagen y carga desde registros.
Depende de systemd para gestionar el ciclo de vida de los contenedores.
Requiere más recursos, pero en un entorno de demo/desarrollo con pocos contenedores no debería de requerir importantes recursos.Dispone de más recursos disponibles para los contenedores, lo que es ideal para un entorno de demos en máquinas virtuales.
Ocupa un espacio menor y requiere de menos dependencias.

Podman es un proyecto respaldado por RedHat que está tomando fuerza y muchos servicios de desarrollo se plantean su adopción (por ejemplo el proyecto syslog-ng: https://www.syslog-ng.com/community/b/blog/posts/replacing-docker-with-podman-in-the-syslog-ng-build-container

Más información en como adaptar Podman en Replacing Docker with Podman.

Como dar de alta una máquina virtual en linux con KVM desde la linea de comandos

Si queremos levantar máquinas virtuales en un entorno Linux que no disponga de entorno gráfico podemos levantar máquinas virtuales desde linea de comandos usando una plantilla XML.

Primero debemos tener instalado libvirt y Qemu-KVM que en Ubuntu/Debian se instala con:

$ sudo apt-get install -y libvirt-daemon-system python-libvirt python-lxml

Y en CentOS/Redhat con:

$ sudo yum install -y libvirt-daemon-kvm python-lxml

Para iniciar el servicio haremos $ sudo systemctl enable libvirtd && sudo systemctl start libvirtd

Libvirt nos provee de una poderosa herramienta para gestión de las máquinas virtuales llamada ‘virtsh’, la cual debemos de utilizar para poder gestionar las máquinas virtuales KVM desde la linea de comandos.

Para una máquina virtual necesitamos principalmente tres elementos, el primero es una configuración de red que entre otras cosas provea a las máquinas virtuales de IP vía DHCP. Para ello libvirt necesita de una plantilla XML como la siguiente (que llamaremos net.xml):

<network>
  <name>NOMBRE_DE_RED</name>
  <forward mode='nat'>
    <nat>
      <port start='1' end='65535'/>
    </nat>
  </forward>
  <bridge name='NOMBRE_DEL_BRIDGE' stp='on' delay='0'/>
  <ip address='IP_HOST' netmask='MASCARA_RED'>
    <dhcp>
      <range start='INICIO_RANGO_DHCP' end='FIN_RANGO_DHCP'/>
    </dhcp>
  </ip>
</network>

Cuyos elementos principales son:

  • NOMBRE_DE RED: Nombre descriptivo que vamos a dar a la red, por ejemplo red_pruebas o red_producción.
  • NOMBRE_DEL_BRIDGE: Cada red crea una interfaz en el servidor anfitrión que servirá para dar salida y entrada a los paquetes de dicha red hacia el exterior. Aquí le asignamos un nombre descriptivo que
  • IP_HOST: La IP que dicho interfaz tendrá en el servidor anfitrión y que será la puerta de enlace de las máquinas virtuales
  • MASCARA_RED: Depende de la red, habitualmente para pruebas y demos siempre una clase C (255.255.255.0)
  • INICIO_RANGO_DHCP: Para asignar IPs a las máquinas virtuales libvirt usa un servidor DHCP interno (basado en dnsmasq), aquí definimos la primera IP que podemos servir a las máquinas virtuales.
  • FIN_RANGO_DHCP: Y aquí le decimos la última IP que podemos servir a las máquinas virtuales para su uso.

El segundo elemento es la imagen de la máquina virtual, la imagen se puede crear o descargar, siendo recomendable lo segundo para reducir el tiempo de despliegue. Una fuente de imágenes para máquinas virtuales con KVM/libvirt es Vagrant ( https://app.vagrantup.com/boxes/search?provider=libvirt ), para obtener una imagen de la máquina virtual que nos interese de las que existen en dicha página nos las descargaremos de https://app.vagrantup.com/NOMBRE_APP/boxes/ETIQUETA_APP/versions/VERSION_APP/providers/libvirt.box siendo NOMBRE_APP el nombre de la aplicación que queremos utilizar (p.e. debian), ETIQUETA_APP es la distribución de dicha aplicación (p.e. stretch64) y por último VERSION_APP es la versión de la aplicación (p.e. 9.9.0). Aún así existen repositorios de imágenes específicos como por ejemplo los de CentOs que están en http://cloud.centos.org/centos/7/vagrant/x86_64/images .

Una vez obtenido el archivo libvirt.box, se descomprime con tar -zcf libvirt.box lo que nos genera tres archivos, uno de los cuales es la imagen de la máquina virtual (box.img) que renombraremos a NOMBRE_DESCRIPTIVO.qcow2 y copiaremos en la carpeta estándar para imágenes de máquinas virtuales de libvirt (/var/lib/libvirt/images) y le daremos permisos al usuario libvirt para que pueda gestionar dicha imagen (chown libvirt /var/lib/libvirt/images/NOMBRE_DESCRIPTIVO.qcow2 )

Es importante indicar que si queremos acceder a la máquina virtual vamos a necesitar una clave que la descargamos con wget -O insecure_private_key https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant, que le aplicamos permisos para el acceso solo para el usuario actual ( $ chmod 600 insecure_private_key )

El tercer elemento para crear la máquina virtual una vez creada la red es una plantilla de máquina virtual, que para un sistema operativo reciente tendrá la siguiente forma:

<domain type='kvm'>
  <name>NOMBRE_VM</name>
  <memory unit='MB'>MEMORY_VM</memory>
  <vcpu>CPU_NUMBER</vcpu>
  <os>
    <type>hvm</type>
    <bootmenu enable='no'/>
    <boot dev='hd'/>
  </os>
  <features>
    <acpi/>
    <apic/>
  </features>
  <clock offset='utc'/>
  <on_poweroff>destroy</on_poweroff>
  <on_reboot>restart</on_reboot>
  <on_crash>destroy</on_crash>
  <devices>
    <disk type='file' device='disk'>
      <driver name='qemu' type='qcow2'/>
      <source file='RUTA_ALMACEN_IMAGENES/NOMBRE_IMAGEN.qcow2'/>
      <target dev='vda' bus='virtio'/>
    </disk>
    <interface type='network'>
      <source network='NOMBRE_DE_RED'/>
      <model type='virtio'/>
    </interface>
    <console type='pty'>
      <target type='serial' port='0'/>
    </console>
    <input type='mouse' bus='ps2'>
      <alias name='input0'/>
    </input>
    <input type='keyboard' bus='ps2'>
      <alias name='input1'/>
    </input>
    <graphics type='spice' port='5900' autoport='yes' listen='127.0.0.1'>
      <listen type='address' address='127.0.0.1'/>
      <image compression='off'/>
    </graphics>
    <video>
      <model type='cirrus' vram='16384' heads='1' primary='yes'/>
      <alias name='video0'/>
    </video>
    <memballoon model='virtio'/>
  </devices>
</domain>

Cuyos elementos son:

  • NOMBRE_VM: El nombre descriptivo que nos permitirá identificar la máquina virtual
  • MEMORY_VM: Cuanta memoria en MB le asignamos a la máquina virtual
  • CPU_NUMBER: Cuantas CPU virtuales va a disponer la máquina virtual
  • RUTA_ALMACEN_IMAGENES: La ruta donde hemos guardado la máquina virtual, la estándar es /var/lib/libvirt/images
  • NOMBRE_IMAGEN: Que nombre descriptivo hemos dado a la imagen en el punto anterior
  • NOMBRE_DE_RED: Que nombre descriptivo le hemos dado a la red que va a gestionar la máquina virtual

Una vez creadas las plantillas y desplegada la imagen ejecutamos:

# sudo virsh net-create plantilla_red.xml
# sudo virsh create plantilla_máquina_virtual.xml

Por último comprobamos que funciona, primero identificamos si está iniciado con # sudo virsh list, segundo obtenemos la ip en uso con # sudo virsh net-dhcp-leases NOMBRE_DE_RED y por último accedemos a la IP asignada con # ssh -i insecure_private_key vagrant@IP

Entorno de pruebas mínimo en linux con KVM y Ansible

En los entornos de desarrollo locales siempre hace falta la simulación de entornos más potentes en casos como la realización de demos.

Para ello vamos a seguir siempre la filosofía «ASAP» (As Simple/Small As Posible) y usaremos aquellos servicios para que nuestro linux requiera el menor uso de recursos posibles.

El primer paso es hacernos con un sistema que nos suministre imágenes de la manera más simple posible y nos vamos a encontrar con que Vagrant es una fuente maravillosa de imágenes. Tenemos dos maneras de usarlo:

  1. Descargar de https://www.vagrantup.com/downloads.html e instalar con sudo dpkg -i vagrant_VERSION_x86_64.deb en entornos Debian/Ubuntu o con sudo rpm -i vagrant_VERSION_x86_64.rpm en entornos RHEL/Centos), para obtener una imagen lo más pequeña posible vamos a hacer uso de una debian 9.9.0 con el siguiente comando:
    $ vagrant box add --provider libvirt debian/stretch64
    ==> box: Loading metadata for box 'debian/stretch64'
    box: URL: https://vagrantcloud.com/debian/stretch64
    ==> box: Adding box 'debian/stretch64' (v9.9.0) for provider: libvirt
    box: Downloading: https://vagrantcloud.com/debian/boxes/stretch64/versions/9.9.0/providers/libvirt.box
    box: Download redirected to host: vagrantcloud-files-production.s3.amazonaws.com
    ==> box: Successfully added box 'debian/stretch64' (v9.9.0) for 'libvirt'!
    La imagen descargada la tendremos en ~/.vagrant.d/boxes/debian-VAGRANTSLASH-stretch64/9.9.0/libvirt, en la forma de tres archivos, siendo el que nos interesa box.img que es una imagen con formato QCOW.

  2. Descargar directamente las imágenes que vamos a utilizar, por ejemplo una imagen Centos: http://cloud.centos.org/centos/7/vagrant/x86_64/images/CentOS-7.Libvirt.box y una imagen Debian: https://app.vagrantup.com/debian/boxes/stretch64/versions/9.9.0/providers/libvirt.box
    Para hacer más fácil el despliegue, se ha configurado Ansible para que haga la descarga automáticamente, guarde la imagen en /root/.images (en futuras versiones lo podremos configurar) y al utilice directamente sin que necesitemos tener que hacer nada más.

Lo siguiente que necesitamos es descargar las tareas de Ansible que nos van a permitir lanzar nuestro entorno de pruebas, el «paquete» está formado por un archivo que será realmente importante llamado «inventory.yml» donde vamos a definir realmente como va a ser nuestra demo, está formado por un fichero de «creación» y otro de «destrucción» del entorno virtualizado. El resto de ficheros son variables y funciones (tareas) que ejecutarán lo necesario para levantar nuestro entorno. Procedemos a descargar el entorno:

$ git clone https://github.com/aescanero/disasterproject
$ cd disasterproject/ansible

Dentro del directorio «ansible» veremos un directorio «files» que tiene la clave privada insegura de Vagrant que nos va a servir para acceder a cada una de las máquinas que desplegaremos. Dicha clave se obtiene de https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant, procedemos a cambiar los permisos para que la clave sea aceptada por SSH:

$ chmod 600 files/insecure_private_key

Para poder ejecutar las tareas de Ansible debemos obtener e instalar Ansible siguiendo las instrucciones de la guía de instalación de Ansible (https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html), por ejemplo para Debian hay que seguir las siguientes instrucciones:

$ sudo sh -c 'echo "deb http://ppa.launchpad.net/ansible/ansible/ubuntu trusty main" >>/etc/apt/sources.list'
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
$ sudo apt-get update && sudo apt-get install -y ansible 

Procedemos además a instalar el resto de paquetes que vamos a necesitar, en debian son:

$ sudo apt-get install -y libvirt-daemon-system python-libvirt python-lxml

Y en CentOS son:

$ sudo yum install -y libvirt-daemon-kvm python-lxml

Para iniciar el servicio haremos $ sudo systemctl enable libvirtd && sudo systemctl start libvirtd

En el siguiente paso editaremos el archivo «inventory.yml» que debe tener un formato parecido al siguiente:

all:
  children:
    vms:
      hosts:
        debian:
          memory: 1024
          vcpus: 1
          vm_ip: "192.168.8.2"
          linux_flavor: "debian"
      vars:
        network_name: demo_network
        network: "192.168.8"

En él vemos la definición de la máquina (nombre: debian, memoria en MB, número de vcpus, la ip, y el tipo – por ahora solo debian o centos-) y una serie de valores globales, como un nombre para definir la red, la red sin el último octeto (va a ser clase C, mascará /24), donde network.1 es la IP que se configurará vía DHCP donde la network.1 sirve de puerta de enlace a todos las máquinas virtuales que desplegaremos.

El siguiente paso es lanzar las MVs con Ansible para ello ejecutamos:

$ ansible-playbook -i inventory.yml create.yml --ask-become-pass

Que nos tiene que devolver todos los pasos para la creación de la máquina virtual:

$ ansible-playbook -i inventory.yml create.yml --ask-become-pass
BECOME password: 

PLAY [Main Task to Create Virtual Machines] *************************************************************************************************************************************************************************************************

TASK [libvirt : obtener info de la red] *****************************************************************************************************************************************************************************************************
fatal: [debian -> localhost]: FAILED! => {"changed": false, "msg": "network demo_network not found"}
...ignoring

TASK [libvirt : include_tasks] **************************************************************************************************************************************************************************************************************
included: /home/aescanero/testbed/disasterproject/ansible/roles/libvirt/tasks/create_net.yml for debian

TASK [libvirt : Enable IP forwarding in sysctl] *********************************************************************************************************************************************************************************************
 [WARNING]: The value 1 (type int) in a string field was converted to u'1' (type string). If this does not look like what you expect, quote the entire value to ensure it does not change.

ok: [debian -> localhost]

TASK [libvirt : destroy libvirt network] ****************************************************************************************************************************************************************************************************
ok: [debian -> localhost]

TASK [libvirt : ensure libvirt network is present] ******************************************************************************************************************************************************************************************
changed: [debian -> localhost]

TASK [libvirt : find facts on libvirt networks] *********************************************************************************************************************************************************************************************
ok: [debian -> localhost]

TASK [libvirt : Active network] *************************************************************************************************************************************************************************************************************
changed: [debian -> localhost]

TASK [libvirt : Creates directory] **********************************************************************************************************************************************************************************************************
ok: [debian -> localhost]

TASK [libvirt : Ansible check if image exists.] *********************************************************************************************************************************************************************************************
ok: [debian -> localhost]

TASK [libvirt : Download Debian Image] ******************************************************************************************************************************************************************************************************
skipping: [debian]

TASK [libvirt : Download Centos Image] ******************************************************************************************************************************************************************************************************
skipping: [debian]

TASK [libvirt : Ansible check if vm image exists.] ******************************************************************************************************************************************************************************************
ok: [debian -> localhost]

TASK [libvirt : Unarchive the image] ********************************************************************************************************************************************************************************************************
 [WARNING]: Consider using the unarchive module rather than running 'tar'.  If you need to use command because unarchive is insufficient you can add 'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to
get rid of this message.

changed: [debian -> localhost]

TASK [libvirt : rename image] ***************************************************************************************************************************************************************************************************************
changed: [debian -> localhost]

TASK [libvirt : image chgrp] ****************************************************************************************************************************************************************************************************************
ok: [debian -> localhost]

TASK [libvirt : image chgrp] ****************************************************************************************************************************************************************************************************************
changed: [debian -> localhost]

TASK [libvirt : create_vm] ******************************************************************************************************************************************************************************************************************
 [WARNING]: 'xml' is given - ignoring 'name'

changed: [debian -> localhost]

TASK [libvirt : getMAC] *********************************************************************************************************************************************************************************************************************
changed: [debian -> localhost]

TASK [libvirt : virt_net] *******************************************************************************************************************************************************************************************************************
changed: [debian -> localhost]

TASK [libvirt : start_vm] *******************************************************************************************************************************************************************************************************************
ok: [debian -> localhost]

TASK [libvirt : pause] **********************************************************************************************************************************************************************************************************************
Pausing for 20 seconds
(ctrl+C then 'C' = continue early, ctrl+C then 'A' = abort)
ok: [debian -> localhost]

TASK [os : Obtiene la información de la mv] *************************************************************************************************************************************************************************************************
 [WARNING]: Platform linux on host debian is using the discovered Python interpreter at /usr/bin/python, but future installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.8/reference_appendices/interpreter_discovery.html for more information.

ok: [debian]

TASK [os : Hostname Configuration] **********************************************************************************************************************************************************************************************************
changed: [debian]

PLAY RECAP **********************************************************************************************************************************************************************************************************************************
debian                     : ok=21   changed=9    unreachable=0    failed=0    skipped=2    rescued=0    ignored=1   

Al terminar la ejecución, la maquina/s virtuales están arrancadas y listas para acceder a las mismas, en nuestro ejemplo accedemos a la MV con la IP 192.168.8.2 con:

$ ssh -i files/insecure_private_key vagrant@192.168.8.2

El usuario vagrant dispone de sudo por lo que no tenemos problemas para gestionar la máquina virtual.

Por último un comentario sobre el rendimiento de KVM y VIrtualBox, aunque es cierto que la consola de VirtualBox es eficiente, el rendimiento de está solución de virtualización sufre cuando hay mucho acceso a disco y/o CPU, aunque en la última versión (6.x) ha mejorado claramente, sigue estando por detrás de KVM y no es recomendable para desarrollo o demos. Más información en openbenchmarking: https://openbenchmarking.org/result/1812203-PTS-VIRTUALI66 y también unos resultados de iozone (/usr/bin/iozone -s24m -r64 -i 0 -i 1 -+u -t 1) que nos indican mejora rendimiento en 3 de las 4 pruebas realizadas:

KVMVirtualBox
Avg throughput per process Avg throughput per process
Throughput for 1 initial writers
922105.38 kB/sec
Throughput for 1 initial writers
712577.69 kB/sec
Mejor
22.7%
Throughput for 1 rewriters
1097535.38 kB/sec
Throughput for 1 rewriters
1244981.12 kB/sec
Peor
13.4%
Throughput for 1 readers
2971712.50 kB/sec
Throughput for 1 readers
1833079.75 kB/sec
Mejor
38.1%
Throughput for 1 re-readers
2219869.75 kB/sec
Throughput for 1 re-readers
559970.25 kB/sec
Mejor
74.7%

Navegación de entradas

1 2
Volver arriba