Redundancy, или избыточность, — это когда ты изначально закладываешь в архитектуру «запас прочности»: дублируешь важные компоненты так, чтобы отказ одного из них не валил весь сервис. Идея в том, чтобы убрать single point of failure и сделать инфраструктуру нормально переживающей сбои.
Избыточность можно городить на разных уровнях: железо, сеть, софт, инфраструктура. На практике это чаще всего выглядит как дополнительные серверы, массивы, блоки питания, маршрутизаторы, лишние линк-и к сети и даже резервные дата-центры.
Хороший пример — сетевой redundancy, особенно когда речь про ISP: к интернету подключаются сразу через нескольких провайдеров. Если один падает, мир не заканчивается, просто весь трафик едет по оставшимся.
В облаках история та же, только уже виртуальная. Дублируют ВМ, базы, сервисы между зонами доступности и иногда между регионами. Это помогает и от потери данных защититься, и от падений целых площадок.
Избыточность — фундамент для High Availability (HA). Модные SLA и RTO/RPO сами по себе не выполнятся, если у тебя всё крутится на одном экземпляре без резервов.
Архитектурно redundancy обычно включает в себя:
- кластера с отказоустойчивостью;
- репликацию данных в нужном режиме;
- автоматический failover и балансировку нагрузки;
- активные и пассивные резервы;
- геораспределение — разные зоны/регионы/ЦОДы.
В критичных отраслях (банки, телеком, медицина, госсектор) это уже просто «по умолчанию». Делать систему без избыточности там — путь к инцидентам и разборкам.
Основные типы избыточности #
Active-Active
Все узлы одновременно обрабатывают трафик. Если кто-то падает, остальные продолжают тащить нагрузку, просто её становится чуть больше на оставшихся. Простой пример — два балансировщика, каждый обслуживает часть запросов.
Active-Passive
Один работает, второй сидит в ожидании. При отказе активного пассивный поднимается и берёт на себя роль основного. Классика — основная СУБД плюс реплика, которая готова стать мастером.
N+1 / N+N
- N+1 — система рассчитана на работу N элементов, плюс один резервный.
- N+N — все элементы продублированы, так что при отказе одной половины вторая теоретически может тянуть всю нагрузку.
Примеры, как это делают на практике #
Облака #
- AWS:
- RDS в режиме Multi-AZ — база автоматически переключается между зонами при проблемах.
- Elastic Load Balancer, распределяющий трафик по нескольким EC2 в разных зонах.
- Azure:
- Availability Sets и Availability Zones, чтобы не зависеть от падения конкретного куска железа или ЦОДа.
- Geo-redundant storage (GRS), когда данные сами реплицируются между регионами.
- Google Cloud:
- Global Load Balancer с бэкендами в разных регионах.
- Cloud SQL в HA-режиме с репликацией и автоперезапуском.
Kubernetes #
- ReplicaSet следит, чтобы нужное число подов всегда было в строю.
- PodDisruptionBudget ограничивает, сколько подов можно «выбить» во время обновлений.
- Node pools в разных зонах снижают риск, что один дата-центр сложит весь кластер.
- HA control plane — когда управляющие компоненты тоже размазаны и не падают от одной аварии.
Чтобы это не выглядело чистой теорией, вот несколько живых кусочков.
- Terraform-модуль с избыточной инфраструктурой (EC2 в двух зонах AWS).
- ReplicaSet в Kubernetes, который держит несколько копий приложения.
- DNS-failover на базе Anycast/DNS.
1. Terraform: два EC2-инстанса в разных зонах (N+1-подход) #
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "web" {
count = 2
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
availability_zone = element(["us-east-1a", "us-east-1b"], count.index)
tags = {
Name = "web-${count.index}"
}
}
Здесь мы поднимаем два инстанса в разных зонах AWS. Одна зона отвалилась — второй инстанс всё ещё живет и может обслуживать трафик (при условии, что балансировщик и остальная обвязка настроены).
2. ReplicaSet в Kubernetes: несколько подов вместо одного #
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: web-rs
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
Kubernetes будет стараться, чтобы всегда было 3 работающих пода. Один умер — создаётся новый. Избыточность на уровне приложений в чистом виде.
3. DNS + Anycast: геораспределённый failover #
Anycast — это когда один и тот же IP анонсируется из разных точек мира. Пользователь подключается к ближайшему (или просто работающему) инстансу, а маршрутизацией занимается сеть.
Сценарий примерно такой:
- Сервис с IP
203.0.113.1поднимается в Европе, Азии и США. - Клиент из Франции летит в ближайший европейский PoP.
- Если этот PoP падает, трафик перенаправляется, например, в США — IP остаётся тот же, DNS менять не надо.
Так работают CDN, глобальные DNS-сервисы, устойчивые API — типичный пример: сети уровня Cloudflare или Google Public DNS.
Дальше можно собрать более сложную схему из Terraform, Kubernetes и GitOps.
- EC2-инстансы в нескольких зонах, за балансировщиком.
- Kubernetes-кластер с ReplicaSet и PodDisruptionBudget.
- DNS-failover через Route 53.
1. Terraform: балансировщик, таргет-группа и health check #
resource "aws_lb" "web_alb" {
name = "web-load-balancer"
internal = false
load_balancer_type = "application"
subnets = [aws_subnet.public_a.id, aws_subnet.public_b.id]
}
resource "aws_lb_target_group" "web_tg" {
name = "web-targets"
port = 80
protocol = "HTTP"
vpc_id = aws_vpc.main.id
health_check {
path = "/"
interval = 30
timeout = 5
healthy_threshold = 2
unhealthy_threshold = 2
}
}
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.web_alb.arn
port = "80"
protocol = "HTTP"
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.web_tg.arn
}
}
resource "aws_lb_target_group_attachment" "web_instances" {
count = 2
target_group_arn = aws_lb_target_group.web_tg.arn
target_id = aws_instance.web[count.index].id
port = 80
}
2. PDB в Kubernetes: не даём выбить все поды разом #
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web
Эта штука говорит Kubernetes: «что бы ты ни делал (обновления, drain нод и т.д.), оставь минимум два пода живыми».
3. Terraform: Route 53 DNS Failover #
resource "aws_route53_health_check" "web" {
fqdn = "web.example.com"
port = 80
type = "HTTP"
resource_path = "/"
failure_threshold = 3
request_interval = 30
}
resource "aws_route53_record" "web_failover_primary" {
zone_id = aws_route53_zone.main.zone_id
name = "web.example.com"
type = "A"
ttl = 60
set_identifier = "primary"
failover = "PRIMARY"
health_check_id = aws_route53_health_check.web.id
records = [aws_instance.web[0].public_ip]
}
resource "aws_route53_record" "web_failover_secondary" {
zone_id = aws_route53_zone.main.zone_id
name = "web.example.com"
type = "A"
ttl = 60
set_identifier = "secondary"
failover = "SECONDARY"
records = [aws_instance.web[1].public_ip]
}
Если health check на основном инстансе начинает фейлиться, Route 53 перекидывает трафик на резервный, сам домен при этом остаётся тем же.
Дальше остаётся ещё один слой — GitOps. Идея: всё описано в Git (Terraform, Helm, YAML), а Argo CD или похожий инструмент следит, чтобы кластер совпадал с тем, что в репозитории.
- Terraform и Helm-чанки лежат в одном репо.
- Argo CD разворачивает Kubernetes-часть с учётом избыточности.
- Сервис автоматически «подлечивается», если кто-то руками что-то подкрутил в кластере.
Структура mono-repo под инфраструктуру #
infrastructure/
├── terraform/
│ ├── main.tf
│ ├── variables.tf
│ └── route53_failover.tf
└── k8s/
├── base/
│ ├── helm-release.yaml # HelmRelease или Application
│ ├── pdb.yaml
│ └── replicas.yaml
└── overlays/
├── staging/
└── production/
Пример Argo CD Application для HA-деплоя #
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web-ha
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/my-org/infrastructure
targetRevision: main
path: k8s/base
helm:
values: |
replicaCount: 3
pdb:
enabled: true
minAvailable: 2
destination:
server: https://kubernetes.default.svc
namespace: web
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- разворачивает Helm-чарт с тремя репликами;
- включает PodDisruptionBudget с минимум двумя доступными подами;
- обеспечивает, что сервис переживает обновления без даунтайма;
- Argo CD сам подтягивает изменения из Git и приводит кластер к этому состоянию;
- self-healing возвращает конфигурацию к описанной в репо, даже если кто-то в кластере что-то «поправил от руки».
CI/CD вокруг GitOps #
- Любое изменение в
k8s/base/илиterraform/идёт через Pull Request. - CI запускает
terraform planи, например,helm lintдля чартов. - После merge Argo CD автоматически применяет новое состояние в кластере.
- Terraform-стейт лежит удаленно (например, S3 + DynamoDB lock), так что над инфраструктурой можно работать командой и из CI.
