Cloud Storage: объектное хранилище
Разбираемся, куда в облаке кладут файлы: что такое бакет и объект, чем Nearline отличается от Archive, сколько это стоит на самом деле и почему одна галочка «сделать публичным» иногда обходится в тысячи долларов.
Cloud Storage — объектное хранилище Google Cloud: вы кладёте файл целиком, получаете на него адрес вида
gs://my-bucket/photos/cat.jpgи платите за реально занятые гигабайты и за операции, а не за сервер и не за выделенный диск.
Зачем нужен отдельный сервис для файлов
Типичный путь новичка: поднять виртуальную машину, примонтировать диск и складывать туда аватарки пользователей. Работает — ровно до первой проблемы, и проблем будет четыре.
- Диск живёт в одной зоне. Зона недоступна — файлы недоступны вместе с ней.
- Диск конечен. Кончилось место — надо руками расширять, останавливать сервис, следить.
- Диск не умеет отдавать файлы в интернет. Нужен nginx, TLS-сертификат, кэш, защита от перегрузки.
- Вы платите за выделенный объём, а не за занятый. Диск на 500 ГБ, забитый на 30 ГБ, стоит как диск на 500 ГБ.
Cloud Storage закрывает все четыре пункта. Данные автоматически размазаны по нескольким зонам, объём безграничен (буквально: вы просто пишете дальше), любой объект можно отдать по HTTPS напрямую, а счёт растёт ровно от того, сколько байт лежит и сколько раз их трогали. Это «диск, у которого нет размера» — и именно так его стоит воспринимать.
Цена вопроса — ограничения. Объект нельзя дописать в середину. Хотите поменять один байт — перезаписываете весь объект. Поэтому базу данных или файл лога, в который постоянно пишут, в Cloud Storage не положишь: это хранилище для «готовых» блобов — картинок, бэкапов, архивов, CSV-выгрузок, артефактов сборки, датасетов.
Бакеты и объекты
Бакет (bucket, «ведро») — контейнер верхнего уровня. Внутри лежат объекты — сами файлы плюс их метаданные (Content-Type, кастомные заголовки, класс хранения). Два факта, о которые спотыкаются все:
- Имя бакета глобально уникально во всём Google Cloud. Не в вашем проекте — во всём мире. Бакет
imagesзанят лет пятнадцать назад. Поэтому в имя принято зашивать домен или проект:codechick-prod-uploads. - Внутри бакета нет папок. Пространство имён плоское. Объект называется
2026/07/report.pdfцеликом — слеши это просто символы в имени. Консоль рисует «папки», потому что группирует объекты по общему префиксу, но физически никаких директорий нет. Отсюда следствие: «переименовать папку» = скопировать и удалить каждый объект.
# создаём бакет: имя, регион, uniform-доступ (см. ниже), класс по умолчанию
gcloud storage buckets create gs://codechick-prod-uploads \
--location=europe-west1 \
--default-storage-class=STANDARD \
--uniform-bucket-level-access
# кладём файл и смотрим, что получилось
gcloud storage cp ./cat.jpg gs://codechick-prod-uploads/photos/cat.jpg
gcloud storage ls --long gs://codechick-prod-uploads/photos/
Результат:
Creating gs://codechick-prod-uploads/...
Copying file://./cat.jpg to gs://codechick-prod-uploads/photos/cat.jpg
Completed files 1/1 | 184.2kiB/184.2kiB
188612 2026-07-14T09:12:44Z gs://codechick-prod-uploads/photos/cat.jpg
TOTAL: 1 objects, 188612 bytes
Локация: одна из главных развилок
При создании бакета вы выбираете локацию, и потом её не изменить. Вариантов три:
| Тип | Пример | Когда брать |
| Region | europe-west1 | Дефолт. Данные рядом с вашими серверами, дёшево, задержка минимальна. |
| Dual-region | eur4 | Нужна устойчивость к падению целого региона при быстрой записи. |
| Multi-region | EU, US | Раздача контента по всему континенту (сайт, картинки). Дороже за гигабайт. |
Правило простое: если бакет обслуживает приложение, ставьте тот же регион, где живёт приложение. Иначе каждый запрос к файлу пойдёт через полконтинента, а трафик между регионами вам ещё и посчитают.
Классы хранения и во что это обходится
Класс хранения — это компромисс «дешевле хранить / дороже читать». Данные всегда доступны мгновенно (никаких «разморозок», как у ленточных архивов), но чем холоднее класс, тем больше вы заплатите за сам факт чтения и тем дольше обязаны хранить объект.
| Класс | Хранение, за ГБ/мес | Плата за чтение (retrieval) | Минимальный срок | Для чего |
| Standard | ≈ $0,020 | нет | нет | Всё, что читают регулярно: аватарки, статика, активные данные |
| Nearline | ≈ $0,010 | ≈ $0,01/ГБ | 30 дней | Читают примерно раз в месяц: свежие бэкапы |
| Coldline | ≈ $0,004 | ≈ $0,02/ГБ | 90 дней | Читают раз в квартал: старые бэкапы, архив логов |
| Archive | ≈ $0,0012 | ≈ $0,05/ГБ | 365 дней | «Положил и забыл»: юридический архив, долгое хранение по регламенту |
Цифры — порядок величин для обычного региона; точные суммы всегда сверяйте с калькулятором Google Cloud, они меняются. Важнее принцип: Archive в 16 раз дешевле Standard за хранение, но чтение терабайта из Archive стоит примерно $50. Прочитаете такой архив дважды в месяц — и «экономия» превратится в перерасход.
Помимо хранения и retrieval в счёте есть ещё два пункта, про которые постоянно забывают:
- Операции. Class A (запись, листинг) — порядка $0,005 за 1000 операций, Class B (чтение) — около $0,0004 за 1000. Миллион мелких файлов по 2 КБ обойдётся дороже, чем один архив на 2 ГБ, — не размером, а количеством операций.
- Исходящий трафик (egress). Отдача файла в интернет — примерно $0,12 за ГБ. Хранение картинки стоит копейки; раздача этой картинки миллиону человек — уже нет.
Бесплатный лимит: 5 ГБ Standard в месяц (только в регионах us-east1, us-west1, us-central1), 5000 операций Class A, 50 000 Class B и 100 ГБ исходящего трафика из Северной Америки. Для учебных экспериментов этого хватает с запасом — но обратите внимание, что бесплатны только US-регионы.
Lifecycle: автоматическое охлаждение
Руками перекладывать объекты между классами никто не будет. Для этого есть правила жизненного цикла — JSON, который Cloud Storage применяет сам раз в сутки.
{
"lifecycle": {
"rule": [
{
"action": { "type": "SetStorageClass", "storageClass": "NEARLINE" },
"condition": { "age": 30, "matchesStorageClass": ["STANDARD"] }
},
{
"action": { "type": "SetStorageClass", "storageClass": "COLDLINE" },
"condition": { "age": 90, "matchesStorageClass": ["NEARLINE"] }
},
{
"action": { "type": "Delete" },
"condition": { "age": 400 }
}
]
}
}
gcloud storage buckets update gs://codechick-prod-uploads \
--lifecycle-file=lifecycle.json
Через месяц бэкап сам уедет в Nearline, через три — в Coldline, через 400 дней исчезнет. Это одна из самых окупаемых десятиминутных настроек в облаке.
Публичный доступ и почему он опасен
Сделать объект доступным всему интернету — одна команда. Именно поэтому это самая частая причина утечек данных в облаках вообще: кто-то «на пять минут для теста» открыл бакет и забыл.
# ОПАСНО: выдаём роль чтения специальному принципалу allUsers = «весь интернет»
gcloud storage buckets add-iam-policy-binding gs://codechick-prod-uploads \
--member=allUsers \
--role=roles/storage.objectViewer
После этого любой объект в бакете читается по ссылке https://storage.googleapis.com/codechick-prod-uploads/photos/cat.jpg — без авторизации, без ограничений, кем угодно. Три последствия, о которых стоит знать заранее:
- Утечка. Публичный бакет находят автоматические сканеры за часы. Если рядом с картинками лежал
db-dump.sql— он утёк. - Счёт. Egress платный. Публичный бакет с тяжёлыми файлами могут «выкачивать» днями — и заплатите за это вы. Известный сценарий: файл попал на популярный форум, к утру счёт на несколько тысяч долларов.
- Листинг. Роль
objectViewerна бакете позволяет ещё и перечислить объекты. То есть посторонний видит не только тот файл, ссылку на который ему дали, а вообще все.
Что делать вместо этого:
- Держите на бакете Public Access Prevention (
--public-access-prevention). Тогда Google просто запретит выдать доступallUsers, даже если кто-то попробует. На всех бакетах с пользовательскими данными это должно быть включено по умолчанию. - Включайте Uniform bucket-level access: права задаются только через IAM на бакет, без древних ACL на каждый объект. Меньше способов ошибиться.
- Нужен временный доступ к одному файлу — выдавайте signed URL: ссылку с подписью и сроком жизни. Бакет остаётся приватным.
# ссылка живёт 15 минут, бакет при этом закрыт
gcloud storage sign-url gs://codechick-prod-uploads/reports/q2.pdf \
--duration=15m \
--impersonate-service-account=uploader@my-project.iam.gserviceaccount.com
Публичный бакет оправдан ровно в одном случае: это заведомо публичная статика — картинки сайта, css, дистрибутивы. И даже тогда перед ним стоит поставить Cloud CDN, чтобы не платить за каждый байт egress по полной.
Загрузка: gcloud storage и gsutil
Исторически с Cloud Storage работали через gsutil. Сегодня Google рекомендует gcloud storage — та же логика, но заметно быстрее на больших объёмах (лучше распараллеливает). Старые команды продолжают работать, в статьях и на StackOverflow вам будут попадаться обе.
| Задача | gcloud storage | gsutil (старое) |
| Копировать | gcloud storage cp a.txt gs://b/ | gsutil cp a.txt gs://b/ |
| Синхронизировать папку | gcloud storage rsync ./dist gs://b/dist --recursive | gsutil -m rsync -r ./dist gs://b/dist |
| Удалить | gcloud storage rm gs://b/old.log | gsutil rm gs://b/old.log |
| Размер бакета | gcloud storage du --summarize gs://b | gsutil du -s gs://b |
Из кода работают через SDK. Обратите внимание: клиент сам умеет докачивать большие файлы кусками (resumable upload), поэтому загрузку десятигигабайтного архива не надо изобретать вручную.
from google.cloud import storage
client = storage.Client()
bucket = client.bucket("codechick-prod-uploads")
# загрузка файла; для больших объектов клиент сам режет на чанки
blob = bucket.blob("reports/q2.pdf")
blob.cache_control = "public, max-age=3600"
blob.upload_from_filename("q2.pdf")
# сразу кладём в холодный класс — платить за хранение будем меньше
cold = bucket.blob("archive/2024.tar.gz")
cold.storage_class = "COLDLINE"
cold.upload_from_filename("2024.tar.gz")
print(blob.public_url) # ссылка сработает, только если объект публичный
Как это работает
Под капотом Cloud Storage — не «файловая система в облаке», а распределённый key-value поверх Colossus, внутренней файловой системы Google. Имя объекта — это ключ. Данные разбиваются на куски, куски дублируются по разным машинам и (для multi-region) по разным дата-центрам, а метаданные лежат в глобальной базе с сильной согласованностью.
Из этой архитектуры следует всё, что вы видите снаружи:
- Запись атомарна и согласована. Как только загрузка завершилась, любой следующий read вернёт новую версию — «а вдруг реплика отстала» здесь не бывает.
- Нет частичной записи. Изменить байт в середине нельзя, потому что объект — неделимая единица с контрольной суммой. Отсюда же версионирование: включите
--versioning, и «перезапись» будет складывать старую версию рядом, а не затирать. - Листинг — дорогая операция. Он идёт по отсортированному индексу ключей. Перечислить бакет с 50 млн объектов — долго и платно, поэтому никогда не проектируйте логику приложения вокруг «а давайте просто пролистаем бакет».
- Класс хранения — свойство объекта, а не бакета. Класс бакета — лишь значение по умолчанию для новых объектов. В одном бакете спокойно лежат и Standard, и Archive.
Частые ошибки
- Положить часто читаемые данные в Archive. Классика «оптимизации»: увидели цену $0,0012 и переложили туда всё. Каждое чтение теперь стоит $0,05/ГБ — счёт вырос, а не упал. Холодные классы — только для данных, к которым реально не обращаются.
- Забыть про минимальный срок хранения. Удалили объект из Nearline через 3 дня — вам всё равно выставят счёт за 30 дней (early deletion fee). В Archive — за целый год. Записывать во временный бакет с классом Archive — верный способ платить за мусор двенадцать месяцев.
- Включить versioning и не настроить lifecycle. Приложение каждый час перезаписывает один и тот же файл; старые версии копятся молча. Через полгода бакет весит в сотни раз больше, чем показывает листинг. Обязательно добавляйте правило с
numNewerVersions. - Публичный бакет «на время отладки». Отладка кончается, публичность остаётся. Включайте Public Access Prevention заранее — так вы физически не сможете совершить эту ошибку.
- Раздавать тяжёлую статику напрямую из бакета. Работает, но за каждый гигабайт egress вы платите по полной. Cloud CDN перед бакетом сокращает этот счёт в разы.
- Миллионы мелких объектов. Логи по одной строке в файл — это миллионы Class A операций. Пакуйте в архивы или пишите крупными блоками.
- Бакет в другом регионе, чем приложение. Тихо добавляет и задержку, и межрегиональный трафик в счёт.
Итоги
- Cloud Storage — хранилище целых объектов: положить, прочитать, перезаписать, удалить. Не файловая система и не база данных.
- Имя бакета уникально во всём мире, папок внутри нет — только префиксы в именах объектов.
- Класс хранения — сделка «дешевле хранить / дороже читать» плюс минимальный срок хранения. Standard для активного, Nearline/Coldline/Archive — для того, что реально не читают.
- В счёте четыре строки: хранение, операции, retrieval и egress. Про две последние забывают чаще всего.
- Бесплатно: 5 ГБ Standard в US-регионах, 5000 Class A и 50 000 Class B операций в месяц.
allUsersозначает «весь интернет». Включайте Public Access Prevention и Uniform bucket-level access, а временный доступ выдавайте signed URL.- Lifecycle-правила — десять минут работы, которые годами экономят деньги.