ROBERTSILVATECH TREINAMENTOS

Python para DevOps: Automatizando a Validação de Health Checks no Kubernetes
wp-capa-automatizando-a-validacao-de-healthchecks-no-kubernetes

No universo do DevOps, a confiabilidade de aplicações em produção é tão importante quanto a velocidade de entrega. Mas como garantir que seus Pods no Kubernetes não apenas estejam rodando, mas também sejam resilientes, saudáveis e corretamente configurados?

A transformação digital exige mais do que automação básica. Exige visibilidade inteligente e validação proativa. Se existe uma linguagem que se tornou a ferramenta preferida do engenheiro de DevOps para integrar e coordenar serviços complexos, essa linguagem é o Python.

Neste guia prático, você aprenderá a usar Python para DevOps para criar um sistema de auditoria automatizada que valida health checks, resource limits e o estado geral dos Pods em seu cluster Kubernetes. Vamos além do simples “está rodando?” e mergulharemos na qualidade real da sua infraestrutura.

O Problema: Pods Rodando Não Significa Pods Saudáveis

O Cenário Real

Imagine que você é responsável por um cluster Kubernetes com dezenas de aplicações distribuídas em múltiplos namespaces. Tudo parece estar funcionando: o comando kubectl get pods mostra a maioria dos Pods com status “Running”.

Mas então, em uma sexta-feira à noite, começam os problemas:

  • Um Pod trava internamente mas o Kubernetes não detecta, pois não há liveness probe configurado
  • Requisições falham intermitentemente porque alguns containers não têm readiness probe, fazendo o Service enviar tráfego para Pods não prontos
  • O cluster fica sem recursos porque vários Pods não têm resource limits definidos, causando contenção de memória e CPU

O pior: você descobriu tudo isso reagindo a incidentes ao invés de preveni-los.

A Solução: Auditoria Automatizada com Python

A solução é simples e elegante: um script Python que audita proativamente todos os Pods do cluster, verificando:

✅ Se todos os containers estão prontos e saudáveis

✅ Se liveness probes estão configurados (para restart automático)

✅ Se readiness probes estão configurados (para controle de tráfego)

✅ Se resource limits estão definidos (para governança de recursos)

Com essa visibilidade, você transforma sua postura de reativa para proativa, identificando problemas antes que eles afetem os usuários.

Por que Python para DevOps no Kubernetes?

A Vantagem Estratégica do Python

O Python para DevOps não é apenas uma escolha técnica, é uma vantagem estratégica:

Sintaxe Clara e Expressiva

Python permite que você escreva scripts complexos que parecem prosa em inglês. Isso facilita a manutenção e colaboração entre equipes.

Ecossistema Rico

O kubernetes-client é o SDK oficial mantido pela comunidade Kubernetes. Ele traduz toda a complexidade da API REST em classes e métodos intuitivos.

Integração Natural

O SDK herda automaticamente as credenciais do kubectl do arquivo ~/.kube/config, tornando a autenticação trivial.

Rápido Desenvolvimento

A tipagem dinâmica e as bibliotecas robustas permitem criar ferramentas de automação em minutos, não dias.

Benefícios Práticos para o DevOps

Redução de MTTR (Mean Time To Recovery)

Scripts Python podem diagnosticar problemas em segundos, enquanto investigação manual levaria minutos ou horas.

Prevenção de Incidentes

Auditoria contínua identifica configurações problemáticas antes de causarem falhas.

Governança e Compliance

Valide automaticamente que todos os recursos seguem as políticas de segurança e resource management da organização.

Escalabilidade

O mesmo script funciona para 10 ou 1000 Pods, sem esforço adicional.

Mãos à Obra: Preparando o Ambiente

Antes de mergulharmos no código, vamos configurar um ambiente de desenvolvimento profissional. Trabalhar com ambientes virtuais é uma prática essencial no desenvolvimento Python.

Criando o Ambiente Virtual

python3 -m venv .venv

