Lasso é uma estrutura de controlador.
Lasso tem alguma paridade funcional com o projeto de tempo de execução do controlador e pode ser usado no lugar do tempo de execução do controlador.
Lasso é de baixo nível. Se quiser gerenciar seus próprios controladores, você pode usá-lo. Se você procura mais comodidade e funcionalidade para criar seus próprios tipos de recursos, recomendamos verificar o Wrangler.
Estrutura do controlador principal usada por Wrangler e Norman.
Este documento foi escrito com dois públicos em mente: alguém que é novo nos controladores do Kubernetes e alguém que não é novo nos controladores do Kubernetes, mas é novo no laço.
Se você se enquadra na categoria “Alguém que não é novo nos controladores Kubernetes”, você pode pular para a Proposta de Valor do Lasso.
Se você é novo nos controladores Kubernetes, é recomendável não pular nenhuma seção.
Se você não tiver conhecimento básico do Kubernetes, é recomendável consultar primeiro outra documentação sobre o básico, como a Visão geral do Kubernetes.
Nota: Este será um curso intensivo muito breve. Incentivamos você a procurar outros recursos sobre o tema, são muitos! Dois recursos recomendados são Sample Controller Project e Introduction To Kubernetes Controller Frameworks.
Da documentação do Kubernetes
No Kubernetes, um controlador é um loop de controle que observa o estado compartilhado do cluster por meio do apiserver e faz alterações tentando mover o estado atual para o estado desejado.
A palavra "controlador" é usada de forma mais vaga do que a definição acima pode fazer você acreditar. Em nossas próprias palavras: Controladores são manipuladores de loop que agem em resposta a eventos de recursos do Kubernetes. No Lasso, também usamos a palavra “controlador” para nos referirmos a toda a estrutura responsável por registrar esse manipulador de loop e fornecer-lhe os recursos de que necessita. Resumindo, controladores são estruturas que agrupam tudo o que é necessário para interagir e operar nos tipos Kubernetes.
Para atingir o objetivo acima, alguns subobjetivos precisam ser alcançados:
Existem muitas estruturas para ajudar a atingir os objetivos acima. Lasso é um deles. O que a maioria dessas estruturas tem em comum, incluindo o laço, é que elas usam funcionalidades do cliente para atingir esses objetivos. Você poderia atingir esses objetivos usando apenas o client-go. No restante desta seção, veremos como um controlador é montado apenas usando conceitos de client-go.
Um informante é usado para atingir os objetivos acima necessários para um controlador Kubernetes. Um informante possui um manipulador de eventos e um indexador. O indexador possui funções de armazenamento e mapa para indexar objetos dentro do armazenamento, chamados indexadores. As estruturas subjacentes são diferentes, mas uma pseudo estrutura é mostrada abaixo para ilustrar isso:
Informer
EventHandler
Indexer
Store
Indexers
O EventHandler pode ter lógica diferente para criar, atualizar e excluir eventos. O manipulador de eventos é onde o controlador entra. Você o usa para configurar a criação, atualização e exclusão de manipuladores de eventos.
O indexador é usado como cache.
A loja é onde o indexador persiste os objetos.
Os indexadores são funções que o indexador pode usar para indexar objetos persistentes. Uma vez indexados com um indexador, os objetos podem ser recuperados usando strings que descrevem uma característica com a qual o indexador se preocupa.
O indexador geralmente será usado para recuperar um objeto por meio de metadados simples, como namespace e nome.
O informante primeiro preenche o indexador listando todos os recursos retornados de um cliente. Em seguida, o informante observa o recurso Kubernetes ao qual está atribuído. Quando um evento ocorre, ele usa esse evento para atualizar seu indexador e executar seus manipuladores de eventos.
Normalmente, um aplicativo deseja vários controladores. Contudo, pode não ser eficiente para cada controlador ter o seu próprio informante e, portanto, o seu próprio indexador. O SharedIndexerInformer do client-go pode ser usado para adicionar vários controladores que usam os mesmos indexadores.
Há um ótimo visual de como um controlador Kubernetes pode funcionar encontrado no repositório kubernetes/sample-controller.
Nota: Os informadores foram simplificados por uma questão de brevidade. Se você estiver interessado em saber exatamente como eles funcionam, é recomendável ler o código client-go.
Lasso padroniza o que é um controlador com seu pacote de controlador. Lasso apresenta fábricas para gerenciar caches, controladores e clientes dentro de um projeto. Isso é fundamental para o fazendeiro que gerencia muitas instâncias de vários tipos.
O controlador laço usa a fila de trabalho do cliente. Lasso registra um manipulador de eventos que processa a fila de trabalho. O manipulador orientado à fila de trabalho faz com que apenas um manipulador de eventos precise ser adicionado para cada tipo de Kubernetes. Agora, registrar um manipulador apenas o adiciona à lista de manipuladores do controlador do tipo. Enfileirar um objeto o adiciona à fila de trabalho. Cada objeto na fila de trabalho será então processado por trabalhadores em execução em goroutines. O manipulador de eventos do controlador enfileirará novamente qualquer objeto que faça com que um ou mais manipuladores do controlador retornem um erro que não seja do tipo ErrIgnore
. Essa fila de trabalho também pode ter objetos enfileirados de outras fontes. Essa funcionalidade é exposta por meio da função Enqueue do controlador. Isso transforma os controladores de apenas reativos aos recursos do Kubernetes e ressincronizações de cache, para também serem invocáveis por código.
O tipo sharedController incorpora a interface do Controller para expor a funcionalidade de seu controlador. O sharedController também expõe a função RegisterHandler junto com a função Client. RegisterHandler adiciona o manipulador passado à lista de manipuladores do controlador e Client retorna um cliente para o tipo de recurso correspondente sharedController; sharedController possui um campo GVK (Group Version Kind) que indica a que tipo se destina.
O aspecto "Compartilhado" começa no Lasso Controller mais primitivo. O uso de uma fila de trabalho permite que um conjunto de manipuladores de eventos seja compartilhado, por exemplo. Em seguida, o Lasso Shared Controller acrescenta isso mantendo um cliente reutilizável para o GVK e adicionando uma maneira de registrar facilmente manipuladores no Controller abaixo com RegisterHandler.
O sharedController pode criar seu controlador subjacente. Isso só acontecerá quando um manipulador for registrado no sharedController.
Envolve o cliente Kubernetes. Contém campos GVK para gerenciar o cliente em uma estrutura pai, como sharedClientFactory. Expõe algumas funcionalidades adicionais, como configurar o cabeçalho User-Agent para o cliente REST subjacente.
O laço Cache é um wrapper leve em torno de um SharedIndexInformer do cliente.
O principal benefício do Lasso Cache é que ele pode receber um Cache Factory, que gerencia SharedIndexInformer na memória pelo GVK para que possam ser reutilizados. Ao utilizar as funções do Lasso Cache ele irá se gerenciar dentro do CacheFactory que foi passado para criá-lo.
A fábrica de cache compartilhado armazena os caches e permite que eles sejam gerenciados por meio de estruturas GVK. Depois que os caches forem criados, eles poderão ser reutilizados.
A Fábrica de Cliente Compartilhada existe para que um cliente para um GVK possa ser reutilizado após ter sido criado.
O SharedControllerFactory une tudo acima. Ele permite um único ponto de entrada para acessar clientes, caches e manipuladores – todos gerenciados para você. É possível interagir principalmente com a fábrica de controladores compartilhados. Ele criará todos os controladores, clientes e caches subjacentes de que você precisa. Isso garantirá que não haja duplicatas desses objetos. Isso evitará condições de corrida.
Depois de usar ForObject, ForKind, ForResource ou ForResourceKind, o SharedControllerFactory fará o seguinte:
O deferredController é uma função que, uma vez executada, criará e retornará um controlador. Ele criará o controlador usando um cache do SharedCacheFactory. O objetivo da função deferredController é adiar o preenchimento do cache até que seja absolutamente necessário. Isso garante que apenas os recursos do Kubernetes que registraram manipuladores, foram enfileirados ou tiveram o cache do controlador solicitado, sejam armazenados na memória.
Quando você obtém um sharedController do SharedControllerFactory, ele ainda não terá um Controller subjacente instanciado. Em vez disso, ele chamará o método deferredController para criar um assim que uma das operações mencionadas for executada.
Abaixo estão as etapas para uma maneira simples de registrar manipuladores e executá-los no laço:
package main
import (
"context"
"github.com/rancher/lasso/pkg/controller"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
)
func main () {
config , err := clientcmd . BuildConfigFromFlags ( "" , os . Getenv ( "KUBECONFIG" ))
if err != nil {
panic ( err )
}
// Setup types you want to work with
scheme := runtime . NewScheme ()
appsv1 . AddToScheme ( scheme )
controllerFactory , err := controller . NewSharedControllerFactoryFromConfig ( config , scheme )
if err != nil {
panic ( err )
}
// ForObject, ForKind, ForResource, and ForResourceKind can all be used to retrieve the controller.
// They all accept different parameters but the first three all eventually call ForResourceKind.
// Refer to [How to use Lasso](#how-to-use-lasso) for more information on how ForResourceKind works.
sharedController , err := controllerFactory . ForObject ( & appsv1. Deployment {})
if err != nil {
panic ( err )
}
// Register a handler on a shared controller. If you need a dedicated queue then use controller.New()
sharedController . RegisterHandler ( context . TODO (), "my-handler" , controller . SharedControllerHandlerFunc ( func ( key string , obj runtime. Object ) (runtime. Object , error ) {
// obj is the latest version of this obj in the cache and MAY BE NIL when an object is finally deleted
dep , ok := obj .( * appsv1. Deployment )
if ! ok {
return obj , nil
}
// Do some stuff ...
// Get stuff from the cache
sharedController . Informer (). GetStore (). Get ( key )
result := & appsv1. Deployment {}
err := sharedController . Client (). Update ( ctx , dep . Namespace , dep , result , metav1. UpdateOptions {})
// return the latest version of the object
return result , err
}))
// the second, int parameter is the number of workers to be used for EACH controller.
err = controllerFactory . Start ( context . TODO (), 5 )
if err != nil {
panic ( err )
}
}
Alguns dos testes do lasso usam envtest para serem executados. Envtest permite que testes sejam executados em um servidor Kubernetes "falso" com pouca/nenhuma sobrecarga.
Para instalar o binário setup-envtest
necessário, use o seguinte comando:
go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest
Antes de executar os testes, você deve executar o seguinte comando para configurar o servidor falso:
# note that this will use a new/latest version of k8s. Our CI will run against the version of k8s that corresponds to lasso's
# current client-go version, as seen in scripts/test.sh
export KUBEBUILDER_ASSETS= $( setup-envtest use -p path )
Os recursos a seguir foram úteis na redação desses documentos. Eles são altamente recomendados para aprender mais do que o apresentado aqui: