Capítulo 14 – Design de configuração e melhores práticas

Por Štěpán Davidovič com Niall Richard Murphy, Christophe Kalt e Betsy Beyer 

 

Configurar sistemas é uma tarefa comum para Engenheiros de Confiabilidade de Sites (SRE, na sigla em inglês) em todos os lugares. Pode ser uma atividade cansativa e detalhada, especialmente se o engenheiro não estiver profundamente familiarizado com o sistema que está configurando ou se a configuração não tiver sido projetada com clareza e usabilidade em mente. Mais comumente, você realiza a configuração em um de dois cenários: durante a configuração inicial, quando você tem bastante tempo, ou durante uma reconfiguração de emergência quando precisa lidar com um incidente.  

Este capítulo examina a configuração sob a perspectiva de alguém que projeta e mantém um sistema de infraestrutura. Ele descreve nossas experiências e estratégias para projetar a configuração de uma maneira segura e sustentável. 

O que é configuração?

Quando implantamos sistemas de software, não os consideramos como algo fixo e imutável. As necessidades empresariais em constante evolução, os requisitos de infraestrutura e outros fatores significam que os sistemas estão constantemente em fluxo. Quando precisamos mudar rapidamente o comportamento do sistema, e o processo de mudança requer uma reconstrução e redeploy muito cara e demorada, uma simples mudança de código não é suficiente. Em vez disso, a configuração – que podemos definir de forma flexível como uma interface humano-computador para modificar o comportamento do sistema – fornece uma maneira de baixo custo para mudar a funcionalidade do sistema. Os SREs tiram proveito disso regularmente, ao implantar sistemas e ajustar seu desempenho, bem como durante a resposta a incidentes. 

Podemos considerar que os sistemas possuem três componentes-chave: 

  • O software 
  • O conjunto de dados com o qual o sistema trabalha 
  • A configuração do sistema 

Embora possamos identificar intuitivamente cada um desses componentes, muitas vezes eles estão longe de estar claramente separados. Por exemplo, muitos sistemas utilizam linguagens de programação para configuração, ou pelo menos têm a capacidade de fazer referência a linguagens de programação. Exemplos incluem o Apache e seus módulos, como mod_lua e seus ganchos de solicitação, ou o gerenciador de janelas XMonad e sua configuração baseada em Haskell. Da mesma forma, os conjuntos de dados podem conter código, como procedimentos SQL armazenados, que podem se tornar aplicativos complexos. 

Uma boa interface de configuração permite alterações de configuração rápidas, confiantes e testáveis. Quando os usuários não têm uma maneira direta de atualizar a configuração, erros são mais prováveis. Os usuários experimentam uma carga cognitiva aumentada e uma curva de aprendizado significativa. 

Configuração e Confiabilidade

Como nossos sistemas são, em última análise, gerenciados por humanos, são os humanos os responsáveis pela configuração. A qualidade da interface humano-computador da configuração de um sistema impacta na capacidade de uma organização executar esse sistema de forma confiável. O impacto de uma interface de configuração bem elaborada (ou mal elaborada) é semelhante ao impacto da qualidade do código na manutenibilidade do sistema ao longo do tempo. 

No entanto, a configuração tende a diferir significativamente do código em vários aspectos. Alterar as capacidades de um sistema por meio de código geralmente é um processo longo e envolvido, envolvendo pequenas mudanças incrementais, revisões de código e testes. Em contraste, alterar uma única opção de configuração pode ter mudanças drásticas na funcionalidade – por exemplo, uma regra de configuração de firewall ruim pode bloquear você do seu próprio sistema. Ao contrário do código, a configuração frequentemente reside em um ambiente não testado (ou até mesmo não testável). 

As mudanças na configuração do sistema podem precisar ser feitas sob pressão significativa. Durante um incidente, um sistema de configuração que possa ser ajustado de forma simples e segura é essencial. Considere o design da interface dos primeiros aviões: controles e indicadores confusos levaram a acidentes. Pesquisas na época mostraram que falhas do operador eram frequentes, independentemente da habilidade ou experiência do piloto. A relação entre usabilidade e confiabilidade se traduz para sistemas de computação. Considere o que acontece se trocarmos um manche e indicadores de discagem por arquivos .conf e gráficos de monitoramento. 

Separando Filosofia e Mecânica

Normalmente discutimos a configuração ao projetar novos softwares ou montar um novo sistema a partir de componentes de software existentes. Como vamos configurá-lo? Como será o carregamento da configuração? Vamos separar o tópico geral da configuração em duas partes: filosofia da configuração e mecânica da configuração.  

A filosofia da configuração diz respeito a aspectos da configuração que são completamente independentes da linguagem escolhida e de outras mecânicas. Nossa discussão sobre filosofia abrange como estruturar a configuração, como alcançar o nível correto de abstração e como suportar casos de uso divergentes de forma harmoniosa. 

Nossa discussão sobre mecânica abrange tópicos como design de linguagem, estratégias de implantação e interações com outros sistemas. Este capítulo se concentra menos em mecânica, em parte porque tópicos como escolha de linguagem já estão sendo discutidos em toda a indústria. Além disso, como uma organização pode já ter requisitos externos sólidos, como infraestrutura de configuração pré-existente, as mecânicas de configuração não são facilmente generalizáveis. O próximo capítulo sobre Jsonnet dá um exemplo prático de mecânica de configuração – em particular, design de linguagem – em software existente. 

Discutir filosofia e mecânica separadamente nos permite raciocinar de forma mais clara sobre a configuração. Na prática, detalhes de implementação como a linguagem de configuração (seja XML ou Lua) não importam se a configuração requer uma quantidade enorme de entrada do usuário difícil de entender. Por outro lado, mesmo as entradas de configuração mais simples podem causar problemas se precisarem ser inseridas em uma interface muito complicada. Considere o (muito) antigo processo de configuração do kernel do Linux: as atualizações de configuração tinham que ser feitas por meio de um terminal de linha de comando que exigia uma sequência de comandos para definir cada parâmetro. Para fazer até mesmo a correção mais simples, os usuários tinham que iniciar o processo de configuração do zero. 

Filosofia da configuração

Esta seção discute aspectos da configuração que são completamente independentes da implementação, portanto, esses tópicos se generalizam em todas as implementações. 

Na filosofia seguinte, nossa configuração ideal é nenhuma configuração. Neste mundo ideal, o sistema reconhece automaticamente a configuração correta com base na implantação, carga de trabalho ou partes da configuração que já existiam quando o novo sistema foi implantado. Claro, para muitos sistemas, esse ideal é improvável de ser alcançado na prática. No entanto, isso destaca a direção desejável da configuração: afastando-se de um grande número de ajustes e em direção à simplicidade. 

Historicamente, os sistemas críticos para missão ofereciam uma grande quantidade de controles (que equivalem à configuração do sistema), mas também exigiam um treinamento significativo dos operadores humanos. Considere a complexa variedade de controles do operador no centro de controle de espaçonaves da NASA na Figura 14-1. Nos sistemas de computador modernos, esse tipo de treinamento já não é viável para a maioria da indústria.

 

Figura 14-1. Painel de controle no centro de controle de espaçonaves da NASA, ilustrando possivelmente uma configuração muito complexa. 

Embora esse ideal reduza a quantidade de controle que podemos exercer sobre um sistema, ele diminui tanto a área de superfície para erros quanto a carga cognitiva sobre o operador. À medida que a complexidade dos sistemas aumenta, a carga cognitiva do operador se torna cada vez mais importante.  

