Embora não seja comum ver conteúdo sobre o assunto, Operators está no coração do Kubernetes e esse definitivamente é um dos temas mais legais na minha opinião no que diz respeito ao projeto.
Nessa série composta por 3 posts, vamos abordar step-by-step os 2 operators do RabbitMQ, mas hoje vamos primeiro elucidar o que são Operators e CRD’s.
Imagina que legal seria poder escrever um manifesto do Kubernetes parecido com isso:




Se você viu o último post, o primeiro da série sobre Cloud Native (Cloud Native | 1 – Definindo Cloud Native) deve ter visto o que Brandan Burns, Ex-líder de engenharia do Kubernetes, de google de 2008 a 2016 disse sobre Cloud Native.
Pegando como ponto de partida a visão do Burns, quando ele diz:
…
Eu posso fazer basicamente o que eu preciso fazer de forma programática, em vez de criar um ticket em um sistema e alguém pegar uma máquina física e colocar em um rack.
É muito mais como se eu só quisesse dizer o que é realmente necessário para criar algo, via API.
…
Brandan Burns
É disso que ele falava, de apenas definir o que precisa. E de forma simplificada, de forma direta ao ponto.
E podemos em um cluster Kubernetes definir POD’s, Deployments, StatefulSets etc, mas podemos também usar definições customizadas para criar objetos de domínio específico, como os que vimos nas imagens.
Podemos até criar as nossas próprias estruturas.
E vou detalhar isso nesse post.
Eu não espero que você crie suas próprias extensões para o Kubernetes, embora pudesse, não é essa a intenção. Minha intenção é mostrar que há esse ponto de extensão, e como funciona. Isso reduz a curva de aprendizado. Nos próximos posts falarei mais especificamente dos operators do RabbitMQ, aqui vamos entender o que são operators e CRD’s.
Extensibilidade no Kubernetes
O Kubernetes permite extensibilidade de diversas formas em diversos momentos. Nós aqui, nessa série, vamos dar atenção a somente 2 desses pontos:
- CRD’s (custom resource definitions)
- e Operators.
Operators
Operators são aplicações que implantamos no cluster kubernetes e elas interagem com o cluster Kubernetes em si, se tornando parte dele. Em vez dessa aplicação entregar uma funcionalidade uma API de negócio, ela entrega um comportamento de infraestrutura.
São POD’s com aplicações que sabem lidar com a API do Kubernetes e usam dessa integração para estender as capacidades de um ambiente kubernetes.
Do ponto de vista do arquiteto, você poderia ir ao https://operatorhub.io/ e buscar por um operator de PostgreSQL (https://operatorhub.io/?category=Database&keyword=Postgres), por exemplo.

