< Blog

Containers como herramientas

Migsar Navarro - 2025-05-19

#containers
#tooling
#podman
#docker
#contenedores

Hoy en la mañana El sábado en la mañana quería probar limbo, un nuevo proyecto de Turso que eventualmente substituirá a libsql, pero no quería tener que instalar cosas en mi computadora, que ya está bastante saturada, así que decidí que usaría podman para crear un contenedor con la última versión de limbo, si por algún motivo no me gustaba simplemente la borraba.

En este post escribiré sobre cómo y por qué usar containers(containers) para experimentar y hacer tareas repetitivas o periódicas, me parece que es un caso de uso excelente y del que se ha hablado muy poco. El uso más común de los contenedores es para desarrollar o poner aplicaciones en el servidor, esencialmente se debe a que ofrecen aislar el ambiente de ejecución y unificarlo, por lo que permiten que tanto el desarrollo como la producción sean mucho más predecibles, sin embargo, también existen grandes contrastes entre desarrollo y producción.

El principal contraste entre desarrollo y producción es el tamaño, en desarrollo me ha tocado ver imágenes gigantescas y con poco o nada de cuidado en la optimización, se incluye todo como si fuera una instalación de escritorio para un usuario normal. Esto tiene un impacto directo en la experiencia de desarrollo, el developer es el que paga las cuentas teniendo largos tiempos muertos, sea en el inicio del desarrollo mientras el ambiente carga, o cuando hace cambios y el ambiente se tiene que actualizar. En producción sucede lo contrario, la prioridad es generar una imagen lo más pequeña y eficiente posible, sin embargo, a veces la eficiencia en términos técnicos deja de lado las necesidades humanas, al final el proceso será, en el mejor de los casos, auditado por humanos para saber que todo está bien, y es preciso tener cierta infraestructura para poder observar los aspectos importantes, parece simple, pero a veces la imagen es demasiado básica y después las personas instalan algo en la instancia y se pierde el determinismo, pues la instancia en ejecución es la única que tiene todo lo necesario y el equipo de trabajo le tiene miedo a tener que crear otra, o sólo una persona sabe los pasos siguientes a la instanciación.

¿Por qué containers?

Existen muchos artículos sobre las ventajas de los contenedores, creo que una búsqueda rápida puede dar suficientes resultados y no hay mucho para agregar. A manera de resumen:

  • Portabilidad: Permiten abstraer el ambiente y lograr que se ejecute del mismo modo en diferentes máquinas para desarrollo o servidores en la nube.
  • Eficiencia: Son mucho más ligeros que las máquinas virtuales, lo que permite aprovechar mejor el servidor.
  • Aislamiento: Establecen una frontera clara entre el sistema y la aplicación, también entre diferentes aplicaciones, haciendo explícito lo que se comparte y lo que no.

Pensando en resultados (outcome)

El concepto de determinismo es muy importante en la computación, pues es generalmente deseable que el mismo proceso produzca el mismo resultado. A veces es fácil visualizarlo cuando se tiene una función o un API que produce un resultado en el sentido matemático (result), pero es más difícil pensar en eso para procesos en los que el resultado no es un dato sino un estado (outcome). Vamos a pensar un poco más en este último caso, cuando clonamos un ambiente de desarrollo no sólo es necesario que el código esté igual, muchas veces queremos también tener un estado inicial semejante, como al correr las migraciones, o al llevar a cabo una prueba integral (E2E, end-to-end).

Existen herramientas y formas de hacer que los resultados sean semejantes, aunque pocas veces iguales, por un lado, la documentación, se asume que un developer al entrar a una empresa tendrá un onboarding, y que le tomará cierto tiempo ser productivo. También existe mucho software para comunicación, screenshots, videos, llamadas, plataformas que prometen un flujo más eficiente de la información. Al final sabemos que a veces programar no es la parte difícil, sino todo lo que lo rodea desde los requerimientos iniciales, hasta el estado en que alguien empieza a usar el sistema para hacer una prueba.

