Kubernetes · Capítulo 1

Fundamentos do Kubernetes

Contêineres, orquestração, a arquitetura de um cluster e como o Kubernetes fala com os runtimes por meio de CRI e OCI.

Antes de falar de Pods, Deployments ou Services, é preciso entender duas ideias: o que é um contêiner e por que precisamos de um orquestrador para gerenciar muitos deles. Esta nota percorre esses fundamentos e termina em um ponto que costuma confundir: como o Kubernetes se comunica com o Docker, o containerd e outros runtimes.

É a primeira nota da minha preparação para a certificação KCNA (Kubernetes and Cloud Native Associate) da CNCF.

O problema que os contêineres resolvem

Uma aplicação moderna raramente é um único programa. Ela pode ter um servidor web, um banco de dados, um sistema de mensageria e um motor de automação, cada um com suas próprias bibliotecas e versões. Quando ainda precisa funcionar no notebook do desenvolvedor, em testes e em produção, surge o que se conhece como a "matriz do inferno": uma tabela enorme de combinações entre componentes, bibliotecas e sistemas operacionais em que qualquer atualização pode quebrar algo.

Os contêineres resolvem isso empacotando cada componente com todas as suas dependências em um ambiente isolado. O que funciona na máquina do desenvolvedor funciona igual em produção, porque é exatamente o mesmo pacote que é entregue.

Contêineres e máquinas virtuais

Um sistema operacional tem duas camadas: o kernel, que conversa com o hardware, e o software por cima (bibliotecas, ferramentas, gerenciador de pacotes), que diferencia o Ubuntu do Fedora ou do Red Hat.

Comparação entre máquinas virtuais e contêineres

  • Uma máquina virtual virtualiza o hardware: cada VM leva seu próprio sistema operacional completo, kernel incluído. Isola muito, mas pesa gigabytes e leva minutos para iniciar.
  • Um contêiner não virtualiza o sistema operacional: ele compartilha o kernel do host e empacota apenas a aplicação e suas bibliotecas. Pesa megabytes e inicia em segundos.

Como todas as distribuições Linux usam o mesmo kernel, um host Ubuntu pode executar um contêiner baseado em Fedora ou Red Hat: o contêiner traz seu próprio software de usuário e usa o kernel do host.

Cuidado com o Windows: um contêiner Windows precisa de um kernel Windows. Ele não pode rodar diretamente em um host Linux, porque eles não compartilham o kernel.

O Docker não inventou o isolamento: ele se apoia em recursos do kernel Linux (namespaces e cgroups) que tecnologias como o LXC já usavam. Sua contribuição foi tornar simples empacotar, distribuir e executar contêineres.

Imagens e contêineres

Dois conceitos que não convém misturar:

  • Uma imagem é o modelo: um pacote somente leitura com a aplicação, suas dependências e as instruções para iniciá-la.
  • Um contêiner é uma instância em execução dessa imagem. A partir da mesma imagem é possível subir muitos contêineres.

Uma imagem serve de modelo para vários contêineres

As imagens ficam guardadas em um registro. O mais conhecido é o Docker Hub, com imagens oficiais de bancos de dados, servidores web e linguagens prontas para usar.

Primeiros comandos com Docker

No Ubuntu ou no Debian, o Docker é instalado a partir dos repositórios e basta um comando para testá-lo:

# Instalar o Docker (Ubuntu / Debian)
sudo apt-get update
sudo apt-get install -y docker.io

# Verificar se funciona
sudo docker run hello-world

# Executar um comando dentro de um contêiner Alpine e removê-lo ao terminar
sudo docker run --rm alpine echo "olá Tenci.io"

# Subir serviços a partir de imagens oficiais
sudo docker run -d --name mi-base mongo
sudo docker run -d --name mi-web -p 8080:80 nginx

O docker run baixa a imagem se ela não estiver na máquina e cria um contêiner a partir dela.

Orquestração de contêineres

Executar um contêiner é fácil. Executar centenas, distribuídos em vários servidores, não é: o que acontece se um servidor cai? Como escalar quando o tráfego aumenta? Como os contêineres se encontram?

A orquestração é a automação de tudo isso sobre um grupo de máquinas (nós):

  • Alta disponibilidade: se um contêiner ou um nó falha, ele é recriado em outro lugar.
  • Escalabilidade: adicionar ou remover cópias conforme a demanda.
  • Balanceamento de carga entre as cópias de uma aplicação.
  • Configuração declarativa: você descreve em arquivos YAML como quer que o sistema esteja, e o orquestrador se encarrega de chegar a esse estado e mantê-lo.

Existem várias opções (o Docker Swarm foi uma das mais simples), mas o Kubernetes se tornou o padrão de fato. Sua instalação é mais exigente, em troca de muito mais recursos e do maior ecossistema do mundo cloud native.

Arquitetura do Kubernetes

Um cluster Kubernetes é um conjunto de nós com dois papéis diferentes:

  • O plano de controle (antes chamado de master): toma as decisões do cluster. Em produção, é replicado em vários nós para não haver um ponto único de falha.
  • Os nós de trabalho (worker nodes, antigamente chamados de "minions"): executam os contêineres das aplicações.

Arquitetura de um cluster Kubernetes

Componentes principais

