← Professional Cloud DevOps Engineer: entrega e fiabilidade
14 / 20 · 135 MIN

Observabilidade: populações e diagnóstico

Investigar perdas de telemetria, interpretar consultas e alertas e usar traces com contexto e população explícitos.

1. Seguir o percurso da evidência

Uma equipa fictícia acompanha um serviço de consulta de posições. A aplicação continua a responder, mas deixaram de chegar traces. Desenha o percurso antes de alterar configurações: aplicação, transporte, receiver, processamento, exporter e destino. Em cada fronteira procura uma observação que consiga distinguir entrada, transformação e entrega. Um processo Collector ativo não prova que a aplicação o alcança. Um contador de receção crescente não prova que o destino aceitou os dados. Um dashboard vazio pode resultar de uma pesquisa incorreta mesmo quando a entrega está a funcionar. No primeiro ensaio, a aplicação regista falhas de ligação OTLP e a receção não aumenta. Investiga endpoint, protocolo e acesso ao receiver. No segundo, a receção aumenta mas o exporter regista PermissionDenied. Verifica a identidade efetiva e a autorização no destino configurado. Aumentar réplicas ou sampling não resolve essa recusa. Para validar a correção, produz uma amostra sintética identificável, acompanha-a no percurso e confirma a chegada. Regista a hora e o identificador do ensaio. O responsável APS deve conseguir explicar qual segmento foi demonstrado e qual continua por investigar, evitando apresentar a saúde de um componente como prova da cadeia completa.

2. Dimensionar filas e limitar o diagnóstico

Uma fila vazia com capacidade para 240 batches recebe 12 batches/s e não consegue exportar. No modelo simplificado, fica cheia em 20 segundos. Esta conta usa batches, não spans nem bytes, e pressupõe ausência de outras filas, saídas ou expiração no intervalo. Antes de aplicar o raciocínio a um Collector real, confirma a unidade e configuração da fila do exporter usado. Acompanha ocupação, recusas, retries e espaço disponível. Uma fila maior compra tempo; a recuperação continua a exigir saída sustentável e tratamento do acumulado. Persistência em armazenamento duradouro pode proteger dados em fila contra reinícios, mas não cria espaço infinito nem recupera dados perdidos antes da gravação. No plano de alteração, indica o que pode acontecer aos dados pendentes e como será verificada a entrega depois do reinício. Se precisares de observar payloads, usa dados sintéticos com a mesma estrutura e uma saída de diagnóstico controlada. Evita copiar números de conta para logs de acesso alargado. Base64 é reversível e não resolve essa exposição. O exercício local desta aula faz contas sobre um inventário fictício; não executa um Collector, não implementa persistência e não valida a entrega de telemetria real.

3. Definir o que as sondas e a auditoria cobrem

Uma resposta HTTP 200 em /health pode coexistir com uma consulta autenticada falhada. Define o percurso funcional que interessa ao negócio: autenticar uma identidade de teste, consultar dados sintéticos e confirmar um resultado conhecido. Estabelece os passos de limpeza quando o ensaio cria estado. Aumentar apenas a frequência de /health melhora a observação desse endpoint, sem acrescentar cobertura funcional. Na passagem a RUN, documenta o que é testado, de onde, com que identidade e como distinguir indisponibilidade da aplicação de expiração da credencial de teste. Para auditoria, confirma primeiro se o evento é gerado. Uma operação DATA_READ num serviço que não a regista por defeito precisa da configuração aplicável ativa. Admin Activity não substitui todas as leituras de dados. Verifica também exceções, configuração herdada, routing e permissões de leitura antes de concluir que ninguém acedeu. Valida uma operação autorizada nova e o respetivo registo. Um sink não cria retroativamente auditoria que nunca foi gerada. Estes exemplos não definem obrigações legais ou políticas de um banco: ajudam a formular perguntas técnicas concretas para os responsáveis de segurança e de operação.

4. Pesquisar e conservar sem inventar dados

Cria três entradas sintéticas: result="ok", result="failed" e uma sem result. Em Cloud Logging, jsonPayload.result!="ok" não inclui o campo ausente. NOT (jsonPayload.result="ok") inclui-o, porque nega uma comparação que falha nesse caso. Escreve o resultado esperado antes de executar a pesquisa. Confirma também parênteses: nesta linguagem OR tem precedência sobre AND. Se a intenção é (A AND B) OR C, conserva essa expressão explícita. Não transportes automaticamente regras de SQL para outro motor de pesquisa. Ao filtrar logName por igualdade, usa o identificador observado, incluindo %2F quando a barra faz parte do nome codificado do log. A ausência de resultados exige ainda verificar janela, âmbito e conservação. Aumentar retenção de 30 para 90 dias não recria entradas definitivamente eliminadas sem cópia. Regista a lacuna e decide como preservar evidência futura. Bloquear um log bucket é irreversível; um plano que promete desbloquear na semana seguinte precisa de ser corrigido antes da execução. Separa essa decisão da criação de sinks e da permissão de leitura. Os exemplos da aula são exercícios de interpretação; nenhuma pesquisa Cloud Logging ou alteração de retenção é executada pelo laboratório Python.

