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

Economia da fiabilidade e capacidade de recuperação

Comparar opções de contingência com objetivos de recuperação, capacidade e custos equivalentes, distinguindo quota, reserva, compromissos e evidência em falta.

1. Definir o serviço que o orçamento precisa de recuperar

Uma opção de contingência deve ser comparada com a necessidade de negócio que pretende satisfazer. Num serviço fictício de processamento de fundos, arrancar uma VM é apenas um passo. O serviço pode precisar de dados coerentes, capacidade para a procura acumulada, autenticação nos parceiros e uma validação funcional antes de retomar. O exemplo não representa normas internas do BNP Paribas. Os valores monetários desta aula são fictícios e não são preços da Google Cloud. Começa por registar os objetivos: tempo máximo de recuperação, ponto de dados aceitável, capacidade mínima e funcionalidades exigidas. Se existir um modo degradado, identifica quem o aceita, que operações permite e durante quanto tempo. Uma opção que serve consultas mas não processa reconciliação não deve ser comparada como se recuperasse todo o serviço. Constrói depois uma ficha de evidência por alternativa. Inclui condições do ensaio, duração observada, idade do ponto recuperado, carga suportada, dependências e custos no mesmo horizonte. Um valor desconhecido deve permanecer desconhecido. O objetivo é permitir uma decisão explícita sobre custo e serviço entregue. Uma poupança que remove uma capacidade necessária altera o risco; precisa de ser apresentada com esse efeito e não apenas como redução da fatura mensal.

2. Distinguir quota, reserva e consumo efetivo

A quota é um limite de utilização permitido. Não garante que exista o recurso pretendido numa zona quando precisas de o criar. Um plano de recuperação que apresenta apenas quota livre deixa a disponibilidade por demonstrar. Identifica a localização, o tipo de máquina e os recursos necessários, e verifica qual o mecanismo de capacidade previsto para o cenário. Uma reserva do Compute Engine tem regras de correspondência. A zona e as propriedades relevantes da VM têm de coincidir; uma reserva na zona A não é automaticamente consumível na zona B. Numa reserva especificamente selecionada, a afinidade também deve apontar para a reserva correta. Não trates vCPUs de configurações diferentes como posições livremente intercambiáveis. Um ensaio com o template real ajuda a descobrir incompatibilidades antes da janela. Confirma a capacidade ainda disponível. Se uma reserva tem 20 posições e 18 continuam ocupadas por serviços que não podem parar, existem duas posições livres para novas VMs. Contar novamente as 18 já usadas sobrestima a opção de contingência. Se o plano depende de libertar capacidade, inclui a sequência, o impacto e a autorização dessa libertação. Regista o estado observado e a data; uma reserva existente hoje pode ter outro nível de consumo no momento do incidente.

3. Separar desconto financeiro de capacidade de recuperação

Um compromisso de utilização e uma reserva devem ser analisados pelas funções que desempenham. O primeiro é uma decisão financeira sujeita a âmbito e condições; a segunda trata capacidade com requisitos de consumo. Podem existir relações entre ambos para determinados recursos, mas não são conceitos equivalentes. Consulta as condições do produto e não atribuas automaticamente uma garantia técnica a um desconto. Num orçamento original, a equipa reduz o custo estimado porque prevê um compromisso. Ainda precisa de demonstrar capacidade na localização de recuperação, compatibilidade dos recursos e possibilidade de ativação. Da mesma forma, uma reserva que deixa de ser consumida não deve ser tratada como capacidade sem custo. A comparação tem de identificar obrigações que permanecem mesmo quando a carga muda de região. Para discutir a proposta com FinOps, apresenta utilização esperada, variabilidade, horizonte e cenários alternativos. Distingue o custo evitável agora do custo já comprometido e do custo incremental de ativar contingência. Evita contar o mesmo benefício em duas rubricas. Se a decisão de arquitetura mudar, revê a aplicabilidade financeira e a capacidade técnica separadamente. Uma estimativa clara pode revelar que a opção mais económica durante funcionamento normal não é a mais económica no conjunto dos ensaios e ativações planeados.

