
Meu nome é Dmitry Sugrobov, sou desenvolvedor na "Leroy Merlin". Neste artigo, falarei sobre a importância do Helm, como ele facilita o trabalho com Kubernetes, o que mudou na terceira versão e como usá-lo para atualizar aplicativos em produção sem interrupções.
Este é um resumo inspirado na apresentação da conferência by — se você não quiser ler, assista ao vídeo.

Por que usamos Kubernetes em produção
"Leroy Merlin" é líder no mercado de varejo DIY na Rússia e Europa. Nossa empresa conta com mais de cem desenvolvedores, 33.000 funcionários internos e um grande número de pessoas que visitam as lojas e o site. Para fazer todos eles felizes, decidimos adotar abordagens padrão na indústria. Desenvolver novos aplicativos usando arquitetura de microsserviços; usar contêineres para isolar ambientes e garantir a entrega correta; e usar Kubernetes para orquestração. O custo do uso de orquestradores está caindo rapidamente: há cada vez mais engenheiros no mercado dominando essa tecnologia, e surgem provedores que oferecem Kubernetes como serviço.
Tudo o que Kubernetes faz, é claro, pode ser feito de outras maneiras, como utilizando scripts com Jenkins e docker-compose, mas por que complicar a vida se existe uma solução pronta e confiável? Portanto, chegamos ao Kubernetes e já o utilizamos em produção há um ano. Agora temos vinte e quatro clusters Kubernetes, o mais antigo deles tem mais de um ano e abriga cerca de duzentos pods.
A maldição do grande número de arquivos YAML no Kubernetes
Para iniciar um microsserviço no Kubernetes, criaremos pelo menos cinco arquivos YAML: para Deployment, Service, Ingress, ConfigMap e Secrets — e os enviaremos para o cluster. Para o próximo aplicativo, escreveremos o mesmo conjunto de arquivos YAML, para o terceiro mais um e assim por diante. Multiplicando o número de documentos pelo número de ambientes, já obtemos centenas de arquivos, sem contar os ambientes dinâmicos.

Adam Reese, mantenedor principal do Helm, introduziu o conceito de "", que se parece com isto:
- Copiar YAML — copiar arquivo YAML.
- Colar YAML — colá-lo.
- Corrigir recuos — ajustar os recuos.
- Repetir — repetir novamente.
Essa abordagem funciona, mas envolve copiar arquivos YAML muitas vezes. Para alterar esse ciclo, foi criado o Helm.
O que é Helm
Em primeiro lugar, o Helm é — gerenciador de pacotes, ajudando a encontrar e instalar os programas necessários. Para instalar, por exemplo, o MongoDB, não é preciso acessar o site oficial e baixar os binários, basta executar o comando helm install stable/mongodb.
Em segundo lugar, o Helm é um template engine, que parametriza arquivos. Vamos voltar à situação dos arquivos YAML no Kubernetes. É mais fácil escrever o mesmo arquivo YAML, adicionar alguns placeholders, onde o Helm irá inserir os valores. Ou seja, em vez de um grande conjunto de YAMLs, haverá um conjunto de templates, onde os valores necessários serão inseridos no momento certo.
Em terceiro lugar, o Helm é um especialista em implantação. Com ele, é possível instalar, reverter e atualizar aplicativos. Vamos entender como fazer isso.

Como usar o Helm para implantar seus próprios aplicativos
Vamos instalar o cliente Helm no computador, seguindo a documentação oficial . Em seguida, criaremos um conjunto de arquivos YAML. Em vez de especificar valores concretos, deixaremos placeholders, que no futuro o Helm preencherá com as informações. Esse conjunto de arquivos é chamado de Helm chart. É possível enviá-lo para o cliente de linha de comando Helm de três maneiras:
- especificar a pasta com os templates;
- empacotar em um arquivo .tar e indicar isso;
- colocar o template em um repositório remoto e adicionar o link do repositório no cliente do Helm.
Também precisamos de um arquivo com os valores — values.yaml. Os dados desse arquivo serão inseridos no template. Vamos criá-lo também.

Na segunda versão do Helm, há um aplicativo de servidor adicional — Tiller. Ele fica fora do Kubernetes e aguarda solicitações do cliente Helm, e quando acionado, insere os valores necessários no template e envia para o Kubernetes.

