¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Introducción a Kubernetes
Notas del curso Docker a fondo e Introducción a Kubernetes: aplicaciones basadas en contenedores
En este módulo vas a aprender los fundamentos de Kubernetes, también conocido como k8s. Estudiaremos:
- Qué es Kubernetes
- El modelo de aplicación de Kubernetes
- Cómo ejecutar Kubernetes en local
- Ejecutar un contenedor en Kubernetes
Para seguir esta sección del curso debes poder ejecutar un clúster de Kubernetes en tu máquina. Por suerte hay multitud de proyectos que te pueden ayudar a ello. Para este curso se ha elegido Minikube.
Minikube es un proyecto, de la propia gente de Kubernetes, destinado a facilitar la ejecución de un clúster de Kubernetes en tu máquina de desarrollo. Más adelante te comento cómo instalar y ejecutarlo y se trata de una forma muy sencilla de que puedas seguir todo el curso.
Pero, Minikube no es la única opción disponible, hay muchos proyectos orientados a facilitar la creación y ejecución de clústeres de Kubernetes locales. Aunque la mayoría de ejemplos funcionarán en cualquier sistema, nosotros hemos preparado todos los ejemplos para que funcionen en Minikube. Si usas algún otro sistema es posible que en algunos casos debas adaptar algo (durante el curso intento avisar en aquellos casos en que, a lo mejor, el ejemplo requiera adaptación si no usas Minikube).
Algunas de las otras opciones disponibles son:
¿Qué es un orquestador (de contenedores)?
Un orquestador es un sistema que se encarga de ejecutar nuestros contenedores y de ofrecer a estos un conjunto de funcionalidades avanzadas tales como:
- Escalado: un orquestador puede autoescalar nuestros contenedores creándolos y destruyéndolos según determinadas reglas de carga del sistema. Es cierto que con Compose podíamos escalar también, pero el escalado de Compose es fijo.
- Multimáquina: un orquestador puede ejecutar contenedores en más de una máquina a la vez, de forma coordinada. Incluso pueden “mover” contenedores de máquina si es necesario.
- Actualización de aplicaciones: suelen ofrecer mecanismos para actualizar las imágenes y configuración de nuestros contenedores de forma coordinada, sin paradas de servicio.
- Versiones “canary”: puedes tener simultáneamente más de una versión en producción, permitiéndote así probar la “siguiente versión” (sirviéndola solo a determinados usuarios).
- Métricas: ofrecen sistemas de métricas a nivel de contenedor, grupo de contenedores, máquina y orquestador.
- Gestión de errores: si un contenedor no responde a peticiones pueden terminarlo e iniciar otro nuevo en su lugar.
Puede parecer que los orquestadores solo ofrecen ventajas en escenarios multimáquina, pero incluso en escenarios con una sola máquina pueden existir ventajas por utilizar un orquestador frente a hacerlo mediante Compose.
Qué es Kubernetes
Kubernetes fue un proyecto creado por Google a partir de su experiencia previa en el uso de contenedores. Su origen es un proyecto llamado Borg, que era un sistema propio de Google para la ejecución de gran cantidad de servicios heterogéneos para distintas aplicaciones, ejecutados de forma distribuida en grandes clústeres de máquinas. Muchas de las ideas de Borg se incorporaron a Kubernetes, en tanto que gran parte de los desarrolladores de Kubernetes lo fueron de Borg.
Kubernetes fue anunciado por Google en 2014 y la versión 1.0 salió en 2015. En aquel momento Google se alió con la Linux Foundation para formar la Cloud Native Computing Foundation (CNCF) y Kubernetes pasó a estar dirigido y desarrollado por esta última. Actualmente es de código abierto y se ha convertido en el líder del mercado de orquestadores de contenedores.
Componentes de Kubernetes
A pesar de que nos referimos a él simplemente como Kubernetes (o k8s), la realidad es que no se trata de un solo producto, sino de la combinación de varios. La siguiente lista muestra componentes open source independientes de Kubernetes, pero que son utilizados en este:
- etcd: es una base de datos clave-valor distribuida que se utiliza para mantener el estado de todo el clúster.
- supervisord: se trata de un monitorizador de procesos. En Kubernetes se usa para garantizar que tanto el daemon de Docker como el propio “kubelet”, el nodo de tipo agente principal de Kubernetes (luego lo veremos), se están ejecutando.
- fluentd: es un sistema de logging unificado, que se usa en Kubernetes para centralizar todos los logs del clúster.
Además, existen varios componentes adicionales (kubelet, kube proxy, kube-controller manager y más) que forman parte de Kubernetes, pero que son más o menos independientes entre sí.
En este post de mi blog se detallan un poco más los componentes que conforman Kubernetes.
Nodos en Kubernetes
Todo clúster de k8s tiene dos tipos de nodos:
- Nodos master: no suelen ejecutar contenedores, sino que se encargan de todas las tareas de sincronización, coordinación y toma de decisiones que afectan al conjunto del clúster.
- Nodos minion (o worker): son los que se encargan de ejecutar los distintos contenedores.
El número mínimo de máquinas necesarias para montar Kubernetes es de uno (un clúster con un solo nodo). Kubernetes soporta clústeres de hasta 5000 nodos. Entre estos dos valores (1 y 5000) la cantidad de nodos será la que necesites.
El número mínimo de máquinas necesarias para montar un Kubernetes productivo de alta disponibilidad es de 5, tres de las cuales son nodos master y las dos restantes son nodos minion.
La creación de un nodo es algo externo a Kubernetes, generalmente lo crea el proveedor de cloud; o bien es una máquina física o virtual que tenemos, configuramos y agregamos al clúster. Lo que sí admite Kubernetes es agregar (o eliminar) máquinas a un clúster estando este en marcha.
Ver cómo desplegar un Kubernetes bare metal (es decir en máquinas, ya sean físicas o virtuales) está fuera del alcance de este curso. Si estás interesado, hay dos buenos recursos que puedes consultar: La documentación oficial y “Kubernetes The Hard Way”.
Una nota sobre el "no soporte a Docker" en Kubernetes 1.20
Hace cierto tiempo hubo una noticia que levantó bastante polvareda. Era algo similar a “Kubernetes dejará de soportar Docker a partir de la versión 1.20”. Aquí tienes la información oficial pero te pongo aquí el párrafo que importa:
Docker support in the kubelet is now deprecated and will be removed in a future release. The kubelet uses a module called “dockershim” which implements CRI support for Docker and it has seen maintenance issues in the Kubernetes community. We encourage you to evaluate moving to a container runtime that is a full-fledged implementation of CRI (v1alpha1 or v1 compliant) as they become available.
Esa noticia, que inicialmente se mencionó en un changelog rodeada de otros cambios que incorpora 1.20, enseguida fue amplificada por varios tweets y posts en blogs (algunos de ellos de colaboradores importantes de Kubernetes) que hicieron correr ríos de tinta, y se creó una alarma: ¿eso significa que los contenedores creados con Docker van a dejar de funcionar?
Mi respuesta a eso es muy sencilla: si te preocupas de que Kubernetes deje de soportar Docker como motor de contenedores entonces es que probablemente no te afecta. Porque para entender lo que implica realmente que Kubernetes deje de soportar Docker hay que comprender un poco la relación entre motores de contenedores y Kubernetes, y si la comprendes verás que los escenarios en que te puede afectar son pocos y relativamente avanzados (de forma que si los usas probablemente ya estarás manejando alternativas). Pero, resumiendo: que Kubernetes deje de soportar Docker no tiene (apenas) ninguna afectación.
En mi opinión, el pánico que generó esa noticia fue debido a una mala comunicación por parte del equipo de Kubernetes. Al final intentaron calmar las aguas con un post donde contaban las implicaciones pero, como pasa siempre en estos casos, la explicaciones suelen pasar más inadvertidas. Voy a intentar contarte en qué afecta que Kubernetes deje de soportar Docker sin entrar en demasiados tecnicismos (que algunos caen fuera del ámbito de este curso).
Cuando Kubernetes apareció, lo hizo con soporte para Docker, eso significa que hay código en Kubernetes para interactuar con el daemon de Docker para poner en marcha contenedores, pararlos, hacer pull de las imágenes, etc…
Poco tiempo después apareció un nuevo motor de contenedores, llamado rkt (lanzado por la gente de CoreOS (ahora Red Hat)) que parecía coger bastante tracción así que la gente de Kubernetes añadió soporte también a rkt en Kubernetes. Como nota a pie de página mencionar que rkt actualmente está deprecado y su uso ya no está recomendado.
Así que ahora la gente de Kubernetes tiene que soportar dos motores de contenedores, que son bastante distintos entre sí, pero con el tiempo sucedieron tres cosas más:
- Docker dejó de ser completamente monolítico para modularizarse
- La iniciativa OCI (Open Container Iniciative) empezó a tener calado
- Surgieron nuevos motores de ejecución de contenedores, aparte de Docker, para ejecutar imágenes OCI
Entonces, la gente de Kubernetes se lanzó a una refactorización con el objetivo de que Kubernetes dejara de requerir el daemon de Docker o rkt y que pudiese utilizar cualquier motor de ejecución de contenedores. Eso haría que, tener que instalar Docker o rkt en los nodos dejase de ser necesario y daría libertad al administrador de cada clúster a usar cualquier motor de contenedores. Para ello, crearon una interfaz (llamada CRI) mediante la cual Kubernetes se podía integrar con cualquier motor de ejecución de contenedores. Además simplificaba el código de Kubernetes ya que traspasaba la responsabilidad de interactuar con el motor de contenedores a cada módulo CRI. Así, el código de Kubernetes dependía solo de la interfaz CRI y cada administrador del clúster podía instalar su propio motor de contenedores siempre y cuando hubiese una implementación de CRI para dicho motor. Todo esto sucedió hace ya mucho tiempo, allá por finales del 2016.
Poco después lanzaron dockershim: una implementación de CRI para usar Docker. El dockershim era pues la idea que había en Kubernetes para seguir soportando Docker, pero haciéndolo a través de CRI y no de forma “directa” como hasta entonces, simplificando el código del core de Kubernetes.
Así fueron evolucionando las cosas, pero con el tiempo el mantenimiento del dockershim se fue volviendo cada vez más pesado. Empezó a verse que el dockershim era superfluo, ya que (como ya sabes) Docker usa containerd para ejecutar contenedores, y containerd empezó a soportar CRI. Es decir, Kubernetes podía usar containerd directamente. Por lo tanto, en este escenario… ¿qué sentido tiene que Kubernetes use Docker, quien a su vez usa containerd, si podía usar containerd directamente?
Recuerda que containerd es un proyecto de Docker y ¡que es el motor de ejecución que usa Docker realmente por debajo!
Pero, si eso por sí solo no fuese suficiente, otra iniciativa había ganado ya madurez: CRI-O. CRI-O es un motor de ejecución de contenedores OCI y compatible con CRI.
Así pues, el escenario es el siguiente:
- Docker genera (y ejecuta) imágenes OCI
- Docker usa containerd para ejecutar contenedores
- Kubernetes interacciona con el motor de contenedores a través de CRI
- El dockershim es una implementación CRI para Docker, pero es compleja de mantener
- Existe una implementación CRI para containerd
- El proyecto CRI-O es un motor de ejecución de contenedores OCI que soporta CRI
Por lo tanto, es bastante evidente que Kubernetes no necesita mantener el dockershim para nada: gracias al CRI de containerd o a CRI-O puede ejecutar cualquier contenedor OCI. Y recuerda que Docker crea y ejecuta contenedores OCI.
Es por ello que se tomó la decisión, en Kubernetes 1.20, de marcar el dockershim como obsoleto (y dejar de soportarlo totalmente en 1.23). Pero, ¿qué implicaciones reales tiene?
- ¿Podrás seguir ejecutando tus imágenes, generadas con
docker builden Kubernetes? Por supuesto, ya que Docker construye imágenes OCI que son las que ejecuta Kubernetes. - ¿Puedes seguir usando Docker en desarrollo? Por supuesto, sin ningún problema. Docker “no desaparece” ni se va a ningún lado.
- ¿Los nodos de Kubernetes deberán tener Docker instalado? No, en su lugar se podrá instalar containerd o bien CRI-O (o cualquier otro motor compatible con OCI y que tenga soporte CRI).
El último punto es el único que puede afectarte y se refiere a los escenarios avanzados que comentaba al principio. Básicamente, si ejecutas contenedores que requieren que Docker esté instalado en los nodos, porque hacen algo concreto con el daemon de Docker, entonces esto te afecta. Un caso típico es ejecutar un contenedor en Kubernetes que requiera a su vez construir otro contenedor. Lo más sencillo para hacer esto era enlazar este contenedor con el daemon de Docker del nodo de Kubernetes y ejecutar un docker build. Como puedes ver, ese es un escenario avanzado que va más allá de “ejecutar mis contenedores en Kubernetes”. Por eso comentaba al principio que si “te preguntas si eso te afecta, lo más probable es que no”, porque solo te afecta en esos casos avanzados, y en este momento probablemente ya tengas claro cómo Kubernetes ejecuta los contenedores y además ya conozcas alternativas a usar como pueden ser buildah o kaniko.
Así pues: tranquilidad. Puedes seguir usando Docker para crear y probar tus imágenes, y Kubernetes las podrá usar sin problemas
Probar Kubernetes en local con Minikube
Existen varias maneras, relativamente sencillas, de usar Kubernetes en local, en tu máquina de desarrollo. Como hemos mencionado antes, hemos elegido MiniKube para este curso. Así, que vamos a ver cómo funciona y cómo instalarlo.
Para ejecutar Kubernetes en tu máquina local, Minikube puede hacerlo de dos maneras: la recomendada y más sencilla es ejecutar Kubernetes como un contenedor. Para ello el único requisito es que tengas Docker instalado en tu máquina. Pero, si no quieres o no puedes usar este mecanismo, Minikube soporta también el crear una máquina virtual que ejecutará Kubernetes.
Elijas la opción que elijas, Minikube va a intentar que el proceso sea lo más transparente para ti: toda la gestión se realiza a través del cliente de Minikube, que es una aplicación de línea de comandos.
En la página de MiniKube explican cómo instalarlo para tu sistema operativo (Linux, Windows o MacOS). Una vez lo tengas instalado debes ejecutar el comando minikube start. Lee la documentación del comando porque hay algunas configuraciones que quizás debas establecer, en función de tu SO y del sistema de virtualización que quieras que utilice MiniKube.
El primer paso es descargar el ejecutable de Minikube para tu SO, ya que toda la instalación y gestión se realiza desde ese ejecutable.
Instalando Minikube ejecutando el driver de Docker
Esta es la manera más sencilla de instalar Minikube. En este modo, Minikube ejecuta Kubernetes como un contenedor de Docker. Así se evita el uso de un sistema de virtualización adicional (como pueda ser Hyper-V o VirtualBox). Si tu sistema es compatible, Minikube intentará usar este modo de forma automática al usar el comando minikube start:
Consejo: a poco que puedas, insisto, utiliza este mecanismo. Es el más sencillo y el más liviano.
Una vez ejecutado el comando, ya tendrás tu Kubernetes de desarrollo listo y kubectl correctamente configurado. Puedes verificar que, efectivamente, Minikube ejecuta Kubernetes como un contenedor a través del comando docker ps:
En la imagen se puede ver como Docker nos muestra el contenedor de la imagen gcr.io/k8s-minikube ejecutándose. Este contenedor es Minikube.
No uses docker stop para parar el contenedor de Minikube, para ello debes usar minikube stop.
Usando Docker no necesitas para nada usar una consola administrativa en Windows, la cual sí es necesaria si usas Hyper-V
Instalando Minikube en Windows usando Hyper-V
A continuación se explica cómo instalar Minikube en Windows, usando Hyper-V. Si por cualquier motivo no es posible usar Docker, o bien prefieres usar una máquina virtual, usar Hyper-V es la opción más natural si estás en Windows.
Por temas de agilidad, voy a asumir cierta experiencia con Hyper-V. Aunque instalar Minikube no presenta dificultad, al final, si debes modificar alguna configuración de la MV creada, deberás hacerlo usando el administrador de Hyper-V.
Por ejemplo, en Windows yo uso el siguiente comando desde una consola administrativa:
minikube start --vm-driver hyperv --hyperv-virtual-switch "Default Switch"
Si no usas una consola administrativa recibirás un error de permisos:
Con vm-driver indicas a MiniKube que emplee Hyper-V para la MV. Y el modificador --hyperv-virtual-switch indica qué adaptador de red virtual debe usarse. En mi caso uso el “Default Switch” que viene preconfigurado con Hyper-V (a partir de Windows 1709).
Puedes agregar también el modificador --kubernetes-version <version-k8s> para instalar una versión concreta como, por ejemplo, --kubernetes-version v.1.12.4.
IMPORTANTE: Dificultades arrancando Minikube:
Es probable que te encuentres con problemas usando Minikube con Hyper-V. En su mayoría suelen ser problemas de red (no es posible conectar con el clúster) y pueden ser muy frustrantes.
En mi experiencia, a partir de Windows 1709 (o sea, a partir de la Fall Creators Update de Windows 10, que apareció en octubre del 2017) las cosas mejoran bastante usando el “Default Switch” que se ve en el comando anterior.
Si usas una versión de Windows 10 anterior a la 1709 (que ya tiene su tiempo y quizá no deberías), en el modificador --hyperv-virtual-switch debes indicarle el nombre de un adaptador externo de Hyper-V a utilizar. Si no usas este modificador, la MV terminará haciendo uso del primer adaptador que encuentre y eso te puede generar problemas. Así que te recomiendo que crees uno específico y lo uses (si estás en 1709 o posterior usa “Default Switch” tal y como se muestra en el ejemplo).
Si usas un adaptador que no es correcto, MiniKube te dará error diciendo que no puede conectarse a la MV vía SSH, o se quedará mucho rato con el mensaje “Starting VM…”.
Si el adaptador no te funciona, termina MiniKube (Ctrl + Ctrl), y desde el administrador de Hyper-V selecciona la MV y cámbiale a mano el adaptador de red virtual. Luego ejecuta simplemente minikube start sin ningún otro parámetro.
Otra opción es borrar Minikube con minikube stop y luego minikube delete y empezar de nuevo.
Si esto también te da problemas, entonces lo mejor es eliminar el directorio .minikube de tu perfil de usuario de Windows, borrar la MV de Hyper-V y volver a empezar de nuevo.
Si todo ha funcionado correctamente, la salida del comando debe ser parecida a la siguiente:
Si abres el administrador de Hyper-V deberías ver la MV minikube:
¡Perfecto! Tenemos un clúster de Kubernetes en local.
Para usar MiniKube con Hyper-V debes hacerlo siempre desde una consola con permisos administrativos, ya que de otro modo te dará problemas. En esta imagen la consola de la derecha es administrativa y el comando minikube ip funciona sin problemas. La primera consola (la de la izquierda) no lo es y se puede ver como el mismo comando da error.
Instalar Minikube en Linux
El escenario en Linux es muy parecido al de Windows, lo más habitual es usar el driver de Docker que ejecuta Minikube como un contenedor Docker:
Como puedes ver en la imagen MiniKube, por defecto, utiliza el driver de Docker (observa el contenedor llamado minikube que aparece en el comando docker ps).
Otra opción bastante habitual es ejecutar Minikube usando VirtualBox a través del modificador --driver=virtualbox del comando minikube start. En su momento, usar VirtualBox era la opción preferida y por defecto de Minikube en Linux, pero en la actualidad usar Docker o KVM2 son las opciones preferidas.
Si tienes VirtualBox instalado, abre un terminal y teclea simplemente minikube start –driver=virtualbox y eso te instalará la MV en VirtualBox. La salida del comando es parecida a la siguiente:
Observa como el propio Minikube nos indica que hay mejores opciones antes que utilizar VirtualBox. Y es que, aunque hace algún tiempo fue la forma recomendada, actualmente existen formas mejores de hacerlo, como usar KVM, que veremos a continuación
Si abres VirtualBox deberías ver la MV “minikube” creada y ejecutándose:
Como hemos dicho antes, otra opción interesante para ejecutar MiniKube usando VMs es utilizar KVM. KVM son las iniciales de “Kernel Virtual Machine” y se trata de un hypervisor que está incluido en el Kernel de Linux. Está muy relacionado con otro proyecto de emulación muy conocido en el mundo Linux: QEMU. QEMU es una “suite” completa de creación y ejecución de máquinas virtuales y puede usar KVM para mejorar su rendimiento.
Para lo que a nosotros nos atañe, Minikube soporta usar KVM para la creación de la MV que se ejecutarán en el clúster de Kubernetes. Para ello asegúrate de que tienes KVM y libvirt instalado en tu sistema. El cómo hacerlo depende de tu distro de Linux. En la documentación de MiniKube hay enlaces a cómo instalar dichos componentes para las distros más famosas. Una vez tengas esos prerrequisitos instalados, el comando minikube start --driver=kvm usará KVM para ejecutar la MV.
Si instalaste virt-manager (componente opcional de KVM) podrás ver la MV ejecutándose:
¡Hay más drivers que soporta Minikube! En la documentación están todos ellos.
Comandos básicos de Minikube
Ya iremos viendo los principales comandos de Minikube, pero por ahora, los que debes conocer son:
minikube versionimprime la versión de Minikubeminikube stoppara parar el clúster localminikube start(sin más parámetros) para iniciar el clúster local. Si no existe clúster local lo crea (usando el driver que especifiques con--driver, siendo Docker el driver por defecto). Si ya existe clúster creado, no debes especificar--driver.minikube ippara ver la IP del nodo de Kubernetesminikube deletepara borrar el clúster local
Ahora sólo nos falta poder conectarnos a nuestro Kubernetes, y para ello necesitamos usar kubectl, la herramienta CLI que se utiliza para administrarlo.
kubectl (pronunciado kubecontrol) es la herramienta administrativa de Kubernetes. Cualquier clúster de Kubernetes puede ser administrado usando kubectl.
Solucionar problemas de MiniKube
MiniKube funciona relativamente bien, pero a veces puede dar problemas cuando se para el clúster (minikube stop). Personalmente me he encontrado esos problemas en Windows usando Hyper-V y la causa parece ser que, por alguna razón, Hyper-V no logra parar la máquina. En esos casos, lo mejor es conectar con la MV, que nos pedirá usuario y clave:
Como login introduce simplemente root, sin clave, y estarás dentro de la MV. Entonces escribe la instrucción shutdown 0 para forzar el apagado. Al cabo de un rato, Hyper-V te dirá que la MV está apagada. Entonces, cuando sea necesario, podrás usar minikube start para levantarla de nuevo.
Kubectl
Kubernetes se gestiona completamente desde la línea de comandos. Para ello se usa kubectl. En este último enlace tienes los detalles de la instalación, pero básicamente es un ejecutable que basta que coloques en el path. No debes configurar nada más.
En este enlace tienes toda la información para instalar kubectl en tu sistema operativo
Cuando has instalado MiniKube, este ya ha agregado la configuración necesaria para que kubectl funcione (más adelante veremos dónde está y cómo podemos operar con esta configuración). Ahora mismo lo que nos interesa es que ya tienes kubectl listo para administrar tu clúster local.
Para verificar que todo funciona, desde la CLI teclea:
kubectl version
La salida debería ser la versión de kubectl (Client Version) y la versión de Kubernetes del clúster (Server Version):
No es obligatorio que la versión del clúster de Kubernetes y la de kubectl sean la misma. En principio, diferencias entre dos versiones con un número de versión arriba o abajo están soportadas. Más allá de eso, no se garantiza que todo funcione (aunque muchas cosas puedan hacerlo). Así, si usas kubectl 1.20, está soportado su uso para las versiones de Kubernetes 1.19 (una abajo), 1.20 (la misma) y 1.21 (una arriba). Con diferencias superiores no está garantizado que todo funcione y en este caso el comando kubectl version te dará un aviso similar a este:
Vamos a ver el comando más básico de kubectl, que es kubectl get [tipo-recurso]. Devuelve un listado con todos los objetos del recurso indicado (ya verás que Kubernetes tiene muchos tipos de recursos). En este caso vamos a listar los nodos de nuestro clúster. Para ello teclea en la línea de comandos:
kubectl get nodes
Y deberías obtener una salida parecida a:
NAME STATUS ROLES AGE VERSION minikube Ready control-plane 47d vx.xx.x
Truco: muchos de los recursos se pueden poner en singular o en plural (así kubectl get node es equivalente a kubectl get nodes).
¡Eso nos indica que tenemos un clúster de k8s con un nodo! ¡Felicidades!
Recuerda: Cuando has instalado Minikube, este ha configurado kubectl por ti.