5. Ler a regra de alerta completa

Uma política pode combinar CPU elevada numa VM com memória elevada noutra. Se o requisito é encontrar ambas na mesma VM, usa a semântica de correspondência de recurso e preserva labels compatíveis. AND simples e AND_WITH_MATCHING_RESOURCE não representam a mesma condição. Confirma ainda a janela de reteste: quatro minutos acima do limiar, uma medição alinhada abaixo e três minutos acima não satisfazem seis minutos contínuos. A amostra saudável reinicia a contagem. A política deve reagir à condição operacional pretendida, não apenas produzir uma notificação num ensaio conveniente. O fecho também precisa de interpretação. Quando dados em falta são tratados como não violação, uma falha de recolha pode fazer fechar um alerta sem provar recuperação da aplicação. Acompanha a saúde da recolha e documenta essa escolha. Para métricas acumuladas em PromQL, calcula rate por série antes de somar, de modo a conservar a deteção de resets individuais. Confirma a unidade do resultado no dashboard. Ao investigar CPU global baixa com uma zona saturada, recupera o agrupamento por zona e relaciona-o com routing e carga. A redução global remove distinções que podem ser essenciais para a decisão.

6. Exercício: janelas e populações diferentes

Guarda o código como run.py e executa python3 run.py com Python 3.13. O modelo usa Fraction para comparar valores exatos. Um alvo de 99,8% permite 0,2% de erros; observar 1% produz burn rate de cinco. A regra didática exige valores superiores a seis nas duas janelas. Prevê os resultados de both_high, short_recovered e equal_threshold antes de ler o JSON. Se uma entrada for desconhecida, este modelo devolve null: não pretende reproduzir a política de dados em falta de um serviço real. Compara as 36 combinações de janelas verificadas pelo programa. O segundo ensaio tem 200 erros em 20 000 pedidos, mas conserva todos os erros e apenas 300 sucessos nos traces. A taxa completa é 1%; a amostra selecionada tem 40%. O programa varia a quantidade de sucessos retidos em 101 casos, mantendo a população original. Confirma que mais detalhe por trace não corrige a seleção desigual. Por fim, verifica reteste, capacidade de fila e p95: 9900 pedidos de 100 ms e 100 de 20 segundos continuam a ter p95 de 100 ms pelo método nearest-rank. O laboratório testa aritmética e decisões explícitas, sem executar PromQL, Cloud Monitoring ou um sampler.

7. Correlacionar traces sem perder contexto

Se A chama B e ambos exportam spans, mas B cria sempre um trace independente, verifica injeção e extração do contexto na chamada. Dar nomes iguais aos spans não estabelece a relação de parent. Usar um identificador fixo para todos os pedidos mistura execuções. Para ligar uma entrada enviada diretamente pela Logging API a um trace existente, preenche trace com o identificador real. Na documentação consultada, o formato simples TRACE_ID é preferido; projects/PROJECT_ID/traces/TRACE_ID continua descrito como legado. Um UUID de negócio em jsonPayload.request_id pode ser útil na pesquisa, mas não substitui automaticamente essa ligação. Define também fronteiras para baggage. O contexto pode seguir para serviços externos através de clientes instrumentados; tokens de sessão e dados de conta não devem ser incluídos no exemplo proposto. Usa identificadores aprovados e verifica o percurso real de propagação. Ao analisar traces retidos por erros ou latência, documenta os critérios de sampling antes de calcular percentagens globais. A evidência detalhada ajuda a investigar pedidos concretos; a estimativa do SLI precisa de uma população adequada. No handover, inclui origem do trace, política de seleção, campos de correlação e responsáveis por cada serviço.

8. Investigar hipóteses e entregar conclusões úteis

Uma release coincide com uma mudança de routing e a latência aumenta. Uma interpretação automática atribui a falha à release. Regista essa conclusão como hipótese e procura observações que distingam versão, região e dependência. Planeia uma mitigação controlada com critério de validação. Mudar tudo ao mesmo tempo e atribuir a melhoria a uma única alteração não produz uma explicação sólida. Durante o incidente, recuperar serviço continua a ser prioritário, mas é possível conservar a sequência de ações e separar o que foi observado do que se infere. Prepara o relatório com três perguntas concretas: que população foi medida, que parte do percurso foi demonstrada e que decisão resulta disso? No caso da fila em memória, descreve o risco antes do reinício e verifica uma entrega nova depois. No caso dos traces selecionados, conserva a taxa do contador completo e usa a amostra para diagnóstico. Em inglês, pratica: “The error counter covers all requests; retained traces are selected for diagnosis and use a different denominator.” O resumo operacional é seguir fronteiras, confirmar geração, pesquisar com semântica correta, interpretar a regra de alerta e comunicar limitações de cobertura. Estes critérios ligam observabilidade a SRE, gestão de incidentes e custo de retenção.

"""Original local observability arithmetic; no query engine or alert service."""
from fractions import Fraction
from hashlib import sha256
from itertools import product
from pathlib import Path
import json
import platform