Quando aplicamos esses princípios em sistemas práticos no Google, eles geralmente resultaram em fácil adoção ampla e baixo custo para suporte ao usuário interno. 

A Configuração pergunta aos usuários

Independentemente do que você está configurando e de como está configurando, a interação humano-computador se resume, em última análise, a uma interface que faz perguntas aos usuários, solicitando entradas sobre como o sistema deve operar. Este modelo de conceptualização é válido independentemente se os usuários estão editando arquivos XML ou usando um assistente de configuração GUI. 

Nos sistemas de software modernos, podemos abordar esse modelo a partir de duas perspectivas diferentes: 

Perspectiva centrada na infraestrutura  

É útil oferecer o maior número possível de controles de configuração. Isso permite aos usuários ajustar o sistema às suas necessidades exatas. Quanto mais controles, melhor, porque o sistema pode ser ajustado à perfeição. 

Perspectiva centrada no usuário  

A configuração faz perguntas sobre a infraestrutura que o usuário deve responder antes de poder voltar a trabalhar em seu objetivo comercial real. Quanto menos controles, melhor, porque responder às perguntas de configuração é uma tarefa. 

Impulsionados pela nossa filosofia inicial de minimizar as entradas do usuário, favorecemos a visão centrada no usuário. 

As implicações dessa decisão de design de software vão além da configuração. Focar a configuração no usuário significa que seu software precisa ser projetado com um conjunto específico de casos de uso para seu público-alvo principal. Isso requer pesquisa de usuário. Em contraste, uma abordagem centrada na infraestrutura significa que seu software fornece efetivamente uma infraestrutura básica, mas transformá-lo em um sistema prático requer uma considerável configuração do usuário. Esses modelos não estão em conflito estrito, mas tentar conciliá-los pode ser bastante difícil. Talvez de forma contraintuitiva, opções de configuração limitadas podem levar a uma melhor adoção do que software extremamente versátil – o esforço de integração é substancialmente menor porque o software funciona principalmente “pronto para uso”.  

Sistemas que partem de uma visão centrada na infraestrutura podem se mover em direção a um foco mais centrado no usuário à medida que o sistema amadurece, removendo alguns controles de configuração por meio de vários meios (alguns dos quais são discutidos em seções subsequentes). 

As perguntas devem estar próximas dos objetivos do usuário  

Ao seguirmos a filosofia da configuração centrada no usuário, queremos garantir que os usuários possam facilmente se relacionar com as perguntas que fazemos. Podemos pensar na natureza das entradas do usuário em um espectro: em uma extremidade, o usuário descreve suas necessidades em seus próprios termos (menos opções de configuração); na outra extremidade, o usuário descreve exatamente como o sistema deve implementar suas necessidades (mais opções de configuração). 

Vamos usar fazer chá como uma analogia para configurar um sistema. Com menos opções de configuração, um usuário pode pedir por “chá verde quente” e obter aproximadamente o que deseja. No extremo oposto do espectro, um usuário pode especificar todo o processo: volume de água, temperatura de fervura, marca e sabor do chá, tempo de infusão, tipo de xícara de chá e volume de chá na xícara. Usar mais opções de configuração pode estar mais próximo da perfeição, mas o esforço necessário para seguir tantos detalhes pode custar mais do que o benefício marginal de uma bebida quase perfeita. 

Essa analogia é útil tanto para os usuários quanto para os desenvolvedores que trabalham no sistema de configuração. Quando um usuário especifica passos exatos, o sistema precisa segui-los. Mas quando o usuário descreve seus objetivos em alto nível, o sistema pode evoluir ao longo do tempo e melhorar como implementa esses objetivos. Um entendimento inicial dos objetivos do usuário para o sistema é um primeiro passo necessário aqui. 

Para uma ilustração prática de como esse espectro se desenrola, considere o agendamento de tarefas. Imagine que você tenha um processo analítico único para executar. Sistemas como Kubernetes ou Mesos permitem que você atinja seu objetivo real de executar análises, sem sobrecarregá-lo com detalhes minuciosos, como decidir em qual(is) máquina(s) física(s) seu processo analítico deve ser executado. 

Perguntas obrigatórias e opcionais

Uma determinada configuração pode conter dois tipos de perguntas: obrigatórias e opcionais. As perguntas obrigatórias devem ser respondidas para que a configuração forneça qualquer funcionalidade. Um exemplo disso pode ser quem cobrar por uma operação. Perguntas opcionais não ditam a funcionalidade principal, mas respondê-las pode melhorar a qualidade da função. Por exemplo, configurar um número de processos de trabalho. 

Para manter o sistema centrado no usuário e fácil de adotar, seu sistema deve minimizar o número de perguntas de configuração obrigatórias. Isso não é uma tarefa fácil, mas é importante. Embora alguém possa argumentar que adicionar um ou dois pequenos passos incorre em pouco custo, a vida de um engenheiro muitas vezes é uma cadeia interminável de passos individualmente pequenos. A redução principiada desses pequenos passos pode melhorar drasticamente a produtividade. 

Um conjunto inicial de perguntas obrigatórias muitas vezes inclui as perguntas que você pensou ao projetar o sistema. O caminho mais fácil para reduzir as perguntas obrigatórias é convertê-las em perguntas opcionais, o que significa fornecer respostas padrão que se apliquem de forma segura e eficaz para a maioria, se não todos, os usuários. Por exemplo, em vez de exigir que o usuário defina se uma execução deve ser apenas de teste ou não, podemos simplesmente fazer a execução de teste por padrão. 

Embora esse valor padrão seja frequentemente um valor estático, codificado diretamente, não precisa ser assim. Ele pode ser determinado dinamicamente com base em outras propriedades do sistema. Aproveitar a determinação dinâmica pode simplificar ainda mais sua configuração. 

Para contextualizar, considere os seguintes exemplos de valores padrão dinâmicos. Um sistema computacionalmente intensivo pode normalmente decidir quantas threads de computação implantar por meio de um controle de configuração. Seu padrão dinâmico implanta tantas threads quanto o sistema (ou contêiner) tem núcleos de execução. Nesse caso, um único padrão estático não é útil. Padrão dinâmico significa que não precisamos perguntar ao usuário para determinar o número correto de threads para o sistema implantar em uma plataforma específica. Da mesma forma, um binário Java implantado sozinho em um contêiner pode ajustar automaticamente seus limites de heap dependendo da memória disponível no contêiner. Esses dois exemplos de padrões dinâmicos refletem implantações comuns. Se você precisar restringir o uso de recursos, é útil poder substituir os padrões dinâmicos na configuração. 

Os padrões dinâmicos implementados podem não funcionar para todos. Com o tempo, os usuários podem preferir abordagens diferentes e pedir maior controle sobre os padrões dinâmicos. Se uma parte significativa dos usuários de configuração relatar problemas com os padrões dinâmicos, é provável que a lógica de decisão não corresponda mais aos requisitos de sua base de usuários atual. Considere implementar melhorias abrangentes que permitam que seus padrões dinâmicos funcionem sem exigir controles de configuração adicionais. Se apenas uma pequena fração dos usuários estiver insatisfeita, eles podem se sair melhor definindo manualmente as opções de configuração. Implementar mais complexidade no sistema cria mais trabalho para os usuários (por exemplo, aumento da carga cognitiva para ler a documentação). 

