
Farm agora é open source: ambientes de preview sem dor
Farm agora é open source: ambientes de preview sem dor
Como o Farm evoluiu de um script self-hosted em uma única VM para um orquestrador de ambientes de preview em Docker e Kubernetes — e por que abrimos seu código.

Não basta cada pull request passar nos testes — você quer mostrá-lo ao vivo: para o designer, para o gerente, para a equipe ao lado, para que todos possam fuçar na UI — e se encantar, ou encontrar bugs — e se chatear. Parece bem simples: é só subir um ambiente e compartilhar o link. Mas quando os pull requests chegam às dezenas todos os dias e as equipes são muitas, “simplesmente subir um ambiente” vira um projeto de infraestrutura completo.
Olá! Meu nome é Mikhail Golbakh e sou Desenvolvedor Frontend Sênior no Yandex Cloud. Há vários anos, minha equipe e eu desenvolvemos a Farm — um serviço que sobe um ambiente de preview para cada pull request. Neste artigo, vou mostrar como a Farm cresceu de um simples script self-hosted em uma única máquina virtual até um orquestrador rodando em Docker e Kubernetes — e por que acabamos abrindo o código dela.
Como chegamos à primeira versão da Farm
O Yandex tem muitas equipes de frontend, que criam centenas de pull requests por dia. Cada pull request dispara uma infinidade de testes e outras verificações no CI. Testes unitários, verificações de tipos e linters rodam sem problemas em máquinas virtuais isoladas.
Mas o que fazer quando é preciso subir uma aplicação completa a partir de uma branch? Dessa pergunta nasce uma tarefa que os desenvolvedores frontend encontram o tempo todo: subir uma “beta” para cada pull request. A primeira coisa que vem à cabeça é montar um ambiente de dev dedicado, espelhando a produção, e dividi-lo entre os desenvolvedores. É o que pequenas equipes costumam fazer no começo, mas elas logo esbarram no problema de escala. Um ambiente único já não dá para dividir, e também não dá para rodar testes e2e nele — a estabilidade deles certamente levantaria dúvidas.
Tradicionalmente, cada equipe monta o próprio processo de implantação de ambientes de dev, gastando nisso um bom esforço, além de recursos com manutenção. Não fomos exceção e criamos uma solução própria. No entanto, desde o início a projetamos para ser uma tecnologia compartilhada, capaz de escalar para dezenas de equipes, apesar das possíveis diferenças de infraestrutura.
Lá em 2018, a equipe de frontend do Yandex Cloud construiu a primeira versão da Farm — essencialmente um serviço self-hosted simples, que baixava o código de uma branch do repositório, executava os comandos de build da aplicação-alvo e iniciava o processo dela em uma VM, roteando o tráfego a partir de um domínio dedicado. Até o conjunto de comandos vinha hardcoded: a Farm era voltada, antes de tudo, para os nossos próprios projetos, construídos sobre componentes compartilhados para serviços web em Node.js. O resultado foi uma espécie de sistema de deploy simplificado com um toque de Yandex.
Com o passar dos anos, o serviço Farm evoluiu: aprendeu a trabalhar com configurações separadas de build e de inicialização para cada projeto, ganhou uma UI completa, um banco de dados e um bom volume de código extra que simplificava o trabalho com a infraestrutura do Yandex. Mas a essência do serviço não mudou — ele continuava resolvendo as seguintes tarefas:
-
baixar o código e fazer o build do projeto a partir de um pull request;
-
rotear o tráfego até a aplicação em execução;
-
unificar a solução em um único componente de infraestrutura, fácil de distribuir e de integrar aos processos de CI do Yandex.
Como funciona
A Farm é uma pequena aplicação Node.js, e seu fluxo de trabalho simplificado é o seguinte:
-
A Farm recebe uma solicitação de geração. Ela pode vir de uma tarefa do CI ou diretamente de um usuário, por meio de um formulário na UI.
-
Uma nova instância da aplicação cai no banco de dados com o status queued, entrando na fila de build.
-
A Farm processa a fila, baixa o código do repositório e da branch de destino, lê a configuração e inicia o build.
-
O processo da aplicação é iniciado: espera-se que ele abra um socket em
<instance_dir>/dist/server.sock. -
A Farm — ou melhor, a essa altura o nginx com uma configuração especial — começa a rotear o tráfego por um domínio único, por exemplo: d008838956420e285cee652262dd5d18b2ed2a2a.farm.example.com.

