Когда вы работаете один, ваша инфраструктура — это не просто ваш бизнес, это и есть ваш бизнес. Вам некого вызвать в 3 часа ночи, нет совещания по реагированию на инцидент, где кто-то другой берёт ситуацию в свои руки. Есть только вы, ваш остывший кофе и горькое осознание того, что ваша производственная база данных превратилась в цифровую историю с привидениями.
Я был в такой ситуации. И, думаю, вы тоже, или же вы достаточно умны, чтобы понимать, что это может произойти.
Хорошие новости? Восстановление после аварий для одиночных разработчиков не требует сложности корпоративного уровня или бюджета в шестизначных цифрах. Оно требует прагматизма, немного автоматизации и, самое главное, плана, который действительно работает, когда всё идёт не так.
Почему у одиночных разработчиков на самом деле всё лучше (своего рода)
Прежде чем погружаться в мрачные детали, давайте признаем кое-что: будучи одиночным разработчиком, вы обладаете огромным преимуществом, которого нет у корпоративных команд. Вы знаете всю свою инфраструктуру. Каждое обслуживание, каждая зависимость, каждый хитрый хак конфигурации, который вы реализовали в 2 часа ночи, потому что это было единственное, что работало — всё это у вас в голове (и, надеюсь, в вашем коде).
Вы можете действовать быстро. Вы можете тестировать немедленно. Вы можете принимать решения без ожидания одобрения от комитетов.
Но когда что-то ломается, вы полностью одиноки. Нет пути эскалации — вы и есть путь эскалации.
Цель здесь не в том, чтобы устранить все риски (спойлер: это невозможно). Она в том, чтобы минимизировать время простоя и обеспечить безопасность данных, используя стратегии, которые действительно работают для одного человека, управляющего несколькими сервисами.
Основа: знайте свои RTO и RPO
Давайте начнём с двух аббревиатур, которые действительно имеют значение.
Цель времени восстановления (RTO) — это сколько времени вы можете позволить себе быть вне строя, прежде чем ваш бизнес пострадает. Для побочного проекта? Может быть, это 24 часа. Для вашего основного источника дохода? Вероятно, измеряется в часах или минутах.
Цель точки восстановления (RPO) — это сколько потери данных вы можете допустить. Потеря 1 часа транзакций? Приемлемо. Потеря целого дня? Вероятно, нет.
Вот практическое упражнение, которое должен выполнить каждый одиночный разработчик:
Для каждого сервиса, который вы используете, ответьте на эти вопросы:
1. Если этот сервис выйдет из строя сегодня, сколько часов пройдёт, прежде чем я потеряю доход/доверие?
→ Это ваш RTO
2. Если я потеряю все данные за последние X часов, сможет ли бизнес выжить?
→ Это ваш RPO
3. Каков радиус поражения?
- Только этот сервис?
- Несколько сервисов?
- Компрометация пользовательских данных?
Пример:
- Блог: RTO = 7 дней, RPO = 1 неделя (люди могут подождать контента)
- SaaS API: RTO = 4 часа, RPO = 1 час (пользователи платят)
- База данных пользователей: RTO = 2 часа, RPO = 15 минут (потеря данных недопустима)
Эти цифры напрямую определяют вашу стратегию аварийного восстановления. Высокий RTO/RPO? Вы можете использовать более дешёвые, медленные решения. Низкие цифры? Вам нужна избыточность и автоматизация.
Трёхслойный стек аварийного восстановления
Забудьте всё, что вы знаете о корпоративном аварийном восстановлении. Вот что действительно важно для одиночных разработчиков:
Слой 1: Автоматизированные бэкапы (Ваша страховочная сетка)
Вы не можете полагаться на ручные бэкапы. Вы забудете. Мы все забываем. Однажды я думал, что делаю бэкап своей базы данных ежедневно — оказалось, что скрипт молча терпел неудачу в течение 47 дней.
Автоматизация — ваш друг. Вот практическая настройка для типичного стека одиночного разработчика:
#!/bin/bash
# backup-everything.sh - запускается ежедневно через cron
set -e # Выход при любой ошибке
BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
RETENTION_DAYS=30
REMOTE_BACKUP_BUCKET="s3://my-backups"
mkdir -p "$BACKUP_DIR"
# Бэкап базы данных с сжатием
echo "Создаю бэкап PostgreSQL..."
pg_dump --verbose --no-owner \
-h localhost \
-U postgres \
my_production_db | gzip > "$BACKUP_DIR/db.sql.gz"
# Файлы приложения
echo "Создаю бэкап кода приложения..."
tar --exclude='.git' \
--exclude='node_modules' \
--exclude='.env' \
-czf "$BACKUP_DIR/app.tar.gz" /app
# Конфигурация и секреты (зашифрованные!)
echo "Создаю бэкап зашифрованной конфигурации..."
tar -czf - /etc/myapp | \
openssl enc -aes-256-cbc -salt -out "$BACKUP_DIR/config.tar.gz.enc" \
-k "$BACKUP_ENCRYPTION_KEY"
# Загрузка в удалённое хранилище
echo "Загружаю в S3..."
aws s3 sync "$BACKUP_DIR" "$REMOTE_BACKUP_BUCKET/$(date +%Y-%m-%d)/"
# Очистка старых бэкапов (хранить 30 дней)
echo "Очищаю старые бэкапы..."
find "$BACKUP_DIR" -mtime +$RETENTION_DAYS -delete
echo "Бэкап завершён в $(date)"
Настройте это в своём cron:
# Ежедневный бэкап в 2 часа ночи
0 2 * * * /usr/local/bin/backup-everything.sh >> /var/log/backups.log 2>&1
Ключевые принципы:
- Автоматизированный (не ручной)
- Проверяемый (на самом деле тестируйте, что восстановление работает)
- Резервное хранилище (локальное + облако)
- Политика хранения (не храните бесконечные бэкапы)
- Зашифрованный (особенно для секретов и конфигураций)
Слой 2: Инфраструктура как код (Помощник памяти)
Вот неприятная правда: когда вы находитесь под давлением во время сбоя, вы забудете, как настроили всё. Вы забудете точные аргументы для перезапуска сервера. Вы забудете, почему установили ту странную правило брандмауэра.
Инфраструктура как код (IaC) решает эту проблему.
Для одиночных разработчиков Terraform — это золотая середина. Он не слишком тяжёлый, широко поддерживается и спасает вас, когда нужно быстро перестроить:
# main.tf - Ваша инфраструктура полностью задокументирована
terraform {
backend "s3" {
bucket = "my-tf-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}
provider "aws" {
region = "us-east-1"
}
# База данных
resource "aws_db_instance" "main" {
identifier = "production-db"
engine = "postgres"
engine_version = "14.10"
instance_class = "db.t3.micro"
allocated_storage = 100
storage_encrypted = true
multi_az = true # Высокая доступность!
backup_retention_period = 30
db_name = "myapp"
username = "dbadmin"
password = random_password.db_password.result
skip_final_snapshot = false
final_snapshot_identifier = "prod-db-final-snapshot-${timestamp()}"
tags = {
Name = "production-database"
Environment = "prod"
ManagedBy = "terraform"
}
}
# Сервер приложения
resource "aws_instance" "app" {
ami = data.aws_ami.ubuntu.id
instance_type = "t3.small"
key_name = aws_key_pair.deployer.key_name
vpc_security_group_ids = [aws_security_group.app.id]
# Это выполняется при запуске экземпляра
user_data = base64encode(file("${path.module}/init-app.sh"))
ebs_block_device {
device_name = "/dev/sda1"
volume_size = 50
volume_type = "gp3"
delete_on_termination = true
}
tags = {
Name = "production-app"
}
}
# Балансер нагрузки
resource "aws_lb" "main" {
name = "prod-alb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.lb.id]
subnets = aws_subnet
