Um guia para arquitetura Kubernetes
Aprenda como os diferentes componentes da arquitetura Kubernetes se encaixam para que você possa estar mais bem equipado para diagnosticar problemas, manter um cluster íntegro e otimizar seu próprio fluxo de trabalho.
Você usa o Kubernetes para orquestrar contêineres. É uma descrição fácil de dizer, mas entender o que isso realmente significa e como você consegue isso é outra questão completamente diferente. Se você estiver executando ou gerenciando um cluster Kubernetes, sabe que o Kubernetes consiste em um computador designado como plano de controle e muitos outros computadores designados como nós de trabalho. Cada um deles possui uma pilha complexa, mas robusta, tornando possível a orquestração, e familiarizar-se com cada componente ajuda a entender como tudo funciona.
(Nived Velayudhan, CC BY-SA 4.0)
Componentes do plano de controle
Você instala o Kubernetes em uma máquina chamada plano de controle. É aquele que executa o daemon Kubernetes e é aquele com quem você se comunica ao iniciar contêineres e pods. As seções a seguir descrevem os componentes do plano de controle.
etc.
Etcd é um armazenamento de valor-chave rápido, distribuído e consistente usado como armazenamento de apoio para armazenamento persistente de dados de objetos Kubernetes, como pods, controladores de replicação, segredos e serviços. Etcd é o único lugar onde o Kubernetes armazena estado e metadados do cluster. O único componente que se comunica diretamente com o etcd é o servidor API Kubernetes. Todos os outros componentes leem e gravam dados no etcd indiretamente por meio do servidor API.
O Etcd também implementa um recurso de observação, que fornece uma interface baseada em eventos para monitorar alterações de forma assíncrona nas chaves. Depois que você altera uma chave, seus observadores são notificados. O componente do servidor API depende muito disso para ser notificado e mover o estado atual do etcd para o estado desejado.
Por que o número de instâncias do etcd deveria ser um número ímpar?
Normalmente, você teria três, cinco ou sete instâncias do etcd em execução em um ambiente de alta disponibilidade (HA), mas por quê? Porque o etcd é um armazenamento de dados distribuído. É possível escalá-lo horizontalmente, mas também é necessário garantir que os dados em cada instância sejam consistentes e, para isso, seu sistema precisa chegar a um consenso sobre qual é o estado. O Etcd usa o algoritmo de consenso RAFT para isso.
O algoritmo requer maioria (ou quorum) para que o cluster avance para o próximo estado. Se você tiver apenas duas instâncias do ectd e uma delas falhar, o cluster do etcd não poderá fazer a transição para um novo estado porque não existe maioria. Se você tiver três instâncias ectd, uma instância poderá falhar, mas ainda terá a maioria das instâncias disponíveis para atingir um quorum.
Servidor API
O servidor API é o único componente do Kubernetes que interage diretamente com o etcd. Todos os outros componentes do Kubernetes devem passar pelo servidor API para funcionar com o estado do cluster, incluindo os clientes (kubectl). O servidor API tem as seguintes funções:
- Fornece uma maneira consistente de armazenar objetos no etcd.
- Executa a validação desses objetos para que os clientes não possam armazenar objetos configurados incorretamente (o que poderia acontecer se eles escrevessem diretamente no armazenamento de dados do etcd).
- Fornece uma API RESTful para criar, atualizar, modificar ou excluir um recurso.
- Fornece bloqueio de simultaneidade otimista para que outros clientes nunca substituam alterações em um objeto no caso de atualizações simultâneas.
- Executa autenticação e autorização de uma solicitação enviada pelo cliente. Ele usa os plug-ins para extrair o nome de usuário do cliente, o ID do usuário, os grupos aos quais o usuário pertence e determinar se o usuário autenticado pode executar a ação solicitada no recurso solicitado.
- Responsável pelo controle de admissão se a solicitação estiver tentando criar, modificar ou excluir um recurso. Por exemplo, AlwaysPullImages, DefaultStorageClass e ResourceQuota.
- Implementa um mecanismo de observação (semelhante ao etcd) para os clientes observarem as alterações. Isso permite que componentes como o Scheduler e o Controller Manager interajam com o API Server de maneira fracamente acoplada.
Gerente de Controlador
No Kubernetes, os controladores são loops de controle que monitoram o estado do seu cluster e, em seguida, fazem ou solicitam alterações quando necessário. Cada controlador tenta aproximar o estado atual do cluster do estado desejado. O controlador rastreia pelo menos um tipo de recurso do Kubernetes e esses objetos possuem um campo de especificação que representa o estado desejado.
Exemplos de controladores:
- Replication Manager (um controlador para recursos ReplicationController)
- Controladores ReplicaSet, DaemonSet e Job
- Controlador de implantação
- Controlador StatefulSet
- Controlador de nó
- Controlador de serviço
- Controlador de terminais
- Controlador de namespace
- Controlador de volume persistente
Os controladores usam o mecanismo de observação para serem notificados sobre alterações. Eles observam o servidor API em busca de alterações nos recursos e executam operações para cada alteração, seja a criação de um novo objeto ou uma atualização ou exclusão de um objeto existente. Na maioria das vezes, essas operações incluem a criação de outros recursos ou a atualização dos próprios recursos monitorados. Ainda assim, como o uso de relógios não garante que o controlador não perderá um evento, eles também realizam uma operação de re-lista periodicamente para garantir que não perderam nada.
O Controller Manager também executa funções de ciclo de vida, como criação e ciclo de vida de namespace, coleta de lixo de eventos, coleta de lixo de pods encerrados, coleta de lixo de exclusão em cascata e coleta de lixo de nós. Consulte Gerenciador do Cloud Controller para obter mais informações.
Agendador
O Agendador é um processo de plano de controle que atribui pods aos nós. Ele observa pods recém-criados que não possuem nós atribuídos. Para cada pod descoberto pelo Agendador, o Agendador se torna responsável por encontrar o melhor nó para execução desse pod.
Os nós que atendem aos requisitos de agendamento de um pod são chamados de nós viáveis. Se nenhum dos nós for adequado, o pod permanecerá não programado até que o Agendador possa colocá-lo. Depois de encontrar um nó viável, ele executa um conjunto de funções para pontuar os nós, e o nó com a pontuação mais alta é selecionado. Em seguida, notifica o servidor API sobre o nó selecionado. Eles chamam esse processo de vinculação.
A seleção dos nós é um processo de duas etapas:
- Filtrar a lista de todos os nós para obter uma lista de nós aceitáveis para os quais você pode programar o pod (por exemplo, o filtro PodFitsResources verifica se um nó candidato tem recursos disponíveis suficientes para atender às solicitações de recursos específicas de um pod).
- Pontuar a lista de nós obtida na primeira etapa e classificá-los para escolher o melhor nó. Se vários nós tiverem a pontuação mais alta, um processo round-robin garantirá que os pods sejam implantados em todos eles de maneira uniforme.
Os fatores a serem considerados nas decisões de agendamento incluem:
- O pod solicita recursos de hardware/software? O nó está relatando uma condição de memória ou pressão de disco?
- O nó possui um rótulo que corresponde ao seletor de nó na especificação do pod?
- Se o pod solicitar ligação a uma porta de host específica, essa porta estará disponível?
- O pod tolera as impurezas do nó?
- O pod especifica regras de afinidade ou antiafinidade do nó?
O Agendador não instrui o nó selecionado para executar o pod. Tudo o que o Agendador faz é atualizar a definição do pod por meio do servidor API. O servidor API então notifica o kubelet de que o pod foi agendado por meio do mecanismo de observação. Em seguida, o serviço kubelet no nó de destino vê que o pod foi agendado para seu nó, cria e executa os contêineres do pod.
[ Leia a seguir: Como o Kubernetes cria e executa contêineres: um guia ilustrado ]
Componentes do nó de trabalho
Os nós de trabalho executam o agente kubelet, o que permite que eles sejam recrutados pelo plano de controle para processar trabalhos. Semelhante ao plano de controle, o nó de trabalho utiliza vários componentes diferentes para tornar isso possível. As seções a seguir descrevem os componentes do nó do trabalhador.
Kubelet
Kubelet é um agente executado em cada nó do cluster e é responsável por tudo que é executado em um nó de trabalho. Ele garante que os contêineres sejam executados no pod.
As principais funções do serviço kubelet são:
- Registre o nó em que ele está sendo executado criando um recurso de nó no servidor API.
- Monitore continuamente o servidor API em busca de pods agendados para o nó.
- Inicie os contêineres do pod usando o ambiente de execução do contêiner configurado.
- Monitore continuamente contêineres em execução e relate status, eventos e consumo de recursos ao servidor da API.
- Execute as sondagens de atividade do contêiner, reinicie-os quando as sondagens falharem e encerre os contêineres quando o pod for excluído do servidor da API (notificando o servidor sobre o encerramento do pod).
Proxy de serviço
O proxy de serviço (kube-proxy) é executado em cada nó e garante que um pod possa se comunicar com outro pod, um nó possa se comunicar com outro nó e um contêiner possa se comunicar com outro contêiner. Ele é responsável por monitorar o servidor API em busca de alterações nos serviços e definições de pod para manter toda a configuração da rede atualizada. Quando um serviço é apoiado por mais de um pod, o proxy realiza o balanceamento de carga entre esses pods.
O kube-proxy recebeu esse nome porque começou como um servidor proxy real que costumava aceitar conexões e fazer proxy delas para os pods. A implementação atual usa regras de iptables para redirecionar pacotes para um pod de back-end selecionado aleatoriamente, sem passá-los por um servidor proxy real.
Uma visão de alto nível de como funciona:
- Quando você cria um serviço, um endereço IP virtual é atribuído imediatamente.
- O servidor API notifica os agentes kube-proxy em execução nos nós do trabalhador que existe um novo serviço.
- Cada proxy kube torna o serviço endereçável configurando regras de iptables, garantindo que cada par IP/porta de serviço seja interceptado e o endereço de destino seja modificado para um dos pods que respaldam o serviço.
- Observa o servidor API em busca de alterações nos serviços ou em seus objetos de endpoint.
Tempo de execução do contêiner
Existem duas categorias de tempos de execução de contêiner:
- Tempos de execução de contêineres de nível inferior: eles se concentram na execução de contêineres e na configuração do namespace e dos cgroups para contêineres.
- Tempos de execução de contêiner de nível superior (mecanismo de contêiner): eles se concentram em formatos, descompactação, gerenciamento, compartilhamento de imagens e fornecimento de APIs para desenvolvedores.
O tempo de execução do contêiner cuida de:
- Extrai a imagem de contêiner necessária de um registro de imagem se ela não estiver disponível localmente.
- Extrai a imagem em um sistema de arquivos copy-on-write e todas as camadas do contêiner se sobrepõem para criar um sistema de arquivos mesclado.
- Prepara um ponto de montagem de contêiner.
- Define os metadados da imagem do contêiner, como substituir CMD, ENTRYPOINT das entradas do usuário e configura regras SECCOMP, garantindo que o contêiner seja executado conforme esperado.
- Altera o kernel para atribuir isolamento como processo, rede e sistema de arquivos a este contêiner.
- Alerta o kernel para atribuir alguns limites de recursos, como limites de CPU ou memória.
- Passe a chamada do sistema (syscall) para o kernel para iniciar o contêiner.
- Garante que a configuração do SElinux/AppArmor esteja correta.
Trabalhando juntos
Os componentes de nível de sistema trabalham juntos para garantir que cada parte de um cluster Kubernetes possa cumprir seu propósito e executar suas funções. Às vezes, pode ser complicado (quando você está editando profundamente um arquivo YAML) entender como suas solicitações são comunicadas dentro do cluster. Agora que você tem um mapa de como as peças se encaixam, você pode entender melhor o que está acontecendo dentro do Kubernetes, o que ajuda a diagnosticar problemas, manter um cluster íntegro e otimizar seu próprio fluxo de trabalho.