Saltar al contenido
Acropolis es experimental: la interfaz, la salida y los valores por defecto pueden cambiar entre versiones.Qué significa
Acropolisby OlimpiaExperimental
Esc
↑↓navegar↵abrir⌘Jvista previa
En esta página

Conceptos

Cómo arma una imagen Acropolis: el plan, el home y sus cachés, sin imagen de builder, descargas segmentadas, runtimes chicos y aislamiento de pasos.

Un build tiene tres fases: detectar la app y escribir un plan, correr los pasos del plan y ensamblar la imagen. acropolis plan imprime solo la primera fase.

Términos clave

término significado
Provider El ecosistema que Acropolis detectó para la app (node, go, rust, python, etc.), elegido con la precedencia de Railpack. ACROPOLIS_PROVIDER fuerza uno.
Plan La lista de pasos que va a correr un build (toolchains, instalaciones, comandos de build) más la especificación de la imagen de runtime. Tiene un hash, planHash en info.json.
Paso Una unidad del plan, con los pasos de los que depende. Cada paso corre aislado (mirá Aislamiento).
Home El directorio con el store direccionado por contenido, los toolchains, el rootfs desempaquetado de las imágenes y las cachés por app. --home o ACROPOLIS_HOME; por defecto $XDG_CACHE_HOME/acropolis, y si no, ~/.cache/acropolis.
Caché por app Las cachés de build de una app dentro del home, seleccionadas con ACROPOLIS_CACHE_KEY (un hash de la ruta de la app si no está definida). Las claves se sanitizan para convertirlas en nombres de directorio, así que team/app y team_app comparten una caché. Se puede exportar e importar como archivo.
Variables de la app Los valores de -e KEY=VALUE y el archivo de configuración. Vienen de la app y no son confiables.
Settings del operador Los settings que figuran en settings del operador (ACROPOLIS_CACHE_KEY, los timeouts y los demás que están ahí). Solo estos se leen exclusivamente del entorno del proceso acropolis, así una app no los puede cambiar; otros settings ACROPOLIS_*, como ACROPOLIS_PROVIDER, también pueden venir de -e o del archivo de configuración.
Imagen de builder ghcr.io/olimpiacloud/acropolis-builder, una imagen Debian 12 con Acropolis y toolchains precalentados, para correr un build por contenedor.

La detección y el plan

Acropolis lee el repo (lockfiles, package.json, go.mod, Cargo.toml, pyproject.toml, configs de frameworks, acropolis.json o railpack.json) y elige un provider con la precedencia de Railpack. El plan lista los toolchains, los pasos de instalación y de build, la base del runtime y los archivos que toma cada capa. Su hash está en info.json como planHash, y tests/plans guarda un snapshot del plan de cada uno de los 131 ejemplos de Railpack, así que cualquier cambio en la detección aparece como diff antes de buildear nada.

Sin imagen de builder

Node, Go y Rust se descargan de sus fuentes oficiales con checksum y se corren directamente. Las dependencias (npm, pnpm, yarn, bun, módulos de Go, crates) las instala el propio Acropolis desde los lockfiles y las verifica mientras se descargan: los módulos de Go contra go.sum, los paquetes de npm contra sus hashes de integridad. Después, los builds de Go corren con GOPROXY=off y sin red.

Los lenguajes sin binarios relocalizables (Ruby, Python con extensiones, PHP, Java, .NET, Elixir) y los paquetes de apt corren en cambio dentro del rootfs de la imagen oficial: sus capas se desempaquetan una sola vez, cada paso tiene su propio upper dir de overlayfs y ese upper dir se convierte en una capa OCI.

Descargas que no se cuelgan

Los blobs de 12 MB o más se descargan en segmentos de 8 MB, seis en paralelo sobre conexiones HTTP/1.1 independientes. Si el segmento que bloquea la salida en orden se atrasa (más de 2,5 s o 2,5 veces la mediana), se arranca un duplicado y gana el primero que termina; los streams que pasan 6 s sin bytes se cortan y se retoman con Range. Copiar node:22-bookworm-slim (80 MB) desde Docker Hub pasó de 12,2 s a 2,9 s.

Las capas base se copian, no se descomprimen

Al ensamblar la imagen final, las capas de la imagen base se copian de registry a registry por digest, arrancando apenas llega el manifest de la plataforma, y en ese camino nunca se descomprimen. Los builds que corren pasos dentro del rootfs de una imagen oficial (mirá más arriba) sí desempaquetan las capas de esa imagen una vez, para correr los pasos. Solo se comprimen las capas nuevas: cada una es una concatenación de frames zstd independientes de ~1 MB (o miembros gzip) comprimidos en paralelo, válida y determinística. El node_modules de producción se escribe directo desde los tarballs del store, sin desempaquetar a disco.

Runtimes chicos

  • Los servidores Node corren sobre distroless con un shell busybox mínimo cuando no necesitan nada de Debian, con exactamente el binario node con el que se buildearon; si no, sobre node:<version>-bookworm-slim.
  • Las apps de Next 15+ sin output configurado se buildean como standalone cuando nada lo impide (un distDir o outputFileTracingRoot propio, scripts de build o de start propios, …), así que la imagen lleva solo lo que traza el servidor. Las condiciones exactas están en D17.
  • Las apps de Bun que arrancan con bun <file> corren sobre distroless/cc con una capa de bun y sin node.
  • Go corre sobre distroless/static y Rust sobre distroless/cc.
  • Las SPAs y los sitios estáticos los sirve Caddy.

Cachés portables

Las cachés de build de Go y Cargo, .next/cache, el node_modules reutilizado y el resto viven en una caché por app en el home. En un builder efímero viaja como archivo: acropolis cache export|import la escribe y la lee como tar.zst.

Aislamiento

Cada paso del build corre en su propio PID namespace (no puede ver a Acropolis ni sus variables), con el filesystem en solo lectura salvo su directorio de trabajo, la caché de su app, /tmp y /var/tmp, un /dev mínimo, las capabilities de un contenedor sin privilegios, un filtro seccomp basado en el perfil por defecto de Docker y su propio network namespace cuando no necesita red. Lo que Acropolis procesa como root (config, lockfiles, patches, capas, cachés) se valida para que no pueda leer ni escribir fuera de su árbol. Con --hermetic=off nada de esto aplica a los pasos del host: corren como procesos comunes del host, y los pasos del host que el plan marca como offline (como los builds de Go y Rust) fallan. Los pasos dentro del rootfs de una imagen oficial mantienen sus namespaces y su filtro seccomp. Mirá Correr en producción para ver qué protege eso y qué no.

Todas las mediciones detrás de estas decisiones, y lo que se probó y se descartó, están en docs/decisions.md.

Última actualización el

¿Te ha resultado útil esta página?