
Farm llega al open source: entornos de preview sin dolor
Farm llega al open source: entornos de preview sin dolor
Cómo Farm pasó de un script self-hosted en una sola VM a un orquestador de entornos de preview sobre Docker y Kubernetes, y por qué abrimos su código.

Cada pull request no solo quieres que pase los tests: quieres enseñarlo en vivo —al diseñador, al mánager, al equipo de al lado— para que todos puedan trastear con la UI y alegrarse, o encontrar bugs y disgustarse. Parece sencillo: levantas un entorno y compartes el enlace. Pero cuando los pull requests llegan por decenas cada día y los equipos son un montón, “simplemente levantar un entorno” se convierte en todo un proyecto de infraestructura.
¡Hola! Me llamo Mikhail Golbakh y soy desarrollador sénior de interfaces en Yandex Cloud. Mi equipo y yo llevamos ya varios años desarrollando Farm: un servicio que levanta un entorno de preview por cada pull request. En este artículo te contaré cómo Farm pasó de ser un simple script self-hosted en una sola máquina virtual a un orquestador sobre Docker y Kubernetes, y por qué acabamos abriendo su código.
Cómo llegamos a la primera versión de Farm
En Yandex hay multitud de equipos de frontend que crean cientos de pull requests al día. Cada pull request lanza un sinfín de tests y otras comprobaciones en CI. Los tests unitarios, los chequeos de tipos y los linters se ejecutan sin problema en máquinas virtuales aisladas.
Pero ¿qué haces cuando necesitas levantar una aplicación completa desde una rama? De esta pregunta nace una tarea con la que los desarrolladores frontend se topan constantemente: levantar una “beta” por cada pull request. Lo primero que viene a la cabeza es montar un entorno de desarrollo dedicado, a imagen de producción, y compartirlo entre los desarrolladores. Es lo que suelen hacer los equipos pequeños al principio, pero enseguida se chocan con el problema del escalado. Un único entorno ya no se puede repartir, y tampoco puedes ponerte a pasar en él los tests e2e: su estabilidad, claramente, plantearía dudas.
Tradicionalmente, cada equipo monta su propio proceso para desplegar entornos de desarrollo, invirtiendo en ello bastante esfuerzo y recursos de mantenimiento. Nosotros no fuimos la excepción y creamos una solución propia. Eso sí, la diseñamos desde el principio como una tecnología común, capaz de escalar a decenas de equipos pese a las posibles diferencias de infraestructura.
Allá por 2018, el equipo de frontend de Yandex Cloud creó la primera versión de Farm: en esencia, un sencillo servicio self-hosted que descargaba el código de una rama del repositorio, ejecutaba los comandos de compilación de la aplicación objetivo y arrancaba su proceso en una máquina virtual, enrutando el tráfico desde un dominio dedicado. Hasta el propio conjunto de comandos estaba hardcodeado: Farm estaba pensado ante todo para nuestros proyectos, que se construyen sobre componentes comunes para servicios web en Node.js. El resultado fue una especie de sistema de despliegue simplificado con sabor a Yandex.
Con los años, el servicio Farm fue evolucionando: aprendió a trabajar con configuraciones de compilación y arranque separadas para cada proyecto, ganó una UI completa, una base de datos y un buen montón de código adicional que simplificaba el trabajo con la infraestructura de Yandex. Pero la esencia del servicio no cambió: seguía resolviendo las siguientes tareas:
-
descargar el código y compilar el proyecto a partir de un pull request;
-
enrutar el tráfico hasta la aplicación en ejecución;
-
unificar la solución en un único componente de infraestructura, fácil de distribuir e integrar en los procesos de CI de Yandex.
Cómo funciona
Farm es una pequeña aplicación Node.js, y su esquema de funcionamiento simplificado es el siguiente:
-
Farm recibe una solicitud de generación. Puede llegar desde una tarea de CI o directamente de un usuario a través de un formulario en la UI.
-
Una nueva instancia de la aplicación aterriza en la base de datos con el estado queued y se pone a la cola de compilación.
-
Farm va procesando la cola, descarga el código del repositorio y la rama objetivo, lee la configuración y lanza la compilación.
-
Arranca el proceso de la aplicación: se espera de él que abra un socket en
<instance_dir>/dist/server.sock. -
Farm —o, mejor dicho, ya nginx con una configuración específica— empieza a enrutar el tráfico hacia un dominio único, por ejemplo: d008838956420e285cee652262dd5d18b2ed2a2a.farm.example.com.

