Después de ver la teoría y aprender que es Kubernetes ahora vamos a ver los primeros pasos.

Los primeros son tareas comunes en Kubernetes (Instalar, ver la información, debugging y diferencias entre comandos). Luego ya entramos en como hacer cada objeto dentro de nuestro Clúster de Kubernetes.

Instalar Kubernetes

Este orquestador de contenedores se puede instalar en casi todos los sistemas operativos y procesadores virtualizables. Incluso en una Raspberry Pi puedes instalar Kubernetes.

Lo podemos seguir de la documentación oficial para instalar Kubernetes. También podemos instalar MiniKube que es indicado cuando solo tenemos un nodo.

Aunque lo normal es utilizar los proveedores Cloud para sacarle el máximo partido. Tenemos 300$ gratis durante 12 meses en Google Cloud. También tenemos 50€ en DigitalOcean al registrarse. Y en Azure tenemos 170€.

Ver la información básica

Ya tenemos el Clúster de Kubernetes instalado (o solo una máquina) y para comprobar que todo esta correcto (Ready) utilizamos:

kubectl cluster-info
kubectl get nodes

Con el comando get podemos ver también los pods o los cualquier otro objeto. El primer comando es para la estructura general del nodo maestro. El segundo es específicamente para el estado de los nodos.

Si hay algún fallo

Es típico que algún nodo no este listo (Ready) y de error (Not Ready). Como hay cientos de posibilidades vamos a analizar en especifico que pasa. Es lo que se conoce como Debugging, en español Depuración de programa. Lo primero es ver el estado de los nodos:

kubectl get nodes

Y ahora vamos a ver un poco más sobre ellos. Después de este comando podemos poner el nombre del nodo solo para ver la información de él o dejarlo así y ver la de todos:

kubectl describe nodes

Buscamos donde pone Events y ahí podemos ver el tipo, si es un error (Warning), la razón y el mensaje.

El proceso es el mismo para los pods. Solo que cambiamos en los anteriores comandos nodes por pods. Ademas podemos utilizar el siguiente comando para ver los eventos específicos del interior de los contenedores del pod.

 kubectl logs (nombre-del-pod)

A su vez, también podemos ver los deployments. Al igual que antes utilizamos get y describe:

kubectl get deployments
kubectl describe deployment

Diferencia entre apply y create

La diferencia entre kubectl apply -f archivo.yaml y kubectl create -f archivo.yaml es muy sutil. Ambos puedes crear los objetos pero con el apply puedes modificar después el archivo YAML.

Después de un apply puedes hacer otro apply para cambiar la configuración existente. Si hacemos un create tenemos que borrarlo primero y volver a crearlo (aunque puedes sobreescribirlo con replace).

Básico en cualquier objeto

Dentro del archivo YAML para crear cualquier objeto tenemos que escribir (o hacer un CTRL+C y CTRL+V de toda la vida) varias especificaciones. Si olvidamos o necesitamos voler a ver cualquiera podemos usar el comando explain. Este comando se puede aplicar directamente al objeto o a una epecificacion tanto general como especifica.

kubectl explain pod
kubectl explain pod.apiVersion

Ademas nos da el enlace directo hacia cada departamento de la documentación oficial.

  • apiVersion:

Podemos modificar la API e incluso usar una propia. Pero lo más normal es usar la v1 o v1beta1.

  • kind:

Podemos representar cualquier Objeto: Pod, ReplicaSet, Service, Namespace, Node.

  • metadata:

Dentro de metadata debe incluir obligatoriamente:

    • name:
  • El nombre de este objeto Pod.
    • namespace:
  • En realidad este lo podemos dejar sin rellenar y va a utilizar por defecto (default) como NameSpace.
    • uid:
  • Al igual que namespace, podemos no ponerlo manual. Si no lo ponemos en el archivo YAML creará su propio identificador. Lo habitual es no poner ni el namespace (a menos que quieras especificar uno en concreto) ni el uid.

Y dentro de metadata puede tener opcionalmente:

    • labels:
  • Son necesarias para organizar o luego utilizar otros objetos conjuntamente. Se pueden poner tantas etiquetas como queramos.
  • spec:

Básicamente son las especificaciones de cada contenedor del Pod/ReplicaSet/Deploy. Es muy configurable así que solo vamos a ver lo básico ahora:

    • containers:
  • Indica, en formato array (que podemos añadir tantos como queramos con el símbolo -), que va crear 1 contenedor o varios. Dentro podemos indicar las características de dicho contenedor específico.
      • image:
    •  La ruta de la imagen para el contendor. Podemos utilizar las de Docker Hub, tanto del repositorio oficial o de uno privado, también imágenes locales. Se pueden usar tags. Igual que en Docker.
      • name:
    •  El nombre de cada contenedor. Puede ser el mismo que el de la imagen o personalizarlo.
    • initContainers:
  • Son también contenedores pero que se ejecutan solo durante el inicio del Pod. Se suele usar la imagen del busybox para hacer tareas por su bajo peso. Tiene las mismas características que un contenedor normal.

Filtrado de labels (etiquetas) con Selectors

Bueno más adelante vamos a ver otros usos de las etiquetas pero el del filtrado también es muy importante cuando nuestro Clúster crece. El filtrado lo hacemos a los objetos que queramos utilizando -l o --selector=' '

kubectl get pod -l tipo=proyectoweb
kubectl get pod --selector='tipo=proyectoweb'

Admite el filtrado de varias etiquetas (añades más con comas) y los operadores binarios =, == y !=

kubectl get pod -l tipo!=proyectoweb,rama=desarrollo

Creación del primer Pod

Como ya hemos visto lo que es un Pod en la teoría, vamos a crear uno. Lo normal es que dentro de un Pod haya solo un contenedor (aunque puede haber más si necesitas, por ejemplo, compartir volúmenes entre ellos o van a interactuar directamente sin necesidad de especificar la IP de cada uno).

Ejemplo YAML de un Pod

Recordamos que el lenguaje YAML tiene 2 normas importante. Hay que respetar los espacios (no recomendado usar tabulaciones) y hay que respetar las mayúsculas. También recordamos que el - en YAML significa un array, por lo que podemos añadir tantos (por ejemplo contenedores) como queramos tan solo añadiendo más.

En este ejemplo vamos a crear 2 contenedores en un mismo Pod, con Nginx (servidor web) y CentOS (un sistema operativo).

apiVersion: v1
kind: Pod
metadata:
 name: ejemplo
spec:
 containers:
 - name: web
   image: nginx
   ports:
   - containerPort: 80
 - name: centos
   image: centos
   command: ["bin/sh", "-c", "while : ;do curl http://localhost:80/; sleep 10; done"]

Como nota, containerPort es meramente informativo, si no lo pusiéramos los contenedores al estar en un mismo Pod se exponen automáticamente (gracias a ClusterIP).

Primeros pasos con Kubernetes 0

kubectl create -f archivo.yaml

En el siguiente comando podemos añadir -o wide para ver más información. Si lo ejecutamos al segundo vemos como están creándose (ContainerCreating).

kubectl get pods

Podemos ver si ha sido todo correcto con logs. Con el -c vamos a ver concretamente el contenedor centos para ver el resultado del curl. Si no te acuerdas puedes ver rápidamente el nombre de los contenedores si no pones -c centos.

kubectl logs ejemplo -c centos

Y por último para borrar el contenedor.

kubectl delete pod ejemplo

Creación del primer ReplicaSet

Elimina las caídas de los nodos gracias a la replicación de estos. Como hemos visto en la teoría utiliza las etiquetas (labels) para saber que pods tiene que replicar. Puede crear sus propios pods con template.

Lo único que cambia de la estructura del pod es la primera parte a la hora de crear el ReplicaSet. El kind (tipo) ahora es ReplicaSet y dentro del spec del ReplicSet tenemos 2 nuevas especificaciones que se llaman:

  • replicas: Indicamos el número de contenedores totales que queremos tener simultáneamente.
  • selector:

Ejemplo YAML de un ReplicaSet

 

apiVersion: apps/v1
kind: ReplicaSet
metadata:
 name: ejemplo2