Ao escolher respostas padrão para perguntas opcionais, independentemente de optar por padrões estáticos ou dinâmicos, pense cuidadosamente sobre o impacto de sua escolha. A experiência mostra que a maioria dos usuários usará o padrão, então isso é tanto uma oportunidade quanto uma responsabilidade. Você pode sutilmente direcionar as pessoas na direção certa, mas designar o padrão errado pode causar muito dano. Por exemplo, considere os padrões de configuração e seu impacto fora da ciência da computação. Países onde o padrão para doadores de órgãos é a opção de adesão (e os indivíduos podem optar por sair se desejarem) têm proporções dramaticamente maiores de doadores de órgãos do que países com uma opção padrão de saída. Selecionar simplesmente um padrão específico tem um impacto profundo nas opções médicas em todo o sistema. 

Algumas perguntas opcionais começam sem um caso de uso claro. Você pode querer remover essas perguntas completamente. Um grande número de perguntas opcionais pode confundir o usuário, então você deve adicionar controles de configuração apenas quando motivado por uma necessidade real. Por fim, se a linguagem de configuração usar o conceito de herança, é útil poder reverter para o valor padrão para qualquer pergunta opcional nas configurações folha. 

Escapando da Simplicidade

Até agora, discutimos a redução da configuração de um sistema à sua forma mais simples. No entanto, o sistema de configuração pode precisar levar em conta também os usuários avançados. Voltando à nossa analogia do chá, e se realmente precisarmos deixar o chá em infusão por uma duração específica?  

Uma estratégia para acomodar usuários avançados é encontrar o denominador comum mais baixo do que os usuários regulares e avançados requerem, e estabelecer esse nível de complexidade como padrão. A desvantagem é que essa decisão afeta a todos; até mesmo os casos de uso mais simples agora precisam ser considerados em termos de baixo nível. 

Ao pensar na configuração em termos de substituições opcionais do comportamento padrão, o usuário configura o “chá verde” e, em seguida, adiciona “deixar o chá em infusão por cinco minutos”. Neste modelo, a configuração padrão ainda é de alto nível e próxima dos objetivos do usuário, mas o usuário pode ajustar detalhes de baixo nível. Esta abordagem não é nova. Podemos fazer paralelos com linguagens de programação de alto nível como C++ ou Java, que permitem que os programadores incluam instruções de máquina (ou VM) em código, de outra forma, escrito na linguagem de alto nível. Em alguns softwares para consumidores, vemos telas com opções avançadas que podem oferecer um controle mais refinado do que a visualização típica. 

É útil pensar na otimização para o total de horas gastas configurando em toda a organização. Considere não apenas o ato de configuração em si, mas também a paralisia de decisão que os usuários podem experimentar quando apresentados com muitas opções, o tempo necessário para corrigir a configuração após tomar um rumo errado, a taxa mais lenta de mudança devido a menor confiança e muito mais. Ao considerar alternativas de design de configuração, a opção que realiza uma configuração complexa em menos, mas significativamente mais difíceis etapas podem ser preferível se isso tornar o suporte aos casos de uso mais comuns significativamente mais fácil. 

Se você descobrir que mais do que um pequeno subconjunto de seus usuários precisa de uma configuração complexa, pode ter identificado incorretamente os casos de uso comuns. Se for esse o caso, reavalie as suposições iniciais do produto para o seu sistema e realize pesquisas adicionais com os usuários. 

Mecânica da Configuração

Nossa discussão até este ponto abordou a filosofia da configuração. Esta seção muda o foco para a mecânica de como um usuário interage com a configuração.  

Separar configuração e dados resultantes

Qual linguagem usar para armazenar a configuração é uma pergunta inevitável. Você poderia optar por ter dados puros, como em um arquivo INI, YAML ou XML. Alternativamente, a configuração poderia ser armazenada em uma linguagem de nível mais alto que permite uma configuração muito mais flexível. 

Fundamentalmente, todas as perguntas feitas ao usuário se reduzem a informações estáticas. Isso pode incluir respostas claramente estáticas para perguntas como “Quantas threads devem ser usadas?” Mas até mesmo “Qual função deve ser usada para cada solicitação?” é apenas uma referência estática a uma função. 

Para responder à antiga questão de se a configuração é código ou dados, nossa experiência mostrou que ter ambos, código e dados, mas separá-los, é o ideal. A infraestrutura do sistema deve operar com dados estáticos simples, que podem estar em formatos como Protocol Buffers, YAML ou JSON. Essa escolha não implica que o usuário precise interagir diretamente com dados puros. Os usuários podem interagir com uma interface de nível mais alto que gera esses dados. No entanto, esse formato de dados pode ser usado por APIs que permitem o empilhamento adicional de sistemas e automação. 

Essa interface de alto nível pode ser quase qualquer coisa. Pode ser uma linguagem de alto nível como uma Linguagem Específica de Domínio (DSL) baseada em Python, Lua, ou linguagens desenvolvidas para um propósito específico, como Jsonnet (que discutiremos em mais detalhes no Capítulo 15). Podemos pensar em tal interface como uma compilação, semelhante ao tratamento dado ao código C++. A interface de alto nível também pode ser nenhuma linguagem, com a configuração sendo inserida por meio de uma interface de usuário web. 

Iniciar com uma interface de configuração deliberadamente separada de sua representação de dados estáticos significa que o sistema tem flexibilidade para implantação. Diversas organizações podem ter diferentes normas culturais ou requisitos de produto (como usar linguagens específicas dentro da empresa ou precisar externalizar a configuração para os usuários finais), e um sistema tão versátil pode ser adaptado para atender a diversos requisitos de configuração. Esse sistema também pode facilmente oferecer suporte a múltiplos idiomas. Consulte a Figura 14-2. 

Essa separação pode ser completamente invisível para o usuário. O caminho comum do usuário pode ser editar arquivos na linguagem de configuração enquanto tudo mais acontece nos bastidores. Por exemplo, uma vez que o usuário envia alterações para o sistema, a configuração recém-armazenada é automaticamente compilada em dados brutos. 

 

Figura 14-2. Fluxo de configuração com uma interface de configuração separada e infraestrutura de dados de configuração. Observe que a interface web geralmente também exibe a configuração atual, tornando o relacionamento bidirecional. 

Depois que os dados de configuração estática são obtidos, eles também podem ser usados em análises de dados. Por exemplo, se os dados de configuração gerados estiverem no formato JSON, eles podem ser carregados no PostgreSQL e analisados com consultas de banco de dados. Como proprietário da infraestrutura, você pode então consultar rapidamente e facilmente quais parâmetros de configuração estão sendo usados e por quem. Essa consulta é útil para identificar recursos que você pode remover ou medir o impacto de uma opção com defeito. 

Ao consumir os dados finais de configuração, será útil também armazenar metadados sobre como a configuração foi ingerida. Por exemplo, se você souber que os dados vieram de um arquivo de configuração em Jsonnet ou tiver o caminho completo para o original antes de ser compilado em dados, você pode rastrear os autores da configuração. 

Também é aceitável que a linguagem de configuração seja um dado estático. Por exemplo, tanto sua infraestrutura quanto sua interface podem usar JSON simples. No entanto, evite o acoplamento rígido entre o formato de dados que você usa como interface e o formato de dados que você usa internamente. Por exemplo, você pode usar uma estrutura de dados internamente que contenha a estrutura de dados consumida da configuração. A estrutura de dados interna também pode conter dados completamente específicos de implementação que nunca precisam ser expostos fora do sistema. 

Importância das ferramentas

As ferramentas podem fazer a diferença entre um pesadelo caótico e um sistema sustentável e escalável, mas muitas vezes são negligenciadas ao projetar sistemas de configuração. Esta seção discute as principais ferramentas que devem estar disponíveis para um sistema de configuração ideal. 

Validação semântica  

