AWS: базовые сервисы, модель ответственности, сеть, стоимость, типичные ошибки
В предыдущей статье — https://courses.digitable.life/post/devops/10-configuration-management/ — мы разбирали, как приводить машины в нужное состояние. Но сами машины откуда-то берутся. В https://courses.digitable.life/post/devops/09-terraform-and-iac/ мы описывали инфраструктуру кодом, не углубляясь в то, что именно этот код создаёт. Настало время открыть коробку.
AWS — не «сервер в аренду». Это примерно двести сервисов, каждый со своей моделью тарификации, своими границами отказа и своим представлением о том, кто за что отвечает. Человек, который умеет запустить EC2, но не понимает, почему счёт вырос на 900 $ без единого нового инстанса, не умеет AWS. Эта статья — про модель, а не про кнопки: про то, что происходит с пакетом, запросом и долларом.
Оговорка про цены: все цифры — регион us-east-1/eu-central-1, порядок величины на момент написания. AWS меняет прайс, вводит новые классы и иногда — как с платными IPv4-адресами в 2024-м — добавляет строки, которых раньше не было. Проверяйте калькулятор и текущие прайс-листы. Важны не конкретные центы, а соотношения: почему одно в двадцать раз дороже другого.
Каркас: аккаунт, регион, зона доступности
Три уровня, которые определяют почти всё остальное.
Аккаунт — граница биллинга, квот и (главное) взрыва. Всё в AWS по умолчанию доступно внутри одного аккаунта и недоступно между аккаунтами. Это не «проект» в терминах GCP и не «подписка» Azure — это самая жёсткая изоляция, которую предлагает платформа. Отсюда практика: прод и стейдж живут в разных аккаунтах, а не в разных VPC. Ошибка в IAM-политике стейджа не должна иметь возможности дотянуться до прод-базы физически, а не по договорённости.
Регион — географически разделённый набор дата-центров со своей плоскостью управления. Регионы почти полностью независимы: S3-бакет в eu-central-1 не виден API в us-east-1 без явной репликации, отказ региона не распространяется. Исключения важны: IAM, Route 53, CloudFront и биллинг — глобальные, их control plane физически живёт в us-east-1. Поэтому крупный сбой us-east-1 исторически ломает вещи по всему миру.
Зона доступности (AZ) — один или несколько ДЦ внутри региона, связанных сетью с задержкой ~1 мс. AZ — это единица отказа, вокруг которой построена вся модель надёжности AWS. У каждого аккаунта имена AZ перемешаны: ваша eu-central-1a и eu-central-1a соседа — разные физические зоны (устойчивый идентификатор — AvailabilityZoneId, например euc1-az2).
карта сервисов)) Вычисления EC2 — виртуалки ECS/Fargate — контейнеры без нод EKS — управляемый Kubernetes Lambda — функции Batch — пакетные задачи Хранение S3 — объекты EBS — блочные тома EFS — сетевая ФС FSx — специализированные ФС Данные RDS — управляемые СУБД Aurora — своя реализация протоколов DynamoDB — key-value без серверов ElastiCache — Redis/Valkey Сеть VPC — своя сеть ALB/NLB — балансировщики Route 53 — DNS CloudFront — CDN PrivateLink — приватный доступ к сервисам Управление IAM — доступ Organizations/SCP — политики аккаунтов CloudWatch — метрики и логи CloudTrail — аудит API Cost Explorer/CUR — деньги
Модель разделяемой ответственности: где заканчивается AWS
Формулировка AWS — «безопасность облака — на нас, безопасность в облаке — на вас» — верна, но бесполезна, пока не приземлена на конкретный сервис. Реальная граница ползёт в зависимости от уровня управляемости.
| Что | EC2 | RDS | Fargate | S3 | Lambda |
|---|---|---|---|---|---|
| Физика, гипервизор, сеть ДЦ | AWS | AWS | AWS | AWS | AWS |
| ОС хоста и её патчи | AWS | AWS | AWS | AWS | AWS |
| Гостевая ОС, ядро, патчи | вы | AWS | AWS | — | AWS |
| Рантайм и его CVE | вы | AWS | вы (образ) | — | AWS (managed runtime) |
| Конфигурация сервиса | вы | вы (параметр-группы) | вы | вы (политики бакета) | вы |
| Сетевой доступ (SG, политики) | вы | вы | вы | вы | вы |
| Шифрование и ключи | вы | вы (включить) | вы | вы (включить) | вы |
| Данные и их классификация | вы | вы | вы | вы | вы |
| Резервные копии и их проверка | вы | вы (настроить + проверить восстановление) | — | вы (версионирование) | — |
| Доступность приложения | вы | вы (Multi-AZ — выбор) | вы | AWS | вы |
Три следствия, которые стоят денег и нервов:
- Управляемость не равна отказоустойчивости. RDS Single-AZ — управляемая база, которая недоступна 10–20 минут при отказе хоста и теряет данные при отказе тома, если нет свежего снапшота. Multi-AZ — это ваш выбор и удвоение цены, не подарок.
- AWS никогда не отвечает за ваши данные. «Удалили бакет» — ваша проблема. Отсюда
MFA Delete, версионирование, Object Lock и отдельный аккаунт-хранилище бэкапов, куда прод имеет право только писать. - SLA — это скидка, а не гарантия. Compute SLA 99.99 % по региону означает возврат части платы при нарушении, а не то, что сервис не упадёт. Проектируйте под отказ AZ; отказ региона — отдельное решение с отдельным ценником.
IAM: единственная вещь, которую надо понять точно
IAM — это распределённая система авторизации, через которую проходит каждый вызов API в AWS, включая внутренние. Ошибки здесь стоят дороже всех остальных, поэтому разберём логику вычисления явно.
Ключевые сущности:
- Principal — кто делает запрос: IAM-пользователь, роль (точнее, её сессия), сервис, анонимный вызов.
- Identity-based policy — политика на принципале («роль может читать этот бакет»).
- Resource-based policy — политика на ресурсе («бакет разрешает читать этой роли»): S3, SQS, KMS, Lambda, Secrets Manager, ECR.
- SCP (Service Control Policy) — потолок прав для всего аккаунта из AWS Organizations. Ничего не разрешает, только ограничивает.
- Permissions boundary — потолок прав для конкретной роли. Инструмент делегирования: «команда может создавать роли, но не мощнее этой границы».
- Session policy — сужение прав при
AssumeRole.
principal, action, resource, условия"] --> DENY{"Есть явный Deny
в любой применимой политике?"} DENY -->|да| NO["ОТКАЗ
(явный Deny всегда побеждает)"] DENY -->|нет| SCP{"SCP организации
разрешает action?"} SCP -->|нет| NO SCP -->|да| RES{"Есть resource-based
политика с Allow?"} RES -->|да, тот же аккаунт| YES["РАЗРЕШЕНО"] RES -->|нет или другой аккаунт| PB{"Permissions boundary
роли разрешает?"} RES -->|да, но межаккаунтный доступ| PB PB -->|нет| NO PB -->|да / не задана| SESS{"Session policy
разрешает?"} SESS -->|нет| NO SESS -->|да / не задана| IDENT{"Identity-based политика
содержит Allow?"} IDENT -->|да| YES IDENT -->|нет| NOD["ОТКАЗ по умолчанию
(нет Allow — значит нет)"] style NO fill:#e76f51,color:#fff style NOD fill:#e76f51,color:#fff style YES fill:#2a9d8f,color:#fff
Два правила, из которых выводится всё остальное: по умолчанию запрещено и явный Deny неотменяем. Второе — главный инструмент безопасности: SCP с Deny на ec2:* в регионах, которыми вы не пользуетесь, невозможно обойти даже администратору аккаунта.
Практический SCP, который стоит включить на день первый:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ЗапретитьВсёВнеРазрешённыхРегионов",
"Effect": "Deny",
"NotAction": ["iam:*", "organizations:*", "route53:*", "cloudfront:*",
"support:*", "sts:*", "budgets:*", "waf:*"],
"Resource": "*",
"Condition": {
"StringNotEquals": { "aws:RequestedRegion": ["eu-central-1", "us-east-1"] }
}
},
{
"Sid": "ЗапретитьВыключениеАудита",
"Effect": "Deny",
"Action": ["cloudtrail:StopLogging", "cloudtrail:DeleteTrail",
"guardduty:DeleteDetector", "config:DeleteConfigurationRecorder"],
"Resource": "*"
},
{
"Sid": "ЗапретитьДолгоживущиеКлючи",
"Effect": "Deny",
"Action": "iam:CreateAccessKey",
"Resource": "*"
}
]
}
Последний блок — самый спорный и самый полезный. Долгоживущие ключи доступа (AKIA...) — источник большинства утечек в AWS. Они попадают в git, в Docker-образы, в Slack. Правильная альтернатива — временные учётные данные через STS, которые AWS выдаёт на 15 минут — 12 часов:
- EC2 → instance profile (IMDSv2 обязателен, IMDSv1 отключить — он уязвим к SSRF);
- ECS/Fargate → task role;
- EKS → IRSA или EKS Pod Identity;
- Lambda → execution role;
- CI вне AWS (GitHub Actions, GitLab) → OIDC federation, без единого секрета в настройках репозитория;
- люди → IAM Identity Center (бывший SSO) с федерацией к вашему IdP.
Как это работает на примере IRSA — самого частого способа дать поду в EKS доступ к S3 без секретов:
(projected token) participant OIDC as OIDC-провайдер кластера participant STS as AWS STS participant S3 as S3 Note over SA,Pod: kubelet монтирует JWT в /var/run/secrets/eks.amazonaws.com/ Pod->>SA: читает токен (живёт 1 час, ротируется) Pod->>STS: AssumeRoleWithWebIdentity(role_arn, token) STS->>OIDC: проверить подпись токена по JWKS OIDC-->>STS: ключ валиден, sub = system:serviceaccount:app:api Note over STS: trust policy роли требует
ровно этот sub и aud=sts.amazonaws.com STS-->>Pod: временные ключи на 1 час Pod->>S3: GetObject с временными ключами S3-->>Pod: объект (если identity-политика роли разрешает)
Trust policy роли — самое важное место, и именно здесь чаще всего делают дыру:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/oidc.eks.eu-central-1.amazonaws.com/id/ABC123" },
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.eu-central-1.amazonaws.com/id/ABC123:aud": "sts.amazonaws.com",
"oidc.eks.eu-central-1.amazonaws.com/id/ABC123:sub": "system:serviceaccount:production:api"
}
}
}]
}
Если вместо StringEquals для sub написать StringLike с system:serviceaccount:*:*, роль сможет принять любой под любого неймспейса — включая тот, куда разработчик задеплоил тестовый контейнер. Это реальная и очень распространённая ошибка.
Проверять политики нужно не глазами, а симулятором — он учитывает SCP, boundary и условия:
$ aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:role/api-prod \
--action-names s3:GetObject s3:DeleteObject \
--resource-arns arn:aws:s3:::app-uploads/customer/1.jpg \
--query 'EvaluationResults[].[EvalActionName,EvalDecision]' --output table
------------------------------------
| SimulatePrincipalPolicy |
+------------------+---------------+
| s3:GetObject | allowed |
| s3:DeleteObject | implicitDeny |
+------------------+---------------+
И регулярно смотреть на фактически использованные права — IAM Access Analyzer умеет генерировать политику по CloudTrail за последние 90 дней. Это единственный практичный способ ужать роль, которую год назад создали с *.
Сеть: VPC и её скрытая стоимость
VPC — программно-определяемая сеть с вашим адресным пространством. Модель проста, но детали тарификации превращают невинную топологию в дорогую.
Ключевые понятия:
- Subnet привязана ровно к одной AZ. «Публичная» — та, у которой в таблице маршрутов есть
0.0.0.0/0 → igw. Никакого флага «public» не существует. - Internet Gateway — горизонтально масштабируемый, отказоустойчивый, бесплатный как объект. Платите только за трафик наружу.
- NAT Gateway — управляемый NAT для приватных подсетей. Стоит ~0,045 $/час (~32 $/мес за факт существования) плюс 0,045 $ за каждый обработанный гигабайт — и входящий, и исходящий. Живёт в одной AZ.
- Security Group — stateful-фильтр на уровне ENI: разрешил исходящее — ответ придёт. Только
Allow-правила. - Network ACL — stateless-фильтр на уровне подсети, с
Deny. Требует явно открывать эфемерные порты для ответов. Используйте только как грубый аварийный барьер.
| Security Group | Network ACL | |
|---|---|---|
| Уровень | сетевой интерфейс | подсеть |
| Состояние | stateful | stateless |
| Правила | только Allow | Allow и Deny |
| Порядок правил | все сразу | по номеру, первое совпадение |
| Ссылки на другие SG | да (главная суперсила) | нет, только CIDR |
| Типичное применение | основной инструмент | блокировка IP, изоляция при инциденте |
Возможность ссылаться в правиле SG на другую SG («в базу можно с чего угодно, где висит sg-app») убирает необходимость знать IP-адреса и переживает автоскейлинг. Это правильный способ описывать доступ внутри VPC.
Рабочий Terraform для базовой сети с оптимизацией стоимости:
# Сеть на две AZ. Один NAT вместо двух — сознательный компромисс: -32 $/мес,
# но при отказе AZ-a приватные подсети обеих зон теряют выход в интернет.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.13"
name = "prod"
cidr = "10.0.0.0/16"
azs = ["eu-central-1a", "eu-central-1b"]
public_subnets = ["10.0.0.0/20", "10.0.16.0/20"]
private_subnets = ["10.0.32.0/20", "10.0.48.0/20"]
database_subnets = ["10.0.64.0/24", "10.0.80.0/24"]
enable_nat_gateway = true
single_nat_gateway = true # для прода с жёстким SLA поставьте false
enable_dns_hostnames = true
# Flow logs — единственный способ потом понять, кто нагенерировал трафик.
# В S3, а не в CloudWatch Logs: дешевле в разы.
enable_flow_log = true
flow_log_destination_type = "s3"
flow_log_max_aggregation_interval = 600
tags = { Environment = "prod", ManagedBy = "terraform" }
}
# Gateway-endpoint для S3 и DynamoDB — БЕСПЛАТЕН и снимает трафик с NAT.
# Обязателен всегда: pull образов из ECR тянет слои именно из S3.
resource "aws_vpc_endpoint" "s3" {
vpc_id = module.vpc.vpc_id
service_name = "com.amazonaws.eu-central-1.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = module.vpc.private_route_table_ids
}
# Interface-endpoints: 0,01 $/час за каждый в каждой AZ + 0,01 $/ГБ.
# Выгодны, когда через NAT идёт заметный трафик к этим сервисам.
resource "aws_vpc_endpoint" "ecr_dkr" {
vpc_id = module.vpc.vpc_id
service_name = "com.amazonaws.eu-central-1.ecr.dkr"
vpc_endpoint_type = "Interface"
subnet_ids = module.vpc.private_subnets
security_group_ids = [aws_security_group.endpoints.id]
private_dns_enabled = true
}
resource "aws_security_group" "endpoints" {
name = "vpc-endpoints"
vpc_id = module.vpc.vpc_id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [module.vpc.vpc_cidr_block] # только изнутри VPC
}
}
Арифметика endpoint против NAT, на которой экономят тысячи долларов. Кластер тянет 3 ТБ/мес образов из ECR:
Через NAT: 3000 ГБ × 0,045 $ = 135,00 $/мес
Через endpoint: 2 AZ × 0,01 $/ч × 730 ч = 14,60 $
+ 3000 ГБ × 0,01 $ = 30,00 $ → 44,60 $/мес
Экономия: ~90 $/мес на одном сервисе. Gateway-endpoint для S3 сделал бы это бесплатно.
Тарифы на трафик, которые надо помнить наизусть:
| Направление | Цена | Комментарий |
|---|---|---|
| Входящий из интернета | 0 $ | всегда бесплатно |
| Исходящий в интернет | ~0,09 $/ГБ | первые 100 ГБ/мес бесплатно |
| Между AZ внутри региона | 0,01 $/ГБ в каждую сторону | то есть 0,02 $ за обмен |
| Внутри одной AZ по приватным IP | 0 $ | но по публичным IP — платно |
| Через NAT Gateway | +0,045 $/ГБ | сверх всего остального |
| Между регионами | 0,02 $/ГБ | |
| Из S3 через CloudFront | 0 $ до CDN | egress платится на стороне CloudFront, дешевле |
Отсюда конкретика: сервис, который болтает с базой в другой AZ на 4 ТБ/мес, платит 80 $ ни за что. Kubernetes с topologyAwareRouting и правильными topologySpreadConstraints (см. https://courses.digitable.life/post/devops/06-kubernetes/) убирает бо́льшую часть этого трафика. А отдача статики напрямую из ALB вместо CloudFront — это переплата в разы плюс лишняя нагрузка на приложение.
Вычисления: что выбрать под свой масштаб
Четыре модели, между которыми реально выбирают.
| EC2 + ASG | ECS Fargate | EKS | Lambda | |
|---|---|---|---|---|
| Единица | виртуалка | задача-контейнер | под | вызов функции |
| Плата за простой control plane | 0 | 0 | 73 $/мес | 0 |
| Цена 2 vCPU / 4 ГБ 24×7 | ~30 $ (on-demand) | ~72 $ | ~30 $ + доля CP | зависит от вызовов |
| Порог входа | низкий | низкий | высокий | средний |
| Эксплуатационная нагрузка | патчи ОС, AMI, автоскейлинг | почти нулевая | высокая (апгрейды, CNI, аддоны) | низкая |
| Холодный старт | минуты | ~40 с | ~40 с (или мгновенно) | 50 мс – 5 с |
| Портируемость | средняя | низкая (vendor lock) | высокая | низкая |
| Когда брать | легаси, GPU, особые требования | 3–20 сервисов, малая команда | много команд, свои платформенные абстракции | событийные и редкие нагрузки |
Разбор без маркетинга:
Fargate дороже EC2 примерно вдвое за тот же размер — это плата за то, что вам не нужно управлять нодами, думать о bin packing и патчить AMI. Для команды из пяти человек с десятком сервисов это отличная сделка: одна инженерная неделя в месяц стоит дороже, чем 40 $ разницы на сервис. Для кластера из ста подов та же арифметика разворачивается: разница становится тысячами долларов, и появляется смысл в EC2 с Karpenter и Spot.
EKS оправдан не размером нагрузки, а количеством команд. 73 $ за control plane — ерунда; настоящая цена — это апгрейды раз в квартал (AWS поддерживает версию ~14 месяцев, потом extended support по 0,60 $/час — в шесть раз дороже), совместимость CNI, CSI-драйверов, cert-manager и всего зоопарка. Если у вас нет человека, для которого это работа, EKS будет источником инцидентов. ECS в этом смысле честнее: скучнее, беднее, но не ломается сам по себе.
Lambda выигрывает на неровной нагрузке. 1 млн вызовов по 200 мс при 512 МБ: 0,20 $ за запросы + 1 000 000 × 0,2 с × 0,5 ГБ × 0,0000166667 = 1,67 $ за вычисления. Меньше двух долларов. Тот же трафик, размазанный ровно 24×7, дешевле держать на EC2. Точка безубыточности примерно там, где утилизация постоянного инстанса переваливает за 40–50 %.
Spot — самая недооценённая скидка в AWS: 70–90 % от on-demand за инстансы, которые могут отобрать с уведомлением за 2 минуты. Это идеально для CI-раннеров (см. https://courses.digitable.life/post/devops/03-modern-ci-platforms/), пакетной обработки и stateless-подов с запасом реплик.
(нужен fallback на on-demand) Running --> Rebalance: EC2 Rebalance Recommendation
(риск повышен, минуты) Rebalance --> Draining: заранее выводим из балансировщика Running --> Interrupting: ITN — уведомление об отзыве Interrupting --> Draining: ровно 120 секунд Draining --> Terminated: SIGTERM подам, дренаж соединений Terminated --> [*] Running --> Terminated: наша собственная остановка note right of Interrupting 120 с — потолок. Если graceful shutdown длится дольше, вы теряете запросы. Проверяйте terminationGracePeriodSeconds. end note
Практический рецепт устойчивого Spot: диверсификация типов инстансов. Пул из одного m5.large отзовут целиком; пул из пятнадцати похожих типов в трёх AZ со стратегией capacity-optimized переживает отзывы почти незаметно. В Kubernetes это делает Karpenter:
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
name: spot-general
spec:
template:
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"] # on-demand — в отдельном NodePool ниже приоритетом
- key: kubernetes.io/arch
operator: In
values: ["amd64", "arm64"] # Graviton дешевле на ~20% при той же производительности
- key: karpenter.k8s.aws/instance-family
operator: In
values: ["m6i", "m6a", "m7i", "m7g", "c6i", "c7g", "r6i"]
- key: karpenter.k8s.aws/instance-size
operator: NotIn
values: ["nano", "micro", "small"]
nodeClassRef:
group: karpenter.k8s.aws
kind: EC2NodeClass
name: default
expireAfter: 168h # принудительная ротация нод раз в неделю
disruption:
consolidationPolicy: WhenEmptyOrUnderutilized
consolidateAfter: 1m # уплотнение: Karpenter сам убирает недозагруженные ноды
limits:
cpu: "400"
Отдельно про Graviton (ARM-процессоры AWS): при переходе на m7g вместо m7i цена ниже примерно на 20 % при сопоставимой или лучшей производительности на большинстве серверных нагрузок. Если ваш стек собирается под arm64 — Go, Java, Python, Node — это самая дешёвая оптимизация из существующих. Мультиархитектурные образы разбирались в https://courses.digitable.life/post/devops/05-containers-and-registries/.
Хранение и данные
S3 — не файловая система, а key-value-хранилище объектов с плоским пространством имён. «Папки» — иллюзия консоли, разделитель / в ключе. С 2020 года чтение после записи строго согласованно (read-after-write consistency), что убрало целый класс костылей.
| Класс | Хранение, $/ГБ·мес | Извлечение | Минимальный срок | Когда |
|---|---|---|---|---|
| Standard | 0,023 | 0 | — | активные данные |
| Intelligent-Tiering | 0,023 → 0,004 | 0 | — | доступ непредсказуем |
| Standard-IA | 0,0125 | 0,01 $/ГБ | 30 дней | ежемесячный доступ |
| One Zone-IA | 0,010 | 0,01 $/ГБ | 30 дней | воспроизводимые данные |
| Glacier Instant | 0,004 | 0,03 $/ГБ | 90 дней | архив с редким мгновенным доступом |
| Glacier Flexible | 0,0036 | 0,01 $/ГБ, минуты–часы | 90 дней | бэкапы |
| Deep Archive | 0,00099 | 0,02 $/ГБ, 12 ч | 180 дней | комплаенс, «никогда не понадобится» |
Ловушка минимальных сроков: объект, удалённый из Standard-IA через 5 дней, оплачивается как за 30. Для мелких и короткоживущих файлов IA дороже Standard. Ещё одна: биллинг Standard-IA округляет объекты меньше 128 КБ до 128 КБ — миллион файлов по 4 КБ будет считаться как 128 ГБ.
Рабочая lifecycle-политика с защитой от обеих ловушек:
{
"Rules": [
{
"ID": "логи-в-архив-и-в-мусор",
"Filter": { "And": { "Prefix": "logs/", "ObjectSizeGreaterThan": 131072 } },
"Status": "Enabled",
"Transitions": [
{ "Days": 30, "StorageClass": "STANDARD_IA" },
{ "Days": 120, "StorageClass": "GLACIER_IR" }
],
"Expiration": { "Days": 730 }
},
{
"ID": "чистить-незавершённые-мультипарт-загрузки",
"Filter": {},
"Status": "Enabled",
"AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
}
]
}
Второе правило обязательно всегда. Незавершённые multipart-загрузки не видны в листинге бакета, но занимают место и тарифицируются годами. Это одна из самых частых «загадочных» строк в счёте.
EBS — сетевой блочный том, привязанный к одной AZ. Практический вывод один: gp2 устарел.
| Тип | $/ГБ·мес | IOPS | Замечание |
|---|---|---|---|
| gp3 | 0,080 | 3000 базово, до 16 000 за доплату | дефолт по умолчанию |
| gp2 | 0,100 | 3 IOPS/ГБ (нужно 1 ТБ ради 3000) | легаси, дороже на 20 % |
| io2 Block Express | 0,125 + 0,065 за IOPS | до 256 000 | только для тяжёлых СУБД |
| st1 | 0,045 | throughput-оптимизированный HDD | последовательное чтение, логи |
Массовая миграция gp2 → gp3 делается онлайн, без остановки, и даёт мгновенные 20 %:
# Найти все gp2-тома и оценить экономию
$ aws ec2 describe-volumes --filters Name=volume-type,Values=gp2 \
--query 'Volumes[].[VolumeId,Size,State]' --output text | \
awk '{s+=$2} END {printf "Томов: %d, всего %d ГБ, экономия ~%.2f $/мес\n", NR, s, s*0.02}'
Томов: 47, всего 8400 ГБ, экономия ~168.00 $/мес
# Конвертация без даунтайма
$ aws ec2 modify-volume --volume-id vol-0a1b2c3d --volume-type gp3 --iops 3000 --throughput 125
Базы данных. RDS — это ваша СУБД на управляемом хосте с автоматикой бэкапов и failover. Aurora — переписанный AWS слой хранения под протоколы MySQL/PostgreSQL: шесть копий данных по трём AZ, репликация на уровне журнала, реплики за миллисекунды и failover за десятки секунд вместо минут. Цена — премия ~20 % к инстансу плюс 0,20 $ за миллион операций ввода-вывода, которая на write-heavy нагрузке легко превышает стоимость самих инстансов (в Aurora I/O-Optimized её нет, но базовая цена выше на 30 %).
DynamoDB — другая история: нет соединений, нет схемы, нет джойнов, зато предсказуемая задержка на любом объёме. Требует проектировать под запросы заранее (single-table design), и переделать потом дорого. Берите, когда паттерн доступа известен и стабилен; не берите как «быструю замену PostgreSQL».
Стоимость: как читать счёт и чем на него влиять
Первое правило FinOps в AWS: сначала выключить лишнее, потом оптимизировать размер, и только потом покупать обязательства. Скидка на неиспользуемый ресурс — самая дорогая скидка в мире.
Модели оплаты вычислений:
| Модель | Скидка | Гибкость | Риск | Кому |
|---|---|---|---|---|
| On-Demand | 0 % | полная | нет | всплески, эксперименты |
| Spot | 70–90 % | отзыв за 2 мин | потеря узла | CI, батчи, stateless |
| Compute Savings Plans | до 66 % | любой регион, семейство, EC2/Fargate/Lambda | обязательство $/час на 1–3 года | дефолтный выбор |
| EC2 Instance SP | до 72 % | привязка к семейству и региону | то же + жёсткая привязка | стабильный крупный кластер |
| Reserved Instances | до 72 % | привязка к типу; Convertible — обмен | обязательство | легаси-подход, RDS/ElastiCache |
Практическое правило: покупайте Savings Plans на базовую нагрузку, которая точно останется — обычно 60–70 % от текущего минимума за последние три месяца. Разница между 1 год no-upfront (~28 %) и 3 года all-upfront (~50–66 %) — это ставка на то, что через три года вы будете здесь и на этой архитектуре. Для стартапа это плохая ставка; для банка — хорошая.
Инструменты, которые надо включить в первый день, а не когда счёт удивил:
# 1. Бюджет с алертом на прогноз, а не на факт — узнать заранее
$ aws budgets create-budget --account-id 123456789012 --budget '{
"BudgetName": "prod-monthly",
"BudgetLimit": {"Amount": "4000", "Unit": "USD"},
"TimeUnit": "MONTHLY",
"BudgetType": "COST"
}' --notifications-with-subscribers '[{
"Notification": {"NotificationType":"FORECASTED","ComparisonOperator":"GREATER_THAN",
"Threshold":90,"ThresholdType":"PERCENTAGE"},
"Subscribers": [{"SubscriptionType":"EMAIL","Address":"platform@example.com"}]
}]'
# 2. Разбивка за месяц по сервисам — что вообще растёт
$ aws ce get-cost-and-usage --time-period Start=2026-06-01,End=2026-07-01 \
--granularity MONTHLY --metrics UnblendedCost \
--group-by Type=DIMENSION,Key=SERVICE \
--query 'ResultsByTime[0].Groups[?Metrics.UnblendedCost.Amount>`100`].[Keys[0],Metrics.UnblendedCost.Amount]' \
--output text | sort -k2 -rn
EC2 - Other 1121.40 <- сюда прячутся NAT, EBS и трафик!
Amazon Elastic Compute Cloud 1849.77
Amazon Relational Database 760.12
AmazonCloudWatch 214.88
# 3. Самая полезная детализация: тип использования внутри "EC2 - Other"
$ aws ce get-cost-and-usage --time-period Start=2026-06-01,End=2026-07-01 \
--granularity MONTHLY --metrics UnblendedCost \
--filter '{"Dimensions":{"Key":"SERVICE","Values":["EC2 - Other"]}}' \
--group-by Type=DIMENSION,Key=USAGE_TYPE --output text | sort -k3 -rn | head -5
EUC1-NatGateway-Bytes 270.00
EUC1-EBS:VolumeUsage.gp3 210.44
EUC1-DataTransfer-Out-Bytes 180.20
EUC1-NatGateway-Hours 65.70
EUC1-DataTransfer-Regional-Bytes 80.06
EC2 - Other — категория, в которой прячется всё, что вы не заказывали явно: NAT, тома, трафик, снапшоты, платные IPv4. Если счёт вырос непонятно почему, начинайте разбор отсюда.
Более серьёзный инструмент — Cost and Usage Report (CUR) в S3 + Athena: это построчная детализация каждого часа каждого ресурса. Только он позволяет ответить «сколько стоит вот эта команда» — при условии, что у вас есть теги.
-- Athena по CUR: во что обошёлся каждый сервис за месяц, с распределением
-- нераспределённого по доле (untagged показываем отдельно — это долг команды)
SELECT
COALESCE(resource_tags_user_service, '<без тега>') AS service,
ROUND(SUM(line_item_unblended_cost), 2) AS cost_usd,
ROUND(100.0 * SUM(line_item_unblended_cost)
/ SUM(SUM(line_item_unblended_cost)) OVER (), 1) AS pct
FROM cur.prod
WHERE line_item_usage_start_date >= DATE '2026-06-01'
AND line_item_usage_start_date < DATE '2026-07-01'
AND line_item_line_item_type IN ('Usage', 'SavingsPlanCoveredUsage', 'DiscountedUsage')
GROUP BY 1
ORDER BY cost_usd DESC;
Теги в AWS не наследуются автоматически и не проставляются задним числом — их нужно активировать в биллинге и навязывать через Terraform (default_tags в провайдере) и SCP, запрещающий создание ресурсов без обязательных тегов. Без этого CUR показывает большой блок «неизвестно чьё», и разговор о стоимости упирается в спор.
Типичные ошибки
Собранное по инцидентам и разборам счетов — в порядке того, как часто встречается.
- Долгоживущие ключи
AKIA.... Утекают в git и образы. Лечение: OIDC/instance profile/IRSA, SCPDeny iam:CreateAccessKey, обязательный секрет-сканер в CI (https://courses.digitable.life/post/devops/17-security-in-pipeline/). - Публичный S3-бакет. Сейчас Block Public Access включён по умолчанию — не выключайте его «на минутку для отладки». Для отдачи файлов наружу — CloudFront + OAC, а не публичный бакет.
- Один аккаунт на всё. Прод, стейдж и песочница в одном аккаунте — это вопрос времени. Разделяйте через Organizations, связывайте ролями, накрывайте SCP.
- NAT Gateway без VPC-endpoints. Сотни долларов за трафик, который мог идти бесплатно. Gateway-endpoint для S3 — обязателен всегда.
- Забытые EBS-тома и снапшоты. Удаление инстанса не удаляет том, если
DeleteOnTermination = false. Снапшоты копятся годами.aws ec2 describe-volumes --filters Name=status,Values=available— список того, что просто лежит. - Логи в CloudWatch без retention. По умолчанию — «никогда не удалять», а приём стоит 0,50 $/ГБ. Логи debug-уровня из подов в CloudWatch — способ платить больше за наблюдаемость, чем за вычисления. Ставьте retention сразу, а объёмные логи отправляйте в S3.
- Cross-AZ-трафик по невнимательности. Kafka, Cassandra и любой чатливый межсервисный обмен, размазанный по зонам. Помните про 0,01 $/ГБ в каждую сторону.
- Отсутствие тестов восстановления. Бэкап, который никогда не разворачивали, — не бэкап. Раз в квартал восстанавливайте RDS-снапшот в отдельный инстанс и прогоняйте проверку целостности.
0.0.0.0/0в security group. Особенно на 22 и 3389. Доступ к машинам — через SSM Session Manager: без открытых портов, с аудитом в CloudTrail.- IMDSv1. Позволяет вытащить учётные данные роли через SSRF в приложении. Требуйте IMDSv2 (
http_tokens = "required"), а лучше — запрещайте IMDS для подов вовсе. t-инстансы под постоянной нагрузкой. Burstable-инстансы копят CPU-кредиты; при их исчерпании производительность падает в разы, а в режимеunlimitedпоявляются неожиданные списания. Для ровной нагрузки беритеm/c.- Игнорирование квот. Лимит на eIP, ENI, инстансы конкретного семейства обнаруживается в момент масштабирования — то есть в худший момент. Запрашивайте повышение заранее.
- Терраформ-стейт в аккаунте, которым он управляет. Классический способ потерять и инфраструктуру, и возможность её восстановить. Стейт — в отдельном аккаунте.
- Аварийное восстановление «мы же в облаке». Мультирегион не появляется сам: нужны репликация S3, глобальные таблицы или read-replica в другом регионе, копии AMI, план переключения DNS и — главное — регулярные учения.
Практика продакшена: как это обычно устроено
Зрелая установка выглядит примерно так, и почти все приходят к ней одинаково:
только биллинг и SCP,
никаких рабочих нагрузок"] subgraph SEC["OU: Security"] LOG["log-archive
CloudTrail, CUR, копии бэкапов
write-only для остальных"] AUD["audit
GuardDuty, Security Hub"] end subgraph WL["OU: Workloads"] PROD["prod"] STG["staging"] DEV["dev / песочницы"] end subgraph INFRA["OU: Infrastructure"] NET["network
Transit Gateway, DNS"] TOOL["tooling
Terraform state, CI"] end end IDP["Корпоративный IdP
(Okta / Entra ID)"] --> SSO["IAM Identity Center"] SSO -->|временные роли| PROD SSO -->|временные роли| STG GH["GitHub Actions
OIDC, без секретов"] -->|AssumeRoleWithWebIdentity| TOOL TOOL -->|роль деплоя| PROD PROD -.->|логи и трейлы| LOG STG -.-> LOG style MGMT fill:#e76f51,color:#fff style LOG fill:#5c6b7a,color:#fff style PROD fill:#2a9d8f,color:#fff style SSO fill:#3d8bcd,color:#fff
Разворачивают это либо Control Tower (быстро, с готовыми guardrails, но со своей магией и ограничениями), либо Terraform + Organizations вручную (дольше, зато всё понятно и версионируется). Для команды, которая уже живёт в IaC, второй путь обычно честнее.
Подключение GitHub Actions без единого секрета — минимальный рабочий кусок, который заменяет AWS_ACCESS_KEY_ID в настройках репозитория:
name: deploy
on:
push:
branches: [main]
permissions:
id-token: write # без этого OIDC-токен не выдадут
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # ручное подтверждение и защита секретов среды
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/gha-deploy-prod
aws-region: eu-central-1
role-duration-seconds: 900 # ровно столько, сколько нужно деплою
- name: Проверить, кем мы стали
run: aws sts get-caller-identity
- name: Обновить сервис ECS
run: |
aws ecs update-service --cluster prod --service api \
--task-definition api:${{ github.run_number }} --force-new-deployment
aws ecs wait services-stable --cluster prod --services api
Trust policy для роли gha-deploy-prod обязана ограничивать sub конкретным репозиторием и веткой — иначе развернуть прод сможет любой чужой репозиторий на GitHub:
"Condition": {
"StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" },
"StringLike": { "token.actions.githubusercontent.com:sub": "repo:acme/api:ref:refs/heads/main" }
}
Минимальный набор «включено с первого дня», который стоит воспринимать как чек-лист:
- CloudTrail с организационным трейлом в отдельный аккаунт, с валидацией целостности файлов
- Block Public Access на уровне аккаунта, шифрование S3 и EBS по умолчанию
- GuardDuty во всех регионах, включая неиспользуемые (там как раз майнят)
- SCP: запрет регионов, запрет отключения аудита, запрет корневого пользователя
- MFA на root, root-ключи удалены, root используется только для нескольких специальных операций
- Бюджеты с алертом по прогнозу и аномалиям (AWS Cost Anomaly Detection)
- Обязательные теги через
default_tagsв Terraform-провайдере и SCP - Backup-план в AWS Backup с копией в отдельный аккаунт и Vault Lock
- Квоты проверены и повышены заранее для критичных ресурсов
Мини-итог
- Аккаунт — единица изоляции и взрыва, регион — независимая плоскость управления, AZ — единица отказа. Проектируйте от этих трёх границ.
- Разделяемая ответственность двигается вместе с уровнем управляемости, но данные, доступы и восстановление всегда ваши.
- IAM: по умолчанию запрещено, явный
Denyнеотменяем, долгоживущих ключей быть не должно. Проверяйте политики симулятором, а не глазами. - Сеть — главный источник неожиданных счетов. NAT, egress и cross-AZ не видны в архитектурной диаграмме, но дают четверть счёта. VPC-endpoints и AZ-осведомлённая маршрутизация окупаются мгновенно.
- Fargate дороже EC2 вдвое и почти всегда стоит своих денег на малом масштабе; EKS оправдан числом команд, а не числом подов; Lambda выигрывает при утилизации ниже ~40 %.
- Порядок оптимизации денег: выключить лишнее → уменьшить размеры → Spot и Graviton → и только потом Savings Plans.
- Всё, что не покрыто тегами и CUR, — не управляется. Начинайте с наблюдаемости счёта, а не с догадок.
Источники
- AWS Well-Architected Framework — шесть столпов, включая Cost Optimization и Sustainability; лучшая отправная точка.
- AWS Shared Responsibility Model — официальная формулировка границы.
- IAM Policy Evaluation Logic — первоисточник по порядку вычисления; читать целиком.
- VPC Documentation и Data transfer costs overview — разбор тарификации трафика с диаграммами.
- Amazon EKS Best Practices Guides — отдельные разделы по безопасности, сети, стоимости и надёжности.
- Karpenter documentation — автоскейлинг нод с диверсификацией Spot.
- AWS Pricing Calculator и AWS Cost and Usage Report — расчёт до и разбор после.
- Corey Quinn, «Last Week in AWS» — трезвый разбор биллинга и практик, включая знаменитый The Cloud Costs Money.
- AWS Security Reference Architecture (SRA) — эталонная структура мультиаккаунтной установки.
- Open Guide to AWS — независимый community-справочник с честными оценками сервисов.
Что дальше
AWS — не единственный вариант, и для части команд не лучший: если стек построен на .NET, Active Directory и корпоративных лицензиях Microsoft, экономика и интеграция выглядят иначе. Об этом — следующая статья: Microsoft Azure: сервисы, интеграция с .NET-миром, AKS, сравнение с AWS.