4. Dimensionar a procura depois da falha

Capacidade normal e capacidade em contingência podem ser muito diferentes. No modelo desta aula, duas regiões recebem 60 operações por segundo cada uma. Cada região foi ensaiada até 100. Se uma falhar e toda a sua procura passar para a outra, a sobrevivente recebe 120 para uma capacidade de 100. A capacidade da região indisponível não pode continuar a ser somada ao plano. Avalia alternativas com base no serviço exigido: capacidade adicional demonstrada, prioridade para funções críticas, limitação de carga não essencial ou um modo degradado aprovado. Acrescentar retries indiscriminados pode aumentar a procura sem produzir resultados úteis. Se existe backlog, não basta suportar apenas a taxa de chegada normal; também é necessário um plano para recuperar trabalho acumulado dentro do prazo relevante. Usa um ensaio representativo para medir o recurso que limita o sistema. Mais workers podem não aumentar throughput se o limite estiver em ligações de base de dados, armazenamento, API externa ou contenção de locks. Regista a mistura de operações, tamanho dos dados, concorrência e critério de qualidade. A capacidade de 120 usada no exercício é uma entrada fornecida, não uma medição feita pelo programa. Revalida-a quando o perfil de procura ou uma dependência relevante mudar.

5. Avaliar Spot com o prazo e a tolerância certos

Spot VMs usam capacidade excedente, cuja disponibilidade varia, e podem ser interrompidas. O desconto pode ser útil para trabalho que tolera interrupção e consegue retomar de forma controlada. Não transforma Spot numa garantia de capacidade para um serviço com prazo rígido. O desenho deve explicar o que acontece quando não há capacidade, quando uma VM termina durante processamento e quando o tempo restante já não permite repetir. Num exercício fictício, tarefas de preparação podem correr com checkpoints e efeitos idempotentes, enquanto o caminho crítico de recuperação tem uma alternativa de capacidade demonstrada. Essa separação precisa de ser testada. Guardar checkpoints apenas no disco que desaparece com o worker pode tornar a política de retry inútil. Regista onde está o estado necessário para retomar e quanto trabalho pode ser repetido. Compara custos por cenário, incluindo trabalho repetido, tempo de ativação e manutenção operacional. Um preço unitário baixo pode coexistir com um custo total superior quando há muitas interrupções. Não atribuas uma frequência universal de preempção à plataforma: usa hipóteses declaradas e análises de sensibilidade. A decisão deve relacionar a tolerância do workload com o prazo de negócio e com a evidência disponível para a alternativa quando Spot não está utilizável.

6. Comparar custos num horizonte comum

Define primeiro o âmbito que entra na conta: standby, replicação, retenção, transferência, licenças aplicáveis, observabilidade, preparação e execução de ensaios. Alguns valores são contínuos, outros aparecem em cada ativação. Identifica também custos comuns às opções e custos excluídos. Uma tabela com apenas VMs pode ser útil como parcela, mas não deve ser apresentada como custo total do serviço. Usamos um modelo anual deliberadamente simples: custo=12×custo mensal+custo por ativação×número de ativações. A opção A custa 100 unidades por mês e 300 por ativação; B custa 150 e 100. Sem ativações, A totaliza 1200 e B 1800. Com quatro, A totaliza 2400 e B 2200. As duas empatam com três ativações. Estes valores são inventados para explorar uma decisão; não descrevem uma fatura nem preços reais. O número de ativações pode representar ensaios planeados e incidentes incluídos num cenário, mas não é automaticamente uma previsão da frequência de falhas. Apresenta mais de um cenário quando essa frequência é incerta. Não somes custos incrementais que já estejam incluídos no valor mensal. Quando faltam componentes materiais, conserva a lacuna e evita uma precisão artificial. Usa a comparação para orientar recolha de evidência e discussão com os responsáveis pelo serviço e pelo orçamento.