O Helm 3 é mais simples: em vez de processar templates no servidor, as informações agora são processadas completamente pelo lado do cliente Helm e enviadas diretamente para a API do Kubernetes. Essa simplificação aumenta a segurança do cluster e facilita o processo de lançamento.
Como tudo isso funciona
Executamos o comando helm install. Vamos especificar o nome da versão do aplicativo, fornecer o caminho para o values.yaml. No final, indicamos o repositório onde o chart está localizado e o nome do chart. Neste exemplo, isso é "lmru" e "bestchart", respectivamente.
helm install --name bestapp --values values.yaml lmru/bestchart
A execução do comando só é possível uma vez, em execuções subsequentes, em vez de install deveria usar upgrade. Para simplificar, podemos executar o comando upgrade com uma chave adicional --install. Na primeira execução, o Helm enviará um comando para instalar a versão, e posteriormente a atualizará.
helm upgrade --install bestapp --values values.yaml lmru/bestchart
Armadilhas na implantação de novas versões do aplicativo com Helm
Neste momento da história, estou jogando com a plateia no 'Quem quer ser um milionário', e estamos descobrindo como fazer o Helm atualizar a versão do aplicativo. .
Quando estudei o funcionamento do Helm, fiquei surpreso com o comportamento estranho ao tentar atualizar versões de aplicativos em execução. O código do aplicativo foi atualizado, carreguei uma nova imagem no docker registry, enviei o comando para implantação – e nada aconteceu. Abaixo, algumas tentativas não tão bem-sucedidas para atualizar aplicativos. Estudando cada uma delas em detalhes, você começa a entender o funcionamento interno da ferramenta e os motivos desse comportamento não tão óbvio.
Método 1. Não alterar informações desde a última execução
Como diz Helm, 'os charts do Kubernetes podem ser grandes e complexos, portanto, o Helm tenta não modificar nada desnecessariamente'. Portanto, se você atualizar a versão mais recente da imagem do aplicativo no docker registry e executar o comando helm upgrade, nada irá acontecer. O Helm irá considerar que nada mudou e não será necessário enviar um comando ao Kubernetes para atualizar o aplicativo.
Aqui e adiante, a tag latest é mostrada apenas como um exemplo. Ao especificar essa tag, o Kubernetes fará o download da imagem do docker registry todas as vezes, independentemente do parâmetro imagePullPolicy. O uso de latest em produção não é recomendado e pode causar efeitos colaterais.
Método 2. Atualizar o LABEL na imagem
Conforme mencionado na mesma , 'o Helm atualizará o aplicativo somente se ele mudou desde o último release'. Uma opção lógica para isso seria atualizar a etiqueta LABEL na própria imagem docker. No entanto, o Helm não olha para as imagens de aplicativos e não tem ideia de quaisquer mudanças nelas. Portanto, ao atualizar labels na imagem, o Helm não ficará ciente e o comando de atualização do aplicativo no Kubernetes não será enviado.
Método 3. Usar a chave --force

Vamos consultar os manuais e procurar pela chave necessária. A que mais se encaixa é a chave --force. Apesar do nome sugestivo, o comportamento é diferente do esperado. Em vez de forçar a atualização do aplicativo, seu verdadeiro objetivo é restaurar uma versão do release que está com status FAILED. Se não usar essa chave, será necessário executar os comandos sequencialmente. helm delete && helm install --replace. Em vez disso, é sugerido usar a chave --force, que automatiza a execução sequencial desses comandos. Mais informações neste . Para que o Helm realmente atualize a versão do aplicativo, infelizmente, essa chave não será apropriada.
Método 4. Alterar labels diretamente no Kubernetes

Atualizar label diretamente no cluster usando o comando kubectl edit — é uma má ideia. Essa ação levará a uma inconsistência entre as informações do aplicativo em execução e o que foi inicialmente enviado para o deploy. O comportamento do Helm durante o deploy nesse caso é diferente de sua versão: o Helm 2 não fará nada, enquanto o Helm 3 implantará uma nova versão do aplicativo. Para entender a razão, é necessário compreender como o Helm funciona.
Como funciona o Helm
Para determinar se o aplicativo mudou desde o último release, o Helm pode se basear em:
- aplicativo em funcionamento no Kubernetes;
- novo values.yaml e chart atual;
- informação interna do Helm sobre os releases.
Para os curiosos: onde o Helm armazena informações internas sobre os releases?Ao executar o comando helm history, obteremos todas as informações sobre versões instaladas através do Helm.

Há também informações detalhadas sobre os templates e valores enviados. Podemos solicitá-las:

Na segunda versão do Helm, essas informações estão no mesmo namespace onde o Tiller está em execução (por padrão — kube-system), em um ConfigMap marcado com o rótulo "OWNER=TILLER":

Com o surgimento da terceira versão do Helm, as informações foram transferidas para secrets, mas no mesmo namespace onde o aplicativo está em execução. Isso tornou possível executar simultaneamente vários aplicativos em diferentes namespaces com o mesmo nome de release. Na segunda versão, isso era uma grande dor de cabeça, pois os namespaces são isolados, mas podem influenciar uns aos outros.

