>_ DevTrendspt

Idioma

Início

Linguagens

Seções

Frontend Backend Mobile DevOps AI / ML GameDev Blockchain Embarcados Segurança
C

Por que reescrever um runtime de container em C

Tente executar o Docker ou Podman com um limite de memória de 512 kilobytes. O runtime padrão runc simplesmente irá travar com um erro nesse limite. Ele não terá memória suficiente nem para configurar estruturas básicas de processos e ler descritores de arquivo.

O motivo está na arquitetura. A grande maioria da pilha moderna de containers é escrita em Go. Para utilitários de alto nível isso é ótimo, mas na camada mais baixa, o Go traz consigo seu próprio runtime, coletor de lixo e sobrecarga de memória. Para configurar namespaces do kernel Linux antes que o processo principal inicie, runc precisa reiniciar a si mesmo e recorrer a soluções alternativas em C.

O projeto crun da organização Containers enfrenta esse problema diretamente. É uma implementação leve da especificação OCI (Open Container Initiative), escrita inteiramente em C puro.

O que a mudança para C puro oferece

O conceito central é direto: remova tudo o que é desnecessário do caminho crítico de criação de containers. Quando o runtime não está sobrecarregado por camadas extras, o consumo de recursos e a velocidade de resposta melhoram drasticamente.

Consumo de memória e inicialização nos limites

Os benchmarks dos desenvolvedores mostram uma diferença 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

Um container com crun inicia de forma confiável mesmo com um limite estrito de memória de 512 KB, enquanto runc tropeça até em 4 MB. Para microsserviços pesados em nuvem, uma diferença de megabyte pode parecer trivial, mas em dispositivos IoT, roteadores e servidores de borda, essa economia libera memória preciosa para cargas de trabalho reais.

Velocidade de inicialização sequencial

Quando você precisa frequentemente iniciar tarefas de vida curta (por exemplo, em plataformas serverless ou executores de CI/CD), o tempo de inicialização do container se torna um gargalo. Em um teste executando 100 containers sequencialmente com o comando /bin/true, a diferença de tempo é quase o dobro:

| Runtime | Tempo de execução (100 containers) | Diferença | | :--- | :--- | :--- | | crun | 0:01.69 | -49.4% | | runc | 0:03.34 | baseline |

A implementação em C inicia duas vezes mais rápido graças à ausência de sobrecarga de inicialização do runtime Go e menos chamadas de sistema desnecessárias.

Usando como uma biblioteca

A maioria dos runtimes existem apenas como executáveis standalone. Se você precisa iniciar um container OCI a partir do seu programa, você tipicamente tem que usar fork() e invocar um utilitário externo.

Em crun, você pode construir uma biblioteca compartilhada libcrun. Isso abre uma API C direta para trabalhar com containers:

  • incorporar inicialização de ambiente isolado em seus próprios daemons
  • sem sobrecarga de criar processos separados
  • suporte pronto para uso em bindings Python e Lua

Compilação e instalação

O utilitário está disponível na maioria das distribuições através dos gerenciadores de pacotes padrão, mas compilar a partir do código-fonte também é direto.

Compilar no Ubuntu requer arquivos de cabeçalho 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

O próprio processo de compilação é padrão:

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

Se você planeja usar a biblioteca no seu código, a flag do configure mudará para ./configure --enable-shared.

Para ambientes reproduzíveis e servidores sem dependências extras, o projeto suporta builds estáticos via Nix Flakes:

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

O resultado é um binário estático compacto para x86_64 com glibc, pronto para executar em qualquer máquina-alvo.

Para quem é

Mudar para crun faz sentido em vários cenários:

  1. Computação de borda e IoT. Quando um dispositivo tem apenas 256 MB ou 512 MB de RAM soldados, abrir mão de megabytes para o runtime é inaceitável.
  2. Serverless e Functions-as-a-Service. Em ambientes com criação e destruição frequente de containers isolados, um speedup de 50% na inicialização reduz diretamente a latência de cold start.
  3. Empacotamento denso de containers. Se centenas de pequenos workers são executados em um único host, a economia total de RAM chega a gigabytes.

Trocar o runtime no Podman é tão simples quanto uma única flag --runtime /usr/bin/crun ou alterar um parâmetro em /etc/containers/containers.conf. Nenhuma alteração em manifestos ou imagens é necessária.

Projetos relacionados