Você conhece o termostato da sua sala: você ajusta para 21 °C e não pensa mais nisso. Ele mede a temperatura, compara com seu setpoint, aquece ou desliga, e se alguém abrir a janela ele se recupera sem você fazer nada. Kubernetes faz exatamente isso com seus contêineres. Você declara "quero três cópias da minha API, sempre disponíveis na porta 8080"; ele compara permanentemente o que você quer com o que está rodando, e corrige: um contêiner morre às três da manhã, ele lança um; uma máquina cai, ele move os contêineres para outro lugar; você muda a versão da imagem, ele substitui as cópias uma por uma sem interromper o serviço. Com docker run, você é o termostato: é você que monitora e relança. Nesta lição, você vai ver a diferença com seus próprios olhos: matar um contêiner Docker e constatar que ele fica morto, depois observar as "peças" do Kubernetes que já estão rodando em sua máquina, prontas para fazer esse trabalho para você.
Três situações que todo mundo acaba vivendo apenas com Docker. Um: sua API está rodando em um contêiner em um servidor; uma noite ela falha (vazamento de memória, dependência externa em pane) e ninguém vê até a manhã. --restart=always relança o processo, mas não te diz nada, não verifica se a aplicação realmente responde, e não faz nada se é o servidor inteiro que desliga. Dois: você tem dez contêineres da mesma API atrás de um reverse proxy e precisa entregar a versão 2 sem que os usuários vejam um erro; na mão, isso significa parar, reiniciar e remover do proxy cada contêiner na ordem certa, dez vezes, sem errar. Três: seu tráfego não cabe mais em uma máquina; é preciso adicionar duas, decidir qual contêiner vai para onde, e que os contêineres se encontrem uns aos outros enquanto seus IPs mudam a cada reinicialização.
Esses três problemas são o trabalho de um orquestrador. Docker oferece duas respostas parciais: Compose descreve vários contêineres em um arquivo mas fica em uma única máquina e não monitora nada; Swarm adiciona multi-máquinas e réplicas mas seu ecossistema permaneceu pequeno. Kubernetes (K8s: "K", oito letras, "s"), nascido no Google em 2014 e hoje gerenciado pela CNCF, tornou-se o padrão: é ele que você encontrará em AWS (EKS), Google (GKE), Azure (AKS), OVH, Scaleway, e nos servidores da maioria das empresas.
| Critério | Docker Compose | Docker Swarm | Kubernetes |
|---|---|---|---|
| Escopo | uma máquina | várias máquinas | várias máquinas (milhares) |
| Relançamento automático | restart: apenas | sim | sim, com sondas de saúde (readiness, liveness) |
| Atualização sem interrupção | não | sim, básico | sim, progressivo, com retorno em um comando |
| Balanceamento de carga integrado | não | sim | sim (Services), mais Ingress, DNS interno |
| Escalabilidade automática | não | não | sim (HPA, sobre CPU, memória ou métricas de negócio) |
| Ecossistema (Helm, operadores, nuvem) | — | pequeno | imenso |
| Curva de aprendizado | suave | média | íngreme, daí este curso |
Um cluster Kubernetes é composto de duas partes. O plano de controle decide; os nós executam. No Docker Desktop, os dois vivem na mesma máquina virtual, mas os papéis permanecem os mesmos que em produção.
| Componente | Onde | Papel em uma frase |
|---|---|---|
kube-apiserver | plano de controle | Recebe todos os comandos (kubectl, controladores, kubelet) em HTTPS na porta 6443; nada acontece sem passar por ele. |
etcd | plano de controle | Banco de dados chave-valor que armazena o estado desejado e o estado observado de todo o cluster; perder sem backup significa perder o cluster. |
kube-scheduler | plano de controle | Escolhe em qual nó colocar cada novo Pod (recursos livres, restrições). Ele não lança nada por si mesmo. |
kube-controller-manager | plano de controle | Agrupa os loops de reconciliação: "falta uma réplica? crio uma", "um nó não responde? marco seus Pods para remover". |
kubelet | cada nó | O agente que lê na API os Pods que lhe são atribuídos, pede ao runtime para lançar os contêineres, e sube seu estado. |
kube-proxy | cada nó | Programa as regras de rede para que um endereço de Service chegue ao Pod certo. |
| runtime | cada nó | O software que realmente executa os contêineres: containerd ou CRI-O em geral, docker://29.3.1 no Docker Desktop. |
O núcleo do modelo é a declaração de estado desejado. Você não ordena "lance um contêiner"; você escreve (ou faz gerar) um objeto que diz "quero um Deployment api, imagem traefik/whoami:v1.10, 3 réplicas". A API o registra em etcd. Os controladores depois comparam, em loop e para sempre, esse estado desejado ao estado observado subido pelos kubelets; cada diferença dispara uma ação. É por isso que um Pod suprimido renasce: ninguém o "reiniciou", o controlador simplesmente constatou 2 em vez de 3 e corrigiu. Você verá o loop em ação na lição 04.
Por que aprender no Docker Desktop em vez de minikube, kind ou um cluster em nuvem? Porque você provavelmente já o tem, um clique ativa um cluster completo, as imagens que você constrói com docker build são diretamente visíveis pelo Kubernetes (mesmo armazém de imagens, nenhum registro a instalar), e os Services do tipo LoadBalancer ganham o endereço localhost: você abre seu navegador e isso responde, como em produção atrás de um verdadeiro balanceador de carga. Suas limitações são igualmente claras: um único nó (sem "máquina que cai" para simular), sem NetworkPolicy aplicada por padrão (o CNI integrado as ignora), sem armazenamento em rede nem IP público. Tudo o que você escrever aqui se aplicará tal qual em um cluster real; apenas a forma de acessá-lo mudará.
| Opção | Instalação | Nós | LoadBalancer em localhost | Imagens locais sem registro | Para quem |
|---|---|---|---|---|---|
| Docker Desktop (este curso) | um clique na aplicação | 1 (kubeadm) ou vários (kind, 4.38+) | sim | sim | aprender e desenvolver em seu computador |
| minikube | binário + driver (Docker, VM) | 1, multi possível | via minikube tunnel | via minikube image load | aprender, add-ons prontos para usar |
| kind | binário, nós = contêineres Docker | vários | não (port-forward) | via kind load docker-image | testes automatizados, CI |
| Nuvem (EKS, GKE, AKS…) | conta, faturamento, rede | quantos quiser | verdadeiro IP público | não, é preciso um registro | produção |
Todos os comandos são digitados no PowerShell (Windows) ou em um terminal (macOS, Linux); eles são idênticos. Nada do que segue modifica seu cluster: você observa.
Lançar um contêiner com Docker, como você já sabe fazer. traefik/whoami é uma mini aplicação web que exibe o nome da máquina que responde: é a imagem fil vermelho dos três primeiros módulos.
docker run -d --name essai-whoami -p 8089:80 traefik/whoami:v1.10
docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"Saída real:
a088124875835ed375ddb25a4e4246faf62416a9772751ec7eedecc48bb758f8
NAMES IMAGE STATUS PORTS
essai-whoami traefik/whoami:v1.10 Up Less than a second 0.0.0.0:8089->80/tcp, [::]:8089->80/tcpO que observar: uma linha, Up, a porta 8089 de sua máquina redirecionada para a 80 do contêiner.
Interrogá-lo. O Hostname retornado é o identificador do contêiner: guarde esse detalhe, ele se tornará o nome de um Pod na lição 04.
curl -s http://localhost:8089No Windows, digite curl.exe (o curl sem extensão é um alias do PowerShell para Invoke-WebRequest). Saída real (duas primeiras linhas):
Hostname: a08812487583
IP: 127.0.0.1Observe bem o que vai acontecer: matar o contêiner. É a pane das três da manhã, em tempo acelerado.
docker rm -f essai-whoami
docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Status}}"Saída real:
NAMES STATUSO que observar: a tabela está vazia. Ninguém vai relançar esse contêiner; curl http://localhost:8089 falha agora (curl: (52) Empty reply from server, (56) Recv failure ou Connection refused dependendo de quando Docker Desktop libera a porta). Docker fez o que lhe foi dito, nada mais nada menos. Guarde essa imagem em mente: na lição 04, você fará o mesmo com um Pod e ele voltará antes de você terminar de reler a linha.
Verificar que Kubernetes está lá. Se você não ativou ainda Kubernetes no Docker Desktop, pule para a lição 02 e volte; senão, observe seu cluster.
kubectl get nodesSaída real:
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane 18d v1.34.1O que observar: um único nó, Ready, papel control-plane. No Docker Desktop, a máquina que decide e aquela que executa são a mesma. AGE é a idade de seu cluster, VERSION é a do Kubernetes (1.34 aqui).
Tocar o plano de controle com o dedo. Os quatro componentes "que decidem" rodam eles mesmos como Pods, no namespace reservado kube-system, rotulados tier=control-plane.
kubectl get pods -n kube-system -l tier=control-planeSaída real:
NAME READY STATUS RESTARTS AGE
etcd-docker-desktop 1/1 Running 0 18d
kube-apiserver-docker-desktop 1/1 Running 0 18d
kube-controller-manager-docker-desktop 1/1 Running 0 18d
kube-scheduler-docker-desktop 1/1 Running 0 18dO que observar: etcd, API server, controller-manager e scheduler, cada um 1/1 Running, 0 relançamentos. Seu nome termina com o nome do nó que os hospeda. Compare com a tabela de "Como funciona": você tem agora um rosto para cada papel.
Ver o resto de kube-system. Sem o filtro, você descobre os componentes do nó e as adições próprias do Docker Desktop.
kubectl get pods -n kube-systemSaída real na máquina do curso (a linha metrics-server vem de um componente que instalaremos no módulo 6: você não o tem ainda, é normal):
NAME READY STATUS RESTARTS AGE
coredns-66bc5c9577-6gwhl 1/1 Running 0 18d
coredns-66bc5c9577-9p89d 1/1 Running 0 18d
etcd-docker-desktop 1/1 Running 0 18d
kube-apiserver-docker-desktop 1/1 Running 0 18d
kube-controller-manager-docker-desktop 1/1 Running 0 18d
kube-proxy-2ddzv 1/1 Running 0 18d
kube-scheduler-docker-desktop 1/1 Running 0 18d
metrics-server-7c5fdf4664-6882s 1/1 Running 0 26m
storage-provisioner 1/1 Running 0 18d
vpnkit-controller 1/1 Running 0 18dO que observar: kube-proxy (as regras de rede do nó), coredns ×2 (o DNS interno que permitirá a frontend chamar api por seu nome), e duas peças específicas do Docker Desktop: storage-provisioner (cria volumes hostpath sob demanda, módulo 5) e vpnkit-controller (é ele que dá localhost aos Services LoadBalancer, lição 04). O kubelet, por sua vez, não aparece: não é um Pod mas um serviço de sistema do nó.
Opcional: ver que Kubernetes é "apenas" contêineres. Se a opção "Show system containers" estiver marcada no Docker Desktop (lição 02), Docker te mostra os contêineres que portam o plano de controle. É o mesmo Docker da etapa 1.
docker ps --filter "name=k8s_kube-apiserver_" --filter "name=k8s_etcd_" --filter "name=k8s_kube-scheduler_" --filter "name=k8s_kube-controller-manager_" --format "table {{.Names}}\t{{.Status}}"Saída real:
NAMES STATUS
k8s_etcd_etcd-docker-desktop_kube-system_0b753cb7812d40a401f3a8f63b18f779_0 Up 44 minutes
k8s_kube-apiserver_kube-apiserver-docker-desktop_kube-system_647244f1c75810d936baf4253b7903ef_0 Up 44 minutes
k8s_kube-scheduler_kube-scheduler-docker-desktop_kube-system_b44739859c757a4712b786569a89a1f3_0 Up 44 minutes
k8s_kube-controller-manager_kube-controller-manager-docker-desktop_kube-system_10b0d524eef4b9a12d5827ba17a36f4f_0 Up 44 minutesO que observar: o nome Docker é construído k8s_<contêiner>_<pod>_<namespace>_<uid>_<n>. Se a opção não estiver marcada, a tabela está vazia: nada de grave, é uma configuração de exibição. O AGE de 18 dias do lado do Kubernetes e o Up 44 minutes do lado do Docker não se contradizem: o cluster existe há 18 dias, Docker Desktop foi relançado há 44 minutos e recriou seus contêineres sem perder o estado, armazenado em etcd.
Onde se fala? Um último comando para localizar a porta de entrada que todos os outros usarão.
kubectl cluster-infoSaída real:
Kubernetes control plane is running at https://kubernetes.docker.internal:6443
CoreDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.O que observar: o API server escuta em HTTPS na porta 6443 de kubernetes.docker.internal, um nome que Docker Desktop aponta para sua máquina. Quando Docker Desktop é parado, é essa porta que "recusa a conexão" (lição 02, "Se der problema"). Nada a limpar: o contêiner essai-whoami foi removido na etapa 3 e você não criou nada em Kubernetes.
docker: Error response from daemon: failed to set up container networking: … Bind for 0.0.0.0:8089 failed: port is already allocated → outro contêiner já publica a porta 8089 (mensagem provocada ao lançar duas vezes a etapa 1; se é um programa fora do Docker que a ocupa, a mensagem diz ports are not available). Mude a porta do lado da máquina (-p 8090:80) ou identifique o ocupante: Get-NetTCPConnection -LocalPort 8089 (PowerShell) / lsof -i :8089 (macOS, Linux).kubectl : O termo 'kubectl' n'est pas reconnu (ou kubectl: command not found) → Kubernetes não está ativado, ou o terminal foi aberto antes da ativação e não recarregou o PATH. Lição 02 para ativá-lo; depois feche e reabra o terminal.Unable to connect to the server: dial tcp 127.0.0.1:6443: connectex: No connection could be made because the target machine actively refused it. → ninguém escuta na 6443: Docker Desktop está parado ou Kubernetes está desativado. Lance Docker Desktop, espere "Kubernetes is up and running" na visualização de Kubernetes, tente novamente.kubectl get pods -n kube-system -l tier=control-plane retorna No resources found → você provavelmente está em outro cluster (contexto minikube, kind-…) onde o plano de controle não tem o mesmo rótulo ou não é visível. kubectl config current-context deve responder docker-desktop; a lição 03 explica como corrigir.docker ps --filter "name=k8s_…" não mostra nada enquanto Kubernetes está rodando → "Show system containers (advanced)" não está marcado nas configurações de Kubernetes do Docker Desktop. Não é uma pane; a etapa 7 é opcional.kube-apiserver (porta de entrada, porta 6443), etcd (memória do cluster), kube-scheduler (escolhe o nó), kube-controller-manager (loops de reconciliação).kubelet (o agente), kube-proxy (rede dos Services), runtime (docker://29.3.1 no Docker Desktop); kubectl get pods -n kube-system -l tier=control-plane mostra os quatro primeiros rodando.docker-desktop v1.34.1, imagens locais compartilhadas com Kubernetes, LoadBalancer em localhost; limitações: um único nó, sem NetworkPolicy aplicada, sem verdadeira nuvem.docker-desktop.O nó único do Docker Desktop esconde uma diferença importante com um cluster real: em produção, o plano de controle vive em máquinas dedicadas (muitas vezes três, para que etcd mantenha um quórum) e não hospeda nenhum Pod de aplicação, graças a uma "taint" node-role.kubernetes.io/control-plane:NoSchedule. Aqui, kubectl describe node docker-desktop mostra Taints: <none>: tudo roda no mesmo lugar. A página Kubernetes Components da documentação oficial detalha cada componente visto nesta lição, com as variantes possíveis (cloud-controller-manager, outros runtimes).