Como dá para ver no diagrama, não colocamos sobre a Farm a responsabilidade pelo balanceamento de carga — usamos o nginx. Essa abordagem veio para ficar: manter o proxy de tráfego é uma tarefa nada trivial, que preferimos não resolver por conta própria. Também havia suporte a Git e a Arc — o sistema de controle de versão interno do próprio Yandex.
O build e a inicialização, por sua vez, se resumiam à própria Farm executando comandos. Tudo do jeito mais bruto e direto possível:
# Build
npm ci
npm run build
# Inicialização
npm run start
Para completar o quadro, vamos ver também um exemplo de configuração 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"
}
}
]
}
}
O que tínhamos em mãos era um verdadeiro burro de carga. Bastava a equipe interessada preparar uma máquina virtual, descrever a configuração da própria Farm e dos seus projetos, subi-la e configurar um processo de CI para os pull requests. Parece simples, não é? Mas…
Crescimento e novos desafios
A Farm viveu nesse estado por um bom tempo e resolvia razoavelmente bem as suas tarefas. No começo, tínhamos até um esquema de “república” — uma única Farm compartilhada por várias equipes de frontend, para não desperdiçar energia na manutenção de várias instalações. Mas as equipes cresceram de tamanho, o número delas aumentou, e a Farm só conseguia funcionar dentro de uma única VM. Com o tempo, nem mesmo as configurações mais parrudas davam conta.
Executar vários builds em paralelo consumia todos os recursos da máquina, e uma instância podia esperar um bom tempo na fila. Assim começou um processo orgânico de desmembrar a “república” em Farms menores — às vezes, até uma por projeto.
Obviamente, esse desmembramento não resolvia o problema — apenas o adiava. Os recursos computacionais continuavam acabando de qualquer jeito, e as novas equipes precisavam gastar esforço extra dando suporte e mantendo a Farm. Por exemplo, o disco da VM lotava com bastante frequência, obrigando a rodar uma limpeza manual de arquivos desnecessários. Então, o primeiro problema é: recursos limitados, sem gerenciamento nem escalabilidade. Vamos guardar isso na memória.
Outro problema era que, por baixo do capô, a Farm usava SQLite como banco de dados, sem nenhuma abstração do tipo Knex. Isso gerava vendor lock e ainda nos obrigava a apagar todos os dados a cada mudança de esquema, já que não havia mecanismo de migrações. Considerando que a Farm estava dividida em muitas instalações por equipe, isso também significava que quase ninguém queria atualizá-la: vai saber o que quebraria — e, muito provavelmente, ainda seria preciso apagar os dados. Como diz o ditado, “em time que está ganhando não se mexe”.
Além disso, a Farm só sabia executar aplicações Node.js, e todos os processos rodavam diretamente no host, sem nenhum isolamento. E, sem isolamento, as aplicações não podiam simplesmente usar sua porta de sempre para receber o tráfego, porque a Farm não fazia gerenciamento de portas. Por isso, a comunicação era feita via sockets Unix, o que obrigava as aplicações a seguir esse contrato meio estranho.
Portanto, precisávamos resolver os seguintes problemas:
-
Recursos limitados, sem gerenciamento nem escalabilidade.
-
Ausência de migrações no banco e dependência rígida do SQLite.
-
Falta de isolamento das instâncias, além da restrição de ser uma aplicação Node.js publicando um socket Unix.
-
O fardo do suporte e da operação — afinal, a ideia original era facilitar a vida das equipes, não criar mais dor de cabeça (esse é um problema extra que vamos deixar de lado por enquanto, mas voltaremos a ele perto do final).
Como enfrentamos tudo isso
Um engenheiro experiente, olhando para essas tarefas, muito provavelmente sugeriria colocar a Farm nos trilhos de um cluster k8s. E foi a essa ideia que chegamos aos poucos: levar o build e a implantação das aplicações para a capacidade computacional do cluster, em pods separados, e aproveitar suas capacidades de escalonamento automático e gerenciamento de recursos. As aplicações, por sua vez, passariam a ser entregues como o velho conhecido contêiner docker — um contrato muito claro e simples para a nossa indústria. Além disso, precisávamos manter a possibilidade de executar aplicações do jeito antigo, como processos, para que as equipes pequenas não tivessem que encarar uma migração trabalhosa para a solução k8s.
Simplesmente abandonar a Farm e passar a implantar as aplicações direto do CI para um cluster k8s era impossível, pelos seguintes motivos:
-
A Farm já tinha criado raízes profundas em todos os nossos processos e no CI. Forçar as equipes a migrar para uma solução totalmente nova não fazia sentido.
-
Se você olhar os detalhes, a Farm não apenas faz o build e inicia a aplicação — ela também assume a responsabilidade pelo roteamento (ainda que via nginx), para e remove instâncias sem uso e faz muito mais.
-
E a UI conveniente também conta muito: ela permite que não só engenheiros de frontend, mas também de backend subam uma instância de uma aplicação para testar as mais variadas funcionalidades.
Na prática, adicionar o suporte a k8s se mostrou mais difícil do que imaginávamos no começo. Desde as primeiras versões, a lógica de trabalho com as instâncias das aplicações estava espalhada pela base de código, e ao longo dos anos ela acumulou uma porção de detalhes e gambiarras elegantes — encaixar mais uma implementação ali do lado era complicado.
Uma grande refatoração
Como primeiro passo, embarcamos em uma grande refatoração do código da Farm. Primeiro, era preciso encapsular a lógica de manipulação das instâncias em uma classe dedicada de provedor — uma espécie de backend da nossa Farm, que definiria a estratégia de build e implantação de uma aplicação. Isso nos permitiria, por ora, manter o esquema legado e o k8s lado a lado e, mais adiante, adicionar novas implementações por meio de uma interface única.
Depois de destrinchar o código, conseguimos extrair todos os métodos específicos de provedor e preparar uma classe abstrata que os descreve. Simplificando um pouco, tudo se resumiu a isto:
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}>;
}
Mais ou menos nessa época, também abstraímos todo o acesso ao banco de dados e o migramos para o Knex, para que no futuro pudéssemos trocar para outro SGBD, mais poderoso e persistente. De bônus, ganhamos um mecanismo barato de migrações, que facilita as mudanças no esquema do banco de dados.
O novo esquema com Kubernetes
No novo esquema, a Farm é implantada no cluster, funciona como um controlador, conecta-se à API do k8s e passa a gerenciar os recursos do cluster: inicia pods para os builds e para a aplicação, cria e exclui outros recursos.
Para entendê-lo, basta cobrir os dois processos principais: o build e a implantação de uma instância da aplicação.
O build é composto por algumas etapas:
-
Um usuário ou o CI inicia o build (o método buildInstance).
-
A configuração da Farm para a aplicação-alvo é obtida da branch (o arquivo farm.json na raiz do repositório).
-
Um pod builder é iniciado: ele baixa o código da branch, constrói uma imagem docker (por padrão, Dockerfile.farm) e faz push dela para um registry predefinido.
-
Todos os logs do pod builder são transmitidos em tempo real e exibidos na UI para depuração.
-
Concluído o build, a Farm exclui o pod builder.