Este comando cria um ambiente isolado na pasta .venv, contendo uma cópia dedicada do interpretador Python e do pip. O isolamento evita conflitos entre dependências de diferentes projetos.

Ativando o Ambiente Virtual

source .venv/bin/activate

No Windows:

.venv\\Scripts\\activate

Você verá (.venv) no prompt, indicando que o ambiente está ativo.

Atualizando o Pip

python3 -m pip install -U pip

A flag -U atualiza o pip para a versão mais recente, garantindo acesso a correções de segurança e melhor compatibilidade.

Instalando as Dependências

pip install kubernetes tabulate
  • kubernetes: cliente oficial Python para a API do Kubernetes
  • tabulate: formatação elegante de dados tabulares no terminal

Salvando as Dependências

pip freeze > requirements.txt

Isso documenta exatamente quais versões estão instaladas, facilitando a reprodução do ambiente.

Ignorando no Git

Adicione ao .gitignore:

.venv/
__pycache__/
*.pyc

O Código Completo: Auditoria Inteligente de Pods

Agora vamos ao código que resolve nosso problema:

import re
from kubernetes import config
from kubernetes import client
from tabulate import tabulate

config.load_kube_config()
v1 = client.CoreV1Api()

emojis = {
    'Running': '🟢',
    'Pending': '🟡'
}

emojis_status = {
    True: '🟢',
    False: '🔴'
}

result = []
pods = v1.list_pod_for_all_namespaces()
if pods:
    for pod in pods.items:
        namespace = pod.metadata.namespace
        name = pod.metadata.name
        containers = pod.spec.containers
        containers_statuses = pod.status.container_statuses
        containers_ready = 0
        containers_liveness_probe = 0
        containers_readiness_probe = 0
        containers_resources = 0
        for container_status in containers_statuses:
            ready = container_status.ready
            if ready:
                containers_ready += 1
        for container in containers:
            liveness_probe = container.liveness_probe
            readiness_probe = container.readiness_probe
            resources = container.resources
            if liveness_probe:
                containers_liveness_probe += 1
            if readiness_probe:
                containers_readiness_probe += 1
            if resources:
                containers_resources += 1
        total_containers = len(containers)
        status = pod.status.phase
        if status in emojis:
            emoji = emojis.get(status)
            status = f'{emoji} {status}'
        containers_status_result = f'{containers_ready}/{total_containers}'
        status_readiness_probe = False
        status_liveness_probe = False
        status_resources = False
        if containers_readiness_probe == total_containers:
            status_readiness_probe = True
        if containers_liveness_probe == total_containers:
            status_liveness_probe = True
        if containers_resources == total_containers:
            status_resources = True

        status_liveness_probe_view = emojis_status.get(status_liveness_probe)
        status_readiness_probe_view = emojis_status.get(status_readiness_probe)
        status_resources_view = emojis_status.get(status_resources)
        row = [
            namespace,
            name,
            status,
            containers_status_result,
            status_liveness_probe_view,
            status_readiness_probe_view,
            status_resources_view
        ]
        result.append(row)

headers = ['Namespace', 'Nome', 'Status', 'READY', 'Liveness', 'Readiness', 'Resources']
print('')
print(tabulate(result, headers=headers, tablefmt='simple'))

Análise Detalhada do Código: Seguindo PEP-8 e PEP-20

Vamos dissecar cada parte do script, entendendo como ele implementa as melhores práticas de Python e DevOps.

Imports e Dependências

import re
from kubernetes import config
from kubernetes import client
from tabulate import tabulate

Importações Essenciais:

  • re: Módulo de expressões regulares do Python (importado mas não utilizado neste script específico)
  • kubernetes.config: Gerencia a configuração e autenticação com o cluster
  • kubernetes.client: Fornece as classes para interagir com a API do Kubernetes
  • tabulate: Biblioteca para formatação elegante de tabelas no terminal

Princípio PEP-8: Os imports estão organizados de forma clara, facilitando a leitura e manutenção do código.

Configuração e Conexão com o Cluster

