Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

8 de abril na conferência Santo HighLoad++ 2019, no âmbito da secção “DevOps e Operações”, foi entregue o relatório “Expandindo e complementando Kubernetes”, na qual participaram três colaboradores da empresa Flant. Nele falamos sobre inúmeras situações em que queríamos ampliar e complementar as capacidades do Kubernetes, mas para as quais não encontramos uma solução pronta e simples. Temos as soluções necessárias na forma de projetos Open Source, e esta palestra também é dedicada a eles.

Por tradição, temos o prazer de apresentar vídeo da reportagem (50 minutos, muito mais informativo que o artigo) e o resumo principal em forma de texto. Ir!

Núcleo e adições em K8s

O Kubernetes está mudando o setor e as abordagens de administração que já foram estabelecidas há muito tempo:

  • Graças a ele abstrações, não operamos mais com conceitos como configurar uma configuração ou executar um comando (Chef, Ansible...), mas utilizamos agrupamento de containers, serviços, etc.
  • Podemos preparar aplicações sem pensar nas nuances do site específico, onde será lançado: bare metal, nuvem de um dos provedores, etc.
  • Com K8s você nunca esteve tão acessível melhores práticas na organização da infraestrutura: técnicas de escalonamento, autocorreção, tolerância a falhas, etc.

No entanto, é claro, nem tudo é tão tranquilo: o Kubernetes também trouxe seus próprios novos desafios.

Kubernetes não é uma colheitadeira que resolve todos os problemas de todos os usuários. núcleo Kubernetes é responsável apenas por um conjunto de funções mínimas necessárias que estão presentes em cada conjunto:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

O núcleo do Kubernetes define um conjunto básico de primitivos para agrupar contêineres, gerenciar tráfego e assim por diante. Falamos sobre eles com mais detalhes em relatório há 2 anos.

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Por outro lado, o K8s oferece grandes oportunidades para expandir as funções disponíveis, o que ajuda a fechar outras - específico - necessidades do usuário. As adições ao Kubernetes são de responsabilidade dos administradores de cluster, que devem instalar e configurar tudo o que for necessário para deixar seu cluster “na forma certa” [para resolver seus problemas específicos]. Que tipo de acréscimos são esses? Vejamos alguns exemplos.

Exemplos de complementos

Depois de instalar o Kubernetes, podemos nos surpreender com o fato de que a rede tão necessária para a interação dos pods dentro de um nó e entre nós não funciona por si só. O kernel do Kubernetes não garante as conexões necessárias; em vez disso, ele determina a rede interface (CNI) para complementos de terceiros. Devemos instalar um desses add-ons, que será responsável pela configuração da rede.

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Um exemplo próximo são as soluções de armazenamento de dados (disco local, dispositivo de bloco de rede, Ceph...). Inicialmente eles estavam no núcleo, mas com o advento CSI a situação muda para algo semelhante ao já descrito: a interface está em Kubernetes e sua implementação está em módulos de terceiros.

Outros exemplos incluem:

  • Ingresso-controladores (veja a crítica deles em nosso artigo recente).
  • gerente de certificação:

    Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

  • operadores é uma classe completa de complementos (que inclui o gerenciador de certificados mencionado), eles definem primitivo(s) e controlador(es). A lógica de seu trabalho é limitada apenas pela nossa imaginação e nos permite transformar componentes de infraestrutura prontos (por exemplo, um SGBD) em primitivos, com os quais é muito mais fácil trabalhar (do que com um conjunto de contêineres e suas configurações). Um grande número de operadores foi escrito - mesmo que muitos deles ainda não estejam prontos para produção, é apenas uma questão de tempo:

    Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

  • Métricas - outra ilustração de como o Kubernetes separou a interface (API Metrics) da implementação (complementos de terceiros, como adaptador Prometheus, agente de cluster Datadog...).
  • Para monitoramento e estatísticas, onde na prática não são apenas necessários Prometeu e Grafana, mas também kube-state-metrics, node-exporter, etc.

E esta não é uma lista completa de adições... Por exemplo, nós da empresa Flant atualmente instalamos 29 adições (todos criando um total de 249 objetos Kubernetes). Simplificando, não podemos ver a vida de um cluster sem acréscimos.

