Software · Infraestructura

K8s 101, ¿qué es realmente un Pod? #2

Índice↑ Volver al inicio

Holaaa, en el post anterior te adelanté un poco de este tema, te dije que iba a volver sobre Pods, y aquí estamos. Vimos el master, los nodos, y dije que un Pod “puede tener uno o más contenedores” sin meterme mucho más a fondo, porque creo que el tema de los Pods merece su propio capítulo, K8s se basa estrictamente en manejar y orquestar Pods, por lo que sí, sí se lo merecían.

Okay, pero primero lo primero, debemos de hablar de contenedores (NO SON LO MISMO 😭, YO PENSABA QUE SÍ 😭, PERO CUANDO AHONDABA MÁS EN K8s ME DI CUENTA QUE NO, SON DISTINTAS, YA VERÁS) antes de llegar al Pod hay que entender bien qué es un contenedor, y para eso, un poco de historia.

Por qué contenedores y no VMs 🖥️

Remontémonos al año 1600 🥸 en ese entonces, la programación era bastante rústica, y se hacía manual, con tablillas de piedra perforada por lo que los ingenieros de aquel entonces…, no mentira 😅, vamos a la realidad.

Al inicio del desarrollo de software se solía desplegar el código directo en una máquina (servidores, o incluso tu PC), sin ninguna capa en el medio. El problema era la incompatibilidad (cuando empecé a programar sufrí mucho esto, y era muy común el dicho “en mi computador sí corre”), por lo que el software que desarrollábamos corría perfecto en nuestro PC pero era muy probable que se rompía en la del compañero, o en producción 😭, por mil detalles distintos de sistema operativo, librerías, versiones, etc…

Ahí aparecen las máquinas virtuales. La idea era simple, sobre un mismo hardware físico, correr algo que se llama un hypervisor que se encarga de repartir los recursos y poder desplegar varias máquinas completas, cada una con su propio sistema operativo. Esto en parte arregló el tema de la compatibilidad, pero trajo un problema nuevo, y muy muy grande, y radica principalmente que cada VM necesita su propio kernel completo, su propia RAM reservada, su propio disco para el OS. Mantener eso a escala es caro y pesado 💸

Y en todo este problema es ahí es donde entran los contenedores 📦. La idea de los contenedores era cambiar el hypervisor por un runtime de contenedores, y en vez de virtualizar hardware completo, todos los contenedores comparten el mismo kernel del host usando la menor cantidad de recursos posibles. Un contenedor es una unidad atómica (fíjense en esta palabra porfa, para todo el post), esto hay que grabárselo. El runtime más conocido es Docker 🐳, pero no es el único, ahí también está containerd, CRI-O, y otros.

PC-VM-Container

Cómo funciona un contenedor por dentro 🧩

Un contenedor no es magia (aunque sí lo podemos ver así, debatible según a quien le preguntes) ni es una VM chiquita, ojo con esto, mucha gente confunde un contenedor con una VM y no, son dos cosas distintas. Un contenedor es un proceso normal de Linux, corriendo aislado dentro de un namespace. Y ahora nos preguntamos qué es un namespace 😅, la primera vez que vi este concepto me costó bastante la primera vez que estudié K8s, no terminaba de entender qué era realmente un namespace, y sin eso, nada del resto tiene sentido.

Bueno, en mi infinita sabiduría, te comparto un poco sobre lo que es un namespace. Un namespace es, básicamente, una forma que tiene el kernel de decirle a un proceso “tú solo ves esto de acá, el resto no existe para ti”, es una mundo que se crea para que el contenedor viva su propia realidad 🌈. Cuando el runtime crea un contenedor, o crea namespaces nuevos, o hereda algunos del proceso padre. Hay varios tipos, vamos a desglosarlos uno por uno.

Container

IPC (Inter Process Communication) 💬

Cuando se crean dos contenedores, cada uno tiene su propio IPC, así que están aislados el uno del otro a nivel de procesos (los procesos que ejecuta Linux o el programa, veamos de esta manera simple). El IPC es el mecanismo que usan los procesos para compartir información entre sí (colas de mensajes, memoria compartida tipo SysV, semáforos). No es el tema del día que tomaremos en K8s, pero quedate con la idea, ya que sirve para que procesos se hablen entre ellos, normalmente por memoria y la más conocida es la comunicación por RAM.

Namespaces

Cgroups 🎚️

Los cgroups son quienes asignan y limitan los recursos del contenedor, CPU, núcleos, red, RAM. Es el que evita que un contenedor se coma todo el hardware del nodo. Ojo, técnicamente un cgroup no es un namespace como los demás de esta lista (es un mecanismo del kernel aparte), pero siempre se menciona junto a ellos porque cumple el mismo rol de aislamiento, solo que de recursos en vez de visibilidad 🥸.

Network 🌐