config.load_kube_config()
v1 = client.CoreV1Api()

config.load_kube_config()

Este é o ponto crucial de autenticação. A função procura automaticamente o arquivo ~/.kube/config (ou a variável de ambiente KUBECONFIG) e carrega:

  • Credenciais de acesso
  • Certificados TLS
  • Contexto ativo do cluster
  • Informações do servidor API

Isso significa que o script herda automaticamente as mesmas permissões que você tem no kubectl, sem necessidade de gerenciar credenciais manualmente.

client.CoreV1Api()

Cria uma instância do cliente da API Core V1 do Kubernetes, que fornece acesso aos recursos fundamentais:

  • Pods
  • Services
  • Namespaces
  • ConfigMaps
  • Secrets
  • Persistent Volumes

Dicionários de Mapeamento Visual

emojis = {
    'Running': '🟢',
    'Pending': '🟡'
}

emojis_status = {
    True: '🟢',
    False: '🔴'
}

Design Intuitivo:

O primeiro dicionário mapeia os estados dos Pods para representações visuais:

  • 🟢 Running: Pod está executando normalmente
  • 🟡 Pending: Pod está aguardando recursos ou agendamento

O segundo dicionário transforma valores booleanos em indicadores visuais:

  • 🟢 True: Configuração presente e correta
  • 🔴 False: Configuração ausente ou incorreta

Princípio PEP-20 “Explicit is better than implicit”:

Os emojis tornam os resultados imediatamente compreensíveis, sem necessidade de consultar documentação.

Inicialização da Estrutura de Dados

result = []
pods = v1.list_pod_for_all_namespaces()

result = []

Inicializa uma lista vazia que armazenará os dados de auditoria de cada Pod. Cada elemento será uma lista representando uma linha da tabela final.

v1.list_pod_for_all_namespaces()

Este método faz uma chamada REST à API do Kubernetes (GET /api/v1/pods) e retorna um objeto contendo todos os Pods de todos os namespaces do cluster. É o equivalente programático de:

kubectl get pods -A

Loop Principal de Auditoria

if pods:
    for pod in pods.items:
        namespace = pod.metadata.namespace
        name = pod.metadata.name
        containers = pod.spec.containers
        containers_statuses = pod.status.container_statuses

Estrutura Condicional:

O if pods: garante que só processamos se houver Pods no cluster, evitando erros em clusters vazios.

Iteração sobre Pods:

pods.items contém a lista de objetos Pod retornados pela API.

Extração de Metadados:

  • pod.metadata.namespace: Namespace onde o Pod está executando
  • pod.metadata.name: Nome único do Pod
  • pod.spec.containers: Lista de especificações dos containers (configuração desejada)
  • pod.status.container_statuses: Lista de status atuais dos containers (estado real)

Contadores de Configurações

        containers_ready = 0
        containers_liveness_probe = 0
        containers_readiness_probe = 0
        containers_resources = 0

Inicialização de Contadores:

Estas variáveis rastreiam quantos containers em cada Pod possuem as configurações críticas:

  • containers_ready: Quantos containers estão prontos e aceitando requisições
  • containers_liveness_probe: Quantos têm health check para detecção de travamentos
  • containers_readiness_probe: Quantos têm health check para controle de tráfego
  • containers_resources: Quantos têm limits/requests de CPU e memória definidos

Princípio PEP-8:

Nomes de variáveis em snake_case descritivos tornam o código autodocumentado.

Análise de Status dos Containers

        for container_status in containers_statuses:
            ready = container_status.ready
            if ready:
                containers_ready += 1

Loop de Verificação de Prontidão:

Este loop itera sobre o status atual de cada container e incrementa o contador quando encontra containers prontos.

Atributo ready:

Um container está “ready” quando:

  • O processo principal está executando
  • O readiness probe (se configurado) está passando
  • O container está pronto para receber tráfego

Esta informação é crítica porque um Pod pode estar “Running” mas com containers não prontos, causando falhas de requisições.