def ratio(errors, total):
    if type(errors) is not int or type(total) is not int:
        raise ValueError('counts must be integers, not booleans')
    if not 0 <= errors <= total:
        raise ValueError('counts must satisfy 0 <= errors <= total')
    return Fraction(errors, total) if total else None


def burn(errors, total, target=Fraction(998, 1000)):
    if not isinstance(target, Fraction) or not 0 < target < 1:
        raise ValueError('target must be an exact fraction between zero and one')
    value = ratio(errors, total)
    return value/(1-target) if value is not None else None


def combined(long_rate, short_rate, threshold=Fraction(6)):
    # Unknown input leaves this educational decision unevaluable.
    # This is not a claim about a configured vendor missing-data policy.
    if long_rate is None or short_rate is None:
        return None
    return long_rate > threshold and short_rate > threshold


def consecutive_high(values):
    longest = current = 0
    for value in values:
        if type(value) is not bool:
            raise ValueError('aligned condition inputs must be booleans')
        current = current+1 if value else 0
        longest = max(longest, current)
    return {'longest': longest, 'current': current}


def run():
    observed = burn(100, 10000)
    assert observed == 5
    fixtures = {
        'both_high': combined(Fraction(8), Fraction(8)),
        'short_recovered': combined(Fraction(8), Fraction(2)),
        'long_recovered': combined(Fraction(2), Fraction(8)),
        'equal_threshold': combined(Fraction(6), Fraction(8)),
        'unknown_short': combined(Fraction(8), None),
        'zero_population': burn(0, 0),
    }
    assert fixtures == dict(both_high=True, short_recovered=False, long_recovered=False,
                            equal_threshold=False, unknown_short=None, zero_population=None)
    combinations = 0
    for long_rate, short_rate in product([None, Fraction(0), Fraction(5), Fraction(6), Fraction(7), Fraction(10)], repeat=2):
        expected = None if long_rate is None or short_rate is None else min(long_rate, short_rate) > 6
        assert combined(long_rate, short_rate) == expected
        combinations += 1
    complete = ratio(200, 20000)
    selected = ratio(200, 500)  # 200 errors + 300 selected successes, fictional inventory.
    assert complete == Fraction(1,100) and selected == Fraction(2,5)
    selection_checks = 0
    for selected_successes in range(0, 19801, 198):
        sample = ratio(200, 200+selected_successes)
        assert sample >= complete
        assert (sample == complete) == (selected_successes == 19800)
        selection_checks += 1
    retest = consecutive_high([True]*4+[False]+[True]*3)
    assert retest == {'longest':4, 'current':3}
    assert Fraction(240, 12) == 20
    ordered = [100]*9900+[20000]*100
    rank = (95*len(ordered)+99)//100
    assert rank == 9500 and ordered[rank-1] == 100 and max(ordered) == 20000
    invalid = 0
    for args in [(-1,10),(11,10),(1,-1),(True,10),(1,False),(1.5,10),(1,'10')]:
        try:
            ratio(*args)
        except ValueError:
            invalid += 1
    assert invalid == 7
    target_invalid = 0
    for target in [Fraction(0),Fraction(1),Fraction(-1),0.998]:
        try:
            burn(1,100,target)
        except ValueError:
            target_invalid += 1
    assert target_invalid == 4
    return {'scriptSha256':sha256(Path(__file__).read_bytes()).hexdigest(),
            'pythonVersion':platform.python_version(),'fixtures':fixtures,
            'burnRate':str(observed),'populationErrorRate':str(complete),
            'selectedErrorRate':str(selected),'windowCombinations':combinations,
            'selectionChecks':selection_checks,'retest':retest,'queueSeconds':20,
            'nearestRankP95Milliseconds':100,'maximumMilliseconds':20000,
            'invalidCountCases':invalid,'invalidTargetCases':target_invalid,
            'network':False,'persistentWrites':False,'vendorExecution':False,
            'independentVerification':False,
            'limitations':'Fictional exact counts and precomputed window rates. No Cloud Monitoring, PromQL, collector, sampling implementation, late-data model or production alert lifecycle.'}


if __name__ == '__main__':
    print(json.dumps(run(), ensure_ascii=False, indent=2))
NA PRÁTICA

Um contador completo indica 1% de erros e os traces selecionados mostram 40%; ambas as observações podem estar corretas.

Armadilhas comuns

Confundir receção com entrega, fecho com recuperação, amostra com população e correlação temporal com causa confirmada.

Tópicos relacionados: SRE e orçamento de erro · FinOps e conservação de telemetria · Gestão de incidentes e comunicação

Leva esta ideia contigo

A observabilidade só sustenta uma decisão quando o percurso, a população e a regra de interpretação estão claros.

Criar conta

Referência: Troubleshooting the Collector · Current linked guide; edition date unconfirmed (2026-09-30 inspection)

Google Cloud é uma marca comercial de Google LLC. A dr.pt é uma plataforma de preparação independente e não está afiliada, associada, patrocinada, autorizada nem aprovada por Google. Os conteúdos e as perguntas são originais, não são perguntas oficiais de exame, e concluir os nossos testes não atribui nem garante qualquer certificação. Os nomes são usados apenas para identificar o tema. Todas as outras marcas pertencem aos respetivos titulares.