Como se ve en el diagrama, no le encargamos a Farm la responsabilidad del balanceo de carga: usamos nginx. Y este enfoque ha venido para quedarse: mantener el proxy del tráfico es una tarea nada trivial que preferimos no resolver por nuestra cuenta. También había soporte de Git y de Arc, el sistema de control de versiones interno del propio Yandex.
La compilación y el arranque en sí no eran más que Farm ejecutando comandos. Todo era lo más directo y a lo bruto posible:
# Compilación
npm ci
npm run build
# Inicio
npm run start
Para completar el cuadro, echemos también un vistazo a un ejemplo de configuración farm.json:
{
"preview-generator": {
"build": ["npm ci", "npm run build"],
"start": {
"command": "npm",
"args": "run start"
},
"env": {
"APP_BUILDER_CDN": "false",
"IS_FARM": "true"
},
"instances": [
{
"name": "preprod",
"urlTemplate": "https://{hash}.farm.example.com",
"env": {
"APP_INSTALLATION": "russia",
"APP_ENV": "preprod"
}
},
{
"name": "prod",
"urlTemplate": "https://{hash}.farm.example.com",
"env": {
"APP_INSTALLATION": "russia",
"APP_ENV": "prod"
}
}
]
}
}
Lo que obtuvimos fue un auténtico “caballo de batalla”. Al equipo en cuestión solo le quedaba preparar una máquina virtual, describir la configuración del propio Farm y de sus proyectos, levantarlo y montar un proceso de CI para los pull requests. Suena fácil, ¿verdad? Pero…
Crecimiento y nuevos desafíos
Farm vivió en ese estado durante bastante tiempo y resolvía de forma decente las tareas que tenía encomendadas. Al principio incluso teníamos un “piso compartido”: un único Farm común para varios equipos de frontend, para no gastar esfuerzos de más manteniendo varias instalaciones. Pero los equipos crecían en tamaño, su número aumentaba, y Farm solo podía funcionar dentro de una única máquina virtual. Con el tiempo, ni siquiera las configuraciones más potentes daban abasto.
Ejecutar varias compilaciones en paralelo consumía todos los recursos de la máquina, y una instancia podía pasarse un buen rato esperando en la cola. Así que arrancó un proceso orgánico de mudanza: el Farm comunal se fue dividiendo en otros más pequeños, a veces incluso uno por proyecto.
Obviamente, esta división no resolvía el problema: solo lo aplazaba. Los recursos de cómputo se seguían agotando igualmente, y los nuevos equipos tenían que dedicar esfuerzo extra a soportar y mantener Farm. Por ejemplo, el disco de la máquina virtual se llenaba con bastante regularidad, obligando a lanzar una limpieza manual de archivos innecesarios. Así que el primer problema son los recursos limitados sin gestión ni escalado. No lo perdamos de vista.
Otro problema era que, bajo el capó, Farm usaba SQLite como base de datos sin ninguna abstracción tipo Knex. Esto generaba vendor lock y, además, nos obligaba a borrar todos los datos con cada cambio de esquema, ya que no existía mecanismo de migraciones. Teniendo en cuenta que Farm estaba repartido en un montón de instalaciones por equipo, esto también hacía que casi nadie quisiera actualizarlo: a saber qué se rompería, y lo más probable es que encima tuvieras que borrar tus datos. Como se suele decir: “si funciona, no lo toques”.
Para colmo, Farm solo sabía ejecutar aplicaciones Node.js, y todos los procesos corrían directamente en el host sin aislamiento alguno. Y como no había aislamiento, las aplicaciones no podían usar sin más su puerto de siempre para recibir tráfico, porque Farm no se ocupaba de la gestión de puertos. Por eso la comunicación se hacía por unix sockets, obligando a las aplicaciones a cumplir ese contrato un tanto peculiar.
Así pues, teníamos que resolver los siguientes problemas:
-
Recursos limitados sin gestión ni escalado.
-
Ausencia de migraciones de base de datos y dependencia rígida de SQLite.
-
Falta de aislamiento de las instancias, más la restricción de ser una aplicación Node.js que publica un unix socket.
-
La carga de soporte y operación: al fin y al cabo, lo que queríamos originalmente era facilitar la vida a los equipos, no añadir dolores de cabeza (es un problema extra que de momento dejaremos aparte, pero volveremos a él hacia el final).
Cómo lo afrontamos
Un ingeniero con experiencia, viendo estas tareas, seguramente propondría subir Farm a los raíles de un clúster de k8s. Y a esa idea llegamos poco a poco: llevar la compilación y el despliegue de las aplicaciones a la capacidad de cómputo del clúster, a pods separados, y aprovechar sus posibilidades de escalado automático y gestión de recursos. Las aplicaciones, por su parte, se entregarían como el contenedor docker de toda la vida: un contrato muy claro y sencillo para nuestra industria. Además, necesitábamos conservar la posibilidad de ejecutar aplicaciones a la antigua usanza, como procesos, para que los equipos pequeños no tuvieran que pasar por una laboriosa migración a la solución de k8s.
Abandonar Farm sin más y pasar a desplegar las aplicaciones directamente desde CI al clúster de k8s era imposible por las siguientes razones:
-
Farm había echado raíces profundas en todos nuestros procesos y CI existentes. Obligar a los equipos a migrar a una solución completamente nueva no tenía sentido.
-
Si entramos en detalles, Farm no solo compila y arranca la aplicación: también asume la responsabilidad del enrutamiento (aunque sea vía nginx), detiene y elimina las instancias sin uso, y hace mucho más.
-
Y también cuenta mucho lo cómodo de su UI, que permite levantar una instancia de una aplicación no solo a los desarrolladores de frontend, sino también a los de backend, para probar distintas funcionalidades.
En la práctica, añadir soporte de k8s resultó más difícil de lo que pensábamos al principio. Desde las primeras versiones, la lógica de trabajo con las instancias de aplicaciones estaba muy dispersa por la base de código, y a lo largo de los años se habían acumulado en ella un montón de detalles y de elegantes apaños: encajar al lado una implementación más era complicado.
Una refactorización a fondo
Como primer paso, emprendimos una gran refactorización del código de Farm. Primero había que encapsular la lógica de trabajo con instancias en una clase de proveedor dedicada: una especie de backend de nuestro Farm que definiría la estrategia de compilación y despliegue de la aplicación. Esto nos permitiría soportar de momento el esquema legacy y k8s en paralelo, y más adelante añadir nuevas implementaciones a través de una interfaz única.
Tras destripar el código, conseguimos extraer todos los métodos específicos del proveedor y preparar una clase abstracta que los describiera. Simplificando un poco, todo se redujo a esto:
class BaseFarmProvider {
startup(): Promise<void>;
buildInstance(
generateData: GenerateInstanceData,
observer: SubscriptionObserver<InstanceObservableEmitValue>,
): Promise<void>;
stopBuilder(hash: string): Promise<void>;
startInstance(instance: Instance): Promise<void>;
stopInstance(hash: string): Promise<void>;
restartInstance(instance: Instance): Promise<void>;
deleteInstance(hash: string): Promise<void>;
getInstanceStatus(instance: Instance): Promise<InstanceProviderStatus>;
getInstances(): Promise<Array<InstanceProviderInfo>>;
getInstanceLogs(params: {
hash: string;
stdout?: LogParams;
stderr?: LogParams;
}): Promise<{stdout?: string; stderr?: string}>;
}
Más o menos en ese momento abstrajimos también todo el acceso a la base de datos y lo pasamos a Knex, para poder cambiar en el futuro a otro SGBD más potente y persistente. Como extra, conseguimos un mecanismo de migraciones barato que facilita modificar el esquema de la base de datos.
El nuevo esquema con Kubernetes
En el nuevo esquema, Farm se despliega en el clúster, funciona como un controlador, se conecta a la API de k8s y pasa a gestionar los recursos del clúster: lanza pods para las compilaciones y para la aplicación, crea y elimina otros recursos.
Para entenderlo, basta con repasar los dos procesos principales: la compilación y el despliegue de una instancia de la aplicación.
La compilación consta de varias etapas:
-
Un usuario o CI lanza la compilación (el método buildInstance).
-
De la rama se obtiene la configuración de Farm para la aplicación objetivo (el archivo farm.json en la raíz del repositorio).
-
Arranca un pod builder: descarga el código de la rama, construye una imagen docker (Dockerfile.farm por defecto) y la sube a un registro predefinido.
-
Todos los logs del pod builder se transmiten en tiempo real y se muestran en la UI para depuración.
-
Al terminar la compilación, Farm elimina el pod builder.

