Как я подружилась с S3: мой путь от локального хранилища до облака в Yandex Cloud

Пока я писала своего Telegram-бота для рецептов на Java, меня настигла вполне земная проблема: куда складывать сами файлы с рецептами? Git - не подходит (слишком много бинарников и частые обновления), Docker - тоже не вариант (образ раздувается, да и не для этого он задуман). Тут-то мне и подсказали про S3.
В этой статье расскажу, как я развернула локальное S3 через LocalStack, потом перешла на Yandex Cloud Object Storage и подключила всё это к Java-коду. Будет полезно, если вы тоже думаете, как хранить файлы вне репозитория и контейнера.

Что же такое S3?

S3 - это облачное объектное хранилище, разработанное Amazon Web Services (AWS) в 2006 году. Оно позволяет хранить данные в виде независимых объектов: фотографий, документов, JSON-файлов с рецептами - чего угодно.

Всё, что вы кладёте в S3, попадает в бакеты - это виртуальные «папки». Внутри бакета файлы могут быть организованы с помощью «псевдо-директорий» - на самом деле это просто префиксы в имени объекта, например: recipes/user_123/recipe_456.json.

Что особенно удобно, API у S3 одинаковый везде: и в облаке (например, в Yandex Cloud Object Storage), и локально, если вы используете эмулятор вроде LocalStack. Это значит, что код, написанный для тестов на локальной машине, без изменений заработает и в продакшене.

В случае с моим Telegram-ботом на Java это стало решающим преимуществом: я могла спокойно загружать и скачивать рецепты через единый интерфейс, не думая о том, где именно сейчас запущено приложение - на моём ноутбуке или в облаке.

Подробнее об S3 и его реализациях можно почитать в документации Yandex Cloud .

Начинаем с локального S3

Чтобы удобно разрабатывать и тестировать бота, я сначала настроила локальное S3-хранилище с помощью LocalStack — это эмулятор AWS-сервисов, который полностью совместим с S3 API.

1. Устанавливаем и запускаем LocalStack

1.1. Убедитесь, что установлен Docker. LocalStack работает в контейнере, поэтому сначала нужно установить Docker (если ещё не установлен).

1.2. Запускаем LocalStack. Выполняем команду:

docker run -d -p 4566:4566 --name localstack -e SERVICES=s3 localstack/localstack

1.3. Локальный S3 теперь доступен по адресу:

http://localhost:4566

1.4. Создаём бакет

Бакет можно создать либо через AWS CLI, либо прямо из Java-кода. Я создавала через java-код. Но учтите, что на проде бакет нужно будет создать руками.

if (!bucketExists()) { S3.createBucket(CreateBucketRequest.builder().bucket(BUCKET).build()); }

2. Подключаем Java-проект к локальному S3

2.1. Добавляем зависимость AWS SDK for Java v2 в pom.xml

<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.38.1</version> </dependency>

2.2. Настраиваем S3-клиент для работы с LocalStack

S3Client s3 = S3Client.builder() .endpointOverride(URI.create("http://localhost:4566")) .region(Region.US_EAST_1) .forcePathStyle(true) .credentialsProvider(StaticCredentialsProvider.create( AwsBasicCredentials.create("test", "test") )) .build();

Важные моменты:

  • .endpointOverride() - перенаправляет все запросы на LocalStack.
  • .forcePathStyle(true) - обязательно для LocalStack (иначе SDK будет использовать виртуальные хосты, которые эмулятор не поддерживает).
  • Учётные данные test/test - стандартные для LocalStack и принимаются без проверки.

Теперь вы можете загружать, скачивать и удалять файлы с рецептами прямо в локальный S3, не выходя из IDE. А значит — спокойно писать и тестировать логику бота.

2. Переходим на глобальный s3 - Yandex Cloud

Мне посоветовали Yandex Cloud Object Storage, потому что он полностью совместим с AWS S3 API. Это значит: тот же код, те же методы, нужно лишь поменять endpoint и учётные данные.

3. Создаём сервисный аккаунт и получаем ключи

Для безопасного доступа к хранилищу из кода нужно создать сервисный аккаунт и сгенерировать статические ключи доступа. Это стандартная практика: вы не используете персональные учётные данные, а даёте приложению только те права, которые ему нужны.

Подробная инструкция от Yandex Cloud здесь:👉 Настройка AWS SDK for Java v2 для работы с Yandex Cloud Object Storage

Кратко:

  • Создайте сервисный аккаунт в Yandex Cloud.
  • Назначьте ему роль storage.editor (или кастомную с правами на ваш бакет).
  • Сгенерируйте статический ключ доступа - вы получите access key и secret key.
  • Никогда не коммитьте эти ключи в Git! Лучше передавать их через переменные окружения (как в примере ниже).

3.1. Создаём бакет

Бакет нужно создать в интерфейсе Yandex Cloud руками. О том, как создать бакет ребята хорошо описывают в документации по ссылке выше.

3.2. Настраиваем Java-клиент для Yandex Cloud

Код почти не отличается от локальной версии - меняется только endpoint и источник учётных данных:

public S3Client createClient() { return S3Client.builder() .endpointOverride(URI.create("путь берете из yandex cloud")) .credentialsProvider( StaticCredentialsProvider.create( AwsBasicCredentials.create( System.getenv("YANDEX_ACCESS_KEY"), System.getenv("YANDEX_SECRET_KEY") ) ) ) .region(Region.US_EAST_1) .build();

Теперь приложение одинаково работает и на локальной машине (с LocalStack), и в облаке (с Yandex) достаточно передать нужный endpoint и ключи.

3.3. Хранение ключей доступа

Что еще важно, так это то, где хранятся ключи доступа.
1. Для локального использования AWS (например, при использовании LocalStack) ключи сохраняют в файле по пути ~/user/.aws/credentials. Это удобно, так AWS автоматически подхватывают их без дополнительной настройки.

[default] aws_access_key_id = test aws_secret_access_key = test

Так же нужно создать еще файл с конфигами по тому же пути ~/user/.aws/config.

[default] region = ru-central1 endpoint_url = соответствующий endpointOverride

2. В продакшене так делать нельзя. Файлы конфигурации не должны попадать в код или образ. Вместо этого ключи передаются через переменные окружения, как в примере с YANDEX_ACCESS_KEY и YANDEX_SECRET_KEY.

Вывод

S3-совместимое хранилище простое и надёжное решение для хранения файлов. С LocalStack я легко тестировала локально, а с Yandex Cloud Object Storage безболезненно вышла в продакшен. Весь код остался тем же, изменились только endpoint и ключи. Попробуйте.

Начать дискуссию