Com base na minha própria experiência, sei quais as motivações e caminhos de desenvolvimento que levam ao surgimento de ferramentas internas: a seguir, tentarei identificar as razões fundamentais para a sua criação usando as nossas soluções como exemplos.
Olá! Quero falar sobre por que as grandes corporações de tecnologia estão obcecadas em criar soluções proprietárias para sua infraestrutura. A resposta parece óbvia: nada mais é do que a síndrome NIH. Mas esta resposta está longe de ser completa, para não dizer objetiva.
Sou o CTO da equipe Yandex Platform Engineering e nosso objetivo é auxiliar os engenheiros na construção de todo o ciclo de desenvolvimento, desde a escrita do código até a operação dos serviços, de forma a torná-lo mais eficiente. Isso inclui agilização de processos: não apenas desenvolvemos ofertas como serviço, mas também auxiliamos na implementação delas dentro da empresa. Isso funciona na escala do Yandex: nossos serviços são usados por milhares de desenvolvedores em toda a empresa.
Exemplos de ferramentas para organizar o ciclo de desenvolvimento
Para resolver um problema, muitas vezes desenvolvemos ferramentas proprietárias em vez de implementar ferramentas já prontas. Por exemplo, enquanto ainda era programador na equipe, trabalhei em um sistema de monitoramento quantitativo em C++ e Python e ajudei a escalá-lo para dezenas de bilhões de métricas processadas. Assim, com base na minha própria experiência, sei quais as motivações e caminhos de desenvolvimento que levam ao surgimento de ferramentas internas: a seguir, tentarei identificar as razões fundamentais para a sua criação usando as nossas soluções como exemplos.
História nº 1: nuvem interna Yandex
Definindo a tarefa. O objetivo de nosso Runtime Cloud interno, ou RTC, é fornecer aos usuários internos ferramentas simples de implantação e gerenciamento de tráfego. Os usuários do RTC são os mesmos engenheiros que desenvolvem os serviços Yandex. E eles precisam lançar dezenas de milhares de aplicativos que criaram em algum lugar, enviar solicitações de usuários para lá, equilibrar a carga e lidar com incidentes, entre outras coisas.
A necessidade de uma nuvem interna surgiu no início de 2010, quando o número de serviços já estava na casa das centenas e o número total de núcleos alocados crescia dezenas de por cento ao ano. Ter servidores dedicados para cada serviço tornou-se proibitivamente caro e precisávamos de ferramentas que nos permitissem executar aplicativos de vários serviços em um único servidor. Tínhamos vários requisitos para essas ferramentas no início:
É necessário automatizar ações rotineiras para economizar recursos operacionais e reduzir o número de incidentes durante as liberações.
Outra tarefa importante era aumentar a utilização da nuvem, especialmente porque a maioria dos serviços tinha um padrão diário de carga e a nuvem ficava ociosa à noite. A situação foi ainda mais complicada pelo fato de que nem todos os serviços Yandex foram hospedados na nuvem durante esses anos, e o aumento constante no número de servidores agravou o problema.
Era fundamental lançar recursos ou correções de bugs para os usuários rapidamente, pois isso afetava diretamente a velocidade de desenvolvimento do Yandex.
É necessário suporte somente IPv6: para fornecer conectividade ponta a ponta entre pods, nossos data centers foram construídos somente IPv6, já que em nossa escala a gama de endereços IPv4 não seria suficiente para nós.
Essencialmente, precisávamos do Kubernetes (e, com o tempo, o RTC chegou muito perto). Mas aqui está o problema: o k8s só foi anunciado em 2014. O Apache Mesos existia na época, mas estava em sua infância.
Implementação de funções básicas. Começamos a resolver o problema com uma espécie de MVP – um conjunto simples de ferramentas, que mais parecia um conjunto de blocos de construção que automatizam ações rotineiras, por exemplo:
alocação de recursos do cluster;
entrega da versão necessária do aplicativo e diversos artefatos ao cluster;
lançamento da versão necessária do aplicativo no cluster.
Com o tempo, tornou-se possível montar um gráfico de layout de serviço completo usando esses blocos de construção (semelhante à entrega contínua). Após um certo número de iterações, o Nanny, sistema de gerenciamento de serviços executado no RTC, surgiu em 2013.
Outro aspecto fundamental do Nanny foi a implementação do isolamento de aplicações baseado no consumo de recursos. Inicialmente lançamos aplicações de diversos serviços sem isolamento de recursos, o que resultou em um grande número de problemas e incidentes operacionais.
Na época, as únicas soluções prontas eram o LXC, que já havia parado de desenvolver, e o Docker, que não podia usar apenas IPv6 e reiniciava todos os containers ao atualizar o dockerd, impossibilitando a atualização do dockerd sem afetar o usuário. Como resultado, começamos a desenvolver nossos sistema de conteinerização no topo do cgroup do kernel Linux em meados de 2014. A maioria das aplicações e alguns agentes de sistema em servidores já foram transferidos para contentores do Porto em 2015.
Resolvendo problemas de utilização. Na época, o gerenciamento de recursos na nuvem interna era realizado por meio de commits no repositório. No entanto, isto atrasou o desenvolvimento do Yandex e entrou em conflito com a tarefa de aumentar a utilização: para resolver o problema, precisávamos de colocar o nosso sistema de redução de mapas nas nuvens, nomeadamente - nosso sistema interno proprietário para armazenamento de big data e computação distribuída, que desenvolvíamos há cerca de 7 anos. Ele teve que ser trazido para a nuvem interna, mas também teve que ser executado junto com os aplicativos do usuário.
Para trazer o YTsaurus para o RTC, era necessária a capacidade de gerenciar pods dinamicamente, em vez de por meio de commits de repositório. Por isso, em 2018, criamos , um sistema para gerenciar recursos de computação em cluster. O Yandex Planner foi integrado ao sistema de implantação Nanny existente, desbloqueando a migração do YTsaurus e transformando o RTC em uma nuvem completa. O RTC tinha várias dezenas de milhares de servidores na época e, com a introdução da redução de mapas, esse número cresceu significativamente.
Novas dores de crescimento. Durante o mesmo período, o k8s evoluiu para uma solução muito mais madura, tornando-se um dos serviços da AWS em 2017. Mas ainda não atendeu a todos os nossos requisitos:
Escala de dezenas de milhares de servidores - uma instalação do k8s ainda não consegue lidar com nossa escala.
O suporte para IPv6 e IPv4 de pilha dupla apareceu no k8s apenas em 2020, e o IPv6 foi fundamental para nós desde o início.
Suporte para contêineres aninhados, necessário porque decidimos criar agendadores de lote e de cálculo separados. Comparamos a eficácia desta opção com a de um agendador geral em determinado momento, e foi mais conveniente e lucrativo não tentar fazer um agendador geral para tudo.
O YTsaurus usou ativamente a capacidade de criar contêineres Porto aninhados em vez de criar um único agendador. Claro, poderíamos adicionar suporte para a mesma pilha dupla em k8s. No entanto, a experiência de desenvolvimento do kernel Linux mostrou que nem tudo pode ser enviado para código aberto, e nos esforçamos para manter o delta do kernel upstream ao mínimo, a fim de simplificar a atualização para novas versões.
Nossa solução hoje. A arquitetura do RTC é muito semelhante à do Kubernetes. O usuário descreve declarativamente seu serviço na forma de alguma especificação que descreve como iniciar o aplicativo especificado e em quais data centers. Cada data center possui sua própria instalação do Yandex Planner, que serve como banco de dados para todos os objetos do cluster, por um lado, e como agendador de pod, por outro. Cada servidor no data center executa um agente que recebe especificações de pod do Yandex Planner e as lança usando nosso sistema proprietário de conteinerização Porto.
Atualmente, a RTC lançou dezenas de milhares de serviços, alocando mais de 5 milhões de núcleos para mais de 100 mil servidores. Todos os dias, mais de 100.000 alterações são feitas nas especificações de serviço.
Planos. E se o k8s puder lidar com nossa escala? Principalmente porque o ecossistema k8s começou a nos superar em termos de funcionalidade em algum momento. Não seria melhor mudar para o k8s e esperar que as ferramentas prontas eventualmente forneçam o volume que necessitamos? Na prática, continuamos a ser um consumidor de nicho para k8s porque apenas uma pequena percentagem de empresas opera numa escala tão grande, cada uma das quais tem as suas próprias soluções de nuvem internas.
Outro ponto crítico a lembrar é a questão da migração. De acordo com julho de 2018 relatório, 90% das aplicações modernas ainda estarão em uso até 2025, e a dívida técnica para o desenvolvimento destes sistemas representará mais de 40% dos orçamentos de TI. Isso está próximo da realidade, com base em nossa experiência na migração de serviços de usuário para o Yandex Planner: em 2023, aproximadamente 100 mil núcleos ainda aguardavam sua vez de migrar para o Yandex Planner.
Em 2021, estimamos quanto custaria mudar de um sistema de implantação para outro ao escolher a nossa estratégia de desenvolvimento. A migração do Yandex para o vanilla k8s seria uma tarefa extremamente cara, custando centenas de homens-ano.
Desta forma simples, acabamos com a nossa nuvem interior, que dificilmente conseguiremos abandonar nos próximos 5 anos, mesmo que estabeleçamos tal meta.
O que deve ser feito em relação à falta de funcionalidade interna da nuvem em comparação com o k8s? Na prática, nossos clientes podem usar o Managed Kubernetes no Yandex Cloud. Esta opção é usada principalmente para projetos onde requisitos rigorosos de conformidade devem ser atendidos – esta é uma pequena proporção de equipes, menos de 1%. Pelas razões expostas acima, o resto da população não vê muitos benefícios em mudar-se.
Ao mesmo tempo, estamos analisando ativamente os k8s e considerando como nos aproximar dos padrões geralmente aceitos. Já estamos experimentando ativamente o k8s em algumas tarefas, como inicialização na nuvem ou organização do IaaC na escala de todo o Yandex. Idealmente, gostaríamos de reutilizar a interface k8s enquanto mantemos nossa própria implementação, que seja o mais adaptada possível às nossas necessidades. Só falta descobrir como fazer isso na prática.
E os outros? Como neste artigo estamos discutindo grandes tecnologias em geral, é importante notar que nosso caso não é único. Confira a ou casos para exemplos.
História nº 2: monorepositório
Problemas e requisitos de solução . Nosso monorepositório, Arcadia, compartilha o mesmo objetivo principal de nossa nuvem interna: fornecer ferramentas de desenvolvimento convenientes. Isso inclui todo um ecossistema de desenvolvimento no caso do repositório:
sistema de controle de versão (Arc),
Interface (Arcano),
construir sistema (você faz),
CI/CD (NovoCI).
Arcadia surgiu na mesma época que a nuvem interna do Yandex. Um dos motivos para a criação do monorepositório foi a necessidade de reutilizar o código dentro do Yandex. Isso foi dificultado na época pela presença de vários sistemas de construção. Um sistema unificado com suporte para compilações distribuídas eficientes era necessário para funcionar na escala de todo o Yandex. Também deve ser estável e utilizável.
Implementação de um sistema de compilação unificado. Nosso sistema proprietário de compilação ya make estreou em 2013, quando era apenas para código C++. Antes de você fazer, usávamos o CMake, mas sua velocidade impedia que ele fosse dimensionado para a escala de um monorepositório. O proprietário ya make funcionou muito mais rápido com o Arcadia. Não havia outras opções de código aberto que pudessem resolver nosso problema: por exemplo, o Bazel foi lançado muito mais tarde, em 2015.
Dimensionamento do sistema de controle de versão. Anteriormente, Yandex usava SVN como sistema de controle de versão. Embora o SVN tivesse grande capacidade, ainda era limitado e difícil de manter. Além disso, estávamos cientes de que eventualmente nos depararíamos com as limitações das capacidades e conveniência do SVN. Por exemplo, heurísticas foram usadas para implementar a capacidade de baixar apenas a parte necessária do repositório ou check-out seletivo. Como resultado, em 2016, começamos a experimentar outros sistemas de controle de versão além do SVN.
Mercurial foi a primeira escolha. Mas o principal problema que tivemos foi a velocidade. Tentamos durante um ano e meio colocar a Mercurial em produção, mas os resultados foram decepcionantes. Por exemplo, eventualmente tivemos que reescrever partes do Mercurial para suportar o FUSE, ou teríamos que trazer o repositório inteiro para o laptop de cada desenvolvedor.
Eventualmente, descobriu-se que era mais barato escrever uma solução interna do zero e, em 2019, apareceu o Arc – um novo sistema de controle de versão para usuários do Arcadia com uma UX semelhante ao git. A base do Arc é o FUSE (sistema de arquivos no espaço do usuário), em vez de checkout seletivo. Além disso, o YDB atua como um banco de dados escalável, o que simplifica bastante a operação do Arc quando comparado ao Mercurial.
Muitas vezes somos questionados por que não usamos o git. Porque também tem limitações de escala e funcionalidade: se importarmos apenas o tronco Arcadia para o git, o status do git levará alguns minutos nesta escala. Ao mesmo tempo, não havia uma implementação estável do FUSE construída sobre o git: o VFS para Git não está mais sendo desenvolvido e o EdenFS acabou sendo transformado no Sapling, mas isso aconteceu muito mais tarde.
O estado atual da solução e os planos para o futuro. Para iniciar o desenvolvimento, um usuário interno simplesmente precisa criar uma pasta em nosso monorepositório, escrever o código e dizer como construir seu aplicativo adicionando o manifesto de construção. Como resultado, o usuário recebe solicitações pull, configuração de CI e a capacidade de reutilizar qualquer código na empresa.
Em termos de escala, o trunk contém atualmente 10 milhões de arquivos, o repositório como um todo ultrapassa 2 TiB e mais de 30 mil commits são feitos a cada semana.
Como resultado, no ecossistema que criamos, devemos criar muitos componentes do zero. No entanto, está agora a avançar no sentido da conformidade com as normas globais. Arc, por exemplo, suporta trabalhar com Git para um conjunto predefinido de projetos.
E os outros? Novamente, se você olhar para as grandes tecnologias em geral, então, como exemplo, você deve prestar atenção a e .
O que essas histórias têm em comum
Então porque é que as grandes empresas tecnológicas têm de inventar as suas próprias soluções, e porque é que não podem ser substituídas por outras que sigam um padrão geralmente aceite?
Inovação. Freqüentemente, as grandes corporações são obrigadas a desenvolver soluções para problemas que só se tornarão comuns no futuro. É assim que poderão surgir soluções inovadoras com potencial para se tornarem padrões de mercado.
Nem sempre acontece que um problema resolvido por uma empresa enfrente alguém que não seja a própria empresa. Às vezes, a experiência das grandes tecnologias com um problema específico ajuda toda a indústria a evitar esse problema, seguindo um caminho de desenvolvimento completamente diferente. Nem sempre é possível prever o desenvolvimento do mercado e, como resultado, diferentes exemplos de soluções proprietárias tiveram resultados muito diferentes.
ClickHouse é um exemplo de projeto inovador verdadeiramente bem-sucedido que enriqueceu enormemente o campo do processamento analítico online (OLAP). Contudo, este não é o caso de todos os projetos. O Porto, que começou como um projeto de código aberto, não conseguiu ganhar força por vários motivos. Embora alguns de seus recursos, como a capacidade de criar contêineres aninhados, permaneçam exclusivos.
Escala. Este ponto é semelhante ao anterior em alguns aspectos, porque nem todas as empresas enfrentam o problema da escalabilidade. Houve um tempo em que 640 kbytes eram mais que suficientes para todos, não é mesmo?
Na verdade, o aumento exponencial da carga do sistema foi uma das razões mais importantes para o desenvolvimento do Arcadia e da nuvem interna. É por isso que Arc e Yandex Planner foram desenvolvidos. O Arc foi criado em resposta à necessidade de um VCS fácil de usar que permita aos usuários trabalhar com um monorepositório contendo dezenas de milhões de arquivos em um tronco sem dificuldade. O Yandex Planner foi criado em resposta à necessidade de trabalhar de forma eficaz com clusters de dezenas de milhares de nós e milhões de pods.
As ferramentas públicas continuam a ter problemas de escala (afinal, este é um cenário relativamente raro e investir nele é frequentemente simplesmente não rentável).
Inércia. Considere uma ferramenta interna que resolva um problema dentro de uma empresa. Uma empresa que utilizasse ativamente esta ferramenta dedicaria recursos para melhor adaptá-la às suas necessidades, acabando por transformá-la em uma ferramenta altamente especializada. Esse processo pode durar anos.
Agora considere a possibilidade de que, em algum momento, surja um padrão universalmente aceito para resolver esse problema específico. Neste caso, a especialização ainda pode ser um fator importante na decisão sobre uma solução interna. Considere construir sistemas. Usamos você no Arcadia, embora exista o Bazel do Google. Eles são conceitualmente semelhantes, mas quando você entra em detalhes, muitos cenários importantes são implementados de forma diferente, porque os padrões de carga para cada carga de trabalho podem ser drasticamente diferentes. Como resultado, os recursos já gastos terão quase certamente de ser reinvestidos para personalizar um novo padrão geralmente aceite.
Migrações. Se a seção anterior abordou a questão da adaptação do projeto aos usuários, vamos agora abordar a questão da migração dos próprios usuários. Na minha opinião, a migração deveria ser considerada o próximo problema mais importante em tecnologia depois da nomenclatura. Se presumirmos que já temos uma ferramenta interna da empresa e queremos substituí-la por uma padronizada, inevitavelmente precisaremos de migrações.
Conhecemos muitos exemplos de migrações devido à nossa experiência no desenvolvimento de uma nuvem interna. As migrações em grande escala levam tempo, por isso ambas as ferramentas devem ser suportadas simultaneamente por longos períodos de tempo. Se este processo envolver um grande número de utilizadores, os problemas de gestão serão inevitáveis. Certamente vale a pena tentar migrar sem a participação dos usuários, mas nem sempre isso é possível.
Continuidade de Negócios. Para ser franco, este ponto ganhou recentemente importância suficiente. Anteriormente, um número muito menor de empresas levava isso a sério devido a preocupações com o aprisionamento do fornecedor. Confiar processos críticos a um fornecedor que pode encerrar a colaboração a qualquer momento é arriscado. A JetBrains é um excelente exemplo disso, tendo restringido o uso de seus IDEs a determinadas empresas. Outro caso digno de nota é o Github Enterprise, que começou a suspender contas de usuários baseados na Rússia.
As soluções internas normalmente são imunes a esse problema. Por um lado, ainda existem soluções de código aberto. Por outro lado, não há garantias de que o modelo de código aberto estará com você o tempo todo: por exemplo, Corona, a melhoria desenvolvida internamente pelo Facebook para o software de agendamento Hadoop MapReduce, apareceu em primeiro lugar devido à incapacidade de comprometer os patches necessários para dimensionar o upstream do Hadoop.
Ao mesmo tempo, o aspecto legal da questão afeta o código aberto: por exemplo, commits em golang ou k8s exigem a assinatura de um CLA. Isso continuará a ser um problema?
NIH. Sim, além de razões objetivas, é possível que as decisões tomadas não sejam pragmáticas. Essa é a síndrome do NIH no seu melhor.
Por exemplo, na tentativa de eliminar a influência do lote na computação, tentamos criar nosso próprio escalonador no kernel do Linux. Na prática, nada de bom resultou disso; alguém poderia ter se contentado com os recursos existentes do kernel Linux. Porém, quanto maiores os custos trabalhistas, maior o esforço necessário para elaborar e resolver o problema e menor a probabilidade de sofrer da síndrome NIH.
Resumindo , como você pode ver, frequentemente são necessárias soluções internas para grandes empresas. A maioria deles se fundirá com soluções padrão globais unificadas ainda não maduras no futuro, enquanto o resto se tornará história. De qualquer forma, decidir entre uma solução proprietária e uma solução pronta continua a ser uma questão difícil que não pode ser respondida sem primeiro compreender o contexto e estimar o custo de tal projeto.