No Helm 2, ao tentar entender se é necessária uma atualização, são usados apenas duas fontes de informações: o que lhe foi fornecido atualmente e a informação interna sobre os releases, que está no ConfigMap.

O Helm 3 utiliza a estratégia de mesclagem em três caminhos: além das informações existentes, ele considera também o aplicativo que está rodando no Kubernetes.

Por essa razão, a versão antiga do Helm não fará nada, pois não leva em conta as informações do aplicativo no cluster, enquanto o Helm 3 receberá as mudanças e implantará o novo aplicativo.
Método 5. Usar a chave —recreate-pods
Com a chave --recreate-pods é possível atingir o que originalmente se pretendia conseguir com a chave --force. Os contêineres serão reiniciados e, conforme a política imagePullPolicy: Always para a tag latest (conforme mencionado na nota acima), o Kubernetes fará o download e iniciará a nova versão da imagem. Isso não será feito da melhor maneira: sem considerar o StrategyType do deployment, desligará abruptamente todas as instâncias antigas do aplicativo e iniciará novas. Durante o reinício, o sistema não funcionará, os usuários sentirão os efeitos.
No próprio Kubernetes, um problema similar também existia por muito tempo. E agora, quatro anos após a sua descoberta, , o problema foi corrigido, e a partir da versão 1.15 do Kubernetes, surge a possibilidade de rolling-restart dos pods.
O Helm simplesmente desliga todos os aplicativos e inicia novos contêineres lado a lado. Isso não pode ser feito em produção para evitar a inatividade do aplicativo. Isso é necessário apenas para fins de desenvolvimento e pode ser executado apenas em ambientes de estágio.
Como atualizar a versão do aplicativo usando o Helm?
Vamos alterar os valores enviados ao Helm. Normalmente, esses são os valores que substituem a tag da imagem. No caso de latest, comumente usado para ambientes não produtivos, a informação que pode ser alterada é a anotação, que é inútil para o próprio Kubernetes, mas servirá como um sinal para o Helm de que o aplicativo precisa ser atualizado. Opções para preenchimento do valor da anotação:
- Valor aleatório usando a função padrão —
{{ randAlphaNum 6 }}.
Há um detalhe: após cada deploy utilizando um chart com essa variável, o valor da anotação será único, e o Helm acreditará que houve alterações. Assim, estaremos sempre reiniciando o aplicativo, mesmo que não tenhamos mudado sua versão. Isso não é crítico, pois não haverá inatividade, mas ainda assim é inconveniente. - Inserir a data e hora atuais —
{{ .Release.Date }}.
A opção é semelhante ao valor aleatório, mas com uma variável sempre única. - Uma maneira mais correta é usar somas de verificação. Esta é a imagem SHA ou o SHA do último commit no git —
{{ .Values.sha }}.
Elas devem ser calculadas e enviadas ao cliente Helm no lado chamador, por exemplo no Jenkins. Se a aplicação mudar, a soma de verificação também mudará. Portanto, o Helm atualizará a aplicação apenas quando necessário.
Resumindo nossas tentativas
- O Helm faz alterações da maneira menos invasiva, portanto qualquer mudança no nível da imagem da aplicação no Docker Registry não resultará em uma atualização: após a execução do comando, nada acontecerá.
- Chave
--forceé usado para recuperar releases problemáticos e não está relacionado a atualizações forçadas. - Chave
--recreate-podsatualizará o aplicativo forçadamente, mas o fará de maneira brusca: desligará todos os contêineres abruptamente. Isso prejudicará os usuários, e não é aconselhável fazer isso em produção. - Fazer alterações diretamente no cluster Kubernetes usando o comando
kubectl editnão é recomendado: isso quebrará a consistência e o comportamento variará dependendo da versão do Helm. - Com o lançamento da nova versão do Helm, surgiram muitas nuances. As Issues no repositório do Helm estão descritas de forma clara e ajudarão a entender os detalhes.
- Adicionar uma anotação modificável ao chart tornará mais flexível. Isso permitirá implantar a aplicação corretamente, sem inatividade.
Uma ideia do tipo "paz mundial", aplicável em todas as áreas da vida: leia as instruções antes de usar, não depois. Apenas sabendo de todas as informações, será possível construir sistemas confiáveis e fazer os usuários felizes.
Outros links sobre o tema:
Esta apresentação foi feita pela primeira vez na by Mail.ru Cloud Solutions. Veja outras apresentações e inscreva-se para receber atualizações de eventos no Telegram .
Fonte: habr.com