Embora a maioria das linguagens ofereça validação sintática pronta para uso, não negligencie a validação semântica. Mesmo que sua configuração seja sintaticamente válida, é provável que ela faça coisas úteis? Ou o usuário fez referência a um diretório inexistente (devido a um erro de digitação), ou precisa de mil vezes mais RAM do que realmente tem (porque as unidades não são o que o usuário esperava)? 

Validar que a configuração é semanticamente significativa, até onde for possível, pode ajudar a prevenir falhas e diminuir os custos operacionais. Para cada possível configuração incorreta, devemos nos perguntar se poderíamos evitá-la no momento em que o usuário confirma a configuração, em vez de depois que as alterações são enviadas. 

Sintaxe da configuração 

Embora seja fundamental garantir que a configuração alcance o que o usuário deseja, também é importante remover obstáculos mecânicos. Do ponto de vista da sintaxe, a linguagem de configuração deve oferecer o seguinte: 

Realce de sintaxe em editores (usados dentro da empresa) 

Muitas vezes, você já resolveu isso reutilizando uma linguagem existente. No entanto, linguagens específicas de domínio podem ter “açúcar sintático” adicional que pode se beneficiar de realce especializado.  

Linter 

Use um linter para identificar inconsistências comuns no uso da linguagem. Pylint é um exemplo popular de linguagem. 

Formatador automático de sintaxe 

A padronização integrada minimiza discussões relativamente menos importantes sobre formatação e diminui a carga cognitiva conforme os colaboradores alternam entre projetos. A formatação padrão também pode permitir uma edição automática mais fácil, o que é útil em sistemas usados amplamente dentro de uma grande organização. Exemplos de autoformatadores em linguagens existentes incluem clang-format e autopep8. 

Essas ferramentas permitem que os usuários escrevam e editem configurações com a confiança de que sua sintaxe está correta. Uma indentação incorreta em configurações orientadas por espaços em branco pode ter potencialmente grandes consequências – algumas das quais a formatação padrão pode prevenir. 

Propriedade e rastreamento de mudanças

Porque a configuração pode potencialmente impactar sistemas críticos de empresas e instituições, é importante garantir um bom isolamento de usuário e entender quais mudanças ocorreram no sistema. Como mencionado no Capítulo 10, uma cultura eficaz de postmortem evita culpar indivíduos. No entanto, é útil tanto durante um incidente quanto durante a realização de um postmortem saber quem alterou uma configuração e entender como a mudança de configuração impactou o sistema. Isso é válido tanto se o incidente for devido a um acidente ou a um ator malicioso. 

Cada trecho de configuração para o sistema deve ter um proprietário claro. Por exemplo, se você usar arquivos de configuração, seus diretórios podem ser de propriedade de um único grupo de produção. Se os arquivos em um diretório só puderem ter um único proprietário, é muito mais fácil rastrear quem faz alterações. 

Versionar a configuração, independentemente de como isso é feito, permite que você volte no tempo para ver como a configuração estava em determinado momento. Adicionar arquivos de configuração a um sistema de controle de versão, como Subversion ou Git, é uma prática comum hoje em dia, mas essa prática é igualmente importante para a configuração ingerida por uma interface web ou APIs remotas. Você também pode desejar ter um acoplamento mais estreito entre a configuração e o software sendo configurado. Ao fazer isso, você pode evitar configurar inadvertidamente recursos que ainda não estão disponíveis ou não são mais suportados no software. 

Em uma nota relacionada, é útil (e às vezes necessário) registrar tanto as mudanças na configuração quanto a aplicação resultante no sistema. O simples ato de realizar um novo commit de uma versão da configuração nem sempre significa que a configuração é aplicada diretamente (mais sobre isso depois). Quando uma mudança na configuração do sistema é suspeita como a causa durante uma resposta a incidentes, é útil ser capaz de determinar rapidamente o conjunto completo de edições na configuração que foram feitas na alteração. Isso permite reversões confiantes e a capacidade de notificar as partes cujas configurações foram impactadas. 

Aplicação segura de mudança na configuração

Como discutido anteriormente, a configuração é uma maneira fácil de fazer grandes alterações na funcionalidade do sistema, mas muitas vezes não é testada unitariamente ou mesmo facilmente testável. Como queremos evitar incidentes de confiabilidade, devemos examinar o que significa a aplicação segura de uma mudança na configuração. 

Para que uma mudança na configuração seja segura, ela deve ter três propriedades principais: 

  • A capacidade de ser implantada gradualmente, evitando uma mudança do tipo tudo-ou-nada. 
  • A capacidade de reverter a mudança se ela se mostrar perigosa. 
  • Reversão automática (ou, no mínimo, a capacidade de interromper o progresso) se a mudança levar à perda de controle do operador. 

Ao implantar uma nova configuração, é importante evitar uma implantação global de uma só vez. Em vez disso, implante a nova configuração gradualmente – fazê-lo permite detectar problemas e abortar uma implantação problemática antes de causar uma falha completa. Esta é uma das razões pelas quais ferramentas como o Kubernetes utilizam uma estratégia de atualização gradual para atualizar o software ou a configuração, em vez de atualizar todos os pods de uma só vez. (Veja o Capítulo 16 para discussões relacionadas.) 

A capacidade de reverter é importante para diminuir a duração de incidentes. Reverter a configuração problemática pode mitigar uma falha muito mais rapidamente do que tentar corrigi-la com um reparo temporário – há uma confiança inerentemente menor de que um reparo melhorará as coisas. 

Para ser capaz de avançar e retroceder na configuração, ela deve ser hermética. Uma configuração que requer recursos externos que podem mudar fora de seu ambiente hermético pode ser muito difícil de reverter. Por exemplo, uma configuração armazenada em um sistema de controle de versão que referência dados em um sistema de arquivos de rede não é hermética. 

Por último, mas não menos importante, o sistema deve ser especialmente cuidadoso ao lidar com mudanças que possam levar a uma perda repentina de controle do operador. Em sistemas desktop, alterações na resolução da tela frequentemente geram uma contagem regressiva e um reset se o usuário não confirmar as alterações. Isso ocorre porque uma configuração incorreta do monitor pode impedir o usuário de reverter a alteração. Da mesma forma, é comum os administradores de sistema se bloquearem acidentalmente fora do sistema que estão configurando no momento. 

Esses princípios não são exclusivos da configuração e se aplicam a outros métodos de alteração de sistemas implantados, como a atualização de binários ou a implantação de novos conjuntos de dados. 

Conclusão 

Mudanças triviais na configuração podem impactar um sistema de produção de maneiras dramáticas, portanto, precisamos projetar a configuração de forma deliberada para mitigar esses riscos. O design da configuração envolve aspectos tanto do design de API quanto do design de UI e deve ser intencional – não apenas um efeito colateral da implementação do sistema. Separar a configuração em filosofia e mecânica nos ajuda a ganhar clareza ao projetar sistemas internos e nos permite delimitar corretamente a discussão. 

Aplicar essas recomendações requer tempo e diligência. Para um exemplo de como aplicamos esses princípios na prática, consulte o artigo da ACM Queue sobre o Serviço de Análise de Canários. Ao projetar esse sistema interno prático, passamos cerca de um mês tentando reduzir as perguntas obrigatórias e encontrar boas respostas para as perguntas opcionais. Nossos esforços resultaram em um sistema de configuração simples. Como era fácil de usar, foi amplamente adotado internamente. Vimos pouca necessidade de suporte ao usuário – como os usuários podem entender facilmente o sistema, eles podem fazer alterações com confiança. Claro, não eliminamos completamente as configurações incorretas e o suporte ao usuário, nem esperamos fazê-lo. 

