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

Controlo de ambientes: planos, estado e atualizações

Liga a aprovação ao plano e ao destino, preserva identidade durante refactors e avalia capacidade antes de interromper workloads.

1. Definir o que a aprovação cobre

Uma mudança de infraestrutura precisa de ligar a intenção aos objetos que serão alterados. Num exercício APS, o ticket aprova o plano A para preview-west, mas o runner recebe o plano B com destino prod-west. O nome do job continua a ser deployment-approved. Esse nome não demonstra que a aprovação cobre o pacote ou o ambiente apresentados. Identifica a revisão da configuração, os inputs, o plano, o destino, o backend e a identidade de execução. Mantém a referência da aprovação num local cujo acesso seja controlado pelo processo acordado. Se o próprio emissor puder substituir plano e aprovação sem controlo, comparar hashes apenas confirma duas peças que ele próprio forneceu. O objetivo de um checksum é reconhecer uma representação concreta. Não prova que alguém com autoridade reviu as ações, que há quota suficiente ou que a aplicação suporta a mudança. A equipa de projeto deve definir o critério de entrada na janela e quem responde por cada verificação. O suporte precisa de conseguir explicar por que motivo a execução foi interrompida quando a identidade do pacote divergiu. Uma divergência documentada é uma tarefa a resolver; não deve desaparecer porque alguém copiou o novo hash para o ticket sem rever o conteúdo.

2. Distinguir plano revisto e plano executado

Um plan sem ficheiro guardado permite rever uma previsão. Um apply posterior sem receber esse ficheiro volta a planear. Mesmo que o código não tenha mudado, intervenções externas podem alterar o resultado. Num processo que exige aprovação das ações concretas, guarda o plano, revê-o e conserva a ligação ao artefacto usado na execução. Passar um plano guardado a apply executa as decisões desse plano sem nova confirmação interativa. Por isso, a autorização do job tem de existir antes dessa chamada; não se deve contar com uma pergunta adicional do CLI como controlo final. Se a intenção mudou, gera um plano novo e faz a revisão correspondente. Não se podem introduzir novas opções de planeamento, como variáveis diferentes, ao aplicar um plano guardado. Alterar tfvars depois de produzir o ficheiro também não muda as decisões já nele guardadas. Durante o handover, distingue «o commit foi aprovado» de «estas ações, neste destino, foram revistas». O primeiro pode ser uma condição necessária do processo, mas não demonstra o segundo. No exercício, se a janela termina antes de rever um plano novo, a opção é reagendar ou obter a decisão prevista no processo de mudança, em vez de reutilizar silenciosamente a aprovação anterior.

3. Conservar visibilidade sobre drift e segredos

Após uma intervenção que aumentou capacidade de quatro para oito unidades, um plano sem refresh pode continuar a raciocinar com informação anterior. A opção refresh=false reduz leituras, mas também pode ocultar a mudança externa. Não uses esse resultado como prova de ausência de drift. Recolhe observação atual e decide se a configuração deve incorporar a mudança ou se o recurso deve regressar à intenção anterior. Um refresh-only atualiza o registo do observado; não demonstra que toda a infraestrutura corresponde à configuração pretendida. Cada afirmação no relatório deve manter esse âmbito. Também deves limitar conclusões após usar target numa recuperação excecional. O sucesso do recurso dirigido não prova convergência dos restantes. Produz um plano completo para avaliar o que ficou fora da intervenção. Ao guardar evidência, lembra-te de que sensitive não significa que todo o output JSON foi expurgado. Um relatório de show -json pode conter valores em texto simples. Distribui uma vista adequada ao público e protege o artefacto completo quando necessário. A equipa de governance pode precisar de ações e identificadores sem precisar de credenciais. Conservar rastreabilidade não exige publicar segredos num ticket ou num log de acesso alargado.

4. Reconhecer o state e controlar dependências

