Uno de los conceptos o tecnologías de moda y con más éxito alrededor de dockers/contenedores es sin duda Kubernetes. Kubernetes es una tecnología creada por Google y sirve para la gestión y orquestación de contenedores Docker. Google utiliza Dockers para muchos de sus servicios como Gmail o Maps.
Lo que te permite Kubernetes es que te olvides de la infraestructura y pienses tan solo en aplicaciones y en cómo empaquetarlas. Basado en este principio todos los conceptos que usa están relacionados con este propósito.
Los conceptos clave de Kubernetes son:
- Pods: Conjunto de contenedores y volúmenes.
- Replication Controllers: Gestor de Pods que asegura que están levantadas las réplicas y permite escalar de forma fácil. Una réplica es una copia exacta de un Pod. Levanta Pods en caso de fallos o reinicios.
- Service: Define como acceder a un grupo de Pods.
Para nuestro artículo vamos a montar un cluster de Kubernetes sobre Vagrant y VirtualBox con 2 nodos, y vamos a instalar un blog basado en Ghost.
Cómo empezar
La forma mas fácil de comenzar es montándote un entorno en local utilizando VirtualBox y Vagrant. Las instrucciones están basadas en Ubuntu pero será sencillo replicarlo para cualquier otra distribución.
Paso 1 - Instalación de requerimientos previos
Como ya hemos comentado necesitamos:
- Virtualbox, que es un paquete de virtualización desarrollado por Oracle;
- Vagrant que es una herramienta que facilita la creación de entornos virtuales portables, reproducibles y ligeros.
El tooling que nos da Kubernetes está preparado para trabajar con estos dos paquetes. Para instalarlo hay que ejecutar el siguiente comando :
sudo apt-get install vagrant virtualbox wget
También podremos necesitar algún paquete más si no los tememos ya instalados:
sudo apt-get install wget tar
Una vez instalados todos los requerimientos podemos continuar con la instalación de Kubernetes en nuestra máquina.
Paso 2 - Descargar y Arrancar un entorno Kubernetes
Hay dos formas de hacerlo:
- Descargando la release del repositorio de Github y ejecutándolo manualmente (más control).
- Ejecutando un script que descarga la ultima versión.
Ambos dan el mismo resultado. Si queremos probar una versión en concreto iremos al primer método si solo queremos probar la ultima pues iremos por el segundo. Para nuestro entorno de ejemplo vamos a instalar, ademas del master, 2 nodos adicionales. Por ello deberemos exportar la variable NUM_NODES
con valor '2'. La variable KUBERNETES_PROVIDER
tambien la definiremos con valor 'vagrant' para que los scripts, que también valen para otros tipos de virtualización e IaaS, sepan que nuestro entorno será sobre vagrant.
Método 1 - Desde releases de Github
- Iremos a la página de releases del repositorio de Kubernetes: https://github.com/kubernetes/kubernetes/releases/
Descargamos y descomprimimos la versión que queramos, por ejemplo, de la siguiente manera:
wget https://github.com/kubernetes/kubernetes/releases/download/v1.3.0/kubernetes.tar.gz
(La versión 1.3.0 ocupa ~1.4GB)
- Descomprimimos:
tar xvfz kubernetes.tar.gz
Este último comando nos creará una carpeta llamada kubernetes con todo lo necesario. No situamos en la carpeta kubernetes y ejecutamos:
export KUBERNETES_PROVIDER=vagrant
export NUM_NODES=2
./cluster/kube-up.sh
Este comando tarda un poco, porque provisiona las máquinas con Vagrant en VirtualBox y tiene que descargar las imágenes, y configurar el entorno.
Método 2 - Con scripts automáticos de Kubernetes
- Simplemente abrimos un terminal y ejecutamos los siguientes comandos.
export KUBERNETES_PROVIDER=vagrant
export NUM_NODES=2
wget -q -O - https://get.k8s.io | bash
En la url https://get.k8s.io está el script que busca la última versión, la descarga y ejecuta la instalación del entorno.
Paso 3 - Comprobando que el cluster está correctamente levantado
Si habéis llegado a este punto seguramente habéis tenido que esperar un rato un poco largo :) . Para empezar a interactuar con Kubernetes el comando básico es kubectl
. En esta versión lo encontrareis en la ruta ./cluster/kubectl.sh
.
Para comprobar que nuestro cluster esta correctamente levantado podéis ejecutar los siguientes comandos:
- Comprobar que los nodos están
Ready
:
$ ./cluster/kubectl.sh get nodes
NAME STATUS AGE
kubernetes-node-1 Ready 1h
kubernetes-node-2 Ready 1h
- Comprobar que los Pods de sistema (namespace kube-system) están ok:
$ ./cluster/kubectl.sh get --namespace=kube-system pods
NAME READY STATUS RESTARTS AGE
heapster-v1.1.0-2101778418-dsjz0 4/4 Running 0 19m
kube-dns-v17-jz2t4 3/3 Running 0 1h
kube-proxy-kubernetes-node-1 1/1 Running 0 1h
kube-proxy-kubernetes-node-2 1/1 Running 0 18m
kubernetes-dashboard-v1.1.0-buseh 1/1 Running 0 1h
monitoring-influxdb-grafana-v3-a21ln 2/2 Running 0 1h
- Ver los logs de todos los namespaces en tiempo real:
$ ./cluster/kubectl.sh get --all-namespaces events -w
NAMESPACE LASTSEEN FIRSTSEEN COUNT NAME KIND SUBOBJECT TYPE REASON SOURCE MESSAGE
kube-system 2016-07-07T10:25:22+02:00 2016-07-07T10:25:22+02:00 1 heapster-v1.1.0-2101778418-dsjz0 Pod spec.containers{heapster-nanny} Normal Created {kubelet kubernetes-node-2} Created container with docker id 194f461e3f9f
kube-system 2016-07-07T10:25:24+02:00 2016-07-07T10:25:24+02:00 1 heapster-v1.1.0-2101778418-dsjz0 Pod spec.containers{heapster-nanny} Normal Started {kubelet kubernetes-node-2} Started container with docker id 194f461e3f9f
kube-system 2016-07-07T10:25:24+02:00 2016-07-07T10:25:24+02:00 1 heapster-v1.1.0-2101778418-dsjz0 Pod spec.containers{eventer-nanny} Normal Created {kubelet kubernetes-node-2} Created container with docker id fc3f52da9e6f
kube-system 2016-07-07T10:25:24+02:00 2016-07-07T10:25:22+02:00 2 heapster-v1.1.0-2101778418-dsjz0 Pod spec.containers{heapster-nanny} Normal Pulled {kubelet kubernetes-node-2} Container image "gcr.io/google_containers/addon-resizer:1.3" already present on machine
kube-system 2016-07-07T10:25:26+02:00 2016-07-07T10:25:26+02:00 1 heapster-v1.1.0-2101778418-dsjz0 Pod spec.containers{eventer-nanny} Normal Started {kubelet kubernetes-node-2} Started container with docker id fc3f52da9e6f
..........
- Ver información del cluster:
$ kubectl cluster-info
Kubernetes master is running at https://10.245.1.2
Heapster is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/heapster
KubeDNS is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/kube-dns
kubernetes-dashboard is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard
Grafana is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/monitoring-grafana
InfluxDB is running at https://10.245.1.2/api/v1/proxy/namespaces/kube-system/services/monitoring-influxdb
Para poder acceder a estas URLs hay que utilizar algún método de los explicados en la documentación. El método más sencillo es el de levantar un 'proxy' con el comando:
$ ./cluster/kubectl.sh proxy --port=8080
Starting to serve on 127.0.0.1:8080
De este modo, accediendo a localhost:8080 en nuestro navegador, podemos acceder a las URLs importantes de nuestro cluster, como el Dashboard:
http://localhost:8080/api/v1/proxy/namespaces/kube-system/services/kubernetes-dashboard
Con este proxy podremos luego acceder también a nuestros servicios.
Otra de las URLs importantes es la de Cockpit (un dashboard para gestionar clusters de Kubernetes u Openshift). Para acceder podemos hacerlo en la URL https://10.245.1.2:9090 (usuario vagrant, password vagrant)
Para más información del comando kubectl
podéis ir al link
Paso 4 - Lanzar nuestra primera aplicación sobre Kubernetes (enmilocalfunciona.io)
Nuestro blog enmilocalfunciona.io esta basado en el gestor de blog Ghost. Nuestro ejemplo se va a basar en este software desarrollado en Node.js.
Para montar nuestra app, vamos a montar un Replication Controller para poder poner más instancias balanceadas. Un Replication Controller es un mecanismo de Kubernetes que asegura que un Pod tiene levantado un número determinado de replicas. Si necesitamos más replicas el Replication Controller levanta más réplicas, si necesitamos menos, las mata, si alguna de ellas falla y muere entonces levanta nuevas réplicas para mantener el número definido. Es conveniente que incluso para pequeñas aplicaciones con un solo Pod lo definamos con un replication controller ya que en caso de fallo nos asegura que nuestra app se levanta automáticamente.
La imagen docker de ghost utiliza por defecto almacenamiento local pero como queremos que todas las instancias sirvan el mismo contenido vamos a definir en Kubernetes un volumen. El volumen no va a ser mas que el uso de un NFS que tendremos que haber montado al efecto en la propia máquina.
¡Empecemos!
Creación de Persistence Volumen
Este es el YAML que tenemos que crear para poder crear en Kubernetes un Persistent Volume y un Persistent Volume Claim para poder usarlo en nuestros pods:
volume.yml:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-cluster
spec:
capacity:
storage: 200Mi
accessModes:
- ReadWriteMany
nfs:
server: 10.245.1.1
path: "/var/nfsshare/ghost"
persistentVolumeReclaimPolicy: Retain
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfs-cluster
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Mi
Ahora tendremos que crearlo en nuestro cluster:
$ ./cluster/kubectl.sh create -f volume.yaml
persistentvolume "nfs-cluster" created
persistentvolumeclaim "nfs-cluster" created
$ ./cluster/kubectl.sh get persistentvolume
NAME CAPACITY ACCESSMODES STATUS CLAIM REASON AGE
nfs-cluster 200Mi RWX Bound default/nfs-cluster 39s
$ ./cluster/kubectl.sh get persistentvolumeclaim
NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
nfs-cluster Bound nfs-cluster 0 43s
Vemos como ya esta creado y listo para usar
Creación del Replication Controller
Ahora vamos a crear nuestra aplicación definiendo como se puede ver el número de replicas y el uso de nuestro volumen compartido.
ghost.yml:
apiVersion: v1
kind: ReplicationController
metadata:
name: ghost
labels:
purpose: enmilocalfunciona.io
spec:
replicas: 1
template:
metadata:
labels:
app: ghost
purpose: enmilocalfunciona.io
spec:
containers:
- name: ghost
image: ghost:latest
resources:
limits:
cpu: 200m
memory: 50Mi
env:
- name: GET_HOSTS_FROM
value: dns
ports:
- containerPort: 2368
volumeMounts:
# name must match the volume name below
- name: ghost-data
mountPath: "/var/lib/ghost"
volumes:
- name: ghost-data
persistentVolumeClaim:
claimName: nfs-cluster
Ahora lo creamos en nuestro cluster:
$ ./cluster/kubectl.sh create -fghost.yml
replicationcontroller "ghost" created
$ ./cluster/kubectl.sh get rc
NAME DESIRED CURRENT AGE
ghost 1 1 34s
$ ./cluster/kubectl.sh get pods -l app=ghost
NAME READY STATUS RESTARTS AGE
ghost-7a6pz 1/1 Running 0 2m
Una vez creada nuestra aplicación podemos crear el servicio para poder acceder desde fuera:
Creación del Service
Definimos un Service de tipo LoadBalancer y le refenciamos a nuestra app (app: ghost). Aquí puedes ver los tipos de servicios que puedes publicar en Kubernetes:
service.yml:
apiVersion: v1
kind: Service
metadata:
labels:
name: ghost-lb
purpose: enmilocalfunciona.io
name: ghost-lb
spec:
ports:
- port: 2368
selector:
app: ghost
type: LoadBalancer
Lo ejecutamos:
$ ./cluster/kubectl.sh create -f service.yml
service "ghost-lb" created
Pues ya tenemos lista nuestra aplicación. Si tenemos levantado el proxy que hicimos anteriormente podremos acceder a nuestra app con la URL:
http://localhost:8080/api/v1/proxy/namespaces/default/services/ghost-lb/
Vamos a jugar con las réplicas
Ahora vamos a ir aumentando nuestro número de réplicas con el siguiente comando:
$ cluster/kubectl.sh scale rc ghost --replicas=2
replicationcontroller "ghost" scaled
$ ./cluster/kubectl.sh get pods -l app=ghost
NAME READY STATUS RESTARTS AGE
ghost-7a6pz 1/1 Running 0 8m
ghost-fqx0c 0/1 ContainerCreating 0 5s
$ ./cluster/kubectl.sh get pods -l app=ghost
NAME READY STATUS RESTARTS AGE
ghost-7a6pz 1/1 Running 0 9m
ghost-fqx0c 1/1 Running 0 30s
Ahí tenemos nuestras 2 réplicas balanceadas. Podemos probar con ApacheBench a ver si mejoramos nuestra disponibilidad o no :)
$ ab -n 100 -c 10 http://localhost:8080/api/v1/proxy/namespaces/default/services/ghost-lb/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/
Benchmarking localhost (be patient).....done
Server Software:
Server Hostname: localhost
Server Port: 8080
Document Path: /api/v1/proxy/namespaces/default/services/ghost-lb/
Document Length: 4948 bytes
Concurrency Level: 10
Time taken for tests: 21.311 seconds
Complete requests: 100
Failed requests: 3
(Connect: 0, Receive: 0, Length: 3, Exceptions: 0)
Total transferred: 515466 bytes
HTML transferred: 493966 bytes
Requests per second: 4.69 [#/sec] (mean)
Time per request: 2131.100 [ms] (mean)
Time per request: 213.110 [ms] (mean, across all concurrent requests)
Transfer rate: 23.62 [Kbytes/sec] received
Y esta es una tabla con los resultados según he ido aumentando instancias:
Instancias | Total Time | Requests per second | Time per request | Transfer rate |
---|---|---|---|---|
1 | 47.357 | 2.11 | 4735.691 | 10.59 |
2 | 21.311 | 4.69 | 2131.100 | 23.62 |
4 | 12.201 | 8.20 | 1220.063 | 41.26 |
Pues hemos terminado. En siguientes artículos de esta serie iremos viendo más aspectos o funcionalidades de Kubernetes. ¡Síguenos en Twitter para estar al día de los siguientes artículos!