spec:
 replicas: 3
 selector:
  matchLabels:
   proyecto: ejemplo
 template:
  metadata:
   name: nginx
   labels:
    proyecto: ejemplo
    utilidad: web
    cualquier: otracosa
  spec:
   containers:
    - name: web
      image: nginx
      ports:
      - containerPort: 80

Pruebas que podemos hacer para comprobar que funciona: Podemos borrar un Pod y ver como lo crea al instante. Podemos borrar el ReplicaSet, crear un Pod con las etiquetas (labels) y luego crear el ReplicaSet y así vemos como utiliza el Pod ya creado y hace solo 2 (en mi caso de ejemplo) replicas. Podemos

Recuerda que las etiquetas pueden ser totalmente personalizadas. Y si quieres poner números utiliza las comillas.

Creación del primer Deployment

Tal y como vimos en la teoría, el Deployment es el siguiente nivel. Controla tanto Pods como ReplicaSet. Y añade la funcionalidad de la liberación continua (rolling updates) con lo que en vez de sustituir todo el programa, sustituye solo la parte actualizada. También añade la vuelta atrás (roll back).

 

Ejemplo YAML de un Deployment

 

apiVersion: apps/v1
kind: Deployment
metadata:
 name: ejemplo3
 labels:
  proyecto: ejemplo3
spec:
 replicas: 3
 selector:
  matchLabels:
   proyecto: ejemplo3
 template:
  metadata:
   labels:
    proyecto: ejemplo3
  spec:
   containers:
   - name: nginx
     image: nginx:1.7.9
     ports:
     - containerPort: 80

Ahora ya podemos ver con un kubectl get tanto los Pods como el ReplicaSet.

Services

Recuerda de la teoría que los Pods son efímeros. Por lo tanto sus IPs también. Si un Pod se cae y se levanta otro, su IP puede cambiar y si lo tenemos conectado con otra aplicación esta dejará de funcionar.

Para que esto no pase utilizamos los Services. Vamos a volver a ver los tipos pero en práctica. Las IPs de las imágenes son simplemente ilustrativas, según cada configuración del clúster.

ClusterIP

Interno. Comunicación entre varios pods, ReplicaSet, Deployments. En vez de tener que configurar cada comunicación hacia cada IP simplemente usamos un Service ClusterIP que nos va a dar una IP "fija" por decirlo así.

En el ejemplo de abajo vemos como hay varios Pods de diferentes aplicaciones. Queremos agrupar los de tipo base datos bajo una misma IP para que los de tipo WordPress se comuniquen solo a esa IP.

Dentro del servicio podemos configurar la redirección de los puertos que queramos.

Primeros pasos con Kubernetes 2

NodePort

Ya no es solo interno. Ahora podemos acceder a una aplicación desde el exterior. Solo tenemos que conectarnos a la IP de cualquier nodo y al puerto que le haya asignado por defecto el servicio.

Primeros pasos con Kubernetes 4

LoadBalancer

Le suma un paso más al Service NodePort. Se suele decir que solo se puede hacer en empresas Cloud ya que en local es difícil tener las medios para que la carga este realmente balanceada.

Como decíamos, le suma un paso más al anterior Service. Con el Service LoadBalancer puedes acceder a la aplicación de forma externa pero sin necesidad de recordar el puerto asignado por defecto de cada nodo.

Primeros pasos con Kubernetes 6

 

Hay una excepción que puedes crear un Ingress-Controller para gestionar varios LoadBalancer. Pero eso lo veremos más adelante. Ahora vamos a ver como crear los 3 tipos de Services.

Ejemplo YAML de un Service ClusterIP

Primero crear el Service (el orden en realidad no importa):

apiVersion: v1
kind: Service
metadata:
  name: nuestro-servicio-para-nginx-cip
spec:
  selector:
    proyecto: ejemplo3
    utilidad: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      name: http

Como podemos ver no especificamos ningún tipo (type) ya que ClusterIP es la opción por defecto. Ahora vamos a crear un ReplicaSet, aunque podía ser un simple Pod o un Deployment, con las mismas etiquetas (labels) para que lo utilice:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      proyecto: ejemplo3
      utilidad: web
      version: "0.1" 
  template:
    metadata:
      name: nginx
      labels:
        proyecto: ejemplo3
        utilidad: web
        version: "0.1"
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