A lineage ajuda a distinguir estados; o serial representa revisões dentro dessa identidade. Num state push, uma lineage diferente ou um serial remoto superior acionam proteções. Um snapshot local com serial 90 não ganha legitimidade sobre um destino de outra lineage só por ter um número maior. Da mesma forma, um ficheiro chamado final-approved com serial 41 pode omitir alterações do remoto 44. Antes de qualquer escrita de recuperação, confirma origem, destino, cópias de segurança e alterações intervenientes. Forçar a escrita remove proteções; não reconcilia os dados. Editar números ou identidades para eliminar o erro deixa a causa por resolver. O ficheiro .terraform.lock.hcl trata de outra dimensão: versões e checksums de providers. Não fixa versões de módulos remotos. Controla essas versões pela configuração adequada e inclui alterações no processo de revisão. Executar init -upgrade pode selecionar um provider mais recente permitido pela restrição, mudando a base usada para produzir o plano. Uma falha de checksum num espelho exige investigar pacote, origem e plataforma. Apagar o lock file para aceitar o que aparecer troca a referência sem esclarecer a divergência. O handover deve distinguir bloqueio de state, seleção de dependências e identidade do artefacto; são verificações diferentes.

5. Refatorar e transferir gestão sem duplicação

Quando um recurso muda de endereço dentro de uma configuração, uma alteração apenas textual pode parecer ao planeador uma remoção seguida de criação. Se a intenção é conservar o objeto existente, um moved block permite representar a associação entre endereço anterior e novo em Terraform compatível. Revê o plano resultante: o bloco resolve a mudança de endereço, mas não garante que outras alterações de atributos sejam inofensivas. No exercício, tipo e atributos mantêm-se e o endereço novo está livre. Assim, o participante consegue isolar a causa do destroy/create que não pretendia. Uma passagem para outra ferramenta tem intenção diferente. Num removed block, destroy=false permite retirar a associação do state sem destruir o recurso remoto. Isso não instala automaticamente um novo gestor, não cancela custos e não transfere responsabilidades humanas. Define quem passa a manter o recurso e quando a pipeline anterior deixa de emitir alterações. Importar o mesmo objeto em dois states ativos não cria uma gestão coordenada; pode criar dois escritores com intenções incompatíveis. A equipa deve ensaiar a passagem, conservar evidência da identidade do recurso e confirmar que o novo plano não propõe substituições inesperadas. A decisão de decommission deve distinguir fim da gestão numa ferramenta e fim real do serviço.

6. Preparar atualizações com capacidade real

Uma exclusão de manutenção GKE controla determinados eventos, mas não é uma garantia de ausência de mudanças na infraestrutura. Serviços subjacentes, reparações e certas situações críticas podem ficar fora desse controlo. A aplicação precisa de tolerar os eventos relevantes para o seu desenho. Antes de uma atualização planeada, confirma versões, capacidade, distribuição e dependências, além do calendário. O PM deve conseguir explicar a diferença entre uma janela autorizada e a capacidade técnica para executar a mudança com o impacto acordado. Num pool Standard com maxSurge=2 e maxUnavailable=0, a estratégia pretende criar capacidade temporária antes de remover a existente. Sem recursos adicionais, a atualização não consegue progredir nessas condições. Aumentar maxSurge não cria quota nem disponibilidade física. Pode ser necessário obter capacidade compatível, usar uma reserva adequada ou rever a estratégia com o risco explícito de indisponibilidade. Se alguns nós já foram atualizados e outros falharam, não assumes rollback automático de todo o pool. Observa versões por nó e regista o estado parcial. Um dashboard de aplicação saudável não demonstra uniformidade de versões, tal como uma versão desejada não prova que todos os nós já a executam.

7. Respeitar o orçamento de disrupção