Automação

Os operadores são projetados para automatizar operações rotineiras que encontramos todos os dias. Aqui estão exemplos da vida real para os quais escrever um operador seria uma excelente solução:

  1. Existe um registro privado (ou seja, exigindo login) com imagens para o aplicativo. Supõe-se que cada pod receba um segredo especial que permite a autenticação no registro. Nossa tarefa é garantir que esse segredo seja encontrado no namespace para que os pods possam baixar imagens. Pode haver muitos aplicativos (cada um dos quais precisa de um segredo) e é útil atualizar os próprios segredos regularmente, de modo que a opção de expor os segredos manualmente seja eliminada. É aqui que o operador vem em socorro: criamos um controlador que aguardará o aparecimento do namespace e, com base nesse evento, adicionará um segredo ao namespace.
  2. Por padrão, o acesso dos pods à Internet é proibido. Mas às vezes pode ser necessário: é lógico que o mecanismo de permissão de acesso funcione de forma simples, sem exigir habilidades específicas, por exemplo, pela presença de um determinado rótulo no namespace. Como a operadora pode nos ajudar aqui? É criado um controlador que aguarda o rótulo aparecer no namespace e adiciona a política apropriada para acesso à Internet.
  3. Uma situação semelhante: suponha que precisássemos adicionar um certo mancha, se tiver um rótulo semelhante (com algum tipo de prefixo). As ações com o operador são óbvias...

Em qualquer cluster, as tarefas rotineiras devem ser resolvidas, e corretamente isso pode ser feito usando operadores.

Resumindo todas as histórias descritas, chegamos à conclusão que para um trabalho confortável no Kubernetes, você precisa: mas) instalar complementos, b) desenvolver operadores (para resolver tarefas administrativas diárias).

Como escrever uma declaração para Kubernetes?

Em geral, o esquema é simples:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

... mas então acontece que:

  • A API Kubernetes não é algo trivial e leva muito tempo para ser dominada;
  • a programação também não é para todos (a linguagem Go foi escolhida como linguagem preferida porque existe uma estrutura especial para ela - SDK do Operador);
  • A situação é semelhante com a própria estrutura.

A linha inferior: escrever um controlador (operador) tem que gastar recursos significativos para estudar material. Isso seria justificado para operadores “grandes” - digamos, para o SGBD MySQL. Mas se lembrarmos dos exemplos descritos acima (desvendar segredos, acessar pods à Internet...), que também queremos fazer corretamente, então entenderemos que o esforço despendido superará o resultado que precisamos agora:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Em geral, surge um dilema: gastar muitos recursos e encontrar a ferramenta certa para escrever declarações, ou fazê-lo à moda antiga (mas rapidamente). Para resolver isso – para encontrar um compromisso entre esses extremos – criamos nosso próprio projeto: operador shell (veja também seu anúncio recente no centro).

Operador Shell

Como ele funciona? O cluster possui um pod contendo um binário Go com um operador shell. Ao lado dele está um conjunto de ganchos (mais detalhes sobre eles - veja abaixo). O próprio operador shell assina certos desenvolvimentos na API Kubernetes, quando ocorre o lançamento dos ganchos correspondentes.

Como o operador shell sabe quais ganchos chamar em quais eventos? Esta informação é transmitida ao operador shell pelos próprios ganchos, e eles fazem isso de forma muito simples.

Um gancho é um script Bash ou qualquer outro arquivo executável que aceite um único argumento --config e responde com JSON. Este último determina quais objetos são de seu interesse e quais eventos (para esses objetos) devem ser respondidos:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Ilustrarei a implementação no operador shell de um de nossos exemplos - decomposição de segredos para acessar um registro privado com imagens de aplicativos. Consiste em duas etapas.

Prática: 1. Escreva um gancho

Em primeiro lugar, no gancho iremos processar --config, indicando que estamos interessados ​​em namespaces e, especificamente, no momento de sua criação:

[[ $1 == "--config" ]] ; then
  cat << EOF
{
  "onKubernetesEvent": [
    {
      "kind": "namespace",
      "event": ["add"]
    }
  ]
}
EOF
…