Capítulo 14 – Design de configuração e melhores práticas

Por Štěpán Davidovič com Niall Richard Murphy, Christophe Kalt e Betsy Beyer 

 

Configurar sistemas é uma tarefa comum para Engenheiros de Confiabilidade de Sites (SRE, na sigla em inglês) em todos os lugares. Pode ser uma atividade cansativa e detalhada, especialmente se o engenheiro não estiver profundamente familiarizado com o sistema que está configurando ou se a configuração não tiver sido projetada com clareza e usabilidade em mente. Mais comumente, você realiza a configuração em um de dois cenários: durante a configuração inicial, quando você tem bastante tempo, ou durante uma reconfiguração de emergência quando precisa lidar com um incidente.  

Este capítulo examina a configuração sob a perspectiva de alguém que projeta e mantém um sistema de infraestrutura. Ele descreve nossas experiências e estratégias para projetar a configuração de uma maneira segura e sustentável. 

O que é configuração?

Quando implantamos sistemas de software, não os consideramos como algo fixo e imutável. As necessidades empresariais em constante evolução, os requisitos de infraestrutura e outros fatores significam que os sistemas estão constantemente em fluxo. Quando precisamos mudar rapidamente o comportamento do sistema, e o processo de mudança requer uma reconstrução e redeploy muito cara e demorada, uma simples mudança de código não é suficiente. Em vez disso, a configuração – que podemos definir de forma flexível como uma interface humano-computador para modificar o comportamento do sistema – fornece uma maneira de baixo custo para mudar a funcionalidade do sistema. Os SREs tiram proveito disso regularmente, ao implantar sistemas e ajustar seu desempenho, bem como durante a resposta a incidentes. 

Podemos considerar que os sistemas possuem três componentes-chave: 

  • O software 
  • O conjunto de dados com o qual o sistema trabalha 
  • A configuração do sistema 

Embora possamos identificar intuitivamente cada um desses componentes, muitas vezes eles estão longe de estar claramente separados. Por exemplo, muitos sistemas utilizam linguagens de programação para configuração, ou pelo menos têm a capacidade de fazer referência a linguagens de programação. Exemplos incluem o Apache e seus módulos, como mod_lua e seus ganchos de solicitação, ou o gerenciador de janelas XMonad e sua configuração baseada em Haskell. Da mesma forma, os conjuntos de dados podem conter código, como procedimentos SQL armazenados, que podem se tornar aplicativos complexos. 

Uma boa interface de configuração permite alterações de configuração rápidas, confiantes e testáveis. Quando os usuários não têm uma maneira direta de atualizar a configuração, erros são mais prováveis. Os usuários experimentam uma carga cognitiva aumentada e uma curva de aprendizado significativa. 

Configuração e Confiabilidade

Como nossos sistemas são, em última análise, gerenciados por humanos, são os humanos os responsáveis pela configuração. A qualidade da interface humano-computador da configuração de um sistema impacta na capacidade de uma organização executar esse sistema de forma confiável. O impacto de uma interface de configuração bem elaborada (ou mal elaborada) é semelhante ao impacto da qualidade do código na manutenibilidade do sistema ao longo do tempo. 

No entanto, a configuração tende a diferir significativamente do código em vários aspectos. Alterar as capacidades de um sistema por meio de código geralmente é um processo longo e envolvido, envolvendo pequenas mudanças incrementais, revisões de código e testes. Em contraste, alterar uma única opção de configuração pode ter mudanças drásticas na funcionalidade – por exemplo, uma regra de configuração de firewall ruim pode bloquear você do seu próprio sistema. Ao contrário do código, a configuração frequentemente reside em um ambiente não testado (ou até mesmo não testável). 

As mudanças na configuração do sistema podem precisar ser feitas sob pressão significativa. Durante um incidente, um sistema de configuração que possa ser ajustado de forma simples e segura é essencial. Considere o design da interface dos primeiros aviões: controles e indicadores confusos levaram a acidentes. Pesquisas na época mostraram que falhas do operador eram frequentes, independentemente da habilidade ou experiência do piloto. A relação entre usabilidade e confiabilidade se traduz para sistemas de computação. Considere o que acontece se trocarmos um manche e indicadores de discagem por arquivos .conf e gráficos de monitoramento. 

Separando Filosofia e Mecânica

Normalmente discutimos a configuração ao projetar novos softwares ou montar um novo sistema a partir de componentes de software existentes. Como vamos configurá-lo? Como será o carregamento da configuração? Vamos separar o tópico geral da configuração em duas partes: filosofia da configuração e mecânica da configuração.  

A filosofia da configuração diz respeito a aspectos da configuração que são completamente independentes da linguagem escolhida e de outras mecânicas. Nossa discussão sobre filosofia abrange como estruturar a configuração, como alcançar o nível correto de abstração e como suportar casos de uso divergentes de forma harmoniosa. 

Nossa discussão sobre mecânica abrange tópicos como design de linguagem, estratégias de implantação e interações com outros sistemas. Este capítulo se concentra menos em mecânica, em parte porque tópicos como escolha de linguagem já estão sendo discutidos em toda a indústria. Além disso, como uma organização pode já ter requisitos externos sólidos, como infraestrutura de configuração pré-existente, as mecânicas de configuração não são facilmente generalizáveis. O próximo capítulo sobre Jsonnet dá um exemplo prático de mecânica de configuração – em particular, design de linguagem – em software existente. 

Discutir filosofia e mecânica separadamente nos permite raciocinar de forma mais clara sobre a configuração. Na prática, detalhes de implementação como a linguagem de configuração (seja XML ou Lua) não importam se a configuração requer uma quantidade enorme de entrada do usuário difícil de entender. Por outro lado, mesmo as entradas de configuração mais simples podem causar problemas se precisarem ser inseridas em uma interface muito complicada. Considere o (muito) antigo processo de configuração do kernel do Linux: as atualizações de configuração tinham que ser feitas por meio de um terminal de linha de comando que exigia uma sequência de comandos para definir cada parâmetro. Para fazer até mesmo a correção mais simples, os usuários tinham que iniciar o processo de configuração do zero. 

Filosofia da configuração

Esta seção discute aspectos da configuração que são completamente independentes da implementação, portanto, esses tópicos se generalizam em todas as implementações. 

Na filosofia seguinte, nossa configuração ideal é nenhuma configuração. Neste mundo ideal, o sistema reconhece automaticamente a configuração correta com base na implantação, carga de trabalho ou partes da configuração que já existiam quando o novo sistema foi implantado. Claro, para muitos sistemas, esse ideal é improvável de ser alcançado na prática. No entanto, isso destaca a direção desejável da configuração: afastando-se de um grande número de ajustes e em direção à simplicidade. 

Historicamente, os sistemas críticos para missão ofereciam uma grande quantidade de controles (que equivalem à configuração do sistema), mas também exigiam um treinamento significativo dos operadores humanos. Considere a complexa variedade de controles do operador no centro de controle de espaçonaves da NASA na Figura 14-1. Nos sistemas de computador modernos, esse tipo de treinamento já não é viável para a maioria da indústria.

 

Figura 14-1. Painel de controle no centro de controle de espaçonaves da NASA, ilustrando possivelmente uma configuração muito complexa. 

Embora esse ideal reduza a quantidade de controle que podemos exercer sobre um sistema, ele diminui tanto a área de superfície para erros quanto a carga cognitiva sobre o operador. À medida que a complexidade dos sistemas aumenta, a carga cognitiva do operador se torna cada vez mais importante.  

Quando aplicamos esses princípios em sistemas práticos no Google, eles geralmente resultaram em fácil adoção ampla e baixo custo para suporte ao usuário interno. 

