>_ DevTrendsnl

Taal

Home

Talen

Secties

Frontend Backend Mobiel DevOps AI / ML GameDev Blockchain Embedded Beveiliging
C

Waarom een Container Runtime Herschrijven in C

Probeer Docker of Podman uit te voeren met een geheugenlimiet van 512 kilobytes. De standaard runtime runc zal gewoon crashen met een fout bij zo'n limiet. Er is niet genoeg geheugen om zelfs maar basale processtructuren op te zetten en bestandsdescriptoren te lezen.

De reden ligt in de architectuur. De overgrote meerderheid van de moderne containerstack is geschreven in Go. Voor top-level utilities is dit geweldig, maar op het allernederste niveau brengt Go zijn eigen runtime, garbage collector en geheugenoverhead met zich mee. Om Linux-kernel namespaces op te zetten voordat het hoofdproces start, moet runc zichzelf opnieuw opstarten en toevlucht nemen tot C-werkarounds.

Het crun-project van de Containers-organisatie pakt dit probleem direct aan. Het is een lichtgewicht implementatie van de OCI (Open Container Initiative)-specificatie, volledig geschreven in pure C.

Wat overschakelen naar pure C je oplevert

Het kernconcept is eenvoudig: verwijder alles onnodige van het kritieke pad van containercreatie. Wanneer de runtime niet belast wordt door extra lagen, verbeteren resourceverbruik en reactiesnelheid dramatisch.

Geheugenverbruik en opstarten aan de limieten

De benchmarks van de ontwikkelaars tonen een veelzeggend verschil:

# Запуск через 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

Een container met crun start betrouwbaar op, zelfs met een strikte geheugenlimiet van 512 KB, terwijl runc al struikelt bij 4 MB. Voor zware cloud microservices kan een megabyte verschil triviaal lijken, maar op IoT-apparaten, routers en edge servers bevrijdt deze besparing kostbaar geheugen voor daadwerkelijke workloads.

Sequentiële opstartsnelheid

Wanneer je regelmatig kortlopende taken moet starten (bijvoorbeeld in serverless platforms of CI/CD runners), wordt containerinitialisatietijd een bottleneck. In een test die 100 containers sequentieel uitvoert met het /bin/true commando, is het tijdsverschil bijna tweevoudig:

| Runtime | Uitvoeringstijd (100 containers) | Verschil | | :--- | :--- | :--- | | crun | 0:01.69 | -49.4% | | runc | 0:03.34 | basislijn |

De C-implementatie start twee keer zo snel dankzij geen Go runtime initialisatie-overhead en minder onnodige systeemcalls.

Gebruiken als bibliotheek

De meeste runtimes bestaan alleen als standalone executables. Als je een OCI-container vanuit je programma moet starten, moet je meestal fork() aanroepen en een extern utility starten.

In crun kun je een gedeelde bibliotheek bouwen libcrun. Dit opent een directe C API voor het werken met containers:

  • het insluiten van geïsoleerde omgevingsopstart in je eigen daemons
  • geen overhead van het spawnen van aparte processen
  • out-of-the-box ondersteuning voor Python en Lua bindings

Bouwen en installeren

Het utility is beschikbaar in de meeste distributies via standaard package managers, maar bouwen vanuit source is ook eenvoudig.

Bouwen op Ubuntu vereist basis header bestanden:

$ 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

Het compilatieproces zelf is standaard:

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

Als je van plan bent de bibliotheek in je code te gebruiken, zal de configure flag veranderen naar ./configure --enable-shared.

Voor reproduceerbare omgevingen en servers zonder extra dependencies ondersteunt het project statische builds via Nix Flakes:

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

Het resultaat is een compact statisch binary voor x86_64 met glibc, klaar om te draaien op elke doelmachine.

Voor wie is het bedoeld

Overschakelen naar crun heeft zin in verschillende scenario's:

  1. Edge computing en IoT. Wanneer een apparaat slechts 256 MB of 512 MB RAM gesoldeerd heeft, is het opgeven van megabytes aan de runtime onacceptabel.
  2. Serverless en Functions-as-a-Service. In omgevingen met frequente creatie en vernietiging van geïsoleerde containers vermindert een 50% snellere opstart direct de cold start latentie.
  3. Dichte container packing. Als honderden kleine workers op één host draaien, tellen de totale RAM-besparingen op tot gigabytes.

De runtime in Podman verwisselen is zo simpel als een enkele vlag --runtime /usr/bin/crun of het veranderen van een parameter in /etc/containers/containers.conf. Geen wijzigingen aan manifests of images zijn nodig.

Gerelateerde projecten