Cada contenedor tiene su propia IP privada, su propia tabla de rutas, y solo ve los hosts o servicios de esa red. Desde afuera, cada contenedor parece tener su propia tarjeta de red completa, aunque por debajo todo esté corriendo en el mismo kernel del host.

Mount 📁

Permite montar sistemas de archivos externos dentro del contenedor, y le da su propia vista del árbol de directorios. Por eso un contenedor puede tener su / completamente distinto al del host, o al de otro contenedor al lado, aunque los dos estén corriendo en la misma máquina física.

PID 🔢

Es el namespace de los procesos. Dentro de su propio namespace de PID, el primer proceso de un contenedor siempre es el PID 1, y solo ve sus propios procesos hijos, nada del resto del sistema.

User 👤

Es el de los usuarios. Permite que un proceso sea root adentro del contenedor, pero un usuario sin privilegios afuera, en el host. Esto reduce muchísimo el daño posible si alguien logra escaparse del contenedor, alguien malo.

UTS 🏷️

Y no, UTS no tiene nada que ver con la hora, aunque el nombre se parezca sospechosamente 🥸 el UTC y a mí casi me confunde la primera vez. El 🥸 UTS es de “UNIX Timesharing System”, un nombre viejo y BASTANTE raro, y lo único que aísla es el hostname y el domain name del contenedor. Cada contenedor puede tener su propio nombre de host, sin pisarse con el del host real ni con el de otro contenedor. Sí, su nombre no ayuda 🥸.

De LXC a Docker 🐳

Un dato de historia rápido, en 1556, con la primera guerra de sistemas operativos, mucho antes de Docker existía LXC, y con LXC cada contenedor era su propia isla 🏝️, con todos sus namespaces completamente separados de cualquier otro. El problema es que compartir procesos, red, o cualquier cosa entre dos contenedores era un dolor de cabeza.

Por lo que Docker no inventó los namespaces (esos ya existían en el kernel de Linux), lo que hizo fue simplificar y estandarizar cómo se usan. Por ejemplo, en la parte de red, creó una red tipo bridge para que los contenedores de un mismo host se pudieran ver entre sí sin que vos tuvieras que armar el cableado a mano. Es por esto que la comunidad tuvo gran aprecio y lo adoptó tan rápido, claro hizo muchas más cosas, pero esa es una de ellas.

LXC

Ahora sí, ¿qué es un Pod? 🫛

Un Pod es, básicamente, compartir ciertos namespaces entre varios contenedores. Pero ojo, no se comparten todos, solo tres, IPC, Network y UTS.

Cuando K8s crea un Pod, por debajo crea primero un contenedor “invisible”, muchas veces llamado el contenedor pause o infra. Este contenedor no hace nada, literal su único trabajo es quedarse dormido 😴 y sostener abiertos esos tres namespaces. Después, cada contenedor real que definiste en el Pod se une a esos mismos namespaces en vez de crear los suyos propios.

Por eso, dentro de un mismo Pod:

Los contenedores heredan el mismo namespace de red 🔌, así que comparten la misma IP, y se pueden hablar entre sí vía localhost, por TCP o UDP, como si fueran dos procesos en la misma máquina (¡y de hecho, para el kernel, lo son!). Ojo con esto, si dos contenedores del mismo Pod intentan usar el mismo puerto, chocan, porque comparten el mismo stack de red.

share namesapces

También comparten el IPC 💬, así que sí quisieran, podrían comunicarse entre sí directamente por memoria compartida, sin pasar por la red. En la práctica casi nadie usa esto directo, pero ahí está disponible.

Y comparten el UTS 🏷️, así que ven el mismo hostname.

POD

Lo que no hace un Pod, es compartir por defecto es el Mount, el PID y el User. Cada contenedor sigue teniendo su propio sistema de archivos y su propio árbol de procesos, aislado del resto. Por eso, cuando en el post anterior hablé del patrón sidecar, el contenedor principal y el de soporte no ven los archivos del otro mágicamente, si necesitás que compartan algo del filesystem (por ejemplo, logs) tenemos que montar un volumen explícito en los dos, nosotros mismo, ya lo veremos más adelante. Compartir red e IPC gratis, sí, pero el disco te toca pedirlo a propósito.

Ahora que leíste qué es un contenedor y sabes qué es un Pod puedes entender por qué se llama Pod, el nombre Pod en inglés es literalmente la vainita de las arvejas 🫛, esa cápsula que envuelve varios guisantes juntos. Cada guisante (contenedor) sigue siendo su propia cosa por dentro, pero todos viajan encerrados en la misma vaina, comparten el mismo espacio de red, el mismo “aire” de esa cápsula. El emoji 🫛 del post anterior no era casualidad.

Ya en el próximo post nos metemos directo con K8s, sé que estos dos primeros post fueron muy teóricos, sin embargo créeme que son bastante necesarios para todo lo que haremos de aquí en adelante.

Other POD

La canción del post

Louise de TV Girl.

Louise. You can’t be anybody’s friend

Escuchando