Auditoria de Configurações de Health Checks e Resources

        for container in containers:
            liveness_probe = container.liveness_probe
            readiness_probe = container.readiness_probe
            resources = container.resources
            if liveness_probe:
                containers_liveness_probe += 1
            if readiness_probe:
                containers_readiness_probe += 1
            if resources:
                containers_resources += 1

Loop de Inspeção de Especificações:

Este é o coração da auditoria. Para cada container, verificamos três configurações críticas:

Liveness Probe:

  • Detecta quando um container está travado ou em deadlock
  • Quando falha, o Kubernetes reinicia automaticamente o container
  • Sem isso, containers travados ficam “Running” mas inutilizáveis

Readiness Probe:

  • Determina quando um container pode receber tráfego do Service
  • Quando falha, o Kubernetes remove temporariamente o Pod do balanceamento
  • Sem isso, requisições são enviadas para Pods não prontos, causando erros

Resources (limits/requests):

  • Define quanto CPU e memória o container pode usar
  • Sem isso, um container pode consumir todos os recursos do nó
  • Causa instabilidade e contenção para outros Pods

Princípio PEP-20 “Simple is better than complex”:

Estruturas condicionais diretas tornam a lógica clara e fácil de entender.

Cálculo de Métricas

        total_containers = len(containers)
        status = pod.status.phase
        if status in emojis:
            emoji = emojis.get(status)
            status = f'{emoji} {status}'
        containers_status_result = f'{containers_ready}/{total_containers}'

Contagem Total:

len(containers) nos dá o número total de containers definidos no Pod.

Formatação Visual do Status:

Se o status do Pod está no dicionário emojis, adicionamos o emoji correspondente para visualização imediata.

Formato Ready:

containers_ready/total_containers mostra quantos containers estão prontos em relação ao total (ex: “2/3” significa que 1 container ainda não está pronto).

F-strings (PEP-498):

A sintaxe f'{emoji} {status}' é mais legível e performática que concatenação tradicional ou .format().

Validação Booleana de Conformidade

        status_readiness_probe = False
        status_liveness_probe = False
        status_resources = False
        if containers_readiness_probe == total_containers:
            status_readiness_probe = True
        if containers_liveness_probe == total_containers:
            status_liveness_probe = True
        if containers_resources == total_containers:
            status_resources = True

Lógica de Validação:

Estas variáveis booleanas determinam se todos os containers do Pod têm as configurações necessárias:

  • Só marcamos como True se 100% dos containers estiverem configurados
  • Mesmo um único container sem configuração falha a validação

Por que isso é importante:

Em produção, não basta ter “alguns” containers configurados corretamente. A ausência de health checks ou resource limits em qualquer container pode causar falhas no Pod inteiro.

Princípio “Explicit is better than implicit”:

Inicializamos com False e explicitamente mudamos para True apenas quando a condição é satisfeita.

Preparação dos Indicadores Visuais

        status_liveness_probe_view = emojis_status.get(status_liveness_probe)
        status_readiness_probe_view = emojis_status.get(status_readiness_probe)
        status_resources_view = emojis_status.get(status_resources)

Mapeamento Visual:

Convertemos os booleanos em emojis usando o dicionário emojis_status:

  • True → 🟢 (configurado corretamente)
  • False → 🔴 (configuração ausente)

Benefício UX:

Em uma tabela com centenas de Pods, os emojis permitem identificar problemas instantaneamente sem ler texto.

Construção da Linha de Resultado

        row = [
            namespace,
            name,
            status,
            containers_status_result,
            status_liveness_probe_view,
            status_readiness_probe_view,
            status_resources_view
        ]
        result.append(row)

Estrutura de Dados:

Cada linha contém exatamente 7 elementos na ordem:

  1. Namespace do Pod
  2. Nome do Pod
  3. Status com emoji (🟢 Running, 🟡 Pending, etc.)
  4. Containers prontos no formato “X/Y”
  5. Indicador de liveness probe (🟢 ou 🔴)
  6. Indicador de readiness probe (🟢 ou 🔴)
  7. Indicador de resources (🟢 ou 🔴)

