Aqui estão todos os principais conceitos do Kubernetes, explicados da maneira simples. A maioria das pessoas memoriza os conceitos do Kubernetes. É por isso que nada fica na memória. Cada conceito do Kubernetes tem uma história, e cada conceito no Kubernetes existe para resolver um problema.

Você executa seu aplicativo como um Pod. Ele trava. Ninguém o reinicia. Simplesmente desaparece. Então você usa um Deployment. Se um pod falha, outro volta a funcionar. Você quer 3 em execução, então ele cria e mantém 3 pods em execução.
Cada Pod recebe um novo endereço IP ao ser reiniciado. Outro serviço precisa se comunicar com seu aplicativo, mas os endereços IP estão sempre mudando. Você não pode codificá-los diretamente no código. Então você usa um Service. Um endereço estável que sempre encontra os pods usando rótulos, não endereços IPs. Os Pods falham e voltam a funcionar. O Service não se importa e sempre estará acessível.
Agora você tem 10 serviços e 10 balanceadores de carga. Sua fatura de nuvem não se importa que apenas 6 desses servidores sejam suficientes para a sua demanda. Então você usa o Ingress, um LoadBalance (balanceador de carga) e todos os serviços atrás dele realizando um roteamento inteligente. Mas o Ingress são apenas regras. Ninguém as executa. Então você adiciona um Controlador de Entrada (Ingress Controller). Nginx, Traefik, Controlador de Balanceador de Carga da AWS. Agora as regras realmente funcionam.
Seu aplicativo precisa de uma configuração, então você a codifica diretamente dentro do contêiner. Banco de dados incorreto no ambiente de teste. Chave de API incorreta no ambiente de produção. Você precisa recriar a imagem sempre que a configuração é alterada. Então você usa um ConfigMap. A configuração reside fora do contêiner, sendo injetada em tempo de execução. A mesma imagem é executada em ambientes de desenvolvimento, teste e produção com configurações diferentes.
Mas agora a senha do seu banco de dados está armazenada em um ConfigMap, sem criptografia. Qualquer pessoa com acesso básico ao kubectl pode lê-la. Isso não é um erro. É um incidente de segurança. Então você usa um Secret. Dados sensíveis armazenados separadamente, com seus próprios controles de acesso. A imagem usa mas nunca vê isso.
Em alguns dias, 100 usuários, em outros, 10.000. Você aumenta manualmente a capacidade para 8 pods durante o pico de demanda e os observa ociosos a noite e reduz. Não dá para ficar monitorando o cluster para sempre. Então você usa HPA. Se o uso da CPU ultrapassa 70%, novos pods são adicionados automaticamente. O Ingress Controler distribui o trafego para todos os pods automaticamente. Se a demanda e o tráfego cairem, reduz a quantidade de pods.
Mas agora seus nós estão cheios. HPA cria novos pods que estão pendentes por falta de recursos. O HPA fez seu trabalho, mas o cluster não tinha onde colocá-los. Então você usa o Karpenter. Quando os pods ficam presos no status "Pendente", um novo nó é ativado, se a carga cai, um nó desaparece. Você paga apenas pelo que usa.
Um dos pods começa a consumir 64 GB de memória. Ninguém avisou o Kubernetes que isso não deveria acontecer. Isso priva de rercursos todos os outros pods daquele nó. Um efeito cascata se inicia. Um pod descontrolado, sem limites, derruba tudo ao seu redor. Assim, você usa Resource Requests e Resource Limits. Os Requests informam ao Kubernetes o mínimo necessário para o seu pod rodar. Os Limits garantem que nenhum pod possa consumir todos os recursos ao seu redor.
Você implanta uma nova imagem e todos os pods são reiniciados ao mesmo tempo. Durante 30 segundos, seu aplicativo fica completamente fora do ar. Os usuários veem erros. Seu telefone de plantão começa a tocar. Então você usa uma estratégia RollingUpdate. O Kubernetes encerra um pod, inicia um novo, espera até que ele esteja íntegro e, em seguida, passa para o seguinte. Seus usuários nem percebem que a nova implantação ocorreu.
Mas a sua nova versão tem um bug silencioso. As verificações de integridade são aprovadas, mas o aplicativo retorna dados incorretos. Então você usa um Readiness Probe. O Kubernetes só envia tráfego para um pod quando ele está realmente pronto para processá-lo. Pods com problemas ficam automaticamente fora de rotação.
O pod do seu banco de dados reinicia e perde todos os seus dados. Os contêineres não mantêm estado. Cada reinicialização é um novo começo com um disco vazio. Isso é ótimo para sua API. Mas não para o banco de dados. Assim, você utiliza PersistentVolumes e PVCs. O armazenamento ocorre fora do ciclo de vida do pod. Seus dados sobrevivem a falhas, reinicializações e reagendamentos dos pods.
Mas você tem um único pod de banco de dados. Após reiniciar, ele é reprogramado para um nó diferente e precisa do mesmo disco para funcionar. PVCs funcionam para Deployments, mas eventos ordenados são perdidos. Então você usa um StatefulSet. Cada pod recebe um nome estável, uma identidade estável e um volume estável associado a ele. O pod-0 é sempre pod-0, não um hash aleatório.
Seu processo de treinamento de aprendizado de máquina leva 6 horas para ser concluído. Você precisa de exatamente uma execução completa. Um Deployment continuaria reiniciando indefinidamente após sua conclusão. Então você usa um Job. O Kubernetes executa um Job até a conclusão dele e para. Sem reinicializações após o sucesso. Sem necessidade de monitoramento constante. Realiza apenas uma única execução limpa.
Você precisa de um coletor de logs ou um agente de monitoramento em cada nó. Um Deployment não garante um pod por nó. Então você usa um DaemonSet. Um pod é instalado automaticamente em cada nó, incluindo dinamicamente os novos nós que acabou de adicionar ao cluster. Sem agendamento manual, sem nós esquecidos.
Sua equipe continua implantando acidentalmente no namespace errado e apagando as configurações de produção. Sem mecanismos de proteção, um único comando kubectl incorreto pode causar danos severos. Então você usa RBAC para definis quais ações são permitidas. Os RoleBindings as associam a usuários ou contas de serviço. Seu desenvolvedor júnior pode ler os logs, mas não pode excluir implantações em produção.
Você tem um serviço crítico e um processo de análise de log rodando no mesmo nó. O processo de análise de log gera picos de uso de CPU e memória, consumindo recursos que poderiam ser utilizados pelo serviço crítico. A latência do seu serviço crítico triplica a cada execução de relatório. Então você usa PriorityClasses. Os pods de serviço crítico têm alta prioridade, os pods de análise de log têm baixa prioridade. Quando os nós ficam sem recursos, o Kubernetes remove primeiro o job de log.
Três times compartilham um mesmo cluster. Um time descontrolado continua deixando os outros sem recursos. Então você usa o ResourceQuota. Cada namespace tem um limite máximo rígido de CPU, memória e quantidade de objetos. Um time não pode sobrecarregar o cluster e prejudicar os demais.
Você precisa executar o Kafka no Kubernetes. O Kafka possui brokers, tópicos, liderança de partições e características muito específicas de como deve ser operado. Os StatefulSets, por si só, não conhecem nada disso. Então você usa um CRD. Você ensina ao Kubernetes o que é um cluster Kafka. Agora o kubectl entende o Kafka como um objeto de primeira classe.
Mas o CRD é apenas um esquema. Ninguém interage com ele. Você cria um recurso KafkaCluster e nada acontece. Então você adiciona um Operator. Ele monitora seus recursos personalizados e toma medidas de provisionando brokers, rebalanceamento e gerenciando atualizações contínuas. Ele incorpora o conhecimento operacional que um especialista humano teria. O Strimzi faz isso para o Kafka. O Operator do Prometheus faz isso para o monitoramento.
Seus nós de GPU são caros. Pods de API comuns continuam sendo executados neles. Oito dólares por hora são desperdiçados servindo apenas JSON. Então você usa Taints e Tolerations. Os nós da GPU são marcados com Taints, então somente os pods que toleram explicitamente esse taint podem ser executados neles. Seus pods da API nunca mais serão lançados em nós de GPU.
Mas Tolerations é apenas permissão, não garantia. Seus pods de aprendizado de máquina podem ser executados em nós com GPU, mas ainda podem acabar caindo em nós com CPU. Então você adiciona Node Affinity. Seus pods de aprendizado de máquina agora declaram um requisito obrigatório para executar em nós com o rótulo gpu=true. Permissão mais preferência se tornam uma garantia.