Cloud SQL: управляемые базы

Как перестать администрировать PostgreSQL руками: что Cloud SQL берёт на себя, чего он не умеет, как подключаться безопасно и почему забытый dev-инстанс тихо съедает бюджет.

Cloud SQL — управляемая реляционная база (PostgreSQL, MySQL, SQL Server) в Google Cloud: сервер, диск, репликация, бэкапы и патчи — на стороне Google, вы работаете с обычной базой по обычному протоколу.

Зачем нужна «база как сервис»

PostgreSQL прекрасно ставится на виртуалку командой apt install. Проблема не в установке. Проблема начинается на второй месяц эксплуатации, и звучит она так:

  • Кто накатывает security-патчи и следит за CVE в вашей версии?
  • Где бэкапы, кто проверял, что они восстанавливаются, и как откатиться на «вчера, 14:32», если разработчик выполнил DELETE без WHERE?
  • Что произойдёт с сервисом, если зона, где живёт эта виртуалка, ляжет на два часа?
  • Кто ночью заметит, что диск заполнился на 100% и база встала?

Cloud SQL отвечает на все эти вопросы галочками в конфигурации. Вы получаете обычный PostgreSQL — тот же psql, тот же SQL, те же миграции, — но с автоматическими бэкапами, восстановлением на точку во времени, резервной копией в другой зоне и обновлениями по расписанию.

Взамен вы теряете часть свободы: нет root-доступа к машине, нельзя поставить произвольное расширение (только из списка поддерживаемых), нельзя тонко крутить всё подряд в postgresql.conf (доступны database flags — заметное, но всё же подмножество). Для 95% приложений это честный обмен.

Создаём инстанс

Единица Cloud SQL — инстанс: по сути виртуальная машина с диском, на которой крутится СУБД. Внутри инстанса — одна или несколько баз данных.

gcloud sql instances create shop-db \
  --database-version=POSTGRES_16 \
  --tier=db-custom-2-7680 \
  --region=europe-west1 \
  --storage-size=20GB \
  --storage-auto-increase \
  --availability-type=ZONAL \
  --backup-start-time=03:00 \
  --enable-point-in-time-recovery

# база и пользователь приложения
gcloud sql databases create shop --instance=shop-db
gcloud sql users create app --instance=shop-db --password='...'

Разберём ключевые флаги, потому что каждый из них — это либо деньги, либо надёжность.

ФлагЧто значитНа что влияет
--tier2 vCPU, 7,5 ГБ RAMОсновная строка счёта. Считается по часам работы инстанса
--storage-sizeстартовый размер дискаПлатите за выделенный объём, а не за занятый
--storage-auto-increaseдиск сам растёт при заполненииСпасает от падения... и никогда не уменьшается обратно
--availability-typeZONAL или REGIONAL (HA)REGIONAL примерно удваивает цену инстанса
--enable-point-in-time-recoveryхранение WAL-логовПозволяет откатиться на любую секунду в прошлом

Важно про цену: у Cloud SQL нет бесплатного уровня. Даже самый маленький инстанс — это порядка $10–15 в месяц, потому что он работает круглосуточно. Здесь нет «scale to zero», как в Cloud Run: инстанс, к которому никто не обращается, стоит ровно столько же, сколько нагруженный. Для учёбы хватает $300 стартового кредита, а инстанс между занятиями можно останавливать: gcloud sql instances patch shop-db --activation-policy=NEVER (за диск платить всё равно придётся, но не за vCPU и RAM).

Бэкапы, PITR и реплики

Автоматические бэкапы

Cloud SQL раз в сутки в заданное окно снимает бэкап и хранит последние N (по умолчанию 7). Восстановление создаёт новый инстанс или перезаписывает существующий — обычная операция на несколько минут.

Но суточный бэкап — грубый инструмент: если беда случилась в 17:40, а бэкап был в 03:00, вы теряете почти день. Для этого есть point-in-time recovery: Cloud SQL дополнительно хранит журнал транзакций (WAL в PostgreSQL, binlog в MySQL) и умеет «доиграть» его от бэкапа до нужной секунды.

# откатываемся ровно на момент перед роковым DELETE
gcloud sql instances clone shop-db shop-db-restored \
  --point-in-time='2026-07-14T17:39:30.000Z'

Обратите внимание: восстановление идёт в новый инстанс. Это правильно — вы сначала смотрите, те ли там данные, и только потом переключаете приложение. И да: PITR стоит денег (хранение логов), но это ровно та страховка, ради которой люди и переходят на управляемые базы.

Реплики чтения

Read replica — асинхронная копия базы, доступная только на чтение. Её создают, когда аналитические или тяжёлые SELECT-запросы мешают жить основной базе.

gcloud sql instances create shop-db-replica \
  --master-instance-name=shop-db \
  --region=europe-west4

Ключевое слово — асинхронная. Реплика отстаёт: обычно на доли секунды, под нагрузкой — на секунды. Классический баг: пользователь сохранил профиль (запись пошла в мастер), приложение тут же читает профиль с реплики и показывает старые данные. Правило: после записи читайте из мастера.

HA — это не реплика

Высокая доступность (--availability-type=REGIONAL) устроена иначе. Google держит в другой зоне standby-инстанс, а диск синхронно реплицируется между зонами. При отказе основной зоны происходит failover: standby поднимается под тем же адресом, приложение просто переподключается. Потери данных нет, простой — десятки секунд.

Разница по смыслу: replica — про масштабирование чтения, HA — про переживание аварии. И стоит HA примерно вдвое дороже: вы платите за два инстанса.

Как подключаться: три способа и один правильный

1. Публичный IP + authorized networks (плохо)

Самый быстрый способ: дать инстансу публичный адрес и разрешить список IP. Он же самый опасный. Во-первых, база торчит в интернет и её найдут сканеры. Во-вторых, соблазн написать 0.0.0.0/0 («у меня динамический IP, потом поправлю») практически непреодолим — а это «пусть подключается кто угодно, лишь бы знал пароль». Так утекают базы.

2. Private IP (правильно для продакшена)

Инстанс получает адрес внутри вашей VPC-сети и не имеет публичного адреса вообще. Достучаться до него можно только изнутри сети — с ваших виртуалок, из Cloud Run через VPC-коннектор, из GKE. Снаружи база не существует.

gcloud sql instances patch shop-db \
  --network=projects/my-project/global/networks/default \
  --no-assign-ip

3. Cloud SQL Auth Proxy (правильно для разработчика)

А как тогда зайти в приватную базу со своего ноутбука? Через Cloud SQL Auth Proxy — маленькую утилиту, которая делает три вещи:

  1. Слушает 127.0.0.1:5432 на вашей машине, как будто это локальный Postgres.
  2. Аутентифицируется в Google Cloud вашими учётными данными (gcloud auth application-default login) и проверяет через IAM, есть ли у вас роль roles/cloudsql.client.
  3. Поднимает шифрованный туннель до инстанса, получая эфемерный сертификат, живущий около часа.
# имя инстанса в формате PROJECT:REGION:INSTANCE
./cloud-sql-proxy --port 5432 my-project:europe-west1:shop-db

# в другом терминале — обычный psql, как будто база локальная
psql "host=127.0.0.1 port=5432 user=app dbname=shop"

Результат:

2026/07/14 12:03:41 Authorizing with Application Default Credentials
2026/07/14 12:03:42 [my-project:europe-west1:shop-db] Listening on 127.0.0.1:5432
2026/07/14 12:03:42 The proxy has started successfully and is ready for new connections!

Что здесь принципиально: у базы не открыт ни один порт наружу, пароль не ходит по открытой сети, доступ выдаётся и отзывается через IAM (уволился человек — убрали роль, и всё), а шифрование включено всегда. В продакшене тот же прокси встраивают как sidecar-контейнер рядом с приложением или используют библиотеку-коннектор прямо в коде.

from google.cloud.sql.connector import Connector
import sqlalchemy

connector = Connector()

def getconn():
    # коннектор сам сходит в IAM, получит сертификат и поднимет TLS-туннель
    return connector.connect(
        "my-project:europe-west1:shop-db",
        "pg8000",
        user="app",
        password="...",
        db="shop",
        ip_type="PRIVATE",
    )

engine = sqlalchemy.create_engine(
    "postgresql+pg8000://",
    creator=getconn,
    pool_size=5,
    max_overflow=2,
    pool_recycle=1800,
)

with engine.connect() as conn:
    rows = conn.execute(sqlalchemy.text("SELECT count(*) FROM orders")).scalar()
    print(rows)

Обратите внимание на pool_size и pool_recycle: у инстанса есть жёсткий лимит соединений, и «каждый воркер открывает по 20 коннектов» — верный способ упереться в него на ровном месте.

Как это работает

Cloud SQL — не магия, а автоматизация: под капотом это виртуальная машина Compute Engine с сетевым диском Persistent Disk, на которой Google сам поставил и настроил PostgreSQL. Понимание этого объясняет всё поведение сервиса:

  • Почему нельзя уменьшить диск. Persistent Disk умеет расти на лету, но не умеет сжиматься. Единственный способ «уменьшить» — сделать дамп и залить в новый инстанс.
  • Почему HA стоит вдвое. Потому что это буквально вторая машина со своей копией диска в другой зоне.
  • Почему инстанс не масштабируется до нуля. Машина работает всегда — иначе первый же запрос ждал бы её загрузки.
  • Почему бывает maintenance window. Обновление минорной версии — это перезапуск процесса СУБД: короткий разрыв соединений. Поэтому приложение обязано уметь переподключаться, а окно обслуживания лучше задать явно на ночь.
  • Как работает failover. Клиенты обращаются не к IP конкретной машины, а к адресу инстанса; при аварии Google переводит этот адрес на standby. Ваш пул соединений в этот момент увидит обрыв — и должен молча переподключиться.

Частые ошибки

  • Забытый dev-инстанс. Самая дорогая ошибка новичка. Инстанс не спит и не масштабируется в ноль: он тикает 24/7 и в конце месяца превращается в счёт. Останавливайте (--activation-policy=NEVER) или удаляйте после экспериментов.
  • 0.0.0.0/0 в authorized networks. Даже «на пять минут». Базы находят автоматические сканеры за часы, а пароль подбирают.
  • Полагаться на storage-auto-increase. Диск вырос на 500 ГБ из-за раздувшегося лога — и назад уже не ужмётся, вы платите за эти 500 ГБ вечно. Ставьте --storage-auto-increase-limit и алерты на заполнение.
  • Включить HA «на всякий случай» в тестовом окружении. Это ровно двойной счёт за окружение, доступность которого никому не нужна.
  • Считать реплику бэкапом. Реплика повторяет всё, включая DROP TABLE. От человеческой ошибки спасают только бэкапы и PITR.
  • Читать сразу после записи с реплики. Асинхронное отставание — не баг, а свойство. После записи читайте с мастера.
  • Не проверять восстановление. Бэкап, который ни разу не восстанавливали, — это не бэкап, а надежда. Раз в квартал поднимайте клон и убеждайтесь, что данные на месте.
  • Не ограничивать пул соединений. Лимит соединений на маленьких тирах невелик; десяток инстансов приложения с большими пулами исчерпают его мгновенно.

Итоги

  • Cloud SQL — это обычный PostgreSQL/MySQL, у которого Google взял на себя железо, патчи, бэкапы и репликацию. SQL и клиенты остаются прежними.
  • Бесплатного уровня нет: инстанс работает круглосуточно и стоит денег, даже когда простаивает. Для учёбы — останавливайте его.
  • Автоматические бэкапы плюс point-in-time recovery позволяют откатиться на конкретную секунду; восстановление создаёт новый инстанс.
  • Read replica — про масштабирование чтения (и она отстаёт). HA (REGIONAL) — про переживание падения зоны, и стоит примерно вдвое дороже.
  • Правильное подключение: приватный IP плюс Cloud SQL Auth Proxy или коннектор. Никаких публичных адресов и 0.0.0.0/0.
  • Диск умеет расти автоматически и не умеет уменьшаться — это ловушка, за которую платят месяцами.
Проверьте себя
1. Чем Cloud SQL Auth Proxy принципиально лучше подключения по публичному IP с authorized networks?
AОн ускоряет запросы за счёт кэширования результатов на клиенте
BОн позволяет базе не иметь публичного адреса: доступ выдаётся через IAM, соединение шифруется, порт наружу не открыт
CОн автоматически создаёт бэкапы перед каждым подключением
DОн снимает лимит на количество одновременных соединений к инстансу
2. Разработчик удалил строки командой DELETE без WHERE 20 минут назад. Последний суточный бэкап был ночью. Что позволит вернуть данные с минимальными потерями?
AПереключиться на read replica — там старые данные ещё сохранились
BВосстановиться из ночного бэкапа, потеряв весь рабочий день
CPoint-in-time recovery: клонировать инстанс на момент за секунду до DELETE
DНичего не сделать, Cloud SQL не хранит журнал транзакций