Por que Kubernetes, e por que Docker Desktop

13 min
Público
desenvolvedor ou estudante que já digitou docker run e docker compose up, nunca kubectl
Duração
25 a 35 min
Módulo
1/8
Competência alvo
explicar o que Kubernetes adiciona ao Docker, nomear os componentes do plano de controle e de um nó, e justificar a escolha do Docker Desktop para aprender

Em uma imagem

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

Como funciona

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érioDocker ComposeDocker SwarmKubernetes
Escopouma máquinavárias máquinasvárias máquinas (milhares)
Relançamento automáticorestart: apenassimsim, com sondas de saúde (readiness, liveness)
Atualização sem interrupçãonãosim, básicosim, progressivo, com retorno em um comando
Balanceamento de carga integradonãosimsim (Services), mais Ingress, DNS interno
Escalabilidade automáticanãonãosim (HPA, sobre CPU, memória ou métricas de negócio)
Ecossistema (Helm, operadores, nuvem)pequenoimenso
Curva de aprendizadosuavemé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.

ComponenteOndePapel em uma frase
kube-apiserverplano de controleRecebe todos os comandos (kubectl, controladores, kubelet) em HTTPS na porta 6443; nada acontece sem passar por ele.
etcdplano de controleBanco 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-schedulerplano de controleEscolhe em qual nó colocar cada novo Pod (recursos livres, restrições). Ele não lança nada por si mesmo.
kube-controller-managerplano de controleAgrupa os loops de reconciliação: "falta uma réplica? crio uma", "um nó não responde? marco seus Pods para remover".
kubeletcada 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-proxycada nóPrograma as regras de rede para que um endereço de Service chegue ao Pod certo.
runtimecada 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çãoInstalaçãoNósLoadBalancer em localhostImagens locais sem registroPara quem
Docker Desktop (este curso)um clique na aplicação1 (kubeadm) ou vários (kind, 4.38+)simsimaprender e desenvolver em seu computador
minikubebinário + driver (Docker, VM)1, multi possívelvia minikube tunnelvia minikube image loadaprender, add-ons prontos para usar
kindbinário, nós = contêineres Dockerváriosnão (port-forward)via kind load docker-imagetestes automatizados, CI
Nuvem (EKS, GKE, AKS…)conta, faturamento, redequantos quiserverdadeiro IP públiconão, é preciso um registroprodução

Passo a passo

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.

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

    bash
    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:

    text
    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/tcp

    O que observar: uma linha, Up, a porta 8089 de sua máquina redirecionada para a 80 do contêiner.

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

    bash
    curl -s http://localhost:8089

    No Windows, digite curl.exe (o curl sem extensão é um alias do PowerShell para Invoke-WebRequest). Saída real (duas primeiras linhas):

    text
    Hostname: a08812487583
    IP: 127.0.0.1
  3. Observe bem o que vai acontecer: matar o contêiner. É a pane das três da manhã, em tempo acelerado.

    bash
    docker rm -f essai-whoami
    docker ps --filter name=essai-whoami --format "table {{.Names}}\t{{.Status}}"

    Saída real:

    text
    NAMES     STATUS

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

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

    bash
    kubectl get nodes

    Saída real:

    text
    NAME             STATUS   ROLES           AGE   VERSION
    docker-desktop   Ready    control-plane   18d   v1.34.1

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

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

    bash
    kubectl get pods -n kube-system -l tier=control-plane

    Saída real:

    text
    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          18d

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

  6. Ver o resto de kube-system. Sem o filtro, você descobre os componentes do nó e as adições próprias do Docker Desktop.

    bash
    kubectl get pods -n kube-system

    Saí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):

    text
    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          18d

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

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

    bash
    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:

    text
    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 minutes

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

  8. Onde se fala? Um último comando para localizar a porta de entrada que todos os outros usarão.

    bash
    kubectl cluster-info

    Saída real:

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

Se der problema

  • 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.
  • O comando 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.

Para reter

  • Docker executa um contêiner e para; Kubernetes mantém um estado desejado (quantas cópias, qual imagem, qual porta) e corrige em loop a diferença com o estado observado.
  • Plano de controle = kube-apiserver (porta de entrada, porta 6443), etcd (memória do cluster), kube-scheduler (escolhe o nó), kube-controller-manager (loops de reconciliação).
  • Nó = 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: um clique, um nó docker-desktop v1.34.1, imagens locais compartilhadas com Kubernetes, LoadBalancer em localhost; limitações: um único nó, sem NetworkPolicy aplicada, sem verdadeira nuvem.
  • Os manifestos que você escreverá aqui funcionam sem mudança em minikube, kind, EKS, GKE ou AKS.
  • Próxima lição: instalar Docker Desktop em seu sistema, ativar Kubernetes, e ver aparecer o contexto docker-desktop.

Para ir mais longe

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