A saída do build é um artefato — uma imagem da aplicação enviada ao registry, pronta para ser implantada e iniciada. De bônus, ganhamos o cache de builds via Docker, o que é muito útil para acelerar o CI.
A implantação começa logo depois do build, no mesmo método buildInstance, então vamos continuar a partir do momento em que a imagem é enviada:
-
Um Deployment é criado no cluster, usando a imagem produzida na etapa anterior.
-
Um Service é criado com a porta que especificamos na configuração da Farm, junto com um Ingress com um domínio baseado no ID da instância.
-
Então um novo jogador entra em campo — o Ingress NGINX Controller. Ele cuida de todo o roteamento e torna a aplicação acessível de fora. Aqui a Farm se mantém fiel a si mesma, delegando o proxy de tráfego a outro componente do sistema.

Tecnicamente, qualquer outro controlador de Ingress poderia ser usado para o roteamento — até algum ALB de nuvem, se necessário. Escolhemos o Ingress NGINX Controller pela simplicidade e pela velocidade de atualização das configurações.
É mais ou menos assim que obtemos uma instância funcional da aplicação, pronta para ser aberta e testada. Além disso, a Farm garante que os recursos não sejam duplicados e que todas as operações sejam idempotentes — o que é crucial para um funcionamento estável.
Um esquema mais simples com Docker puro
Assim que surgiu a ideia do suporte a k8s na Farm, veio junto uma proposta bastante óbvia: por que não suportar a execução de instâncias simplesmente em uma máquina virtual, no Docker? Isso resolveria o problema de isolamento das instâncias, eliminaria as restrições de aplicação Node.js e de portas, e daria à Farm mais um modo de operação — desta vez para equipes pequenas sem um cluster k8s. Mais adiante, poderíamos abandonar de vez o jeito antigo de execução como processos Node.js puros, o que simplificaria muito a manutenção, permitindo apagar uma pilha de código legado.
E como isso funciona no fim das contas? A Farm se conecta ao Docker Engine por um socket e ela mesma faz o papel de builder — aqui não há pods separados para o build. E eis a primeira diferença importante em relação ao esquema k8s: a imagem da instância é construída localmente, no mesmo daemon, e é lá que ela fica. Não é preciso fazer push dela para um registry — o registry só é útil para puxar as imagens base durante o build. É bem mais simples, e é exatamente disso que as equipes pequenas sem cluster precisam.
Projetamos dois modos de operação:
-
docker_container — a própria Farm roda como um contêiner com o socket do host montado e rege os contêineres “vizinhos” no mesmo daemon;
-
vm — a Farm mora direto na VM, com seu próprio Docker Engine.
Tanto a Farm quanto todas as instâncias entram em uma rede Docker compartilhada (farm, por padrão), e cada instância da aplicação ganha um contêiner com o nome autoexplicativo farm-docker-<hash>. É por esse nome que a encontramos depois, na hora de rotear o tráfego.
Como no esquema k8s, o build e a inicialização convivem no mesmo método buildInstance, só que sem pods separados:
-
Um usuário ou o CI inicia o build (o método buildInstance).
-
O código é baixado da branch e a configuração da Farm é lida do farm.json — é de lá que vêm o caminho do Dockerfile e as variáveis de build e de execução.
-
A Farm constrói a imagem docker localmente e aplica nela a tag
farm-docker-<hash>, transmitindo pelo caminho todos os logs do build para a UI, para depuração. -
A partir da imagem resultante, um contêiner é criado e iniciado na rede farm — com as variáveis de ambiente repassadas e, se desejado, o comando de inicialização sobrescrito.

