Cada concepto con su definición, dónde aparece en este proyecto y el malentendido típico que conviene poder corregir si sale en la exposición.
Imagen vs. contenedor
La imagen es una plantilla inmutable de solo lectura: un sistema de archivos congelado más metadatos de cómo arrancarlo. El contenedor es una instancia en ejecución de esa imagen, con una capa de escritura propia encima.
Aquí La imagen del devcontainer se construye una vez desde .devcontainer/Dockerfile; cada vez que se abre el proyecto se crea un contenedor a partir de ella.
Ojo La analogía correcta no es «imagen = ISO, contenedor = máquina virtual». Un contenedor no virtualiza hardware ni arranca un kernel: comparte el kernel del anfitrión y solo aísla su vista del sistema.
Namespaces y cgroups
Los dos mecanismos del kernel de Linux que hacen posible un contenedor. Los namespaces aíslan lo que el proceso VE (su tabla de procesos, su red, sus puntos de montaje); los cgroups limitan lo que CONSUME (CPU, memoria).
Aquí Es la razón de que los dos servidores libSQL puedan escuchar ambos en el puerto 8080 dentro de su propio namespace de red, y se publiquen fuera como 8080 y 8081.
Ojo Un contenedor no es una máquina virtual ligera: no hay hipervisor ni kernel invitado. Por eso arranca en milisegundos, y también por eso el aislamiento es más débil que el de una VM.
Capas y caché de construcción
Cada instrucción del Dockerfile produce una capa apilada sobre la anterior. Si una instrucción y todo lo previo no cambian, Docker reutiliza la capa en vez de reconstruirla.
Aquí Por eso la instalación de Chromium va en su propia instrucción y no mezclada con otras: rehacer la imagen por un cambio menor no vuelve a descargar 114 MB de navegador.
Ojo Borrar un archivo en una capa posterior no lo elimina de la imagen: sigue presente en la capa donde se creó. Por eso un secreto que entra al contexto de construcción puede quedar en la imagen aunque el Dockerfile parezca no copiarlo.
Tag vs. digest
Un tag (:latest, :22.12) es una etiqueta móvil: quien publica la imagen puede reapuntarla a otro contenido cuando quiera. El digest (sha256:…) es el hash del contenido: identifica una imagen exacta e irrepetible.
Aquí Las tres imágenes del proyecto están fijadas por digest. El comentario deja el número de versión legible al lado, pero quien manda es el hash.
Ojo Fijar :22.12 en vez de :latest parece suficiente y no lo es: ese mismo tag se reconstruye con parches distintos. Una reproducibilidad que depende de que nadie mueva un tag no es reproducibilidad.
Volumen y bind mount
La capa de escritura de un contenedor muere con él. Un volumen es almacenamiento gestionado por Docker que sobrevive; un bind mount expone directamente una carpeta del anfitrión dentro del contenedor.
Aquí El código fuente entra por bind mount (se edita en el anfitrión y se ve dentro al instante); los datos de las bases y node_modules van en volúmenes con nombre.
Ojo node_modules va deliberadamente en volumen y no en el bind mount: montarlo desde el anfitrión mezclaría binarios compilados para dos sistemas distintos, y esos fallos no se parecen en nada a su causa.
Red de Compose y resolución por nombre
Compose crea una red privada donde cada servicio es alcanzable por su nombre. La publicación de puertos (8080:8080) es un puente hacia afuera, no cómo se hablan los servicios entre sí.
Aquí Desde dentro del devcontainer la base es http://libsql-main:8080; desde el anfitrión es http://127.0.0.1:8080. Es la misma base vista desde dos lados.
Ojo Dentro de un contenedor, «localhost» es ese contenedor, no la máquina. Apuntar a localhost para hablar con otro servicio es el error más común al empezar con Compose.
Capacidades (capabilities)
Linux parte los privilegios de root en unidades independientes. En vez de «root o no root», se puede conceder exactamente la facultad que hace falta: cambiar dueños de archivo, saltarse permisos, bajar de usuario.
Aquí Los contenedores de base de datos arrancan con cap_drop: ALL y solo cuatro capacidades reañadidas, medidas una por una.
Ojo Ante un «permission denied» la salida fácil es --privileged. Eso devuelve todos los privilegios de golpe y convierte la herramienta en una superficie de ataque nueva.
Compose y devcontainer
Compose declara en un archivo un conjunto de servicios, sus redes y volúmenes, y los levanta juntos. Un devcontainer es un estándar abierto que le dice al editor: «abre este proyecto DENTRO de este contenedor».
Aquí compose.yaml declara las dos bases; .devcontainer/compose.yaml lo reutiliza con include y añade el servicio de desarrollo. Una sola definición de la infraestructura, no dos que acaban divergiendo.