Como seria a lógica? Também bastante simples:

…
else
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  kubectl create -n ${createdNamespace} -f - << EOF
Kind: Secret
...
EOF
fi

O primeiro passo é descobrir qual namespace foi criado, e o segundo é criá-lo usando kubectl segredo para este namespace.

Prática: 2. Montando a imagem

Resta apenas passar o gancho criado para o operador shell - como fazer isso? O próprio operador shell vem como uma imagem Docker, então nossa tarefa é adicionar o gancho a um diretório especial nesta imagem:

FROM flant/shell-operator:v1.0.0-beta.1
ADD my-handler.sh /hooks

Resta montá-lo e empurrá-lo:

$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

O toque final é implantar a imagem no cluster. Para fazer isso, vamos escrever desenvolvimento:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1 # 1
      serviceAccountName: my-operator              # 2

Há dois pontos aos quais prestar atenção:

  1. indicação da imagem recém-criada;
  2. Este é um componente do sistema que (no mínimo) precisa de direitos para assinar eventos no Kubernetes e alocar segredos para namespaces, então criamos uma ServiceAccount (e um conjunto de regras) para o gancho.

Resultado - resolvemos nosso problema para parentes para Kubernetes de uma forma que cria um operador para decomposição de segredos.

Outros recursos do operador shell

Para limitar os objetos do tipo escolhido com os quais o gancho funcionará, eles podem ser filtrados, selecionando de acordo com determinados rótulos (ou usando matchExpressions):

"onKubernetesEvent": [
  {
    "selector": {
      "matchLabels": {
        "foo": "bar",
       },
       "matchExpressions": [
         {
           "key": "allow",
           "operation": "In",
           "values": ["wan", "warehouse"],
         },
       ],
     }
     …
  }
]

Forneceu mecanismo de desduplicação, que - usando um filtro jq - permite converter objetos JSON grandes em pequenos, onde permanecem apenas os parâmetros que queremos monitorar quanto a alterações.

Quando um gancho é chamado, o operador shell o passa dados do objeto, que pode ser usado para qualquer necessidade.

Os eventos que acionam ganchos não estão limitados aos eventos do Kubernetes: o operador shell fornece suporte para chamando ganchos por tempo (semelhante ao crontab em um agendador tradicional), bem como um evento especial no arranque. Todos esses eventos podem ser combinados e atribuídos ao mesmo gancho.

E mais dois recursos do operador shell:

  1. Funciona de forma assíncrona. Como um evento do Kubernetes (como a criação de um objeto) foi recebido, outros eventos (como a exclusão do mesmo objeto) podem ter ocorrido no cluster, e os ganchos precisam levar em conta isso. Se o gancho foi executado com erro, por padrão será lembrar até a conclusão bem-sucedida (esse comportamento pode ser alterado).
  2. Exporta Métricas para Prometheus, com o qual você pode entender se o operador shell está funcionando, descobrir o número de erros para cada gancho e o tamanho da fila atual.

Para resumir esta parte do relatório:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Instalando complementos

Para um trabalho confortável com o Kubernetes, também foi mencionada a necessidade de instalação de add-ons. Vou contar a vocês sobre isso usando o exemplo da trajetória de nossa empresa até como fazemos isso agora.

Começamos a trabalhar com Kubernetes com vários clusters, a única adição foi o Ingress. Precisava ser instalado de forma diferente em cada cluster, e fizemos diversas configurações YAML para diferentes ambientes: bare metal, AWS...

Como havia mais clusters, havia mais configurações. Além disso, melhoramos essas próprias configurações, e como resultado elas se tornaram bastante heterogêneas:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Para colocar tudo em ordem, começamos com um script (install-ingress.sh), que tomou como argumento o tipo de cluster no qual iremos implantar, gerou a configuração YAML necessária e a implementou no Kubernetes.

Em suma, o nosso caminho posterior e o raciocínio a ele associado foram os seguintes:

  • para trabalhar com configurações YAML, é necessário um mecanismo de template (nos primeiros estágios é simples sed);
  • com o aumento do número de clusters, surgiu a necessidade de atualização automática (a primeira solução foi colocar o script no Git, atualizar usando o cron e executá-lo);
  • um script semelhante foi necessário para o Prometheus (install-prometheus.sh), porém, destaca-se pelo fato de exigir muito mais dados de entrada, bem como seu armazenamento (no bom sentido - centralizado e em cluster), e alguns dados (senhas) podem ser gerados automaticamente:

    Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

  • o risco de implementar algo errado em um número crescente de clusters crescia constantemente, então percebemos que os instaladores (ou seja, dois scripts: para Ingress e Prometheus) foi necessário staging (várias ramificações no Git, vários crons para atualizá-los nos clusters correspondentes: estáveis ​​​​ou de teste);
  • с kubectl apply tornou-se difícil trabalhar com ele porque não é declarativo e só pode criar objetos, mas não pode tomar decisões sobre seu status/excluí-los;
  • Faltavam algumas funções que não havíamos implementado naquele momento:
    • controle total sobre o resultado das atualizações do cluster,
    • determinação automática de alguns parâmetros (entrada para scripts de instalação) com base em dados que podem ser obtidos do cluster (descoberta),
    • seu desenvolvimento lógico na forma de descoberta contínua.

Implementamos toda esta experiência acumulada no âmbito do nosso outro projeto - operador de complemento.

Operador de complemento

É baseado no já mencionado operador shell. Todo o sistema se parece com isto:

O seguinte é adicionado aos ganchos do operador shell:

  • armazenamento de valores,
  • Gráfico do leme,
  • componente que monitora o armazenamento de valores e - em caso de qualquer alteração - pede ao Helm para relançar o gráfico.

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Assim, podemos reagir a um evento no Kubernetes, lançar um gancho, e a partir deste gancho podemos fazer alterações no armazenamento, após o que o gráfico será baixado novamente. No diagrama resultante, separamos o conjunto de ganchos e o gráfico em um componente, que chamamos módulo:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Pode haver muitos módulos, e a eles adicionamos ganchos globais, um armazenamento de valores globais e um componente que monitora esse armazenamento global.

Agora, quando algo acontece no Kubernetes, podemos reagir usando um gancho global e mudar algo no armazenamento global. Esta mudança será notada e fará com que todos os módulos do cluster sejam implementados:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

Este esquema satisfaz todos os requisitos para instalação de complementos declarados acima:

  • Helm é responsável pela modelagem e declaratividade.
  • O problema da atualização automática foi resolvido usando um gancho global, que vai para o registro de acordo com uma programação e, se encontrar uma nova imagem do sistema lá, a lança (ou seja, “ele mesmo”).
  • O armazenamento de configurações no cluster é implementado usando Mapa de configuração, que contém os dados primários dos armazenamentos (na inicialização eles são carregados nos armazenamentos).
  • Problemas com geração de senha, descoberta e descoberta contínua foram resolvidos usando ganchos.
  • O staging é alcançado graças às tags, que o Docker suporta imediatamente.
  • O resultado é monitorado por meio de métricas pelas quais podemos entender o status.

Todo esse sistema é implementado na forma de um único binário em Go, chamado addon-operator. Isso faz com que o diagrama pareça mais simples:

Expansão e complementação do Kubernetes (visão geral e relatório em vídeo)

O principal componente neste diagrama é um conjunto de módulos (destacado em cinza abaixo). Agora podemos escrever um módulo para o complemento necessário com um pouco de esforço e ter certeza de que ele será instalado em cada cluster, será atualizado e responderá aos eventos necessários no cluster.

"Flant" usa operador de complemento em mais de 70 clusters Kubernetes. Status atual - versão alfa. Agora estamos preparando a documentação para lançar o beta, mas por enquanto no repositório exemplos disponíveis, com base no qual você pode criar seu próprio complemento.

Onde posso obter os módulos para o operador addon? Publicar nossa biblioteca é a próxima etapa para nós; planejamos fazer isso no verão.

Vídeos e slides

Vídeo da performance (~50 minutos):

Apresentação do relatório:

PS

Outras reportagens em nosso blog:

Você também pode estar interessado nas seguintes publicações:

Fonte: habr.com

Adicionar um comentário