Roteamento de tráfego
Aqui a Farm mais uma vez delega o proxy ao nginx. O contêiner da instância não publica nenhuma porta no host — ele pode ser alcançado pelo nome farm-docker-<hash> dentro da rede Docker farm, onde o DNS embutido do Docker resolve esse nome.
A única questão é onde mora esse nginx capaz de resolver o nome — e isso depende do modo:
-
No modo docker_container, a requisição vinda do host entra no contêiner da Farm, e o nginx interno dela, presente na rede farm, leva o tráfego direto até a instância.
-
No modo vm, o nginx da Farm roda diretamente no host e não enxerga os nomes dos contêineres, então ele encaminha a requisição para um contêiner separado, o Docker Proxy (o mesmo nginx, só que rodando dentro da rede farm), que por sua vez entrega o tráfego à instância certa.

E é assim que obtemos uma instância funcional em uma máquina virtual comum com Docker, sem nenhum cluster envolvido: o código vira uma imagem, a imagem vira um contêiner, e o nginx entrega o tráfego até ele.
De quebra, também resolvemos o problema de isolamento junto com aquele contrato estranho dos sockets Unix — agora a aplicação é entregue como uma imagem docker comum e simplesmente escuta na sua porta, sem saber nada dos bastidores da Farm.
A evolução da Farm e a ida para o open source
Depois de todas as melhorias — as abstrações de provedores, o suporte a diferentes esquemas de build e execução e a migração para o Knex —, a Farm já parecia um produto maduro e completo, no qual as especificidades do Yandex não tinham mais grande peso. Aos poucos, a Farm havia se tornado um orquestrador sem acoplamento rígido às tecnologias que executava por baixo.
Ela agora lembrava um sistema de deploy simplificado nos moldes do Heroku, só que na forma de um serviço self-hosted. Assim surgiram planos mais ambiciosos — publicar o código da Farm como parte do nosso projeto open source Gravity UI, para que ela fosse útil não apenas dentro da empresa, mas para toda a comunidade.
Mas essa bela ideia de abrir o código tinha um porém. Mesmo tendo ficado mais abstrata e limpa, a Farm ainda carregava bastante especificidade do Yandex: autenticação interna, suporte ao Arc, postagem de status no nosso tracker de tarefas, telemetria e outros detalhes amarrados à infraestrutura interna. Publicar tudo isso não é uma boa ideia — e, de qualquer forma, nada disso teria utilidade para a comunidade. Mas simplesmente cortar tudo fora também não era uma opção: são exatamente essas especificidades que mantêm de pé as nossas próprias instalações, usadas todos os dias por dezenas de equipes.
Então a tarefa se resumiu ao seguinte: tirar de cena tudo o que é específico do Yandex e extrair da Farm um núcleo configurável, no qual as especificidades pudessem ser plugadas de volta a partir de fora, sem tocar no código do núcleo. No fundo, precisávamos traçar mais uma fronteira entre o que estamos prontos para abrir para o mundo e o que continua vivendo apenas internamente.
No fim, dividimos fisicamente a Farm em duas bases de código.
-
O núcleo aberto: toda a orquestração, os provedores de Docker e k8s, o suporte a Git, o banco de dados sobre Knex, a API, a UI, a fila de builds, o healthcheck — em suma, tudo o que faz da Farm um produto. O núcleo não sabe nada sobre o Yandex e pode tranquilamente ser publicado como está.
-
Uma camada fina de extensões, que acrescenta por cima tudo o que é proprietário.
A cola entre as duas é um registro de plugins: o núcleo sobe sozinho e fornece um ponto de entrada único, pelo qual a camada de extensões registra suas implementações seguindo interfaces bem definidas. Como resultado, toda a parte do Yandex agora mora em um único módulo pequeno e é plugada de forma declarativa:
initCoreExtension(async () => {
// uma forma própria de executar instâncias
coreRegistry.farmProviders.plugIn('process', {
constructor: ({internalApi, config}) => new ProcessFarmProvider(internalApi, config),
});
// um sistema de controle de versão próprio
coreRegistry.vcs.plugIn('arc', {constructor: () => new ArcVcs()});
// uma ação de webhook própria — por exemplo, postar um status no tracker
coreRegistry.webhookActions.plugIn('tracker', new TrackerWebhookAction());
// autenticação própria
coreRegistry.authProviders.plugIn('internal', {constructor: () => new InternalAuthProvider()});
// ...e ainda telemetria, domínios de CSP, itens de menu da UI
});
O registro é organizado como um conjunto de pontos de extensão separados, cada um com sua própria interface — tudo o que pode variar é exposto por meio deles:
-
provedores (farmProviders) — como e onde fazer o deploy;
-
sistemas de controle de versão (vcs) — o git público, o arc interno;
-
autenticação (authProviders) — como deixar os usuários entrarem na UI e na API;
-
ações de webhook (webhookActions) — o que fazer em resposta a um evento do CI, como devolver um status para o tracker;
-
o esquema do farm.json (farmJsonConfig) — campos personalizados na configuração da aplicação para as necessidades de um provedor específico;
-
UI e segurança (uiConfiguration, cspDirectives) — itens de menu e a lista de domínios confiáveis no CSP.
O ponto-chave é que, para o núcleo, todas essas implementações são equivalentes: o arc não é diferente do git, e o process não é diferente do docker ou do k8s. Assim, o núcleo pode ser aberto sem olhar para os bastidores internos, enquanto a nossa instalação continua funcionando como antes — bastando fazer o build do núcleo junto com a camada de extensões.
Implantando a Farm no k8s
Depois de percorrer um longo caminho, finalmente chegamos ao momento de migrar as nossas instalações da Farm para o k8s. A estratégia escolhida foi esta: colocar a maior instalação nos trilhos do Kubernetes e depois, se tudo funcionar bem, ressuscitar a “república”, migrando os projetos restantes das Farms locais para uma única instalação compartilhada. É justamente essa abordagem que resolve o problema da complexidade de manutenção que sinalizamos no início. As equipes voltam a não precisar se preocupar em manter a própria instalação e vigiar os recursos dela, porque o cluster compartilhado escala automaticamente conforme a carga.
Temos liberdade para usar nossos próprios serviços e ofertas de nuvem na construção da infraestrutura. Então criamos um cluster com o Yandex Managed Service for Kubernetes® e descrevemos absolutamente todos os recursos com Terraform. Também configuramos um gerenciamento de segredos separado para cada projeto, com permissões de acesso atômicas em cada recurso. O resultado é um módulo Terraform reutilizável, que torna bem fácil implantar uma instalação k8s da Farm.
Em um único dia, migramos com sucesso todos os projetos atuais para a instalação da Farm no k8s. Não foi muito difícil, já que o contrato e as configurações originais se mantiveram. A nova Farm se saiu bastante bem, e outros projetos também começaram a migrar para ela. Logo apareceu um problema novo: embora o cluster escale por RAM e CPU, o espaço em disco continuava enchendo, porque todos os dias eram construídas muitas imagens, e elas consumiam o armazenamento.
Aqui resolvemos o problema da forma mais direta possível, adicionando um mecanismo de cleaners:
-
no Docker, ele funciona assim: você define um horário específico, na forma de uma expressão cron, para a exclusão das imagens não utilizadas;
-
no k8s, o mecanismo é construído sobre o recurso nativo CronJob, que inicia pods em cada nó — e eles também excluem as imagens antigas.
Declaramos o experimento da Farm no k8s um sucesso com base na resposta das equipes: no feedback, no número de problemas e na quantidade de chamados aos mantenedores.
O que a Farm sabe fazer hoje
Ao longo dessa jornada, a Farm deixou de ser um burro de carga de uma stack só e se tornou um orquestrador maduro de ambientes de preview. Segue um resumo rápido do que ela sabe fazer hoje:
-
Build e execução a partir de uma branch. Acionada por um webhook ou pela UI, a Farm busca o código, faz o build da aplicação e sobe uma instância isolada com URL própria.
-
Múltiplos provedores. k8s para instalações grandes com escalonamento horizontal, docker para uma única máquina virtual sem cluster. O contrato da aplicação é o mesmo em todos os provedores.
-
Núcleo configurável com registro de plugins. Provedores, sistemas de controle de versão, autenticação e ações de webhook se conectam por um registro único, permitindo estender a Farm sem mudar o seu núcleo.
-
Agendador e fila de builds. As solicitações de geração entram na fila, e o agendador a processa respeitando os limites de builds simultâneos e de instâncias em execução.
-
Gerenciamento do ciclo de vida. Início automático de instâncias pela UI, além de parada e exclusão de instâncias ociosas por timeout.
-
Healthcheck. A Farm monitora a saúde das instâncias e reflete o status atual na UI e na API: com verificações de disponibilidade próprias no Docker e no modo de processo, e por meio das probes nativas de liveness/readiness no Kubernetes.
-
Repasse de variáveis de ambiente. Configuração flexível do ambiente: variáveis de build e de execução, variáveis protegidas que não podem ser sobrescritas na geração e, no Docker, também a herança de variáveis do host ou do contêiner.
-
Banco de dados persistente com migrações. Knex sobre SQLite, PostgreSQL ou outro SGBD.
-
UI e API. Uma interface web para iniciar e depurar instâncias e uma API HTTP para integração com o CI.
-
Cleaners de espaço em disco. Limpeza regular das imagens não utilizadas no Docker e no k8s.
O que vem a seguir
Agora, sobre os planos. Ainda há muitas coisas que gostaríamos de melhorar na Farm. Estas são as principais frentes no momento:
-
Migrar para a Gateway API em substituição ao Ingress no provedor k8s.
-
Suporte a aliases, para que uma instância possa ser acessada por um nome legível em vez de um hash.
-
Autorização completa — um sistema de usuários, papéis e permissões de acesso.
-
Cotas separadas para projetos diferentes.
Como em qualquer outro projeto open source, seguiremos melhorando a documentação e os exemplos, para que usar a Farm fique cada vez mais fácil e conveniente — e para que o número de usuários externos cresça rapidamente. Adoraríamos que, com o tempo, a Farm se tornasse uma das ferramentas de referência para ambientes de preview na indústria — mas vamos ver como as coisas caminham.
Como experimentar
Venha conhecer o nosso repositório, explore o README e a documentação correspondente. Para experimentar, você pode tranquilamente subir a Farm localmente, bastando ter o Docker Engine instalado. Ficaremos gratos por qualquer feedback, então abra issues, envie PRs, tire dúvidas e traga feature requests se precisar de alguma funcionalidade a mais!
Obrigado pela leitura! Se você gostou do nosso projeto, ficaremos muito felizes com as suas estrelas! E, se quiser acompanhar as últimas novidades da equipe do Yandex Cloud, entre no nosso canal.

Mikhail Golbakh
Desenvolvedor Frontend Sênior no Yandex Cloud