GNU/Linux >> Tutoriales Linux >  >> Linux

Una historia de tiempos de ejecución de contenedores de Linux de bajo nivel

En Red Hat nos gusta decir:"Los contenedores son Linux, Linux es contenedores". Esto es lo que esto significa. Los contenedores tradicionales son procesos en un sistema que suelen tener las siguientes tres características:

1. Restricciones de recursos

Contenedores Linux

  • ¿Qué son los contenedores de Linux?
  • Una introducción a la terminología de contenedores
  • Descargar:Introducción a los contenedores
  • Operadores de Kubernetes:automatización de la plataforma de orquestación de contenedores
  • eBook:Patrones de Kubernetes para diseñar aplicaciones nativas de la nube
  • ¿Qué es Kubernetes?

Cuando ejecuta muchos contenedores en un sistema, no desea que ningún contenedor monopolice el sistema operativo, por lo que usamos restricciones de recursos para controlar cosas como la CPU, la memoria, el ancho de banda de la red, etc. El kernel de Linux proporciona la función cgroups, que se puede configurar para controlar los recursos del proceso del contenedor.

2. Restricciones de seguridad

Por lo general, no desea que sus contenedores puedan atacarse entre sí o atacar el sistema host. Aprovechamos varias funciones del kernel de Linux para configurar la separación de seguridad, como SELinux, seccomp, capacidades, etc.

3. Separación virtual

Los procesos del contenedor no deben tener una vista de ningún proceso fuera del contenedor. Deben estar en su propia red. Los procesos de contenedor deben poder vincularse al puerto 80 en diferentes contenedores. Cada contenedor necesita una vista diferente de su imagen, necesita su propio sistema de archivos raíz (rootfs). En Linux usamos espacios de nombres del núcleo para proporcionar una separación virtual.

Por lo tanto, un proceso que se ejecuta en un cgroup, tiene configuraciones de seguridad y se ejecuta en espacios de nombres puede denominarse contenedor. Si observa el PID 1, systemd, en un sistema Red Hat Enterprise Linux 7, verá que systemd se ejecuta en un cgroup.

# tail -1 /proc/1/cgroup
1:name=systemd:/

El ps El comando muestra que el proceso del sistema tiene una etiqueta SELinux ...

# ps -eZ | grep systemd
system_u:system_r:init_t:s0             1 ?     00:00:48 systemd

y capacidades.

# grep Cap /proc/1/status
...
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
CapBnd:    0000003fffffffff

Finalmente, si observa el /proc/1/ns subdir, verá el espacio de nombres en el que se ejecuta systemd.

ls -l /proc/1/ns
lrwxrwxrwx. 1 root root 0 Jan 11 11:46 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 root root 0 Jan 11 11:46 net -> net:[4026532009]
lrwxrwxrwx. 1 root root 0 Jan 11 11:46 pid -> pid:[4026531836]
...

Si el PID 1 (y realmente todos los demás procesos del sistema) tiene restricciones de recursos, configuraciones de seguridad y espacios de nombres, argumento que todos los procesos del sistema están en un contenedor.

Las herramientas de tiempo de ejecución de contenedores simplemente modifican estas restricciones de recursos, configuraciones de seguridad y espacios de nombres. Luego, el kernel de Linux ejecuta los procesos. Después de lanzar el contenedor, el tiempo de ejecución del contenedor puede monitorear el PID 1 dentro del contenedor o el stdin del contenedor. /stdout —el tiempo de ejecución del contenedor administra los ciclos de vida de estos procesos.

Tiempos de ejecución del contenedor

Podrías decirte a ti mismo, bueno, systemd suena bastante similar a un tiempo de ejecución de contenedor. Bueno, después de tener varias discusiones por correo electrónico sobre por qué los tiempos de ejecución de contenedores no usan systemd-nspawn como herramienta para lanzar contenedores, decidí que valdría la pena analizar los tiempos de ejecución de los contenedores y brindar un contexto histórico.

Docker a menudo se denomina tiempo de ejecución de contenedor, pero "tiempo de ejecución de contenedor" es un término sobrecargado. Cuando la gente habla de un "tiempo de ejecución de contenedor", en realidad se refiere a herramientas de nivel superior como Docker, CRI-O y RKT que vienen con funcionalidad de desarrollador. Están impulsados ​​por API. Incluyen conceptos como extraer la imagen del contenedor del registro del contenedor, configurar el almacenamiento y finalmente lanzar el contenedor. Lanzar el contenedor a menudo implica ejecutar una herramienta especializada que configura el kernel para ejecutar el contenedor, y estos también se conocen como "tiempos de ejecución del contenedor". Me referiré a ellos como "tiempos de ejecución de contenedores de bajo nivel". Los demonios como Docker y CRI-O, así como las herramientas de línea de comandos como Podman y Buildah, probablemente deberían llamarse "administradores de contenedores".