7. Relacionar poupança com a capacidade que desaparece

Um ambiente com pouca utilização pode existir para recuperação. Antes de o descomissionar, verifica referências em planos de rollback, dependências de dados e condições de substituição. Se a opção de retorno ainda depende dele, a eliminação não é apenas limpeza de recursos. Pode ser uma mudança válida, mas necessita de uma alternativa demonstrada ou de uma decisão explícita sobre o risco e os objetivos do serviço. Examina também o benefício atribuído à redundância. Duas regiões que dependem da mesma chave indisponível no cenário escolhido podem falhar em conjunto. Duplicar compute não resolve essa dependência. Um desenho mais caro deve ser relacionado com os modos de falha que realmente consegue cobrir. Não atribuas independência a componentes apenas porque têm nomes ou localizações diferentes. Na apresentação ao comité, mostra o custo, a capacidade entregue, os resultados de ensaios e as lacunas. Se A custa menos mas suporta 90 operações por segundo quando são necessárias 120, apresenta esse desvio ao lado da diferença financeira. Distingue uma opção que falhou um requisito de outra ainda sem evidência. O responsável deve conseguir perceber o que está a aprovar, quais as condições de validade e quando a análise precisa de ser repetida.

8. Exercício: filtrar restrições antes de ordenar preços

O programa local recebe opções com custo mensal e por ativação em cêntimos fictícios, duração de recuperação observada, idade do ponto recuperado, capacidade e disponibilidade declarada de recursos. Compara os valores com os objetivos fornecidos. None representa desconhecido. Um requisito conhecido que falha continua visível mesmo quando falta outra informação. O programa não confirma os ensaios nem consulta quotas, reservas ou preços. A ordenação inclui apenas opções que satisfazem as restrições declaradas e têm custo calculável no cenário. Se duas opções empatam, ambas aparecem no conjunto de menor custo. A ordem de entrada não decide o vencedor. O resultado refere-se apenas às opções demonstradas pelos dados fornecidos; não prova que nenhuma opção desconhecida pudesse vir a ser melhor. Mesmo com todas as entradas completas, produção continua sem autorização automática. Executa primeiro o cenário de quatro ativações, depois zero e três. Reduz a capacidade da opção barata para 90 e observa a sua exclusão do ranking. Substitui capacidade por None e compara falha conhecida com evidência incompleta. Por fim, deixa o custo de ativação desconhecido: com zero ativações esse campo não afeta o total; com uma já impede o cálculo. Explica cada resultado e identifica que evidência operacional falta para transformar a folha de comparação numa proposta executável.

"""Original offline recovery comparison. All prices and measurements are fictional.

This is a supplied-evidence model, not a cloud quote or production authorization.
Annual cost = 12 * monthly fixed cents + activation count * incremental cents.
"""
import copy
import hashlib
import itertools
import json
from pathlib import Path

FIELDS = {'id','monthlyCents','activationCents','recoveryMinutes','recoveryPointAgeMinutes','capacity','resourcesAvailable'}

def integer(value, nullable=False):
    if value is None and nullable: return
    if type(value) is not int or value < 0: raise ValueError('nonnegative integer required')

