Kubernetes – Apuntes del CKA

Bueno, aquí dejo algunos apuntes (ojo, ésto no es una guía ni una biblia; son sólo eso, apuntes) tras repasarme los cursos «Kubernetes Quick Start» y «Cloud Native Certified Kubernetes Administrator (CKA)» de LinuxAcademy.com

Cómo instalar Docker + Kubernetes from scratch en CentOS

Usando Flannel para la gestión de la red.

Dejo un copy&paste de las instrucciones originales de LinuxAcademy.

Nota:
Al final k8s es una capa encima de docker que usa la API de docker para manipularlo. Puedes correr un docker ps en las instancias para ver exactamente qué está pasando por debajo de k8s (en docker)

La arquitectura del cluster

Master nodes

  • API Server: expone la API de k8s.
  • Scheduler: asigna tu aplicación a un worker.
  • Controller Manager: mantiene el cluster (node failures, número deseado de Pods, etc.)
  • etcd: mantiene la base de datos con la configuración del cluster.

Worker nodes (responsables de correr y monitorizar las aplicaciones):

  • kubelet: corre y gestiona los containers.
  • kube-proxy: mantiene el listado de endpoints y actualiza iptables para balancear el tráfico entre componentes de una aplicación.
  • container runtime: docker, rkt, containerd.

Los componentes no hablan entre sí directamente, si no que siempre pasan por el API Server.
kubelet es el único componente que corre como servicio en el sistema, y no como Pod.

API

Un objeto de k8s tendrá una sección «spec» con la declaración del estado que queremos, y otra sección «status» con el estado real. k8s se asegurará de que el estado hace match con la especificación. Ésto podemos verlo con el siguiente comando:

En el yaml original que define el objeto, no tendremos la sección «status» obviamente, únicamente el «spec» y en función del tipo de objeto declarado (deployment, Pod, etc.) también un «spec» para el container.

Podemos filtrar objectos de k8s con «–field-selector» tal que así:

Los objetos de k8s pueden filtrarse también mediante etiquetas (o labels) y usar estas labels para aplicar acciones en grupo

KUBECTL Basic commands

Un namespace vendría a ser un «cluster virtual» o una separación virtual entre entornos. K8s crea por defecto los namespaces default, kube-public y kube-system.

Los ficheros yaml de definición de objetos de k8s, pueden tener un atributo «kind» que indica el tipo de recurso de k8s que describe (un Pod, servicio, replica-set, etc.) con lo que no haría falta especificarlo durante el «create». Ésto además, permite tener todos los yaml para los diferentes servicios y deployments en un folder, y directamente pasarle ese folder en el momento del create para que kubectl se encargue de crear todos los recursos definidos

Pods

Tanto el Pod como sus containers tienen la misma IP (comparten el espacio de IPs).
Los containers comparten el espacio de puertos del Pod, es decir, cuando corren un programa escuchando en el 80 localhost, automáticamente el puerto se expone en el Pod (IP-pod:80).
Los containers de un mismo Pod, pueden comunicarse entre ellos vía localhost (si el container A expone el puerto 80 y el B el 3306, el A puede comunicarse con el B símplemente haciendo localhost:3306).

Networking

La gestión de direcciones IP y del networking se hace via plugins de red. K8s necesita pues, de una capa por encima, CNI (Container Network Interface), la cual simplifica las comunicaciones entre containers en un cluster, gestionando las IPs, encapsulando paquetes y mappings en el espacio de usuario. Es necesario hacerle saber a kubelet que vamos a usar un CNI. Estas instrucciones variarán en función del plugin que usemos.

Flannel CNI

  • Flannel es bastante sencillo de usar y no necesita post-configuración
  • Es un binario (flanneld) corriendo directamente en el master y los nodos (que no en los containers) que además modifica IPtables para permitir routing y la comunicación entre Pods y nodos
  • Ejecutando un «ip route» en el master, veremos las rutas que ha establecido flannel para el master y los nodos
  • Lanzando un «kubectl pods –all-namespaces -o wide» en el master vemos que todos los Pods principales, corren en el espacio de IPs del host, aunque el Pod de los DNS corre dentro del espacio de IPs configurado en flannel (el espacio de IPs de los Pods del usuario)
  • Todo se guarda en etcd