Append à Lista:

Adicionamos cada linha à lista result, construindo gradualmente a tabela completa.

Princípio de Organização:

A ordem dos elementos corresponde exatamente aos headers definidos no final, tornando o código previsível.

Formatação e Exibição da Tabela

headers = ['Namespace', 'Nome', 'Status', 'READY', 'Liveness', 'Readiness', 'Resources']
print('')
print(tabulate(result, headers=headers, tablefmt='simple'))

Headers Descritivos:

Define os cabeçalhos das colunas de forma clara e profissional.

Biblioteca Tabulate:

tabulate() transforma automaticamente a lista de listas em uma tabela ASCII formatada com:

  • Alinhamento correto de colunas
  • Separadores visuais
  • Largura ajustada ao conteúdo

Formato ‘simple’:

Cria uma tabela limpa com linhas separadoras horizontais, ideal para terminais e logs.

Print Vazio Inicial:

O print('') adiciona uma linha em branco antes da tabela, melhorando a legibilidade no terminal.

Executando o Script e Interpretando os Resultados

Salve o código em um arquivo chamado k8s_pod_audit.py e execute:

python k8s_pod_audit.py

Saída Esperada

Namespace      Nome                                   Status        READY  Liveness  Readiness  Resources
-------------  -------------------------------------  ----------  -------  ---------  ----------  -----------
argocd         argocd-server-76f9b87d6b-z6wh6        🟢 Running     1/1  🟢         🟢          🟢
cert-manager   cert-manager-8484dc6898-flww9         🟢 Running     1/1  🟢         🟢          🔴
cp-tools       postgres-contacts-787f8d6cd-pb5rg     🟢 Running     1/1  🟢         🔴          🔴
default        nginx-error                            🟡 Pending     0/1  🔴         🔴          🔴
istio-system   istiod-74f575db47-k6j9r               🟢 Running     1/1  🟢         🟢          🟢

Interpretando os Resultados

Pod Totalmente Saudável (argocd-server):

  • Status: 🟢 Running
  • READY: 1/1 (todos os containers prontos)
  • Liveness: 🟢 (configurado – reinício automático habilitado)
  • Readiness: 🟢 (configurado – controle de tráfego habilitado)
  • Resources: 🟢 (configurado – limites de CPU/memória definidos)

Pod com Problemas de Governança (cert-manager):

  • Status: 🟢 Running (parece saudável)
  • READY: 1/1
  • Liveness e Readiness: 🟢
  • Resources: 🔴 PROBLEMA – sem limites de recursos, pode consumir toda CPU/memória do nó

Pod com Múltiplos Problemas (postgres-contacts):

  • Status: 🟢 Running (mas não confiável)
  • READY: 1/1
  • Liveness: 🟢
  • Readiness: 🔴 PROBLEMA – pode receber tráfego antes de estar realmente pronto
  • Resources: 🔴 PROBLEMA – sem governança de recursos

Pod em Estado Crítico (nginx-error):

  • Status: 🟡 Pending (não está executando)
  • READY: 0/1 (nenhum container pronto)
  • Liveness, Readiness, Resources: 🔴🔴🔴 (todas as configurações ausentes)

Boas Práticas de Python para DevOps Aplicadas

Seguindo PEP-8 (Style Guide)

Nomes de Variáveis Descritivos:

containers_ready = 0
containers_liveness_probe = 0

Ao invés de cr = 0 ou lp = 0, usamos nomes que documentam a intenção.

Snake_case para Variáveis e Funções:

containers_status_result
status_liveness_probe_view

O PEP-8 recomenda snake_case para variáveis e funções, melhorando a legibilidade.

Estruturas Condicionais Claras:

if status in emojis:
    emoji = emojis.get(status)
    status = f'{emoji} {status}'

Lógica simples e direta, fácil de entender e manter.

Seguindo PEP-20 (The Zen of Python)