def evaluate(options, activations, max_rto, max_rpo, min_capacity):
    for value in (activations,max_rto,max_rpo,min_capacity): integer(value)
    if type(options) is not list or not options: raise ValueError('nonempty option list required')
    ids=set();result=[]
    for option in options:
        if type(option) is not dict or set(option)!=FIELDS: raise ValueError('option fields')
        ident=option['id']
        if type(ident) is not str or not ident.strip() or ident in ids: raise ValueError('unique nonempty ID required')
        ids.add(ident)
        for key in FIELDS-{'id','resourcesAvailable'}: integer(option[key],True)
        if option['resourcesAvailable'] is not None and type(option['resourcesAvailable']) is not bool:
            raise ValueError('resourcesAvailable must be boolean or None')
        failed=[];unknown=[]
        for key,limit,direction in [('recoveryMinutes',max_rto,'max'),('recoveryPointAgeMinutes',max_rpo,'max'),('capacity',min_capacity,'min')]:
            value=option[key]
            if value is None: unknown.append(key)
            elif (value>limit if direction=='max' else value<limit): failed.append(key)
        if option['resourcesAvailable'] is None: unknown.append('resourcesAvailable')
        elif option['resourcesAvailable'] is False: failed.append('resourcesAvailable')
        monthly,activation=option['monthlyCents'],option['activationCents']
        missing_cost=[]
        if monthly is None: missing_cost.append('monthlyCents')
        if activations and activation is None: missing_cost.append('activationCents')
        annual=None if missing_cost else 12*monthly+(activations*activation if activations else 0)
        status='fails-declared-constraints' if failed else 'evidence-incomplete' if unknown else 'meets-declared-constraints'
        result.append({'id':ident,'status':status,'failedConstraints':failed,'unknownConstraints':unknown,
                       'missingCostFields':missing_cost,'annualCents':annual,
                       'rankable':not failed and not unknown and annual is not None})
    result.sort(key=lambda r:r['id'])
    ranking=sorted(({'id':r['id'],'annualCents':r['annualCents']} for r in result if r['rankable']),
                   key=lambda r:(r['annualCents'],r['id']))
    best=[r['id'] for r in ranking if r['annualCents']==ranking[0]['annualCents']] if ranking else []
    return {'options':result,'ranking':ranking,'lowestCostAmongDemonstratedOptions':best,
            'allInputsComplete':not any(r['unknownConstraints'] or r['missingCostFields'] for r in result),
            'realPricesVerified':False,'actualRecoveryProven':False,'productionAuthorized':False}

def candidate(ident='warm'):
    return {'id':ident,'monthlyCents':10000,'activationCents':30000,'recoveryMinutes':20,
            'recoveryPointAgeMinutes':5,'capacity':120,'resourcesAvailable':True}