La salida de la compilación es un artefacto: la imagen de la aplicación subida al registro, lista para desplegarse y arrancar. Como extra, obtuvimos el cacheo de compilaciones mediante Docker, algo muy útil para acelerar el CI.
El despliegue arranca justo después de la compilación, en el mismo método buildInstance, así que continuamos desde el momento en que la imagen ya está subida:
-
Se crea un Deployment en el clúster, que usa la imagen obtenida en la etapa anterior.
-
Se crean un Service con el puerto que indicamos en la configuración de Farm y un Ingress con un dominio basado en el ID de la instancia.
-
Y entonces entra en juego un nuevo actor: el Ingress NGINX Controller. Se encarga de todo el enrutamiento y hace que la aplicación sea accesible desde fuera. Aquí Farm se mantiene fiel a su estilo y delega el proxy del tráfico en otro componente del sistema.

Técnicamente, para el enrutamiento se podría usar cualquier otro controlador de Ingress, hasta un ALB en la nube si hiciera falta. Elegimos el Ingress NGINX Controller por su sencillez y por la velocidad con la que actualiza la configuración.
Así es, más o menos, como conseguimos una instancia funcional de la aplicación, lista para abrirla y ponerse a probar. Además, Farm se asegura de que los recursos no se dupliquen y de que todas las operaciones sean idempotentes, algo crucial para un funcionamiento estable.
Un esquema más sencillo con Docker a secas
En cuanto apareció la idea del soporte de k8s en Farm, surgió también una propuesta bastante evidente: ¿por qué no soportar la ejecución de instancias simplemente en una máquina virtual con Docker? Eso resolvería el problema del aislamiento de instancias, eliminaría las restricciones de la aplicación Node.js y de los puertos, y le daría a Farm un modo de funcionamiento más, esta vez para equipos pequeños sin clúster de k8s. Más adelante podríamos abandonar por completo la vieja forma de arrancar procesos Node.js a pelo, lo que simplificaría mucho el mantenimiento al dejarnos borrar un buen montón de código legacy.
¿Cómo funciona esto al final? Farm se conecta al Docker Engine por un socket y hace él mismo de builder: aquí no hay pods separados para la compilación. Y esta es la primera diferencia importante respecto al esquema de k8s: la imagen de la instancia se construye en local, en el mismo daemon, y ahí se queda a vivir. No hace falta subirla a ningún registro: el registro solo resulta útil para bajar las imágenes base durante la construcción. El resultado es notablemente más sencillo, y eso es justo lo que necesitan los equipos pequeños sin clúster.
Diseñamos dos modos de funcionamiento:
-
docker_container — el propio Farm corre como un contenedor con el socket del host montado y dirige como un director de orquesta los contenedores “vecinos” en el mismo daemon;
-
vm — Farm vive directamente en la máquina virtual con su propio Docker Engine.
Tanto Farm como todas las instancias se conectan a una red Docker compartida (farm por defecto), y cada instancia de la aplicación recibe un contenedor con el elocuente nombre farm-docker-<hash>. Ese es justo el nombre con el que luego lo encontramos cuando llega la hora de enrutar el tráfico.
Igual que en el esquema de k8s, la compilación y el arranque conviven en el mismo método buildInstance, solo que sin pods separados:
-
Un usuario o CI lanza la compilación (el método buildInstance).
-
Se baja el código de la rama y se lee la configuración de Farm desde farm.json: de ahí salen la ruta al Dockerfile y las variables de compilación y de ejecución.
-
Farm construye la imagen docker en local y le pone la etiqueta
farm-docker-<hash>, transmitiendo de paso todos los logs de la construcción a la UI para depuración. -
A partir de la imagen resultante se crea y se arranca un contenedor en la red farm, con las variables de entorno inyectadas y, si se desea, el comando de arranque sobrescrito.

