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.
- 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.
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.
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 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.