¿Sería posible usar los contenedores para mejorar este aspecto? Yo creo que sí, y mucho. El software es usado por un individuo o una empresa de un modo bastante particular, idiomático si se quiere llamar de un modo. Esa capa adicional de configuración me parece que es un candidato ideal para crear herramientas containerizadas.

Ejemplo: Limbo, sqlite escrito en rust

Ahora regreso al punto que empezó todo: Limbo.

Se trata de una base de datos escrita en Rust, inspirada y compatible con sqlite pero con más funcionalidad y un modelo diferente para la gestión del código. Vamos por pasos, lo primero, escrita en Rust, que de algún modo permite que sea un poco más sencilla de mantener y extender que en C. Después, la funcionalidad, una de las cosas que más me interesa es el hecho de poder servir el contenido, es decir, a través de la red y HTTP. Finalmente la gestión del código, sqlite tiene un modelo muy particular, y limbo y libsql usan modelos más comunes, con el código en un repositorio git y participación, en issues y pull requests, de un modo bastante estandarizado.

El archivo para crear el contenedor puede ser tan simple o tan complejo como queramos, algunas veces toma un poco de tiempo saber cuáles son las dependencias necesarias, pero además de eso, el proceso debe de ser directo y en la mayoría de los casos no importa si la imagen está optimizada o no.

En este caso sólo incluí el código que copié del repositorio, sé que es importante verificar la seguridad y puede que no sea lo mejor copiar y pegar tan a la ligera, mi archivo, que llamé Containerfile.limbo-dev quedó así:

FROM ubuntu:24.04

RUN apt-get update apt install -y curl xz-utils
RUN curl --proto '=https' --tlsv1.2 -LsSf \
  https://github.com/tursodatabase/limbo/releases/latest/download/limbo_cli-installer.sh | sh

WORKDIR /app

A veces uso Alpine Linux porque la imagen es más pequeña, pero en este caso me decía que no existía un archivo para esa arquitectura, entonces use Ubuntu. Necesité instalar xz-utilspara poder descomprimir el archivo del repositorio. Por otro lado, pusé /appcomo el directorio de trabajo porque sabía montaría un volumen en el contenedor para que mi archivo no desaparezca cuando borre el contenedor.

Para crear la imagen usé el siguiente comando:

podman build -t limbo:20250516 -f Containerfile.limbo-dev

Tengo muy mala memoria, por lo que usé limbocomo el nombre y la fecha en la que lo creé como tag. Además como tengo un directorio llamado containersen donde creo muchos contenedores que no tienen proyecto necesito pasar el nombre del archivo también.

Finalmente, para crear el contenedor y entrar en él uso el siguiente comando:

podman run -it --rm -v .:/app limbo:20250516 /bin/bash

La opción ies para que sea interactivo, tes para tener una pseudo-terminal tty, rm es para borrar el contenedor al salir, y ves para montar un volumen, en este caso el directorio actual será /appdentro del contenedor. A veces puede ser mejor ejecutar algún programa o script dentro del contenedor, pero en este caso me resultó muy cómodo abrir bash.

Conclusiones

En este caso parece que sólo estamos haciendo un ejercicio de exploración, y es cierto, sin embargo, aunque sea simple, se puede crear esta imagen y poner en un registro privado que sea de fácil acceso, a partir de ese momento, se crea una abstracción en la que no será necesario saber usar shell, y posiblemente ni siquiera tener las herramientas, como la biblioteca xz-utils, más importante aún no será necesario instalar limbo en la computadora, sólo dockero podman, y es algo que servirá para el resto de las herramientas, por lo que es un conocimiento sencillo que es fácilmente reutilizado. De ahora en adelante se puede acceder y modificar los archivos de sqlite dentro del contenedor aún si la base de datos permanece en el sistema de archivos local. Además, si fuera necesario, sería posible incluir scripts u otras herramientas en la misma imagen para, por ejemplo, crear un csv a partir de la base de datos.