def exercise():
    fixtures=[]
    def record(label,opts,count=4):
        before=copy.deepcopy(opts);r=evaluate(opts,count,30,10,120);assert opts==before
        fixtures.append({'id':label,**r});return r
    a=candidate('a');b=candidate('b');b.update(monthlyCents=15000,activationCents=10000)
    assert record('four-activations',[a,b])['lowestCostAmongDemonstratedOptions']==['b']
    assert record('no-activations',[a,b],0)['lowestCostAmongDemonstratedOptions']==['a']
    r=record('three-activations-break-even',[a,b],3)
    assert r['lowestCostAmongDemonstratedOptions']==['a','b']
    assert all(x['annualCents']==210000 for x in r['ranking'])
    a=candidate('cheap');a.update(monthlyCents=100,capacity=90)
    assert record('cheap-capacity-fails',[a,candidate()])['lowestCostAmongDemonstratedOptions']==['warm']
    a=candidate('unknown');a.update(capacity=None)
    assert record('unknown-capacity',[a])['ranking']==[]
    a=candidate();a.update(recoveryMinutes=40,resourcesAvailable=None)
    r=record('known-failure-and-gap',[a]);assert r['options'][0]['failedConstraints']==['recoveryMinutes'];assert not r['allInputsComplete']
    a=candidate();a.update(monthlyCents=None)
    assert record('unknown-cost',[a])['ranking']==[]
    a=candidate();a.update(activationCents=None)
    assert record('unused-unknown-activation-cost',[a],0)['ranking'][0]['annualCents']==120000
    assert record('required-unknown-activation-cost',[a],1)['ranking']==[]
    a=candidate('b');b=candidate('a')
    assert record('equal-cost-tie',[a,b])['lowestCostAmongDemonstratedOptions']==['a','b']
    a=candidate();a.update(resourcesAvailable=False)
    assert record('resources-unavailable',[a])['ranking']==[]
    a=candidate();a.update(recoveryMinutes=30,recoveryPointAgeMinutes=10,capacity=120)
    assert record('inclusive-boundaries',[a])['ranking']
    a=candidate();a.update(monthlyCents=0,activationCents=0)
    assert record('explicit-zero-cost',[a])['ranking'][0]['annualCents']==0
    combinations=0
    for rto,rpo,capacity,available in itertools.product((20,40,None),(5,15,None),(120,90,None),(True,False,None)):
        a=candidate();a.update(recoveryMinutes=rto,recoveryPointAgeMinutes=rpo,capacity=capacity,resourcesAvailable=available)
        r=evaluate([a],4,30,10,120)['options'][0]
        failures=sum([rto==40,rpo==15,capacity==90,available is False])
        unknown=sum(v is None for v in (rto,rpo,capacity,available))
        assert len(r['failedConstraints'])==failures and len(r['unknownConstraints'])==unknown
        assert r['rankable']==(not failures and not unknown)
        combinations+=1
    cost_combinations=0
    for monthly,activation,count in itertools.product((0,100,None),(0,200,None),(0,1,4)):
        a=candidate();a.update(monthlyCents=monthly,activationCents=activation)
        r=evaluate([a],count,30,10,120)['options'][0]
        known=monthly is not None and (count==0 or activation is not None)
        assert (r['annualCents'] is not None)==known
        if known:assert r['annualCents']==monthly*12+(count*activation if count else 0)
        cost_combinations+=1
    opts=[candidate('c'),candidate('a'),candidate('b')];expected=evaluate(opts,4,30,10,120);permutations=0
    for order in itertools.permutations(opts):
        assert evaluate(list(order),4,30,10,120)==expected;permutations+=1
    invalid=[lambda o:o.clear(),lambda o:o.append(copy.deepcopy(o[0])),lambda o:o[0].update(id=''),
             lambda o:o[0].update(extra=1),lambda o:o[0].pop('capacity'),lambda o:o[0].update(capacity=-1),
             lambda o:o[0].update(capacity=True),lambda o:o[0].update(monthlyCents=1.5),
             lambda o:o[0].update(activationCents='100'),lambda o:o[0].update(resourcesAvailable=1)]
    for mutate in invalid:
        opts=[candidate()];mutate(opts)
        try:evaluate(opts,4,30,10,120)
        except ValueError:pass
        else:raise AssertionError('invalid option accepted')
    settings=[(-1,30,10,120),(True,30,10,120),(4,-1,10,120),(4,30,None,120),(4,30,10,1.5)]
    for args in settings:
        try:evaluate([candidate()],*args)
        except ValueError:pass
        else:raise AssertionError('invalid setting accepted')
    return {'scriptSha256':hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),'fixtures':fixtures,
            'constraintCombinations':combinations,'costCombinations':cost_combinations,'orderPermutations':permutations,
            'invalidInputs':len(invalid)+len(settings),'inputPreserved':True,
            'cloudExecuted':False,'network':False,'persistentWrites':False}

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

A custa menos e recupera em 20 minutos, mas suporta apenas 90 operações/s para uma necessidade de 120. B custa mais e satisfaz as restrições ensaiadas. A comparação deve apresentar esse desvio.

Armadilhas comuns

Tratar quota como reserva; contar capacidade já consumida duas vezes; considerar Spot uma garantia de prazo; omitir ensaios e custos de ativação; eliminar o único rollback para reduzir a fatura.

Tópicos relacionados: Planeamento de recuperação e RTO/RPO · Gestão de capacidade e sobrecarga · FinOps e descomissionamento

Leva esta ideia contigo

Compara custo entre opções que demonstram o serviço exigido e mantém lacunas explícitas. A menor estimativa não corrige requisitos técnicos por cumprir.

Criar conta

Referência: Professional Cloud DevOps Engineer exam guide · 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.