A Configuração pergunta aos usuários

Independentemente do que você está configurando e de como está configurando, a interação humano-computador se resume, em última análise, a uma interface que faz perguntas aos usuários, solicitando entradas sobre como o sistema deve operar. Este modelo de conceptualização é válido independentemente se os usuários estão editando arquivos XML ou usando um assistente de configuração GUI. 

Nos sistemas de software modernos, podemos abordar esse modelo a partir de duas perspectivas diferentes: 

Perspectiva centrada na infraestrutura  

É útil oferecer o maior número possível de controles de configuração. Isso permite aos usuários ajustar o sistema às suas necessidades exatas. Quanto mais controles, melhor, porque o sistema pode ser ajustado à perfeição. 

Perspectiva centrada no usuário  

A configuração faz perguntas sobre a infraestrutura que o usuário deve responder antes de poder voltar a trabalhar em seu objetivo comercial real. Quanto menos controles, melhor, porque responder às perguntas de configuração é uma tarefa. 

Impulsionados pela nossa filosofia inicial de minimizar as entradas do usuário, favorecemos a visão centrada no usuário. 

As implicações dessa decisão de design de software vão além da configuração. Focar a configuração no usuário significa que seu software precisa ser projetado com um conjunto específico de casos de uso para seu público-alvo principal. Isso requer pesquisa de usuário. Em contraste, uma abordagem centrada na infraestrutura significa que seu software fornece efetivamente uma infraestrutura básica, mas transformá-lo em um sistema prático requer uma considerável configuração do usuário. Esses modelos não estão em conflito estrito, mas tentar conciliá-los pode ser bastante difícil. Talvez de forma contraintuitiva, opções de configuração limitadas podem levar a uma melhor adoção do que software extremamente versátil – o esforço de integração é substancialmente menor porque o software funciona principalmente “pronto para uso”.  

Sistemas que partem de uma visão centrada na infraestrutura podem se mover em direção a um foco mais centrado no usuário à medida que o sistema amadurece, removendo alguns controles de configuração por meio de vários meios (alguns dos quais são discutidos em seções subsequentes). 

As perguntas devem estar próximas dos objetivos do usuário  

Ao seguirmos a filosofia da configuração centrada no usuário, queremos garantir que os usuários possam facilmente se relacionar com as perguntas que fazemos. Podemos pensar na natureza das entradas do usuário em um espectro: em uma extremidade, o usuário descreve suas necessidades em seus próprios termos (menos opções de configuração); na outra extremidade, o usuário descreve exatamente como o sistema deve implementar suas necessidades (mais opções de configuração). 

Vamos usar fazer chá como uma analogia para configurar um sistema. Com menos opções de configuração, um usuário pode pedir por “chá verde quente” e obter aproximadamente o que deseja. No extremo oposto do espectro, um usuário pode especificar todo o processo: volume de água, temperatura de fervura, marca e sabor do chá, tempo de infusão, tipo de xícara de chá e volume de chá na xícara. Usar mais opções de configuração pode estar mais próximo da perfeição, mas o esforço necessário para seguir tantos detalhes pode custar mais do que o benefício marginal de uma bebida quase perfeita. 

Essa analogia é útil tanto para os usuários quanto para os desenvolvedores que trabalham no sistema de configuração. Quando um usuário especifica passos exatos, o sistema precisa segui-los. Mas quando o usuário descreve seus objetivos em alto nível, o sistema pode evoluir ao longo do tempo e melhorar como implementa esses objetivos. Um entendimento inicial dos objetivos do usuário para o sistema é um primeiro passo necessário aqui. 

Para uma ilustração prática de como esse espectro se desenrola, considere o agendamento de tarefas. Imagine que você tenha um processo analítico único para executar. Sistemas como Kubernetes ou Mesos permitem que você atinja seu objetivo real de executar análises, sem sobrecarregá-lo com detalhes minuciosos, como decidir em qual(is) máquina(s) física(s) seu processo analítico deve ser executado. 

Perguntas obrigatórias e opcionais

Uma determinada configuração pode conter dois tipos de perguntas: obrigatórias e opcionais. As perguntas obrigatórias devem ser respondidas para que a configuração forneça qualquer funcionalidade. Um exemplo disso pode ser quem cobrar por uma operação. Perguntas opcionais não ditam a funcionalidade principal, mas respondê-las pode melhorar a qualidade da função. Por exemplo, configurar um número de processos de trabalho. 

Para manter o sistema centrado no usuário e fácil de adotar, seu sistema deve minimizar o número de perguntas de configuração obrigatórias. Isso não é uma tarefa fácil, mas é importante. Embora alguém possa argumentar que adicionar um ou dois pequenos passos incorre em pouco custo, a vida de um engenheiro muitas vezes é uma cadeia interminável de passos individualmente pequenos. A redução principiada desses pequenos passos pode melhorar drasticamente a produtividade. 

Um conjunto inicial de perguntas obrigatórias muitas vezes inclui as perguntas que você pensou ao projetar o sistema. O caminho mais fácil para reduzir as perguntas obrigatórias é convertê-las em perguntas opcionais, o que significa fornecer respostas padrão que se apliquem de forma segura e eficaz para a maioria, se não todos, os usuários. Por exemplo, em vez de exigir que o usuário defina se uma execução deve ser apenas de teste ou não, podemos simplesmente fazer a execução de teste por padrão. 

Embora esse valor padrão seja frequentemente um valor estático, codificado diretamente, não precisa ser assim. Ele pode ser determinado dinamicamente com base em outras propriedades do sistema. Aproveitar a determinação dinâmica pode simplificar ainda mais sua configuração. 

Para contextualizar, considere os seguintes exemplos de valores padrão dinâmicos. Um sistema computacionalmente intensivo pode normalmente decidir quantas threads de computação implantar por meio de um controle de configuração. Seu padrão dinâmico implanta tantas threads quanto o sistema (ou contêiner) tem núcleos de execução. Nesse caso, um único padrão estático não é útil. Padrão dinâmico significa que não precisamos perguntar ao usuário para determinar o número correto de threads para o sistema implantar em uma plataforma específica. Da mesma forma, um binário Java implantado sozinho em um contêiner pode ajustar automaticamente seus limites de heap dependendo da memória disponível no contêiner. Esses dois exemplos de padrões dinâmicos refletem implantações comuns. Se você precisar restringir o uso de recursos, é útil poder substituir os padrões dinâmicos na configuração. 

Os padrões dinâmicos implementados podem não funcionar para todos. Com o tempo, os usuários podem preferir abordagens diferentes e pedir maior controle sobre os padrões dinâmicos. Se uma parte significativa dos usuários de configuração relatar problemas com os padrões dinâmicos, é provável que a lógica de decisão não corresponda mais aos requisitos de sua base de usuários atual. Considere implementar melhorias abrangentes que permitam que seus padrões dinâmicos funcionem sem exigir controles de configuração adicionais. Se apenas uma pequena fração dos usuários estiver insatisfeita, eles podem se sair melhor definindo manualmente as opções de configuração. Implementar mais complexidade no sistema cria mais trabalho para os usuários (por exemplo, aumento da carga cognitiva para ler a documentação). 