“Explicit is better than implicit”:

status_readiness_probe = False
if containers_readiness_probe == total_containers:
    status_readiness_probe = True

Deixamos claro quando a validação passa, não assumimos valores implícitos.

“Readability counts”:

emojis = {
    'Running': '🟢',
    'Pending': '🟡'
}

Código que se lê como documentação, sem necessidade de comentários excessivos.

“Simple is better than complex”:

for container_status in containers_statuses:
    ready = container_status.ready
    if ready:
        containers_ready += 1

Loop direto e compreensível, sem otimizações prematuras que sacrificam clareza.

“Flat is better than nested”: O código evita indentações excessivas, mantendo a lógica no mesmo nível sempre que possível.

Melhorias e Extensões Possíveis

Adicionar Type Hints (PEP-484)

from typing import Dict, List

def audit_pods() -> List[List[str]]:
    result: List[List[str]] = []
    # ... resto do código
    return result

Criar Funções Reutilizáveis

def count_ready_containers(containers_statuses) -> int:
    return sum(1 for status in containers_statuses if status.ready)

def has_all_probes(containers, probe_type: str) -> bool:
    return all(getattr(c, probe_type) for c in containers)

Adicionar Docstrings (PEP-257)

def audit_pods():
    """
    Audita todos os Pods do cluster Kubernetes.

    Verifica health checks, resource limits e estado dos containers.

    Returns:
        List[List[str]]: Dados formatados para exibição em tabela
    """

Exportar Resultados

import json

with open('audit_result.json', 'w') as f:
    json.dump(result, f, indent=2)

Integração com CI/CD

import sys

# Verifica se há problemas críticos
has_errors = any(row[4] == '🔴' or row[5] == '🔴' for row in result)
if has_errors:
    print("❌ Problemas de configuração detectados!")
    sys.exit(1)  # Falha o pipeline
else:
    print("✅ Todos os Pods estão configurados corretamente!")
    sys.exit(0)

Filtrar por Namespace

import sys

namespace_filter = sys.argv[1] if len(sys.argv) > 1 else None

if namespace_filter:
    pods = v1.list_namespaced_pod(namespace_filter)
else:
    pods = v1.list_pod_for_all_namespaces()

Adicionar Alertas

def send_alert(problems: List[str]) -> None:
    import requests

    webhook_url = "<https://hooks.slack.com/services/YOUR/WEBHOOK>"
    message = f"⚠️ Problemas encontrados no cluster:\\n" + "\\n".join(problems)

    requests.post(webhook_url, json={"text": message})

Conclusão

A união de Python para DevOps e a API do Kubernetes não é apenas uma tendência, mas o novo padrão para quem busca eficiência e controle total sobre a infraestrutura. O script que analisamos hoje demonstra como poucas linhas de código Python podem transformar tarefas complexas de auditoria em processos automatizados, confiáveis e visuais.

Você aprendeu a:

✅ Conectar-se automaticamente ao cluster Kubernetes usando suas credenciais existentes

✅ Consumir a API do Kubernetes de forma programática

✅ Auditar configurações críticas de health checks e resource limits

✅ Apresentar resultados de forma visual e profissional

✅ Seguir as melhores práticas do PEP-8 e PEP-20

O mais importante: você transformou uma operação reativa (descobrir problemas em produção) em uma operação proativa (identificar problemas antes que causem incidentes).

Este script é apenas o começo. Você pode expandi-lo para:

  • Validar políticas de segurança (NetworkPolicies, PodSecurityPolicies)
  • Auditar imagens de containers vulneráveis
  • Monitorar consumo de recursos em tempo real
  • Gerar relatórios executivos automatizados
  • Integrar com sistemas de alertas e monitoramento

Python para DevOps é mais do que uma ferramenta, é uma mentalidade: automatize o máximo possível, valide continuamente, e deixe que o código trabalhe para você enquanto você foca em desafios mais estratégicos.

Agora é com você: qual será o próximo recurso do seu cluster que você automatizará com Python?