Componente Onde fica O que faz
kube-apiserver Plano de controle A porta de entrada do cluster. Usuários, ferramentas e os demais componentes sempre falam com ele.
etcd Plano de controle Banco de dados chave-valor distribuído onde fica todo o estado do cluster. Mantém a consistência entre vários nós do plano de controle.
kube-scheduler Plano de controle Decide em qual nó roda cada novo contêiner (Pod). Atribui, mas não executa.
kube-controller-manager Plano de controle Os controladores observam o estado real e agem quando ele não coincide com o desejado: se um nó ou um contêiner cai, eles reagem. São o "cérebro" da orquestração.
kubelet Cada nó Agente que recebe instruções do plano de controle e garante que os contêineres do seu nó estejam rodando como esperado.
Container runtime Cada nó O software que realmente executa os contêineres: containerd, CRI-O etc.

Outro componente que você verá em cada nó é o kube-proxy, que mantém as regras de rede para que os serviços fiquem acessíveis.

kubectl: a linha de comando do Kubernetes

O kubectl é a ferramenta para gerenciar o cluster: criar recursos, consultá-los e ver seu estado. Tudo o que ele faz passa pelo kube-apiserver.

# Informações do cluster e dos nós
kubectl cluster-info
kubectl get nodes

# Criar um Pod a partir de uma imagem
kubectl run hola-nginx --image=nginx

# Ver os Pods em execução
kubectl get pods

Como o Kubernetes fala com os runtimes: CRI e OCI

No começo, o Kubernetes só sabia trabalhar com o Docker, com código específico para ele. Quando surgiram outros runtimes, o Kubernetes definiu uma interface comum:

  • CRI (Container Runtime Interface): o contrato, baseado em gRPC, que o kubelet usa para falar com qualquer runtime. Se um runtime implementa a CRI, o Kubernetes pode usá-lo: containerd, CRI-O e outros.
  • OCI (Open Container Initiative): o padrão sobre os próprios contêineres. Tem duas partes:
  • image-spec: como uma imagem deve ser construída.
  • runtime-spec: como um runtime deve se comportar ao executá-la.

O Kubernetes se comunica com os runtimes por meio da CRI

O fim do dockershim

O Docker é anterior à CRI e nunca a implementou. Para continuar dando suporte a ele, o Kubernetes manteve uma camada de tradução chamada dockershim. Era uma solução temporária e foi removida por completo no Kubernetes 1.24.

Isso significa que as imagens do Docker não servem mais? Não. As imagens construídas com o Docker seguem o padrão OCI, então qualquer runtime compatível (como o containerd) as executa sem mudanças. O que desapareceu foi o uso do motor do Docker como runtime do cluster.

Docker e containerd

O Docker não é apenas um runtime: é um conjunto de ferramentas (CLI, API, construção de imagens, volumes, autenticação, segurança). Por baixo, o Docker usa o containerd para executar os contêineres.

O containerd é um projeto independente e graduado na CNCF. Pode ser instalado sozinho, sem o Docker, como runtime do Kubernetes: é o habitual hoje quando o restante das ferramentas do Docker não é necessário.

Ferramentas de linha de comando para runtimes

Sem Docker no nó, há três ferramentas que convém diferenciar:

Ferramenta Vem de Para que serve Uso diário?
ctr containerd Depurar o containerd. Pouco amigável e limitado. Não
nerdctl Projeto containerd CLI compatível com Docker para o containerd. Sim, como substituto do Docker
crictl Kubernetes Inspecionar e depurar qualquer runtime compatível com CRI. Para diagnóstico nos nós

ctr

Vem incluído no containerd. Serve para testes e depuração, não para administrar ambientes:

ctr images pull docker.io/library/redis:alpine
ctr run docker.io/library/redis:alpine redis

nerdctl

Oferece uma experiência quase idêntica à do Docker sobre o containerd, suporta Docker Compose e acrescenta recursos que o Docker não tem, como o download preguiçoso de imagens (lazy pulling), imagens criptografadas, distribuição P2P e assinatura e verificação de imagens:

nerdctl run --name redis redis:alpine
nerdctl run --name web -p 8080:80 -d nginx:latest

crictl

É mantido pela comunidade Kubernetes e funciona com qualquer runtime CRI. É instalado à parte e sua sintaxe lembra a do Docker. Foi pensado para inspecionar e depurar, não para criar contêineres: se você criar algo com o crictl fora do controle do kubelet, o kubelet pode apagá-lo.

crictl pull busybox
crictl images
crictl pods
crictl ps -a
crictl logs <id-do-conteiner>
crictl exec -it <id-do-conteiner> ls

Uma diferença em relação ao Docker: o crictl entende Pods (crictl pods), porque fala a mesma língua do kubelet.

Se o nó tiver vários runtimes, indica-se com qual trabalhar:

crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps

# ou de forma permanente para a sessão
export CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock

Ideias-chave

  • Um contêiner compartilha o kernel do host; uma VM leva seu próprio sistema operacional.
  • Uma imagem é o modelo; um contêiner, uma instância em execução.
  • A orquestração automatiza disponibilidade, escalabilidade, balanceamento e configuração declarativa. O Kubernetes é o padrão.
  • Um cluster tem plano de controle (kube-apiserver, etcd, kube-scheduler, kube-controller-manager) e nós de trabalho (kubelet, runtime, kube-proxy).
  • O kubelet fala com os runtimes por meio da CRI; as imagens seguem o padrão OCI.
  • O dockershim foi removido no Kubernetes 1.24; as imagens do Docker continuam funcionando.
  • ctr para depurar o containerd, nerdctl como substituto do Docker, crictl para depurar qualquer runtime CRI.

Na próxima nota: os recursos básicos do Kubernetes, começando pelos Pods.