Ao escolher respostas padrão para perguntas opcionais, independentemente de optar por padrões estáticos ou dinâmicos, pense cuidadosamente sobre o impacto de sua escolha. A experiência mostra que a maioria dos usuários usará o padrão, então isso é tanto uma oportunidade quanto uma responsabilidade. Você pode sutilmente direcionar as pessoas na direção certa, mas designar o padrão errado pode causar muito dano. Por exemplo, considere os padrões de configuração e seu impacto fora da ciência da computação. Países onde o padrão para doadores de órgãos é a opção de adesão (e os indivíduos podem optar por sair se desejarem) têm proporções dramaticamente maiores de doadores de órgãos do que países com uma opção padrão de saída. Selecionar simplesmente um padrão específico tem um impacto profundo nas opções médicas em todo o sistema. 

Algumas perguntas opcionais começam sem um caso de uso claro. Você pode querer remover essas perguntas completamente. Um grande número de perguntas opcionais pode confundir o usuário, então você deve adicionar controles de configuração apenas quando motivado por uma necessidade real. Por fim, se a linguagem de configuração usar o conceito de herança, é útil poder reverter para o valor padrão para qualquer pergunta opcional nas configurações folha. 

Escapando da Simplicidade

Até agora, discutimos a redução da configuração de um sistema à sua forma mais simples. No entanto, o sistema de configuração pode precisar levar em conta também os usuários avançados. Voltando à nossa analogia do chá, e se realmente precisarmos deixar o chá em infusão por uma duração específica?  

Uma estratégia para acomodar usuários avançados é encontrar o denominador comum mais baixo do que os usuários regulares e avançados requerem, e estabelecer esse nível de complexidade como padrão. A desvantagem é que essa decisão afeta a todos; até mesmo os casos de uso mais simples agora precisam ser considerados em termos de baixo nível. 

Ao pensar na configuração em termos de substituições opcionais do comportamento padrão, o usuário configura o “chá verde” e, em seguida, adiciona “deixar o chá em infusão por cinco minutos”. Neste modelo, a configuração padrão ainda é de alto nível e próxima dos objetivos do usuário, mas o usuário pode ajustar detalhes de baixo nível. Esta abordagem não é nova. Podemos fazer paralelos com linguagens de programação de alto nível como C++ ou Java, que permitem que os programadores incluam instruções de máquina (ou VM) em código, de outra forma, escrito na linguagem de alto nível. Em alguns softwares para consumidores, vemos telas com opções avançadas que podem oferecer um controle mais refinado do que a visualização típica. 

É útil pensar na otimização para o total de horas gastas configurando em toda a organização. Considere não apenas o ato de configuração em si, mas também a paralisia de decisão que os usuários podem experimentar quando apresentados com muitas opções, o tempo necessário para corrigir a configuração após tomar um rumo errado, a taxa mais lenta de mudança devido a menor confiança e muito mais. Ao considerar alternativas de design de configuração, a opção que realiza uma configuração complexa em menos, mas significativamente mais difíceis etapas podem ser preferível se isso tornar o suporte aos casos de uso mais comuns significativamente mais fácil. 

Se você descobrir que mais do que um pequeno subconjunto de seus usuários precisa de uma configuração complexa, pode ter identificado incorretamente os casos de uso comuns. Se for esse o caso, reavalie as suposições iniciais do produto para o seu sistema e realize pesquisas adicionais com os usuários. 

Mecânica da Configuração

Nossa discussão até este ponto abordou a filosofia da configuração. Esta seção muda o foco para a mecânica de como um usuário interage com a configuração.  

Separar configuração e dados resultantes

Qual linguagem usar para armazenar a configuração é uma pergunta inevitável. Você poderia optar por ter dados puros, como em um arquivo INI, YAML ou XML. Alternativamente, a configuração poderia ser armazenada em uma linguagem de nível mais alto que permite uma configuração muito mais flexível. 

Fundamentalmente, todas as perguntas feitas ao usuário se reduzem a informações estáticas. Isso pode incluir respostas claramente estáticas para perguntas como “Quantas threads devem ser usadas?” Mas até mesmo “Qual função deve ser usada para cada solicitação?” é apenas uma referência estática a uma função. 

Para responder à antiga questão de se a configuração é código ou dados, nossa experiência mostrou que ter ambos, código e dados, mas separá-los, é o ideal. A infraestrutura do sistema deve operar com dados estáticos simples, que podem estar em formatos como Protocol Buffers, YAML ou JSON. Essa escolha não implica que o usuário precise interagir diretamente com dados puros. Os usuários podem interagir com uma interface de nível mais alto que gera esses dados. No entanto, esse formato de dados pode ser usado por APIs que permitem o empilhamento adicional de sistemas e automação. 

Essa interface de alto nível pode ser quase qualquer coisa. Pode ser uma linguagem de alto nível como uma Linguagem Específica de Domínio (DSL) baseada em Python, Lua, ou linguagens desenvolvidas para um propósito específico, como Jsonnet (que discutiremos em mais detalhes no Capítulo 15). Podemos pensar em tal interface como uma compilação, semelhante ao tratamento dado ao código C++. A interface de alto nível também pode ser nenhuma linguagem, com a configuração sendo inserida por meio de uma interface de usuário web. 

Iniciar com uma interface de configuração deliberadamente separada de sua representação de dados estáticos significa que o sistema tem flexibilidade para implantação. Diversas organizações podem ter diferentes normas culturais ou requisitos de produto (como usar linguagens específicas dentro da empresa ou precisar externalizar a configuração para os usuários finais), e um sistema tão versátil pode ser adaptado para atender a diversos requisitos de configuração. Esse sistema também pode facilmente oferecer suporte a múltiplos idiomas. Consulte a Figura 14-2. 

Essa separação pode ser completamente invisível para o usuário. O caminho comum do usuário pode ser editar arquivos na linguagem de configuração enquanto tudo mais acontece nos bastidores. Por exemplo, uma vez que o usuário envia alterações para o sistema, a configuração recém-armazenada é automaticamente compilada em dados brutos. 

 

Figura 14-2. Fluxo de configuração com uma interface de configuração separada e infraestrutura de dados de configuração. Observe que a interface web geralmente também exibe a configuração atual, tornando o relacionamento bidirecional. 

Depois que os dados de configuração estática são obtidos, eles também podem ser usados em análises de dados. Por exemplo, se os dados de configuração gerados estiverem no formato JSON, eles podem ser carregados no PostgreSQL e analisados com consultas de banco de dados. Como proprietário da infraestrutura, você pode então consultar rapidamente e facilmente quais parâmetros de configuração estão sendo usados e por quem. Essa consulta é útil para identificar recursos que você pode remover ou medir o impacto de uma opção com defeito. 

Ao consumir os dados finais de configuração, será útil também armazenar metadados sobre como a configuração foi ingerida. Por exemplo, se você souber que os dados vieram de um arquivo de configuração em Jsonnet ou tiver o caminho completo para o original antes de ser compilado em dados, você pode rastrear os autores da configuração. 

Também é aceitável que a linguagem de configuração seja um dado estático. Por exemplo, tanto sua infraestrutura quanto sua interface podem usar JSON simples. No entanto, evite o acoplamento rígido entre o formato de dados que você usa como interface e o formato de dados que você usa internamente. Por exemplo, você pode usar uma estrutura de dados internamente que contenha a estrutura de dados consumida da configuração. A estrutura de dados interna também pode conter dados completamente específicos de implementação que nunca precisam ser expostos fora do sistema. 

Importância das ferramentas

As ferramentas podem fazer a diferença entre um pesadelo caótico e um sistema sustentável e escalável, mas muitas vezes são negligenciadas ao projetar sistemas de configuração. Esta seção discute as principais ferramentas que devem estar disponíveis para um sistema de configuração ideal. 

Validação semântica  