DNS

  • CoreDNS es ahora el plugin de DNS por defecto en Kubernetes
  • Se configura en background cuando seteamos el networking (no hay que hacer nada)
  • Los Pods hacen búsquedas relativas a su propio namespace (basta con poner «db» en lugar de «db.namespace»)
  • Todos los servicios en un cluster tienen un registro DNS
  • El cluster tiene un Pod DNS y kubelets se encargará que los containers usen el servicio de DNS del cluster (el /etc/resolv.conf de los containers tendrá la entrada correspondiente)

Replica Sets

  • Es un conjunto de Pods que tienen las mismas etiquetas (labels).
  • En la definición del replica set se especifica cuantos Pods vamos a gestionar (réplicas).
  • Las Replica Sets no cubren las necesidades de HA y escalado así que habitualmente usaremos «Deployents» para gestionar las Replica Sets.

Services

Los servicios exponen al exterior, símplificando encontrar los componentes de nuestras aplicaciones, incluso cuando los componentes se mueven o levantan nuevas réplicas. Proporcionan HA y LB entre Pods replicados proporcionando una única IP virtual, la cual se distribuye y asigna automáticamente.

Para ello, en la especificación del servicio, se usa el «selector» para elegir los Pods que tengan determinado «label», además de configurar el puerto que expone el servicio. Si escalamos, el servicio se ajustará automáticamente pues tendrá más/menos Pods con ese label.

Cuando un servicio se crea, el API Server notifica a todos los agentes «kube-proxy» en los workers para que estén actualizados. Los kube-proxy entonces, setean algunas reglas en iptables para permitir el acceso al servicio. Además del servicio se crea un endpoint, que no es más que una caché con las IPs de los Pods para ese servicio.

Hay varios tipos de servicios además del habitual «NodePort«, como el «ClusterIP» que se crea automáticamente con el cluster y gesiona el enrutado intra-cluster permitiendo que si un Pod se mueve, el resto sepa dónde está y pueda comunicarse con él.

También está el tipo «LoadBalancer«, que en este caso tendrá una EXTERNAL-IP que permitirá el acceso desde fuera del cluster, y balanceará las peticiones entre los nodos que tienen Pods que forman parte del servicio. Es decir, por defecto no balancea entre Pods si no entre nodos. Podemos habilitar el «pod-awareness» pero ésto añadirá algo de latencia:

Además de los servicios, tenemos «ingress rules» (kind: Ingress) que operando en la capa de aplicación, permite enviar peticiones a un servicio o a otro en función del dominio de destino.

Deployments

La forma de deployar aplicaciones en k8s es via «deployments». Los Deployments gestionan «Replica Sets» por nosotros, definiendo una estrategia para los updates (p.ej. RollingUpdateStrategy)

Con el Deployment creado, podemos crear un servicio que gestione los Pods con esas etiquetas, igual que haríamos únicamente con Replica Sets. Ésto nos permitiría por ejemplo, cambiar la imagen de docker de los containers de los Pods de los replica sets, por una nueva versión, sin downtime, siguiendo la estrategia definida para los updates en el manifiesto del deployment:

O si modificamos el yaml del deployment, podemos aplicar (y registrar en el history del deployment) los cambios vía:

Cuando se hace el RollingUpdate, el deployment en realidad crea un nuevo replica set y después elimina los Pods del replica set anterior. Ésto significa que se mantiene un histórico de versiones y además permite volver a versiones anteriores.

Finalmente, también podemos símplemente escalar el número de réplicas del deployment

Pasar parámetros de configuración a tu aplicación

Via variables de entorno, usando configMap o secrets

Posteriormente usados en el container

Es best practice no pasar secretos vía variables de entorno, pues las aplicaciones pueden hacer un volcado de esta y otra información, incluso en forma de logs.

También podemos directamente montar un volúmen con el configMap para exponer un fichero con los secrets en el container. Éstos volúmenes usan sistemas de fichero en memoria (tmpfs), con lo que los secrets no se escriben en el disco físico. Cada Pod tiene un volúmen para secrets atachado automáticamente por defecto, que monta el secret con el certificado, el namespace y el token para poder usar su Service Account.

Storage

Kubernetes ofrece una capa de abstracción entre la aplicación y el storage, llamada PersistentVolumes, o símplemente «pv» (kind: PersistentVolume).