Se fizesse isso agora, junto comigo, enquanto escrevo esse post, encontraria esses 8 operators.
O próximo passo seria entrar em cada um desses links e entender o que cada um desses operators é capaz de fazer. Cada operator implementa recursos, e podem ser melhores, piores, mais ou menos completos, mais ou menos estáveis.
Nesse momento é bom nos debruçarmos em testes e análises. É importante entender o operator certo, porque é muito incomum que operators diferentes sejam complementares.
Essa interoperabilidade entre operators não é comum, portanto precisamos lidar com muito cuidado e cautela da escolha do operator.
O operator é implantado em geral com um yaml.
Por exemplo eu busquei um operator para PostgreSQL,
- encontrei o EDB Postgres for Kubernetes,
- descobri que era pago,
- mas também, que era fork de outro operator o CloudNativePG,
- que por sua vez é gratuito.
E ao analisar o CloudNativePG percebi que beira a perfeição.
A instalação funciona assim:
kubectl apply -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.15.0.yaml
Funciona assim: Você roda esse comando. O YAML em questão possui diversas definições como Pods, CRD’s, Roles. Um em específico, o ClusterRole descreverá as permissões que esse operator precisa no cluster. Ele narra quais permissões seu operator precisa para poder se integrar ao Kubernetes. Essas permissões permitem que seu componente escute eventos, e envie comandos.
Parece bom demais para ser verdade? Não é?!
Voltando ao nosso operator, o CloudNativePG, olhando a documentação conseguimos ver que deplois de implantado, ele nos permite aplicar essa configuração abaixo para criar um cluster PostgreSQL.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: cluster-example
spec:
instances: 3
primaryUpdateStrategy: unsupervised
storage:
size: 1Gi
E parece incrível. Ele se preocupa com os detalhes de cada um dos componentes e configurações necessárias para criar o cluster. Claro que a configuração é extensa, tem muitos detalhes, e fazer o setup certo vai levar algum tempo.
O próximo passo é testar e avaliar. Para isso preciso de um Kubernetes rodando. E no meu caso eu usei o k3d para criar um ambiente kubernetes de PoC.
Mas qual é o papel do Operator?
O Operator é um componente que instalamos no cluster kubernetes que passa a fazer parte do cluster, ele passa a estender as capacidades do cluster Kubernetes.
No nosso caso, o CloudNativePG tem a função de era criar e cuidar de um cluster de banco, cuidando do backup, recriação de nós, eleição de master.
Já no cenário do RabbitMQ temos 2 operators e vou abordá-los em 2 posts dessa série aqui. Um operator criará o cluster e outro operator que criará os objetos (filas, exchanges, binds) do RabbitMQ.
Resource Definition
Toda configuração de implantação no kubernetes tem pelo menos 1 ou mais objetos. Cada objeto possui no mínimo algumas pripriedades:
apiVersion: Descreve a API e versão da API. É como um identificador do produto/componente e a versão dele.
kind: É o tipo de objeto tratado por esse componente.
metadata: são metadados adicionais.
spec: aqui entra a CRD, a spec é mutante, cada Kind/apiVersion possui um schema específico para a spec. E é na spec que definimos as estruturas de dados específicas de cada componente.
CRD – Custom Resource Definition
As CRD’s estendem o Kubernetes permitindo definir novos formatos de Yaml. Funciona como um JSONSchema ou XMLSchema, em que ele define que a partir da instalação da CRD, um formato novo de yaml pode ser criado.
Por exemplo a partir da CRD que define Vhosts (abaixo):
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.8.0
creationTimestamp: null
name: vhosts.rabbitmq.com
spec:
group: rabbitmq.com
names:
categories:
- all
- rabbitmq
kind: Vhost
listKind: VhostList
plural: vhosts
singular: vhost
scope: Namespaced
versions:
- name: v1beta1
schema:
openAPIV3Schema:
description: Vhost is the Schema for the vhosts API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
spec:
description: VhostSpec defines the desired state of Vhost
properties:
name:
description: Name of the vhost; see https://www.rabbitmq.com/vhosts.html.
type: string
rabbitmqClusterReference:
description: Reference to the RabbitmqCluster that the vhost will
be created in. Required property.
properties:
connectionSecret:
description: Secret contains the http management uri for the RabbitMQ
cluster. The Secret must contain the key `uri`, `username` and
`password` or operator will error. Have to set either name or
connectionSecret, but not both.
properties:
name:
description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
TODO: Add other useful fields. apiVersion, kind, uid?'
type: string
type: object
name:
description: The name of the RabbitMQ cluster to reference. Have
to set either name or connectionSecret, but not both.
type: string
namespace:
description: The namespace of the RabbitMQ cluster to reference.
Defaults to the namespace of the requested resource if omitted.
type: string
type: object
tags:
items:
type: string
type: array
tracing:
type: boolean
required:
- name
- rabbitmqClusterReference
type: object
status:
description: VhostStatus defines the observed state of Vhost
properties:
conditions:
items:
properties:
lastTransitionTime:
description: The last time this Condition status changed.
format: date-time
type: string
message:
description: Full text reason for current status of the condition.
type: string
reason:
description: One word, camel-case reason for current status
of the condition.
type: string
status:
description: True, False, or Unknown
type: string
type:
description: Type indicates the scope of the custom resource
status addressed by the condition.
type: string
required:
- status
- type
type: object
type: array
observedGeneration:
description: observedGeneration is the most recent successful generation
observed for this Vhost. It corresponds to the Vhost's generation,
which is updated on mutation by the API Server.
format: int64
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
Uma vez que essa CRD esteja implantada no cluster podemos enviar para o Kubernetes um yaml assim:
apiVersion: rabbitmq.com/v1beta1
kind: Vhost
metadata:
name: test-vhost
spec:
name: test-vhost
rabbitmqClusterReference:
name: test
Em geral as CRD’s são parte do arquivo de manifesto (yaml) do Operator. É importante entendermos porque é olhando para elas que vamos achar os detalhes mal documentados do Operator.
Quando você instala o operator, as CRD’s (ou pelo menos as principais) são instaladas, habilitando você a usá-las mais tarde.
Como um schema foi definido, o próprio Kubernetes lida com a validação do formato. O operator já valida a consistência e se estiver tudo certo, ele tem a capacidade de chamar a API do Kubernetes para criar novos Pods (do seu cluster) com todas as principais boas práticas.
Da mesma forma como com classes base da CLR, como string, int, double, boolean, object, conseguimos construir novas classes e usá-las, as CRD’s fazem o mesmo no nível das configurações do kubernetes.
O que isso tem a ver com Cloud Native?
O resultado desses operators é que temos um ambiente em que simplesmente definimos o que precisamos. No nível mais alto, não nos preocupamos se o cluster precisa de novos nós para atender um rollout. Simplesmente descrevemos o que precisamos.
Já quando pensamos na criação de um cluster PostgreSQL, não nos preocupamos com detalhes do processo de criação e configuração detalhada desses pods. Isso é abstraído para que possamos apenas expressar que precisamos de um cluster com X parâmetros. E a partir daí a mágica acontece.
Mas quando pensamos no RabbitMQ, temos também a oportundiade de criar filas, exchanges, vhosts etc. Sem que precisemos nos preocupar com detalhes de como fazer isso.
Era isso que o Brandan Burns dizia, era sobre trabalhar com a cloud no DNA, como se fizesse parte desde sempre, usando abstrações que simplificam nossa vida.
Conclusão
É nosso papel definindo uma arquitetura, em um ambiente Kubernetes, pensar em quais operators queremos no nosso cluster. Seja no ambiente de produção quanto no ambiente de desenvolvimento.
Isso deve fazer parte do processo de tomada de decisão, pois hora vamos querer usar serviços PaaS, gerenciados, hora vamos querer usar Operators.
Hora vamos usar operators apenas para subir ambientes de testes, isolados, para teste local. Hora vamos querer usar os operators em produção.
Cada caso é um caso.
Se você pensa que “O cara do DevOps” vai resolver isso por você, então, dificilmente. O conhecimento específico sobre as tecnologias é do arquiteto, não dele.
É você, arquiteto, que tem de ter consciência e tomar decisão se o RabbitMQ Messaging Topology Operator faz sentido ou não no seu ambiente. Se o RabbitMQ Cluster Operator faz ou não sentido.
Saber da existência dos operators, é o primeiro passo.
O segundo é conhecer Operators úteis, que fariam sentido nos seus projetos. Mesmo que não seja em produção.
Então aqui vão minhas dicas:
- RabbitMQ Cluster Kubernetes Operator for Kubernetes | link
- RabbitMQ Messaging Topology Operator for Kubernetes | link
- CloudNativePG – PostgreSQL Operator | link
Que tal fazer um teste?
Agora que você sabe da existência de Operators e CRD’s, e no próximo post dessa série detalhar como subir o RabbitMQ Cluster Kubernetes Operator for Kubernetes usando k3D.





0 comentários