Модуль: DevOps · Уровень: Middle+/Senior

TL;DR#

  • Compute — спектр от «управляй сам» до «не думай о серверах»: VM (EC2 / Compute Engine) → контейнеры-оркестратор (ECS, EKS / GKE) → serverless-контейнеры (Fargate, Cloud Run) → FaaS (Lambda / Cloud Functions). Чем правее, тем меньше операционки и тем дороже за единицу вычислений, но дешевле при низкой/рваной нагрузке.
  • Storage — объектное хранилище S3 / GCS (для файлов, бэкапов, статики, data lake), блочное (EBS / Persistent Disk) для дисков VM. Объектное — не файловая система: нет частичной перезаписи, eventual/strong consistency-нюансы, оплата за объём + запросы + трафик.
  • Managed DB — RDS / Aurora (AWS), Cloud SQL / AlloyDB / Spanner (GCP). Облако берёт на себя бэкапы, патчи, реплики, failover. Вы платите за удобство и теряете часть контроля (версии, расширения, суперюзер).
  • Очереди/события — SQS (очередь) + SNS (pub/sub fan-out) в AWS; Pub/Sub в GCP (и то, и другое в одном). Для бэкендера это основа асинхронной обработки, развязки сервисов, ретраев и DLQ.
  • IAM — кто (principal) может делать что (action) с чем (resource) при каких условиях. Принцип наименьших привилегий. Для сервисов — роли/service accounts, а не статичные ключи; короткоживущие креды через assume-role / Workload Identity.

Теория#

Спектр compute#

УровеньAWSGCPОперационкаКогда
VMEC2Compute Engineвы патчите ОС, масштабируетеlegacy, специфичные требования, GPU
Контейнеры + оркестраторEKS, ECSGKEуправляете кластером/нодамимикросервисы, сложная маршрутизация
Serverless-контейнерыFargate, App RunnerCloud Runтолько образ, без нодHTTP-сервисы, рваная нагрузка
FaaSLambdaCloud Functionsтолько функциясобытия, glue-логика, cron

Для Go-бэкендера типичный выбор сегодня — Cloud Run (GCP) или ECS/Fargate / EKS (AWS): пушишь Docker-образ, платформа крутит и масштабирует.

# Cloud Run: деплой контейнера одной командой
gcloud run deploy my-svc \
  --image=gcr.io/proj/my-svc:sha-abc123 \
  --region=europe-west1 \
  --min-instances=1 --max-instances=20 \
  --concurrency=80 \
  --no-allow-unauthenticated

Cloud Run / Fargate vs Lambda для Go:

  • Go в Lambda — быстрый cold start (компилируемый, маленький бинарь), но модель «один запрос на инстанс» (нет внутреннего конкуррентного обслуживания), лимиты на время (15 мин) и размер.
  • Cloud Run обслуживает несколько запросов на инстанс (concurrency), что отлично ложится на Go-горутины — дешевле и эффективнее под HTTP-нагрузку.

Storage: объектное хранилище (S3 / GCS)#

Объектное хранилище — не POSIX-ФС. Объект = ключ + байты + метаданные. Нет директорий (только префиксы), нет частичной перезаписи (PUT перезаписывает целиком), нет «переименования» (это copy+delete).

Ключевые понятия:

  • Классы хранения: Standard (горячие) → Infrequent Access → Glacier/Archive (холодные, дёшево за хранение, дорого/медленно за извлечение). Lifecycle-политики автоматически перемещают/удаляют.
  • Консистентность: S3 теперь strong read-after-write для PUT и DELETE. Но листинг и кросс-региональная репликация — eventual.
  • Доступ: приватный по умолчанию; presigned URL для временного прямого доступа клиента (загрузка/скачивание минуя бэкенд).
  • Versioning и Object Lock — защита от случайного удаления / WORM-комплаенс.
// presigned URL: клиент грузит файл напрямую в S3, минуя ваш сервис
ps := s3.NewPresignClient(s3Client)
req, _ := ps.PresignPutObject(ctx, &s3.PutObjectInput{
    Bucket: aws.String("uploads"),
    Key:    aws.String("user/42/avatar.png"),
}, s3.WithPresignExpires(15*time.Minute))
// отдаём req.URL клиенту, он делает PUT сам

Блочное хранилище (EBS / Persistent Disk) — это «диск» для VM: умеет частичную перезапись, привязан к зоне, имеет IOPS/throughput-лимиты.

Managed реляционные БД#

AWSGCP
Managed Postgres/MySQLRDSCloud SQL
Cloud-native совместимыйAurora (Postgres/MySQL-совместимый)AlloyDB (Postgres)
Globally-distributedDynamoDB (NoSQL), Aurora GlobalSpanner (NewSQL, горизонтальный)

Что даёт managed: автоматические бэкапы и PITR (point-in-time recovery), minor-патчи, Multi-AZ/HA с автоматическим failover, read-replicas, мониторинг.

Что теряете: root/superuser, произвольные расширения и версии, полный контроль над postgresql.conf (только через parameter groups), иногда задержку с новыми мажорными версиями.

Для Go: подключение через стандартный database/sql + драйвер (pgx). Важные нюансы в облаке:

  • Connection pooling: managed Postgres имеет лимит соединений; serverless-инстансы (Lambda/Cloud Run) легко его выедают. Нужен пулер (RDS Proxy, PgBouncer, Cloud SQL connector) или жёсткий лимит SetMaxOpenConns.
  • IAM-аутентификация к БД: вместо пароля — короткоживущий токен (RDS IAM auth, Cloud SQL IAM).
db.SetMaxOpenConns(20)          // не дать пулу превысить лимит инстанса БД
db.SetMaxIdleConns(10)
db.SetConnMaxLifetime(30 * time.Minute)  // переоткрывать, чтобы ловить failover/ротацию
db.SetConnMaxIdleTime(5 * time.Minute)

Очереди и события#

AWS:

  • SQS — управляемая очередь (point-to-point). Standard (at-least-once, без строгого порядка, высокий throughput) и FIFO (exactly-once-ish, порядок, ниже throughput). Консьюмер делает long-poll ReceiveMessage, обрабатывает, явно DeleteMessage. Невидимость (visibility timeout) и DLQ (dead-letter queue) после N неудачных попыток.
  • SNS — pub/sub fan-out: одно сообщение → много подписчиков (SQS-очереди, Lambda, HTTP). Классический паттерн SNS → несколько SQS для надёжного fan-out.
  • EventBridge — событийная шина с маршрутизацией по правилам.

GCP:

  • Pub/Sub — совмещает роли SQS и SNS: topic (publish) + subscription (по одной на консьюмера). Pull или push доставка, at-least-once, DLQ, ordering keys для порядка. Один topic с несколькими subscriptions = fan-out.
// SQS consumer: long-poll + явное удаление после успешной обработки
out, _ := sqsClient.ReceiveMessage(ctx, &sqs.ReceiveMessageInput{
    QueueUrl:            &queueURL,
    MaxNumberOfMessages: 10,
    WaitTimeSeconds:     20,           // long polling
    VisibilityTimeout:   60,
})
for _, m := range out.Messages {
    if err := handle(ctx, m); err != nil {
        continue // не удаляем -> сообщение вернётся после visibility timeout, потом в DLQ
    }
    sqsClient.DeleteMessage(ctx, &sqs.DeleteMessageInput{
        QueueUrl: &queueURL, ReceiptHandle: m.ReceiptHandle,
    })
}

Ключевой инвариант для бэкендера: доставка at-least-once ⇒ обработчик должен быть идемпотентным (дедупликация по message-id / бизнес-ключу), потому что одно сообщение может прийти дважды.

IAM базово#

Модель: principal (пользователь/роль/service account) — action (что можно: s3:GetObject) — resource (над чем) — condition (когда: IP, MFA, тег). Запрос разрешён, только если есть явный allow и нет явного deny (deny всегда побеждает в AWS).

  • AWS: пользователи, группы, роли (assume-role даёт временные креды), политики (JSON). Сервисы получают права через роль, прикреплённую к инстансу/таску/функции (instance profile / task role / Lambda execution role) — без статичных ключей.
  • GCP: members (user/serviceAccount/group) ← привязка роли (predefined/custom) ← на ресурсе/проекте/организации (иерархия наследования). Сервисы используют service accounts; в GKE — Workload Identity связывает k8s SA с GCP SA.
// AWS: read-only доступ к одному бакету
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"]
  }]
}

Правила гигиены: наименьшие привилегии, никаких долгоживущих ключей в коде/CI (используйте роли + OIDC), отдельные роли на сервис, регулярный аудит (Access Analyzer / IAM Recommender).

Подводные камни / gotchas#

  • Объектное хранилище ≠ файловая система: нет append/частичной записи, «папки» — фикция, rename = copy+delete. Не стройте на S3 БД или общий мутабельный стейт.
  • At-least-once доставка очередей ⇒ дубликаты неизбежны. Без идемпотентности — двойные списания/письма. FIFO/exactly-once смягчает, но дороже и медленнее.
  • Исчерпание соединений к managed БД из serverless: тысячи инстансов × пул = лимит БД превышен. Нужен пулер или connection-aware дизайн.
  • Egress-трафик платный (особенно cross-region, в интернет, между AZ). Архитектура «болтливых» сервисов через регионы дорого обходится. NAT Gateway тоже стоит за трафик.
  • Visibility timeout < времени обработки в SQS — сообщение станет видимым и обработается повторно параллельно. Настраивайте под p99 обработки или продлевайте heartbeat’ом.
  • Cold start у Lambda при больших зависимостях/VPC-привязке. Go помогает (малый бинарь), но VPC ENI и provisioned concurrency — отдельная тема.
  • Широкие IAM-политики ("Action": "*", "Resource": "*") — частая дыра. Wildcard-доступ к S3/секретам = инцидент при компрометации.
  • Региональность и зональность: ресурс в одной зоне/регионе недоступен из другой без репликации; падение зоны кладёт single-AZ ресурсы. Multi-AZ ≠ бесплатно.
  • Стоимость холодного хранилища: Glacier дёшев за хранение, но извлечение медленное и платное — не для горячих данных.

Вопросы на собеседовании#

В: Когда выбрать Lambda/Cloud Functions, а когда контейнеры (Cloud Run/ECS)? О: FaaS — для событийной/рваной нагрузки, glue-логики, cron, где важна нулевая операционка и оплата за вызовы. Контейнеры (Cloud Run/Fargate) — для постоянных HTTP-сервисов, длительных/конкуррентных задач, контроля рантайма. Для Go под HTTP Cloud Run обычно эффективнее: один инстанс обслуживает много запросов горутинами (concurrency), тогда как Lambda — запрос-на-инстанс.

В: Чем объектное хранилище отличается от файловой системы и блочного диска? О: Объектное (S3/GCS) — плоское key→bytes, нет частичной записи (PUT целиком), нет директорий (только префиксы), rename = copy+delete, оплата за объём+запросы+трафик, масштабируется почти бесконечно. Блочное (EBS/PD) — это «диск» для VM с частичной записью и IOPS, привязан к зоне. ФС поверх блочного даёт POSIX-семантику. На S3 нельзя строить мутабельный стейт как на ФС.

В: В чём разница между SQS и SNS, и как сделать надёжный fan-out в AWS? О: SQS — очередь point-to-point: сообщение читает один консьюмер. SNS — pub/sub fan-out: одно сообщение уходит всем подписчикам, но без буферизации/ретраев на стороне получателя. Надёжный fan-out — SNS topic → несколько SQS-очередей (каждый консьюмер читает свою очередь со своим темпом, DLQ, ретраями). В GCP это одним сервисом: Pub/Sub topic с несколькими subscriptions.

В: Почему обработчик сообщений из очереди должен быть идемпотентным? О: Потому что облачные очереди дают at-least-once доставку: сбой консьюмера до удаления сообщения, истечение visibility timeout, ретраи — всё ведёт к повторной доставке. Если обработка не идемпотентна (нет дедупликации по message-id/бизнес-ключу или транзакционной защиты), получаем дублирующиеся эффекты — двойные списания, повторные письма.

В: Что вы теряете и что получаете, переходя на managed БД (RDS/Cloud SQL)? О: Получаете: автоматические бэкапы и PITR, патчи, Multi-AZ HA с failover, read-replicas, мониторинг — минус операционка. Теряете: суперюзера, произвольные расширения/версии, прямой доступ к конфигам ОС и postgresql.conf (только parameter groups), иногда отставание по новым версиям и более высокую цену за тот же ресурс.

В: Как сервису в облаке безопасно получать доступ к ресурсам без статичных ключей? О: Через роли/identity, привязанные к самому compute: в AWS — IAM role на EC2/ECS task/Lambda (временные креды от STS), в GCP — service account на инстансе/Cloud Run, в GKE — Workload Identity (k8s SA ↔ GCP SA). Для CI — OIDC-федерация. Это убирает долгоживущие ключи, даёт автоматическую ротацию и привязку прав к конкретному воркладу.

В: Как не выесть лимит соединений managed БД из serverless-сервиса? О: Serverless масштабируется до многих инстансов, каждый со своим пулом — суммарно превышает лимит БД. Решения: внешний пулер (RDS Proxy, Cloud SQL connector, PgBouncer), жёсткий SetMaxOpenConns на инстанс, использование транзакционного пулинга, и ConnMaxLifetime, чтобы корректно подхватывать failover.

В: Как работает IAM-модель и что значит «deny побеждает»? О: Доступ описывается как principal → action → resource → condition. В AWS запрос разрешён, только если есть явный Allow и нет ни одного явного Deny — explicit deny всегда перекрывает любой allow. По умолчанию всё запрещено. Принцип наименьших привилегий: давать минимально необходимые action на конкретные ресурсы, избегать wildcard.

На что копают на senior+#

  • Дизайн под отказоустойчивость: Multi-AZ vs Multi-Region, RTO/RPO, как failover влияет на соединения и кэши, чтение из реплик и lag.
  • Cost engineering: egress/NAT-стоимость, классы хранения и lifecycle, spot/preemptible, right-sizing, savings plans, FinOps-мышление.
  • Идемпотентность и exactly-once в распределённой обработке: дедупликация, outbox-паттерн, транзакционная публикация событий.
  • Сетевая модель: VPC, приватные подсети, endpoints/PrivateLink (доступ к S3/БД без интернета), security groups, egress-контроль.
  • Безопасность данных: шифрование at-rest/in-transit, KMS/CMEK, ротация ключей, secret manager, отзыв доступа.
  • Выбор БД под задачу: реляционка vs DynamoDB/Spanner, single-table design, hot partition, глобальное масштабирование и его цена консистентности.