Cuando Docker se escribió originalmente, lanzaba contenedores usando el lxc conjunto de herramientas, que es anterior a systemd-nspawn . El trabajo original de Red Hat con Docker fue intentar integrar libvirt (libvirt-lxc ) en Docker como alternativa a lxc herramientas, que no eran compatibles con RHEL. libvirt-lxc tampoco usó systemd-nspawn . En ese momento, el equipo de systemd decía que systemd-nspawn era solo una herramienta para probar, no para producir.

Al mismo tiempo, los desarrolladores upstream de Docker, incluidos algunos miembros de mi equipo de Red Hat, decidieron que querían una forma nativa de golang para lanzar contenedores, en lugar de lanzar una aplicación separada. Se comenzó a trabajar en libcontainer, como una biblioteca nativa de golang para lanzar contenedores. El departamento de ingeniería de Red Hat decidió que este era el mejor camino a seguir y eliminó libvirt-lxc .

Más tarde, se formó la Open Container Initiative (OCI), parte porque la gente quería poder lanzar contenedores de formas adicionales. Los contenedores separados por espacio de nombres tradicionales eran populares, pero la gente también deseaba el aislamiento a nivel de máquina virtual. Intel y Hyper.sh estaban trabajando en contenedores separados por KVM y Microsoft estaba trabajando en contenedores basados ​​en Windows. La OCI quería una especificación estándar que definiera qué es un contenedor, por lo que nació la especificación de tiempo de ejecución de la OCI.

La especificación de tiempo de ejecución de OCI define un formato de archivo JSON que describe qué binario se debe ejecutar, cómo se debe contener y la ubicación de rootfs del contenedor. Las herramientas pueden generar este archivo JSON. Luego, otras herramientas pueden leer este archivo JSON y ejecutar un contenedor en rootfs. Las partes libcontainer de Docker se desglosaron y donaron a la OCI. Los ingenieros upstream de Docker y nuestros ingenieros ayudaron a crear una nueva herramienta de interfaz para leer el archivo JSON de especificación de tiempo de ejecución de OCI e interactuar con libcontainer para ejecutar el contenedor. Esta herramienta, llamada runc , también fue donado a la OCI. Mientras runc puede leer el archivo OCI JSON, los usuarios pueden generarlo ellos mismos. runc desde entonces se ha convertido en el tiempo de ejecución de contenedores de bajo nivel más popular. Casi todas las herramientas de administración de contenedores son compatibles con runc , incluidos CRI-O, Docker, Buildah, Podman y Cloud Foundry Garden. Desde entonces, otras herramientas también han implementado OCI Runtime Spec para ejecutar contenedores compatibles con OCI.

Tanto Clear Containers como runV de Hyper.sh Se crearon herramientas para usar la especificación de tiempo de ejecución de OCI para ejecutar contenedores basados ​​en KVM, y están combinando sus esfuerzos en un nuevo proyecto llamado Kata. El año pasado, Oracle creó una versión de demostración de una herramienta de tiempo de ejecución de OCI llamada RailCar, escrita en Rust. Han pasado dos meses desde que se actualizó el proyecto GitHub, por lo que no está claro si todavía está en desarrollo. Hace un par de años, Vincent Batts trabajó para agregar una herramienta, nspawn-oci , que interpretó un archivo de especificación de tiempo de ejecución de OCI y lanzó systemd-nspawn , pero nadie realmente se dio cuenta y no era una implementación nativa.

Si alguien quiere implementar un systemd-nspawn --oci OCI-SPEC.json nativo y hacer que el equipo de systemd lo acepte para obtener soporte, luego CRI-O, Docker y eventualmente Podman podrían usarlo además de runc  y Clear Container/runV (Kata). (Nadie de mi equipo está trabajando en esto).

La conclusión es que, hace tres o cuatro años, los desarrolladores que estaban aguas arriba querían escribir una herramienta golang de bajo nivel para lanzar contenedores, y esta herramienta terminó convirtiéndose en runc . Esos desarrolladores en ese momento tenían una herramienta basada en C para hacer esto llamada lxc y se alejó de él. Estoy bastante seguro de que en el momento en que tomaron la decisión de construir libcontainer, no habrían estado interesados ​​en systemd-nspawn o cualquier otra forma no nativa (golang) de ejecutar contenedores separados por "espacio de nombres".


Linux
  1. 5 razones por las que debe desarrollar una estrategia de contenedores de Linux

  2. 7 funciones divertidas de transporte de imágenes/contenedores de Linux

  3. ¿Cuál es la diferencia entre un contenedor de Linux y una imagen?

  4. Mis 5 imágenes favoritas de contenedores de Linux

  5. Cómo crear, enumerar y eliminar contenedores Docker en Linux

Comando de historial en Linux (Bash History)

Comando de historial en Linux con ejemplos

Introducción a la gestión de contenedores de Linux

Cómo instalar Mcfly en Linux.

Comando de historial en Linux:ver el historial de terminales de Linux

Cómo gestionar contenedores Docker