Uma aplicação tem três réplicas esperadas, mas só duas estão saudáveis. O seu PDB exige minAvailable:2 e seleciona apenas essas réplicas. Retirar mais uma saudável por Eviction API deixaria apenas uma, pelo que essa nova eviction deve ser impedida enquanto as condições se mantiverem. A réplica já indisponível conta contra o orçamento. PDB não cria uma quarta réplica, não reserva um nó e não corrige uma falha de readiness. Investiga primeiro por que motivo a réplica não recupera: recursos, placement, dependências ou o próprio arranque podem ser relevantes. Eliminar diretamente um Pod para ultrapassar um drain pode contornar a proteção do PDB. A existência do orçamento não transforma essa ação numa interrupção segura. A equipa tem de decidir como recuperar margem saudável ou obter um plano de continuidade diferente. Evita também prometer que PDB impede falhas de hardware: disrupções involuntárias não são bloqueadas por esse mecanismo. No handover, associa a indisponibilidade ao serviço e ao critério de aceitação, em vez de reportar apenas que o comando aguardou. «Duas de três réplicas saudáveis; mínimo duas; sem capacidade adicional confirmada» dá à equipa seguinte uma base concreta para a decisão.

8. Exercício de correspondência e decisão

O exercício Python trabalha com um relatório fictício pequeno: destino, lineage, serial, efeitos desconhecidos e ações. Este formato não é o JSON de Terraform, e o programa não interpreta providers nem executa plan ou apply. Compara o relatório com um registo de aprovação fornecido ao modelo e avalia seis condições: digest, destino, lineage, serial, efeitos conhecidos e âmbito de destruição. O resultado readyForReview significa apenas que estas condições do exercício foram satisfeitas. executionAuthorized permanece falso. Uma referência não autenticada, um sistema alterado depois da observação ou um lock em falta não são resolvidos pela igualdade de campos. Executa os oito exemplos e prevê primeiro o motivo de rejeição. Um replace sem autorização de destruição falha mesmo que o digest corresponda. Um relatório com unknownEffects também falha pela regra didática, que exige efeitos conhecidos antes desta revisão operacional. Essa regra não afirma que todo o valor desconhecido num plano Terraform real seja inválido. As 64 combinações verificam as seis condições sem depender de serviços externos. No fim, prepara uma nota curta: evidência aceite para revisão, condições ainda por demonstrar, destino e responsável. Liga essa nota ao plano real apenas através de um processo que valide origem, atualidade, acesso e execução.

"""Original evidence worksheet. Input is NOT Terraform plan JSON.

No provider calls, state locks, signatures or execution authorization. Equality
with a trusted approval record is only a classroom prerequisite for review.
"""
from copy import deepcopy
from hashlib import sha256
from itertools import product
import json
from pathlib import Path


def digest(report):
    return sha256(json.dumps(report, sort_keys=True, separators=(',', ':')).encode()).hexdigest()


def validate(report, approval):
    required = {'target', 'lineage', 'serial', 'unknownEffects', 'actions'}
    if not isinstance(report, dict) or set(report) != required:
        raise ValueError('invalid worksheet schema')
    if not isinstance(approval, dict) or set(approval) != {'digest', 'target', 'lineage', 'serial', 'allowDestruction'}:
        raise ValueError('invalid approval schema')
    for obj in (report, approval):
        for field in ('target', 'lineage'):
            if not isinstance(obj[field], str) or not obj[field].strip():
                raise ValueError('nonempty identity required')
        if type(obj['serial']) is not int or obj['serial'] < 0:
            raise ValueError('nonnegative integer serial required')
    if type(report['unknownEffects']) is not bool or type(approval['allowDestruction']) is not bool:
        raise ValueError('boolean flags required')
    if not isinstance(approval['digest'], str) or len(approval['digest']) != 64 or any(c not in '0123456789abcdef' for c in approval['digest']):
        raise ValueError('lowercase SHA256 digest required')
    actions = report['actions']
    if not isinstance(actions, list) or any(not isinstance(x, str) or x not in {'no-op', 'create', 'update', 'delete', 'replace'} for x in actions):
        raise ValueError('unsupported worksheet action')


def assess(report, approval):
    validate(report, approval)
    checks = {
        'digest': digest(report) == approval['digest'],
        'target': report['target'] == approval['target'],
        'lineage': report['lineage'] == approval['lineage'],
        'serial': report['serial'] == approval['serial'],
        'known_effects': not report['unknownEffects'],
        'destruction_scope': approval['allowDestruction'] or not any(a in {'delete', 'replace'} for a in report['actions']),
    }
    return {'readyForReview': all(checks.values()),
            'failedChecks': [key for key, ok in checks.items() if not ok],
            'executionAuthorized': False}


