Разработка используя локальное S3 хранилище

Рассматриваем установку и настройку S3 хранилища для локальной разработки.

Автор . Дата: 27.03.2019

Облачные технологии уже давно прочно обосновались в web разработке. Хранение файлов, кластеры обработки данных, очереди, вычисления на видеокартах - одни из множества сервисов, которые предоставляют современные облака. Лидером среди всех является Amazon со своим AWS (Amazon Web Services). Он до сих пор является генератором новых идей, на него ориентируются многие облачные сервисы. Даже если вы по каким-то причинам не хотите использовать сервисы Амазона (нет датацентров в вашем регионе, например, или потенциальные проблемы с законом), всегда можно найти аналоги, которые работают по совместимому API. В России таким аналогом пытается быть недавно появившийся Yandex Cloud. У них уже запущено свое хранилище файлов совместимое по API с Амазоном, так же в стадии альфа тестирования некоторые другие сервисы (например аналог Amazon SQS). Держать полностью проекты в облаках очень удобно. Но и дорого. Поэтому, чаще всего используют какие-то определенные сервисы, а остальным по старинке занимаются сами. Чаще всего, первым делом в облака стараются перенести хранилище файлов.

Если вы в своей карьере сталкивались с задачей хранения и отдачи несколько сотен гигабайт статики, думаю вы понимаете какие проблемы обычно возникают. Очень часто, одна, даже малейшая ошибка при планировании архитектуры файлового хранилища выливается в кучу проблем на больших объемах. Проблемы могут быть связаны со скорость отдачи клиентам, горизонтального масштабирования, резервного копирования, чистки от мусора. Поэтому, в современном мире, проще всего отдать хранение файлов стороннему сервису (облаку). Облако решает сразу почти все проблемы, вам нужно будет только хранить путь до файла, и отдавать клиентам ссылку на файл. Клиент будет качать файл напрямую с облака. Это очень удобно, и можно даже организовать такой своеобразный CDN, но, к сожалению, исходящий траффик в облаках тоже тарифицируется, и может составлять ощутимую статью расходов. На случай экономии денег на траффике, умные люди придумали поднимать кеширующие сервера (например Varnish). Клиент обращается за файлов на Varnish, если у него нет файла в кэше, он лезет за ним в облако, качает, отдает клиенту и кладет в кэш. В следующий раз файл будет отдан уже сразу из кэша, без запросов в облако.

Предположим, мы решили сделать файловое хранилище нашего проекта в S3 совместимом хранилище. На проде, тесте, стейдже все хорошо: создали бакеты, ключи доступа, настроили каждое окружение, файлы заливаются, отдаются, все прекрасно. А что делать при локальной разработке? Можно создать каждому разработчику по отдельному бакету, свои ключи, или один общий. Но тут могут возникнуть потенциальные проблемы конфликта файлов, или проблемы безопасности. Например у вас общий бакет для всех разработчиков, один уволился не в самом хорошем настроении, и он может начать устраивать подлянки для других разработчиков. Либо, если у вас отдельные бакеты для каждого, можно забыть отозвать доступы при увольнении, и тогда он нагрузит петабайты не очень законных видеофайлов, и в лучшем случае компании потом придет счет на кругленькую сумму. А в худшем... Ну не будем об этом.

Самым оптимальным будет, организовать локальное облако нашим разработчикам. Там они смогут творить любой ад, и это никак не будет касаться компании. Поскольку разработка у нас идет в Docker окружении, к нему мы и будем подключать наш мини облачный сервис.

Подключаем S3

Я некоторе время пошарился по интернету, и нашел хороший аналог S3, который можно развернуть локально. К тому же, для его есть Docker образ. Его и возьмем. Подключать будем к проекту, описанному в прошлой статье. Пример конфигурации в docker-compose.yml:

  s3:
    image: scality/s3server
    environment:
      ENDPOINT: s3
      LISTEN_ADDR: 0.0.0.0
    networks:
      project:
        aliases:
          - my-bucket.s3

Здесь мы создали новый контейнер с именем s3, подключили к нему образ scality/s3server, в переменных окружения указали в качестве endpoint доменное имя нашего контейнера (s3), а так же, чтобы он принимал подключения со всех адресов. В блоке networks мы задали алиас для этого контейнера: bucket-name.s3. Это сделано потому, что клиенты api s3 при обращении к конкретному бакету, обращаются к нему как к поддомену. После такой настройки поддомен будет существовать, и запросы будут идти правильно. Если вы хотите использовать несколько бакетов, просто добавьте для них алиасы.

PHP

Настроить подключение из PHP достаточно просто, для этого вам необходимо будет указать endpoint url: s3, а так же key и secret которые по умолчанию в этом сервере равны: accessKey1 и verySecretKey1. Проблемой может быть передача endpoint url в библиотеку. C aws-sdk-php проблем обычно нет, но обертки для неё не всегда поддерживают конфигурирование endpoint. Для yii2 я использовал библиотеку frostealth/yii2-aws-s3 и она не поддерживает возможность указания endpoint. Поэтому пришлось форкать и добавлять необходимый код. Мою версию установить можете командой:

composer require lan143/yii2-aws-s3

Пример настройки компонента:

    's3' => [
        'class' => \frostealth\yii2\aws\s3\Service::class,
        'credentials' => [
            'key' => 'accessKey1',
            'secret' => 'verySecretKey1',
        ],
        'endpoint' => 'http://s3:8000',
        'region' => 'us-east-1',
        'defaultBucket' => 'bucket-name',
        'defaultAcl' => 'public-read',
    ],

Создание бакетов

Хоть у нас уже есть доменные имена для бакетов, но бакеты на сервере нужно так же создать. Создать их можно с помощью aws-cli. Я предпочел добавить контейнер с aws-cli к проекту, и производить какие-то манипуляции уже через него. Конфигурация контейнера:

  aws-cli:
    image: mesosphere/aws-cli
    environment:
      AWS_ACCESS_KEY_ID: accessKey1
      AWS_SECRET_ACCESS_KEY: verySecretKey1
      AWS_DEFAULT_REGION: us-east-1
    depends_on:
      - s3
    networks:
      - project

И теперь мы можем создать бакет следующей командой:

docker-compose run --rm aws-cli s3api create-bucket --bucket my-bucket --endpoint-url=http://s3:8000

Можно так же собрать свой образ scality/s3server включающий в себя aws-cli. Попробуйте сделать это сами, на примере сборки образа для контейнера PHP.

PS

Сервис scality/s3server можно использовать не только для локальной разработки, если вам необходимо, вы можете развернуть его на своих серверах и сделать свое мини облако :-)