Ahora ya podríamos ver la IP interna asignada con kubectl get service y para ver también más datos, como los EndPoints (por decirlo así, son como la IP de cada Pod pero asignada por la API) utilizamos:

kubectl describe service nuestro-servicio-para-nginx

Primeros pasos con Kubernetes 8

Con ambos comandos vamos a poder ver la IP interna. Kubernetes guarda esa IP en una variable de entorno así que podemos utilizar esta variable o utilizar directamente la IP.

${NOMBRE DEL SERVICIO}_SERVICE_HOST
${NOMBRE DEL SERVICIO}_SERVICE_PORT
${NOMBRE DEL SERVICIO}_PORT
${NOMBRE DEL SERVICIO}_PORT_${PUERTO}_${PROTOCOLO}
${NOMBRE DEL SERVICIO}_PORT_${PUERTO}_${PROTOCOLO}_PROTO
${NOMBRE DEL SERVICIO}_PORT_${PUERTO}_${PROTOCOLO}_PORT
${NOMBRE DEL SERVICIO}_PORT_${PUERTO}_${PROTOCOLO}_ADDR

También al crear cualquier Service el servidor DNS le asigna un nombre personalizado (siempre que tengamos el add-on). Puedes ver más información sobre esto en la documentación oficial.

Para hacer la comprobación y utilizar ya de paso las variables podemos hacer un Pod rápido y luego ver con logs:

apiVersion: v1
kind: Pod
metadata:
  name: comprueba-cip
spec:
  containers:
  - name: centos
    image: centos
    command: ["/bin/sh", "-c", "while : ;do curl http://${NUESTRO_SERVICIO_PARA_NGINX_CIP_SERVICE_HOST}:80/; sleep 10; done"]

Recuerda las variables aunque las tienes que declarar con barra bajas (_) siempre. En nuestro ejemplo el Service se llama nuestro-servicio-para-nginx-cip pero la variable va a pasar a llamarse NUESTRO_SERVICIO_PARA_NGINX_CIP a parte de lo que queramos indicar. En el ejemplo solo el host.

Primeros pasos con Kubernetes 10

Ejemplo YAML de un Service NodePort

Primero vamos a borrar solo el anterior servicio ClusterIP nuestro-servicio-para-nginx-cip y vamos a reutilizar el mismo ejemplo de ReplicaSet, así que no hace falta borrarlo, solo asignamos los selector con las mismas labels.

kind: Service
apiVersion: v1
metadata:
  name: nginx-nodeport
spec:
  type: NodePort
  selector:
    proyecto: ejemplo3
    utilidad: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Ya podrías acceder con el puerto y el nodo. Para ver el puerto utilizamos:

kubectl get services

Y para ver la IP externa de los nodos utilizamos:

kubectl get nodes -o wide

http://IP DEL NODO:PUERTO ASIGNADO POR EL SERVICIO

Como podemos ver en el YAML lo que cambiaría sería que ahora ya tenemos que especificar el tipo (type) de Service que es.

Ejemplo YAML de un Service LoadBalancer

Como decíamos, este tipo es especifico en Cloud (Azure, AWS, Google, etc).

kind: Service
apiVersion: v1
metadata:
  name: nginx-loadbalancer
spec:
  type: LoadBalancer
  selector:
    proyecto: ejemplo3
    utilidad: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80

Una vez creado ya solo tenemos que ejecutar un comando para ver la IP externa puesta por el proveedor Cloud. Esperamos un poco mientras lo crea pondrá en External IP <pending>.

kubectl get services

Y accediendo a esa IP ya podemos ver nuestra aplicación web sin necesitad de puertos.

Primeros pasos con Kubernetes 12

Un consejo es qué en vez de poner todo el rato kubectl se puede poner tan solo k y ya lo interpreta(o sino usar un alias clásico de Linux: alias k=kubectl). Esto ahorra mucho tiempo. También hay otros como en vez de Pod poner po, en vez de ReplicaSet poner rs y en vez de service poner svc. Podemos utilizar tanto el plural como el singular para los objetos (daría igual Pod que Pods).

 

Más información: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.10

Puntuación