def fixture(name, report, approval):
    return {'id': name, **assess(report, approval)}


def main():
    base = {'target': 'preview-west', 'lineage': 'classroom-estate-a', 'serial': 41,
            'unknownEffects': False, 'actions': ['update']}
    approval = {key: base[key] for key in ('target', 'lineage', 'serial')}
    approval.update(digest=digest(base), allowDestruction=False)
    fixtures = [fixture('matching-evidence', base, approval)]
    for key, value in [('digest', '0' * 64), ('target', 'prod-west'),
                       ('lineage', 'classroom-estate-b'), ('serial', 44)]:
        changed = {**approval, key: value}
        fixtures.append(fixture('different-' + key, base, changed))
    unknown = {**base, 'unknownEffects': True}
    fixtures.append(fixture('unknown-effects', unknown, {**approval, 'digest': digest(unknown)}))
    destructive = {**base, 'actions': ['replace']}
    for allowed in (False, True):
        fixtures.append(fixture('replacement-' + str(allowed).lower(), destructive,
            {**approval, 'digest': digest(destructive), 'allowDestruction': allowed}))
    combinations = 0
    for bits in product((False, True), repeat=6):
        r = deepcopy(base); a = deepcopy(approval)
        r['unknownEffects'] = not bits[4]
        r['actions'] = ['update'] if bits[5] else ['replace']
        a['digest'] = digest(r) if bits[0] else '0' * 64
        if not bits[1]: a['target'] = 'prod-west'
        if not bits[2]: a['lineage'] = 'classroom-estate-b'
        if not bits[3]: a['serial'] = 44
        result = assess(r, a)
        assert result['readyForReview'] == all(bits)
        assert len(result['failedChecks']) == bits.count(False)
        assert result['executionAuthorized'] is False
        combinations += 1
    invalid = [({}, approval), ({**base, 'serial': True}, approval),
       ({**base, 'unknownEffects': 'false'}, approval),
       ({**base, 'actions': ['unsupported']}, approval),
       ({**base, 'actions': 'update'}, approval),
       ({**base, 'lineage': ''}, approval),
       (base, {**approval, 'digest': 'A' * 64}),
       (base, {**approval, 'allowDestruction': 1}),
       (base, {**approval, 'serial': -1})]
    for r, a in invalid:
        try: assess(r, a)
        except ValueError: pass
        else: raise AssertionError('invalid input accepted')
    before = deepcopy((base, approval)); assess(base, approval)
    assert before == (base, approval)
    assert [f['id'] for f in fixtures if f['readyForReview']] == ['matching-evidence', 'replacement-true']
    print(json.dumps({'fixtures': fixtures, 'gateCombinations': combinations,
       'invalidInputs': len(invalid), 'inputPreserved': True,
       'network': False, 'persistentWrites': False, 'terraformExecuted': False,
       'authorizationProof': False, 'independentVerification': False,
       'scriptSha256': sha256(Path(__file__).read_bytes()).hexdigest()}, indent=2))


if __name__ == '__main__':
    main()
NA PRÁTICA

O ticket aprova digest A para preview-west, mas o runner recebe B para prod-west. Interrompe, confirma a identidade do state e revê o plano novo. Copiar B para o ticket sem revisão não resolve a divergência.

Armadilhas comuns

Confundir commit aprovado com plano executado; forçar state sem reconciliar; apagar checksums divergentes; importar o mesmo objeto em dois gestores; usar delete direto para ultrapassar PDB; assumir que um freeze elimina manutenção.

Tópicos relacionados: Gestão de configuração e drift · Resiliência durante mudanças · Governance e passagem ao RUN

Leva esta ideia contigo

A identidade do plano, a atualidade do estado e a capacidade operacional são provas distintas. A aprovação deve ligar-se ao que vai acontecer no destino certo.

Criar conta

Referência: Terraform apply command · 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.