сделать disaster recovery воспроизводимым, тестируемым и привязанным к RTO и RPO
Disaster Recovery as Code: автоматизация RTO (время восстановления) и RPO (точка восстановления) через шаблоны
13 минут
RTO ограничивает допустимый downtime сервиса, RPO — допустимую потерю данных. В материале — оба target в Terraform, верификация backup, failover-инфраструктура и orchestration recovery через проверенные pipeline.
Что такое RTO и RPO и зачем они нужны в disaster recovery
Recovery Time Objective (RTO) — максимально допустимое время простоя после сбоя: как быстро сервис должен снова работать. Recovery Point Objective (RPO) — максимально допустимая потеря данных: насколько далеко назад вы готовы откатиться при восстановлении. Если RPO — один час, backup или репликация должны быть не старше; если RTO — четыре часа, runbook'и, failover stack и drill'ы должны доказывать восстановление в этом окне. Эти два числа задают cadence backup, бюджет multi-region и глубину orchestration — они превращают размытое «нам нужен DR» в измеримые инженерные targets, которые можно проверить на game day.
Почему ручные DR-планы проваливаются в реальных инцидентах
У большинства команд есть документ disaster recovery, написанный один раз и почти не проверяемый. Когда падает production, runbook ссылается на выведенные из эксплуатации сервисы, формат backup изменился незаметно, а порядок восстановления живёт в чьей-то памяти. Четыре пробела повторяются везде: документация расходится с live-инфраструктурой, restore не замеряют end to end, RTO и RPO различаются между командами без измерений, шаги cutover — tribal knowledge. Disaster Recovery as Code (DRaC) переносит дисциплину IaC на recovery: versioned templates, automated drill'ы, auditable changes и явные targets, которые можно тестировать.
Строительные блоки DRaC: targets, topology, verification, orchestration
Объявить RTO и RPO в коде — не значит их гарантировать: оба доказываются timed drill'ами. Топологию recovery выбирают осознанно. Backup and restore — минимальная стоимость и самый высокий RTO. Pilot light держит минимальные DR-ресурсы и масштабируется при failover. Warm standby постоянно крутит урезанный replica stack. Multi-region active-passive или active-active дороже, но сжимает RTO и RPO. Объявляйте targets versioned variables, поднимайте DR-инфраструктуру из тех же modules что production с вариантами region или account, верифицируйте backup scheduled restore test'ами и оркестрируйте cutover явным dependency graph вместо ad hoc runbook.
Объявите RTO и RPO и выровняйте cadence backup
Храните RTO и RPO как Terraform variables с review как у production change. Маппите RPO на backup schedule, который AWS Backup реально поддерживает — обычно cron expression, а не произвольный rate в минутах. Organizations backup policies полезны на enterprise-масштабе; aws_backup_plan — понятнее для application-команд. Зафиксируйте: выполнение RPO требует replication или backup frequency ниже target плюс мониторинг возраста last-successful-backup.
variable "rto_minutes" {
description = "Maximum acceptable service downtime in minutes"
type = number
default = 60
}
variable "rpo_minutes" {
description = "Maximum acceptable data loss window in minutes"
type = number
default = 60
}
locals {
# Align schedule to the nearest practical cadence for your RPO
backup_schedule = var.rpo_minutes <= 60 ? "cron(0 * * * ? *)" : "cron(0 0 * * ? *)"
}
resource "aws_backup_vault" "dr" {
name = "application-dr-vault"
}
resource "aws_backup_plan" "application" {
name = "application-dr-plan"
rule {
rule_name = "rpo-aligned-backup"
target_vault_name = aws_backup_vault.dr.name
schedule = local.backup_schedule
lifecycle {
delete_after = 35
}
}
}Автоматизируйте restore drill'ы с замером RPO и RTO
Backup без restore test — это стоимость хранения. Запускайте изолированные drill'ы с restore в throwaway instance name, мерьте elapsed time для database availability waiter и проверяйте snapshot age против RPO. Полный RTO включает DNS propagation и application warm-up — явно ограничивайте scope метрик drill, чтобы команда не путала database restore time с end-to-end service recovery. Публикуйте результаты в CloudWatch или incident metrics store и алертьте при failure.
import time
from datetime import datetime, timezone
import boto3
class BackupVerifier:
def __init__(self, rto_minutes=60, rpo_minutes=60):
self.rto_minutes = rto_minutes
self.rpo_minutes = rpo_minutes
self.rds = boto3.client("rds")
def test_restore(self, source_instance_id, drill_instance_id):
start = time.time()
snapshots = self.rds.describe_db_snapshots(
DBInstanceIdentifier=source_instance_id,
SnapshotType="automated",
)["DBSnapshots"]
if not snapshots:
raise RuntimeError("No automated snapshots found")
latest = max(snapshots, key=lambda item: item["SnapshotCreateTime"])
snapshot_time = latest["SnapshotCreateTime"]
if snapshot_time.tzinfo is None:
snapshot_time = snapshot_time.replace(tzinfo=timezone.utc)
snapshot_age = (
datetime.now(timezone.utc) - snapshot_time
).total_seconds() / 60
if snapshot_age > self.rpo_minutes:
raise AssertionError(
f"RPO breach: snapshot age {snapshot_age:.1f}m exceeds {self.rpo_minutes}m"
)
self.rds.restore_db_instance_from_db_snapshot(
DBInstanceIdentifier=drill_instance_id,
DBSnapshotIdentifier=latest["DBSnapshotIdentifier"],
DBInstanceClass="db.r6g.large",
MultiAZ=False,
PubliclyAccessible=False,
DeletionProtection=False,
)
waiter = self.rds.get_waiter("db_instance_available")
waiter.wait(DBInstanceIdentifier=drill_instance_id)
elapsed = (time.time() - start) / 60
if elapsed > self.rto_minutes:
raise AssertionError(
f"RTO breach: restore took {elapsed:.1f}m, target {self.rto_minutes}m"
)
return {
"status": "PASS",
"rto_actual_minutes": round(elapsed, 1),
"rpo_actual_minutes": round(snapshot_age, 1),
"snapshot_id": latest["DBSnapshotIdentifier"],
}DR-инфраструктура и DNS failover as code
Определяйте secondary region или account stack как module variant production и синхронизируйте через те же CI gates. Scheduled terraform plan в DR workspace ловит template drift до инцидента. Route 53 failover routing переключает трафик при падении primary health check — используйте fqdn-based HTTPS check или ALB target health evaluation с alias records. DNS TTL и resolver caching всё равно добавляют минуты к cutover; закладывайте это в RTO budget.
name: DR Infrastructure Sync
on:
push:
paths: ['infrastructure/**']
schedule:
- cron: '0 */6 * * *'
workflow_dispatch:
jobs:
dr-sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_wrapper: false
- name: Terraform plan (DR region)
working-directory: infrastructure/dr
run: |
terraform init -input=false
terraform plan -detailed-exitcode -input=false -var="environment=dr" -out=dr.tfplan
- name: Apply DR infrastructure
if: github.ref == 'refs/heads/main'
working-directory: infrastructure/dr
run: terraform apply -input=false dr.tfplanresource "aws_route53_health_check" "primary" {
fqdn = var.primary_healthcheck_fqdn
port = 443
type = "HTTPS"
resource_path = "/health"
failure_threshold = 3
request_interval = 30
tags = {
Name = "primary-api-health-check"
}
}
resource "aws_route53_record" "failover_primary" {
zone_id = var.zone_id
name = "api.example.com"
type = "A"
set_identifier = "primary"
failover_routing_policy {
type = "PRIMARY"
}
alias {
name = var.primary_alb_dns
zone_id = var.primary_alb_zone_id
evaluate_target_health = true
}
health_check_id = aws_route53_health_check.primary.id
}
resource "aws_route53_record" "failover_secondary" {
zone_id = var.zone_id
name = "api.example.com"
type = "A"
set_identifier = "secondary"
failover_routing_policy {
type = "SECONDARY"
}
alias {
name = var.dr_alb_dns
zone_id = var.dr_alb_zone_id
evaluate_target_health = true
}
}Оркестрация recovery с явным dependency graph
Свяжите validation, data restore, cache warm-up, DNS cutover и post-cutover health checks в workflow, который можно запускать на game day. Argo Workflows подходит Kubernetes-эстейтам; Step Functions или CI workflow — для более простых stack'ов. Оставьте human approval gates для объявления disaster и production DNS failover — это policy decisions, не полностью автономные действия.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
name: disaster-recovery
spec:
entrypoint: recover
templates:
- name: recover
dag:
tasks:
- name: validate-dr-infra
template: validate-infra
- name: restore-databases
template: restore-db
dependencies: [validate-dr-infra]
- name: sync-cache-layer
template: sync-cache
dependencies: [restore-databases]
- name: update-dns
template: dns-failover
dependencies: [sync-cache-layer]
- name: verify-recovery
template: health-check
dependencies: [update-dns]
- name: validate-infra
container:
image: registry.example.com/dr-tools:latest
command: [python, /scripts/validate_dr_infra.py]
- name: restore-db
container:
image: registry.example.com/dr-tools:latest
command: [python, /scripts/restore_database.py, --from-latest-snapshot, --verify-rpo]
- name: sync-cache
container:
image: registry.example.com/dr-tools:latest
command: [python, /scripts/warm_cache.py]
- name: dns-failover
container:
image: registry.example.com/dr-tools:latest
command: [python, /scripts/failover_dns.py]
- name: health-check
container:
image: registry.example.com/dr-tools:latest
command: [python, /scripts/verify_recovery.py]Операционные практики, которые держат DR правдоподобным
Проводите timed game day ежемесячно в изолированном account или region, а не раз в год по слайдам. Версионируйте DR templates рядом с production и проверяйте DR impact в том же pull request при добавлении data store. Алертьте, когда возраст last successful backup превышает порог из RPO. Храните backup в immutable object storage вроде S3 Object Lock как защиту от ransomware. Подбирайте DR spend по размеру: pilot light или warm standby для большинства workload, active-active только когда бизнес требует sub-minute RTO. Аудируйте encryption, retention и cross-region copy через AWS Config или OPA непрерывно. Задокументируйте, кто может объявить disaster и когда fail over versus fail back. DR — ongoing practice; DRaC делает её тестируемой, а не теоретической.
RTO и RPO — это явные обязательства по надёжности; их задаём в гайде по SLO, SLI и error budget для платформенных команд.
Recovery drill'ы естественно сочетаются с controlled failure practice из playbook по Chaos Engineering для DevOps.