Embora a maioria das linguagens ofereça validação sintática pronta para uso, não negligencie a validação semântica. Mesmo que sua configuração seja sintaticamente válida, é provável que ela faça coisas úteis? Ou o usuário fez referência a um diretório inexistente (devido a um erro de digitação), ou precisa de mil vezes mais RAM do que realmente tem (porque as unidades não são o que o usuário esperava)? 

Validar que a configuração é semanticamente significativa, até onde for possível, pode ajudar a prevenir falhas e diminuir os custos operacionais. Para cada possível configuração incorreta, devemos nos perguntar se poderíamos evitá-la no momento em que o usuário confirma a configuração, em vez de depois que as alterações são enviadas. 

Sintaxe da configuração 

Embora seja fundamental garantir que a configuração alcance o que o usuário deseja, também é importante remover obstáculos mecânicos. Do ponto de vista da sintaxe, a linguagem de configuração deve oferecer o seguinte: 

Realce de sintaxe em editores (usados dentro da empresa) 

Muitas vezes, você já resolveu isso reutilizando uma linguagem existente. No entanto, linguagens específicas de domínio podem ter “açúcar sintático” adicional que pode se beneficiar de realce especializado.  

Linter 

Use um linter para identificar inconsistências comuns no uso da linguagem. Pylint é um exemplo popular de linguagem. 

Formatador automático de sintaxe 

A padronização integrada minimiza discussões relativamente menos importantes sobre formatação e diminui a carga cognitiva conforme os colaboradores alternam entre projetos. A formatação padrão também pode permitir uma edição automática mais fácil, o que é útil em sistemas usados amplamente dentro de uma grande organização. Exemplos de autoformatadores em linguagens existentes incluem clang-format e autopep8. 

Essas ferramentas permitem que os usuários escrevam e editem configurações com a confiança de que sua sintaxe está correta. Uma indentação incorreta em configurações orientadas por espaços em branco pode ter potencialmente grandes consequências – algumas das quais a formatação padrão pode prevenir. 

Propriedade e rastreamento de mudanças

Porque a configuração pode potencialmente impactar sistemas críticos de empresas e instituições, é importante garantir um bom isolamento de usuário e entender quais mudanças ocorreram no sistema. Como mencionado no Capítulo 10, uma cultura eficaz de postmortem evita culpar indivíduos. No entanto, é útil tanto durante um incidente quanto durante a realização de um postmortem saber quem alterou uma configuração e entender como a mudança de configuração impactou o sistema. Isso é válido tanto se o incidente for devido a um acidente ou a um ator malicioso. 

Cada trecho de configuração para o sistema deve ter um proprietário claro. Por exemplo, se você usar arquivos de configuração, seus diretórios podem ser de propriedade de um único grupo de produção. Se os arquivos em um diretório só puderem ter um único proprietário, é muito mais fácil rastrear quem faz alterações. 

Versionar a configuração, independentemente de como isso é feito, permite que você volte no tempo para ver como a configuração estava em determinado momento. Adicionar arquivos de configuração a um sistema de controle de versão, como Subversion ou Git, é uma prática comum hoje em dia, mas essa prática é igualmente importante para a configuração ingerida por uma interface web ou APIs remotas. Você também pode desejar ter um acoplamento mais estreito entre a configuração e o software sendo configurado. Ao fazer isso, você pode evitar configurar inadvertidamente recursos que ainda não estão disponíveis ou não são mais suportados no software. 

Em uma nota relacionada, é útil (e às vezes necessário) registrar tanto as mudanças na configuração quanto a aplicação resultante no sistema. O simples ato de realizar um novo commit de uma versão da configuração nem sempre significa que a configuração é aplicada diretamente (mais sobre isso depois). Quando uma mudança na configuração do sistema é suspeita como a causa durante uma resposta a incidentes, é útil ser capaz de determinar rapidamente o conjunto completo de edições na configuração que foram feitas na alteração. Isso permite reversões confiantes e a capacidade de notificar as partes cujas configurações foram impactadas. 

Aplicação segura de mudança na configuração

Como discutido anteriormente, a configuração é uma maneira fácil de fazer grandes alterações na funcionalidade do sistema, mas muitas vezes não é testada unitariamente ou mesmo facilmente testável. Como queremos evitar incidentes de confiabilidade, devemos examinar o que significa a aplicação segura de uma mudança na configuração. 

Para que uma mudança na configuração seja segura, ela deve ter três propriedades principais: 

  • A capacidade de ser implantada gradualmente, evitando uma mudança do tipo tudo-ou-nada. 
  • A capacidade de reverter a mudança se ela se mostrar perigosa. 
  • Reversão automática (ou, no mínimo, a capacidade de interromper o progresso) se a mudança levar à perda de controle do operador. 

Ao implantar uma nova configuração, é importante evitar uma implantação global de uma só vez. Em vez disso, implante a nova configuração gradualmente – fazê-lo permite detectar problemas e abortar uma implantação problemática antes de causar uma falha completa. Esta é uma das razões pelas quais ferramentas como o Kubernetes utilizam uma estratégia de atualização gradual para atualizar o software ou a configuração, em vez de atualizar todos os pods de uma só vez. (Veja o Capítulo 16 para discussões relacionadas.) 

A capacidade de reverter é importante para diminuir a duração de incidentes. Reverter a configuração problemática pode mitigar uma falha muito mais rapidamente do que tentar corrigi-la com um reparo temporário – há uma confiança inerentemente menor de que um reparo melhorará as coisas. 

Para ser capaz de avançar e retroceder na configuração, ela deve ser hermética. Uma configuração que requer recursos externos que podem mudar fora de seu ambiente hermético pode ser muito difícil de reverter. Por exemplo, uma configuração armazenada em um sistema de controle de versão que referência dados em um sistema de arquivos de rede não é hermética. 

Por último, mas não menos importante, o sistema deve ser especialmente cuidadoso ao lidar com mudanças que possam levar a uma perda repentina de controle do operador. Em sistemas desktop, alterações na resolução da tela frequentemente geram uma contagem regressiva e um reset se o usuário não confirmar as alterações. Isso ocorre porque uma configuração incorreta do monitor pode impedir o usuário de reverter a alteração. Da mesma forma, é comum os administradores de sistema se bloquearem acidentalmente fora do sistema que estão configurando no momento. 

Esses princípios não são exclusivos da configuração e se aplicam a outros métodos de alteração de sistemas implantados, como a atualização de binários ou a implantação de novos conjuntos de dados. 

Conclusão 

Mudanças triviais na configuração podem impactar um sistema de produção de maneiras dramáticas, portanto, precisamos projetar a configuração de forma deliberada para mitigar esses riscos. O design da configuração envolve aspectos tanto do design de API quanto do design de UI e deve ser intencional – não apenas um efeito colateral da implementação do sistema. Separar a configuração em filosofia e mecânica nos ajuda a ganhar clareza ao projetar sistemas internos e nos permite delimitar corretamente a discussão. 

Aplicar essas recomendações requer tempo e diligência. Para um exemplo de como aplicamos esses princípios na prática, consulte o artigo da ACM Queue sobre o Serviço de Análise de Canários. Ao projetar esse sistema interno prático, passamos cerca de um mês tentando reduzir as perguntas obrigatórias e encontrar boas respostas para as perguntas opcionais. Nossos esforços resultaram em um sistema de configuração simples. Como era fácil de usar, foi amplamente adotado internamente. Vimos pouca necessidade de suporte ao usuário – como os usuários podem entender facilmente o sistema, eles podem fazer alterações com confiança. Claro, não eliminamos completamente as configurações incorretas e o suporte ao usuário, nem esperamos fazê-lo. 

Experimente agora, grátis!