>_ DevTrendses

Idioma

Inicio

Lenguajes

Secciones

Frontend Backend Móvil DevOps AI / ML GameDev Blockchain Embebidos Seguridad
C

¿Por qué reescribir un runtime de contenedores en C?

Intenta ejecutar Docker o Podman con un límite de memoria de 512 kilobytes. El runtime estándar runc simplemente fallará con un error ante tal límite. No tendrá suficiente memoria ni siquiera para configurar estructuras básicas de procesos y leer descriptores de archivos.

La razón radica en la arquitectura. La gran mayoría de la pila moderna de contenedores está escrita en Go. Para utilidades de alto nivel esto es excelente, pero en la capa más baja Go arrastra su propio runtime, recolector de basura y sobrecarga de memoria. Para configurar los namespaces del kernel de Linux antes de que comience el proceso principal, runc tiene que reiniciarse y recurrir a soluciones en C.

El proyecto crun de la organización Containers aborda este problema directamente. Es una implementación ligera de la especificación OCI (Open Container Initiative), escrita completamente en C puro.

Qué te da cambiar a C puro

El concepto central es directo: eliminar todo lo innecesario de la ruta crítica de creación de contenedores. Cuando el runtime no está cargado con capas adicionales, el consumo de recursos y la velocidad de respuesta mejoran drásticamente.

Consumo de memoria y lanzamiento en los límites

Los benchmarks de los desarrolladores muestran una diferencia reveladora:

# Запуск через runc падает
$ podman --runtime /usr/bin/runc run --rm --memory 4M fedora echo it works
Error: container_linux.go:346: starting container process caused "process_linux.go:327: getting pipe fds for pid 13859 caused \"readlink /proc/13859/fd/0: no such file or directory\"": OCI runtime command not found error

# Запуск через crun отрабатывает без запинки
$ podman --runtime /usr/bin/crun run --rm --memory 512k fedora echo it works
it works

Un contenedor con crun inicia de manera confiable incluso con un límite estricto de memoria de 512 KB, mientras que runc falla incluso a 4 MB. Para microservicios pesados en la nube, un megabyte de diferencia puede parecer trivial, pero en dispositivos IoT, routers y servidores edge, este ahorro libera memoria preciosa para cargas de trabajo reales.

Velocidad de inicio secuencial

Cuando necesitas lanzar frecuentemente tareas de corta duración (por ejemplo, en plataformas serverless o runners de CI/CD), el tiempo de inicialización del contenedor se convierte en un cuello de botella. En una prueba ejecutando 100 contenedores secuencialmente con el comando /bin/true, la diferencia de tiempo es casi del doble:

| Runtime | Tiempo de ejecución (100 contenedores) | Diferencia | | :--- | :--- | :--- | | crun | 0:01.69 | -49.4% | | runc | 0:03.34 | referencia |

La implementación en C inicia el doble de rápido gracias a que no tiene sobrecarga de inicialización del runtime de Go y menos llamadas al sistema innecesarias.

Uso como biblioteca

La mayoría de los runtimes solo existen como ejecutables independientes. Si necesitas lanzar un contenedor OCI desde tu programa, típicamente tienes que hacer fork() e invocar una utilidad externa.

En crun, puedes construir una biblioteca compartida libcrun. Esto abre una API en C directa para trabajar con contenedores:

  • incrustar el inicio de entornos aislados en tus propios demonios
  • sin sobrecarga por generar procesos separados
  • soporte listo para usar de enlaces de Python y Lua

Compilación e instalación

La utilidad está disponible en la mayoría de distribuciones a través de los gestores de paquetes estándar, pero también es sencillo compilar desde el código fuente.

Compilar en Ubuntu requiere los archivos de cabecera básicos:

$ sudo apt-get install -y make git gcc build-essential pkgconf libtool \
   libsystemd-dev libprotobuf-c-dev libcap-dev libseccomp-dev libjson-c-dev \
   go-md2man autoconf python3 automake

El proceso de compilación en sí es estándar:

$ ./autogen.sh
$ ./configure
$ make
$ sudo make install

Si planeas usar la biblioteca en tu código, la bandera de configure cambiará a ./configure --enable-shared.

Para entornos reproducibles y servidores sin dependencias adicionales, el proyecto soporta compilaciones estáticas a través de Nix Flakes:

$ nix --extra-experimental-features "nix-command flakes" build "path:.#crun-static-amd64"
$ ./result/bin/crun --version

El resultado es un binario estático compacto para x86_64 con glibc, listo para ejecutarse en cualquier máquina objetivo.

Para quién es

Cambiar a crun tiene sentido en varios escenarios:

  1. Edge computing e IoT. Cuando un dispositivo tiene solo 256 MB o 512 MB de RAM soldados, entregar megabytes al runtime es inaceptable.
  2. Serverless y Functions-as-a-Service. En entornos con creación y destrucción frecuente de contenedores aislados, una mejora del 50% en el tiempo de inicio reduce directamente la latencia de cold start.
  3. Empaquetado denso de contenedores. Si cientos de pequeños workers se ejecutan en un solo host, el ahorro total de RAM se acumula hasta gigabytes.

Cambiar el runtime en Podman es tan simple como una sola bandera --runtime /usr/bin/crun o cambiar un parámetro en /etc/containers/containers.conf. No se necesitan cambios en manifiestos o imágenes.

Proyectos relacionados