Enrutamiento del tráfico
Aquí Farm vuelve a delegar el proxy en nginx. El contenedor de la instancia no publica ningún puerto en el host: se puede llegar a él por el nombre farm-docker-<hash> dentro de la red Docker farm, donde el DNS integrado de Docker resuelve ese nombre.
La única duda es dónde vive ese nginx capaz de resolver el nombre, y eso ya depende del modo:
-
En el modo docker_container, la petición llega desde el host al contenedor de Farm, y su nginx interno, asentado en la red farm, lleva el tráfico directamente hasta la instancia.
-
En el modo vm, el nginx de Farm corre directamente en el host y no ve los nombres de los contenedores, así que reenvía la petición a un contenedor aparte, el Docker Proxy (el mismo nginx, solo que levantado dentro de la red farm), y es este quien entrega el tráfico a la instancia adecuada.

Y así conseguimos una instancia funcional en una máquina virtual normal y corriente con Docker, sin ningún clúster de por medio: el código se compila en una imagen, la imagen se convierte en un contenedor y nginx le hace llegar el tráfico.
De paso resolvimos también el problema del aislamiento junto con aquel contrato tan peculiar de los unix sockets: ahora la aplicación se entrega como una imagen docker de las de siempre y se limita a escuchar en su puerto, sin saber nada de la cocina interna de Farm.
La evolución de Farm y su paso al código abierto
Después de todas las mejoras —las abstracciones de proveedores, el soporte de distintos esquemas de compilación y ejecución, y el paso a Knex—, Farm ya tenía pinta de producto maduro y completo, en el que la especificidad de Yandex no jugaba un gran papel. Poco a poco, Farm se había convertido en un orquestador sin acoplamiento rígido a las tecnologías que ejecutaba por debajo.
Ya recordaba a un sistema de despliegue simplificado al estilo de Heroku, solo que como servicio self-hosted. Así que surgieron planes más ambiciosos: publicar el código de Farm como parte de nuestro proyecto de código abierto Gravity UI, para que resultara útil no solo dentro de la empresa, sino a toda la comunidad.
Pero esta bonita idea de abrir el código tenía una pega. Aunque Farm se había vuelto más abstracto y limpio, todavía quedaba mucha especificidad de Yandex: la autenticación interna, el soporte de Arc, la publicación de estados en nuestro tracker de incidencias, la telemetría y otros detalles atados a la infraestructura interna. Publicar todo eso no es buena idea, y a la comunidad tampoco le sirve de nada. Pero recortarlo sin más tampoco era una opción: de esa misma especificidad viven nuestras propias instalaciones, que decenas de equipos usan a diario.
Así que la tarea quedó formulada así: sacar de la ecuación todo lo específico de Yandex y extraer de Farm un núcleo configurable, al que esa especificidad pudiera volver a enchufarse desde fuera sin tocar el código del núcleo. En esencia, necesitábamos trazar una frontera más entre lo que estamos dispuestos a abrir al mundo y lo que se queda dentro.
Al final, dividimos Farm físicamente en dos bases de código.
-
El núcleo abierto: toda la orquestación, los proveedores de Docker y k8s, el soporte de Git, la base de datos sobre Knex, la API, la UI, la cola de compilaciones, el healthcheck — en resumen, todo lo que hace de Farm un producto. El núcleo no sabe nada de Yandex y puede publicarse tranquilamente tal cual.
-
Una fina capa de extensiones que añade encima todo lo propietario.
El pegamento entre ambos es un registro de plugins: el núcleo se levanta a sí mismo y ofrece un único punto de entrada a través del cual la capa de extensiones registra sus implementaciones contra interfaces bien definidas. Como resultado, toda la parte de Yandex vive ahora en un pequeño módulo y se conecta de forma declarativa:
initCoreExtension(async () => {
// una forma propia de lanzar instancias
coreRegistry.farmProviders.plugIn('process', {
constructor: ({internalApi, config}) => new ProcessFarmProvider(internalApi, config),
});
// un sistema de control de versiones propio
coreRegistry.vcs.plugIn('arc', {constructor: () => new ArcVcs()});
// una acción de webhook propia — por ejemplo, publicar un estado en el tracker
coreRegistry.webhookActions.plugIn('tracker', new TrackerWebhookAction());
// autenticación propia
coreRegistry.authProviders.plugIn('internal', {constructor: () => new InternalAuthProvider()});
// ...y además telemetría, dominios de CSP, elementos del menú de la UI
});
El registro está organizado como un conjunto de puntos de extensión independientes, cada uno con su propia interfaz; a través de ellos queda expuesto todo lo que puede variar:
-
proveedores (farmProviders) — cómo y dónde desplegar;
-
sistemas de control de versiones (vcs) — el git público, el arc interno;
-
autenticación (authProviders) — cómo dejar entrar a los usuarios en la UI y la API;
-
acciones de webhook (webhookActions) — qué hacer en respuesta a un evento de CI, por ejemplo devolver un estado al tracker;
-
el esquema de farm.json (farmJsonConfig) — campos propios en la configuración de la aplicación para las necesidades de un proveedor concreto;
-
UI y seguridad (uiConfiguration, cspDirectives) — los elementos del menú y la lista de dominios de confianza de la CSP.
La clave está en que, para el núcleo, todas estas implementaciones son iguales: arc no se diferencia en nada de git, ni process de docker o k8s. Así que el núcleo se puede abrir sin mirar de reojo a la cocina interna, mientras nuestra instalación sigue funcionando como siempre: simplemente compilando el núcleo junto con la capa de extensiones.
La puesta en marcha de Farm en k8s
Tras recorrer un largo camino, por fin llegamos al momento de trasladar nuestras instalaciones de Farm a k8s. La estrategia elegida: subir la instalación más grande a los raíles de Kubernetes y después, si todo va bien, resucitar el “piso compartido”, trasladando el resto de proyectos de los Farms locales a una única instalación común. Este enfoque es justo el que resuelve el problema de la complejidad de mantenimiento que señalamos al principio. Los equipos, de nuevo, no tienen que preocuparse de mantener su propia instalación ni de vigilar sus recursos, porque el clúster compartido escala automáticamente con la carga.
Para construir la infraestructura podemos usar libremente nuestros propios servicios y productos en la nube. Así que creamos un clúster con Yandex Managed Service for Kubernetes® y describimos absolutamente todos los recursos con Terraform. También configuramos una gestión de secretos separada para cada proyecto, con permisos de acceso atómicos sobre cada recurso. El resultado es un módulo de Terraform reutilizable con el que resulta bastante fácil desplegar una instalación de Farm en k8s.
En un solo día trasladamos con éxito todos los proyectos actuales a la instalación de Farm en k8s. No fue demasiado difícil, ya que el contrato original y las configuraciones se mantenían. El nuevo Farm se comportaba bastante bien, y otros proyectos también empezaron a mudarse a él. Al poco tiempo afloró un problema nuevo: aunque el clúster escala por RAM y CPU, el espacio en disco se seguía llenando, porque cada día se construían un buen número de imágenes que iban ocupando almacenamiento.
Aquí atacamos el problema de frente y añadimos un mecanismo de “cleaners”:
-
en Docker funciona así: se define un momento concreto, en forma de expresión cron, en el que deben borrarse las imágenes sin uso;
-
en k8s el mecanismo se apoya en el recurso nativo CronJob, que lanza en cada nodo pods que, igualmente, borran las imágenes viejas.
Dimos por exitoso el experimento de Farm sobre k8s basándonos en la respuesta de los equipos: el feedback, el número de problemas y el volumen de consultas a los mantenedores.
Qué puede hacer Farm hoy
A lo largo de este viaje, Farm ha pasado de ser un “caballo de batalla” para un único stack a un orquestador maduro de entornos de preview. Un resumen rápido de lo que sabe hacer ahora:
-
Compilación y arranque desde una rama. Por webhook o desde la UI, Farm recoge el código, compila la aplicación y levanta una instancia aislada con URL propia.
-
Varios proveedores. k8s para instalaciones grandes con escalado horizontal, docker para una única máquina virtual sin clúster. El contrato de la aplicación es el mismo en todos los proveedores.
-
Núcleo configurable con registro de plugins. Los proveedores, los sistemas de control de versiones, la autenticación y las acciones de webhook se conectan a través de un registro único, lo que permite extender Farm sin cambiar su núcleo.
-
Planificador y cola de compilaciones. Las solicitudes de generación entran en la cola, y el planificador la va procesando respetando los límites de compilaciones simultáneas y de instancias en ejecución.
-
Gestión del ciclo de vida. Arranque automático de la instancia desde la UI, además de la parada y eliminación de instancias ociosas por timeout.
-
Healthcheck. Farm vigila la salud de las instancias y refleja su estado actual en la UI y la API: con comprobaciones de disponibilidad propias en Docker y en el modo de procesos, y mediante las sondas nativas de liveness/readiness en Kubernetes.
-
Paso de variables de entorno. Configuración flexible del entorno: variables de compilación y de ejecución, variables protegidas que no se pueden sobrescribir al generar y, en el caso de Docker, también la herencia de variables del host o del contenedor.
-
Base de datos persistente con migraciones. Knex sobre SQLite, PostgreSQL u otro SGBD.
-
UI y API. Una interfaz web para lanzar y depurar instancias y una API HTTP para la integración con CI.
-
Cleaners de espacio en disco. Limpieza regular de las imágenes sin uso en Docker y k8s.
Qué sigue
Ahora, hablemos de planes. Todavía hay muchas cosas que nos gustaría mejorar en Farm. Estas son las principales líneas de trabajo en este momento:
-
Pasar a la Gateway API en sustitución de Ingress en el proveedor de k8s.
-
Soporte de alias, para poder acceder a una instancia por un nombre legible en lugar de por un hash.
-
Autorización completa: un sistema de usuarios, roles y permisos de acceso.
-
Cuotas separadas para distintos proyectos.
Como con cualquier otro proyecto de código abierto, seguiremos mejorando la documentación y los ejemplos para que usar Farm sea más fácil y cómodo, y para que el número de usuarios externos crezca a buen ritmo. Nos encantaría que, con el tiempo, Farm se convirtiera en una de las herramientas de referencia para entornos de preview en la industria; pero eso ya se verá.
Cómo probarlo
Pásate por nuestro repositorio, échale un vistazo al README y a la documentación correspondiente. Para probarlo, puedes levantar Farm en local sin problema con Docker Engine instalado. Agradeceremos cualquier feedback, así que abre issues, envía PRs, plantea tus preguntas y trae tus feature requests si necesitas alguna funcionalidad extra.
¡Gracias por leernos! Si te ha gustado nuestro proyecto, ¡nos encantará recibir tus estrellas! Y si te interesa estar al día de las novedades del equipo de Yandex Cloud, únete a nuestro canal.

Mikhail Golbakh
Desarrollador sénior de interfaces en Yandex Cloud