Un PersistentVolumeClaim (kind: PersistentVolumeClaim) permitiría a un developer usar ese claim en el momento de definir el Pod, sin que el developer tenga que preocuparse de los detalles del volumen en sí.

Mediante StorageClass (kind: StorageClass) se pueden definir otros tipos de storage (según el provider), por ejemplo un SSD, para poder especificarlos en la definición del pvc mediante «storageClassName».

Seguridad

Se recomienda:
– Securizar las comunicaciones intra-cluster usando HTTPS.
– Habilitar HA replicando el master.

Cuando se accede a la API (ya sea un serviceaccount o un usuario normal) para consultar o modificar el estado del cluster (i.e. crear un Pod) se sigue este camino:
– Autenticación para identificar a quien hace la petición (via http header o certificado). Retorna el username, userid y los grupos a los que pertenece.
– Autorización para validar si el usuario puede realizar la operación sobre el recurso.
– Admisión para crear, modificar o eliminar objetos (no para consultarlos). Pasa por todos los plugins que tienen permiso para modificar el recurso.
– Validación del recurso
– Acceso a etcd para guardar el nuevo estado

roles: definen qué se puede hacer.
role bindings: definen quién puede hacerlo.

Los «role» y «role bindings», para recursos a nivel de namespace. También hay «cluster role» y «cluster role bindings», para recursos a nivel de cluster (i.e. listar los servicios de un namespace).

ServiceAccounts

Los containers que corren en un Pod, pueden usar un «Service Account» para comunicarse con el API Server del cluster.

Podemos ver en cada Pod, su fichero con el token para autenticarse con la API:

Cuando se crea un serviceaccount, a su vez se crea un secret que mantiene la clave pública del API Server así como un token json firmado

Con ésto, podríamos ir al server de Jenkins, añadirle el plugin de kubernetes-cli y el token, y con eso el server de Jenkins podría controlar los Pods que usaran ese ServiceAccount.
Si en la definición del Pod no especificamos el serviceAccount, se usará el default.

[Más]

Políticas de red

Por defecto los Pods están abiertos y accesibles. Podemos gestionar las políticas de red mediante un plugin externo, como canal, añadiéndo una política de red deny-all para todas las conexiones de entrada (ingress).

Mediante otro objeto del tipo «NetworkPolicy» pero con un selector appropiado (para que aplique únicamente a los Pods con una etiqueta determinada) podemos definir una ingress rule que sí permita la comunicación hacia un puerto determinado desde unos Pods determinados, con otro label selector (o con un «namespaceSelector» o incluso con rango de IPs).

Exactamente igual para las conexiones de salida (egress).

Configurando un docker registry privado

Security Contexts

Los Security Contexts permiten asegurar los containers para especificar qué procesos pueden hacer qué cosas. Por ejemplo:

1) Ejecución como root:
En la definición del container, dentro del Pod, podemos especificar un securityContext para forzar al container ejecutar el proceso principal como un ususario específico, entre otras opciones.

2) Acceso a funciones de Kernel:
También podemos especificar, dentro del securityContext, las «capabilities» que queremos añadir o quitar al container para que pueda ejecutar ciertas funciones a nivel de kernel.

3) Además, el securityContext permite especificar opciones como «readOnlyRootFilesystem: true», muy útil junto con un volumen con la opción «readOnly: false» para asegurar la escritura únicamente en el volúmen evitando problemas en el sistema de ficheros raíz

Logs y Monitoring

El Metrics Server descubre todos los nodos y Pods (y sus containers) del cluster y recoge métricas de CPU y memoria de todos ellos, para poder consultarlo desde kubectl o con llamadas a la API del servidor de métricas.

Por otra parte, en kubernetes podemos especificar checkeos en la definición de un container, para asegurar:

  • livenessProbe: que está «vivo». En caso contrario reinicia el container.
  • readinessProbe: que está listo para operar. En caso contrario lo saca del endpoint.

Logs

Los containers guardan los logs (stdout) en /var/log/containers a nivel de nodo.
Puedes ver los logs (stdout y stderr) de un container determinado via kubectl y sus diversas opciones de filtrado.

Chuleta

Recursos útiles

Testeando el cluster

kubetest es una herramienta para tests end-to-end que merece la pena mirarse.
Gran lista de comprobaciones y sus comandos en The pod of Minerva (Install, config and validate > End-to-end tests)

+ https://linuxacademy.com/cp/modules/view/id/371

Links

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *