Apresentando o operador de shell: tornando os operadores do Kubernetes ainda mais fáceis

Já existem artigos em nosso blog sobre os recursos dos operadores no Kubernetes e sobre como escrever um operador simples . Desta vez, queremos chamar a atenção para a nossa solução de código aberto, que leva a criação de operadores a um nível super fácil - familiarize-se com o operador de shell !

Porque


A idéia de um operador de shell é bastante simples: assine eventos de objetos Kubernetes e, ao receber esses eventos, inicie um programa externo, fornecendo informações sobre o evento:



A necessidade disso surgiu quando, durante a operação de clusters, começaram a aparecer pequenas tarefas que realmente queríamos automatizar da maneira correta. Todas essas pequenas tarefas foram resolvidas com a ajuda de scripts simples do bash, embora, como você sabe, os operadores sejam melhor escritos em Golang. Obviamente, investir em um desenvolvimento de operador em larga escala para cada tarefa tão pequena seria ineficiente.

Operador em 15 minutos


Vejamos um exemplo do que pode ser automatizado em um cluster Kubernetes e como o operador de shell ajudará. Um exemplo seria o seguinte: duplicar um segredo para acessar o registro do docker.

Os pods que usam imagens do registro privado devem conter no manifesto um link para um segredo com dados para acessar o registro. Esse segredo deve ser criado em cada espaço para nome antes da criação dos pods. É bem possível fazer isso manualmente, mas se configurarmos ambientes dinâmicos, haverá muito espaço para nome para um aplicativo. E se os aplicativos também não forem 2-3, o número de segredos se torna muito grande. E mais uma coisa sobre segredos: gostaria de alterar a chave para acessar o registro de tempos em tempos. Como resultado, operações manuais como solução são completamente ineficazes - você precisa automatizar a criação e atualização de segredos.

Fácil automação


Escreveremos um script de shell que será executado uma vez a cada N segundos e verificará o espaço de nomes em busca de um segredo e, se não houver nenhum segredo, ele será criado. A vantagem desta solução é que ela se parece com um script de shell no cron - uma abordagem clássica e compreensível. A desvantagem é que, no intervalo entre os lançamentos, um novo espaço para nome pode ser criado e, por algum tempo, permanecerá sem segredo, o que levará a erros no lançamento dos pods.

Automação com operador de shell


Para que nosso script funcione corretamente, o lançamento clássico do cron precisa ser substituído pelo lançamento quando o evento de espaço para nome é adicionado: nesse caso, você pode criar um segredo antes de usá-lo. Vamos ver como implementar isso usando o operador de shell.

Primeiro, vamos analisar o script. Os scripts em termos de operador de shell são chamados ganchos. Cada gancho na inicialização com o sinalizador --config informa o operador shell sobre suas ligações, ou seja, por quais eventos ele precisa ser lançado. No nosso caso, usaremos onKubernetesEvent :

 #!/bin/bash if [[ $1 == "--config" ]] ; then cat <<EOF { "onKubernetesEvent": [ { "kind": "namespace", "event":["add"] } ]} EOF fi 

É descrito aqui que estamos interessados ​​em eventos para adicionar ( add ) objetos do tipo namespace .

Agora você precisa adicionar o código que será executado quando o evento ocorrer:

 #!/bin/bash if [[ $1 == "--config" ]] ; then #  cat <<EOF { "onKubernetesEvent": [ { "kind": "namespace", "event":["add"] } ]} EOF else # : # ,  namespace  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH) #      kubectl create -n ${createdNamespace} -f - <<EOF apiVersion: v1 kind: Secret metadata: ... data: ... EOF fi 

Ótimo! O resultado é um script pequeno e bonito. Para "revivê-la", restam duas etapas: preparar a imagem e executá-la no cluster.

Preparando uma imagem com um gancho


Se você olhar o script, poderá ver que os jq kubectl e jq . Isso significa que a imagem deve ter o seguinte: our hook, um operador de shell que monitorará eventos e ativará o hook, bem como os comandos usados ​​pelo hook (kubectl e jq). O Hub.docker.com já possui uma imagem pronta na qual o operador shell, kubectl e jq são empacotados. Resta adicionar o gancho com um Dockerfile simples:

 $ cat Dockerfile FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9 ADD namespace-hook.sh /hooks $ docker build -t registry.example.com/my-operator:v1 . $ docker push registry.example.com/my-operator:v1 

Lançamento de cluster


Mais uma vez, vamos dar uma olhada no gancho e desta vez escrever quais ações e com quais objetos ele executa no cluster:

  1. Inscreve-se em eventos de espaço para nome
  2. cria um segredo em espaços para nome diferentes daquele em que está sendo executado.

Acontece que o pod em que nossa imagem será lançada deve ter permissões para essas ações. Isso pode ser feito criando sua própria ServiceAccount. A permissão deve ser feita na forma de ClusterRole e ClusterRoleBinding, porque estamos interessados ​​em objetos de todo o cluster.

A descrição final no YAML é mais ou menos assim:

 --- apiVersion: v1 kind: ServiceAccount metadata: name: monitor-namespaces-acc --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRole metadata: name: monitor-namespaces rules: - apiGroups: [""] resources: ["namespaces"] verbs: ["get", "watch", "list"] - apiGroups: [""] resources: ["secrets"] verbs: ["get", "list", "create", "patch"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: monitor-namespaces roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: monitor-namespaces subjects: - kind: ServiceAccount name: monitor-namespaces-acc namespace: example-monitor-namespaces 

Você pode executar a imagem montada na forma de uma implantação simples:

 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: my-operator spec: template: spec: containers: - name: my-operator image: registry.example.com/my-operator:v1 serviceAccountName: monitor-namespaces-acc 


Por conveniência, um espaço para nome separado é criado onde o operador shell será iniciado e os manifestos criados serão aplicados:

 $ kubectl create ns example-monitor-namespaces $ kubectl -n example-monitor-namespaces apply -f rbac.yaml $ kubectl -n example-monitor-namespaces apply -f deployment.yaml 


Isso é tudo: o operador do shell iniciará, se inscreverá nos eventos de criação do espaço para nome e iniciará o gancho quando necessário.



Assim, um simples script de shell se transformou em um operador real para o Kubernetes e funciona como parte integrante do cluster. E tudo isso - sem o processo complicado de desenvolver operadores na Golang:



Há outra ilustração sobre esse assunto ...


Revelaremos seu significado em mais detalhes em uma das seguintes publicações. ATUALIZADO (1 de maio de 2019): consulte “ Estendendo e expandindo o Kubernetes (revisão e relatório de vídeo) ”.

Filtragem


O rastreamento de objetos é bom, mas geralmente é necessário responder a alterações em algumas propriedades de um objeto , por exemplo, a uma alteração no número de réplicas em uma Implantação ou a uma alteração nos rótulos de um objeto.

Quando um evento chega, o operador de shell recebe o manifesto JSON do objeto. Você pode selecionar as propriedades de interesse para nós neste JSON e executar o gancho apenas quando elas mudarem. Para fazer isso, o campo jqFilter é jqFilter , onde você precisa especificar a expressão jq que será aplicada ao manifesto JSON.

Por exemplo, para responder às alterações de rótulo nos objetos Deployment, você precisa filtrar o campo de labels campo de metadata . A configuração será assim:

 cat <<EOF { "onKubernetesEvent": [ { "kind": "deployment", "event":["update"], "jqFilter": ".metadata.labels" } ]} EOF 

Essa expressão no jqFilter transforma o manifesto JSON longo do Deployment em JSON curto com rótulos:



O operador shell só acionará um gancho quando esse JSON curto for alterado e as alterações em outras propriedades forem ignoradas.

Contexto de lançamento do gancho


A configuração do gancho permite especificar várias opções para eventos - por exemplo, 2 opções para eventos do Kubernetes e 2 planejamentos:

 {"onKubernetesEvent":[ {"name":"OnCreatePod", "kind": "pod", "event":["add"] }, {"name":"OnModifiedNamespace", "kind": "namespace", "event":["update"], "jqFilter": ".metadata.labels" } ], "schedule": [ { "name":"every 10 min", "crontab":"0 */10 * * * *" }, {"name":"on Mondays at 12:10", "crontab": "0 10 12 * * 1" ]} 

Uma pequena digressão: sim, o operador shell suporta a execução de scripts no estilo crontab . Você pode ler mais na documentação .

Para distinguir por que o gancho foi iniciado, o operador de shell cria um arquivo temporário e passa o caminho para ele até o BINDING_CONTEXT_TYPE na variável BINDING_CONTEXT_TYPE . O arquivo contém uma descrição JSON do motivo pelo qual o gancho foi iniciado. Por exemplo, a cada 10 minutos, um gancho começará com o seguinte conteúdo:

 [{ "binding": "every 10 min"}] 

... e na segunda-feira começará com isso:

 [{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}] 

Para onKubernetesEvent haverá mais disparos JSON desde contém uma descrição do objeto:

 [ { "binding": "onCreatePod", "resourceEvent": "add", "resourceKind": "pod", "resourceName": "foo", "resourceNamespace": "bar" } ] 

O conteúdo dos campos pode ser entendido a partir de seus nomes e, em mais detalhes, leia a documentação . Um exemplo de como obter o nome do resourceName campo resourceName usando jq já foi mostrado em um gancho que replica segredos:

 jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH 

Da mesma forma, você pode obter os campos restantes.

O que vem a seguir?


No repositório do projeto, no diretório / examples , existem exemplos de ganchos prontos para execução no cluster. Ao escrever seus ganchos, você pode tomá-los como base.

Há suporte para a coleta de métricas usando o Prometheus - as métricas disponíveis são gravadas na seção METRICS .

Como você pode imaginar, o operador de shell é escrito em Go e distribuído sob a licença Open Source (Apache 2.0). Seremos gratos por qualquer ajuda no desenvolvimento do projeto no GitHub : asteriscos, problemas e solicitações de recebimento.

Abrindo o véu de sigilo, também informamos que o operador de shell é uma pequena parte do nosso sistema, que pode manter atualizados os complementos instalados no cluster Kubernetes e executar várias ações automáticas. Falamos sobre esse sistema com mais detalhes na segunda-feira no HighLoad ++ 2019 em São Petersburgo - o vídeo e a transcrição deste relatório serão publicados em breve.

Temos um plano para abrir o restante deste sistema: addon-operator e nossa coleção de ganchos e módulos. A propósito, o addon-operator já está disponível no GitHub , mas a documentação para ele ainda está a caminho. O lançamento da coleção de módulos está planejado para o verão.

Fique atento!

PS


Leia também em nosso blog:

Source: https://habr.com/ru/post/pt447442/


All Articles