Русский
Русский
English
Статистика
Реклама

S3

Погружение в Serverless. По следам протокола S3

30.03.2021 10:05:43 | Автор: admin

Продолжаем беседовать с разработчиками экосистемы сервисов Serverless. В прошлый раз Глеб Борисов рассказал о возможностях и перспективах функций в Yandex.Cloud, сегодня Данил Ошеров погрузит нас в мир бессерверных систем и сервис Object Storage.

У меня был опыт разработки систем, обеспечивающих доступность файлов в разных дата-центрах. Мне надо было синхронизировать файлики, для чего пришлось разрабатывать целую систему, и я был не одинок в такой работе, это делали практически все разработчики корпоративного ПО.

Со временем такие системы перестали быть актуальными, они ушли на второй план, а на смену пришли новые, построенные по протоколу S3. Почему так случилось? Почему S3 стал главным протоколом для синхронизации данных?

Разберемся по порядку. Протокол S3 придумала Amazon, их удобное облачное решение для хранения данных своим выходом взорвало рынок и поменяло правила игры остальных игроков. Другие компании были вынуждены повторять этот протокол, чтобы их данные были доступны для тех же инструментов, которые работают с Amazon. Отсюда и такая популярность. S3 не единственный подобный протокол для взаимодействия с облачным хранилищем, но он самый популярный.

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

Как-то так получилось, что и решение оказалось удачным, и протокол стал популярным. Это заслуга сильной компании Amazon, или рынок созрел для переноса данных в облака?

Рынок созрел. Бизнес готов доверить свои данные облачным хранилищам. Если бы на месте Amazon оказалась Google, взорвав рынок своим протоколом, то мы бы сейчас пользовались им.

Еще один важный момент: большие компании, такие как Google и Microsoft, также предоставляют услугу облачного хранилища, но при этом вынужденно ушли от протокола S3. Свой протокол, свои клиенты, свои библиотеки для работы со своим хранилищем. Этот набор необходим для поддержания общей экосистемы облака, предоставления унифицированного интерфейса, а не потому что S3 чем-то плох.

Мы предоставляем S3-совместимый протокол, чтобы клиентам было максимально удобно как мигрировать к нам, так и использовать уже готовый инструментарий: всевозможные консольные утилиты, готовые приложения, написанные программы. Но в любой момент клиенты могут просто переключиться на использование Yandex Object Storage, и всё также будет работать. Это большой плюс.

В перспективе может появиться наш собственный SDK и наш собственный протокол. Потому что бежать за Amazon и смотреть только на него не совсем правильное решение.

Типовая история для всех облачных провайдеров. Они обязаны реализовать хранилище с поддержкой S3-протокола. Но в какой-то момент собственная реализация начинает предоставлять больше возможностей в соответствии с существующей архитектурой облачного решения, и возможностей протокола S3 начинает не хватать.

Да, мы не должны себя ограничивать. Сейчас рынок максимально сфокусирован на S3-совместимых решениях. Это нормально, поэтому мы никоим образом не отказываемся от него, а продолжаем развивать нашу совместимость. Мы фокусируемся сейчас именно на поддержке S3-совместимого решения, но при этом всегда держим в голове и в планах развитие собственного протокола, собственных SDK. Но это вопрос будущего.

Решение на базе S3 стало настолько популярным, что появилось множество реализаций этого протокола.

Это так. Вот только самое важное во всех реализациях техническая сторона, как и где ты хранишь сами файлы. Готовый веб-сервер, который умеет отвечать S3-совместимым протоколом, отлично, но вопрос в том, где он эти файлы хранит. Если они лежат на одной машине, то такое решение не отказоустойчивое и не масштабируемое. Такое хранилище подойдет для тестов или простых сценариев, но что-то серьезное уже не потянет, и ты захочешь большего. Одной машины станет недостаточно, и возникнет вопрос: а как расширяться? Поэтому что-то готовое, что можно было бы построить поверх любого облака, сложно представить. Как итог, хранилище это базовый сервис любого облака, фундаментальный.

Object Storage существует поверх инфраструктуры дата-центров в разных местах. Сейчас есть три большие точки присутствия, три зоны доступности. А как между этими дата-центрами перемещаются копии файлов?

Никак, они сразу находятся везде. Когда ты отправляешь файл на загрузку в наш storage, мы его сохраняем сразу в нескольких точках присутствия и получаем сразу несколько копий.

Получается, на входе существует какой-то прокси, который разделяет эти данные в момент записи?

Всё так. Если бы мы сохраняли только одну копию, а затем этот файл синхронизировали на остальные копии, то в зазор времени между сохранением и репликацией могло бы произойти какое-то изменение или катастрофа. Машина, на которую мы сохранили файл, могла умереть, или мог выйти из строя диск. Это грозило бы потерей данных, что недопустимо для storage. Поэтому мы сразу сохраняем файлы в нескольких копиях.

Пока файл находится в нашем storage, возникают разные ситуации, вплоть до выхода из строя всего дата-центра. Поэтому наличие дополнительных копий критично необходимо, чтобы обеспечить непрерывный доступ к данным. Мы не можем ограничиваться только одним дата-центром. Аварии случаются не только с дата-центром, но и с машинами или дисками. Они могут умирать, унося с собой на тот свет и одну из копий данных. Ее нужно восстанавливать, и за этим сложным процессом у нас следит внутренний механизм, который определяет нехватку копий и автоматически сразу их восстанавливает.

Наша цель обеспечивать постоянную доступность файлов. Мы стараемся предугадывать возможные потери, недоступность зон или машин.

Как реагирует Object Storage на отключение одного из дата-центров во время учений? Правильно я понимаю, что сервис, который следит за количеством живых копий файла, срочно начинает восстанавливать их количество?

Инфраструктуры Яндекса и Yandex.Cloud независимые, учения Яндекса никак не влияют на работоспособность сервисов Yandex.Cloud. Это важный момент.

Архитектура любых сервисов Яндекса построена так, чтобы обеспечивать их работу в случае отказа любого одного дата-центра. Таким же образом и Object Storage обязан хранить файлы в достаточном количестве копий, чтобы при отключении любого дата-центра файлы оставались доступны.

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

А учения это регулярные процессы, которые подсказывают нам, как ведут себя сервисы при возможной аварии.

Правильно я понимаю, что на S3 как на базовом элементе инфраструктуры внутри Яндекса и Yandex.Cloud построено огромное количество других сервисов, отвечающих за работу других публичных услуг?

Да, объектное хранилище использует огромное количество сервисов. Причем сервис Object Storage, предоставляемый как услуга платформы Yandex.Cloud, построен на тех же технологиях, что и хранилище для внутренней инфраструктуры. Это не раздельные решения, специально написанные для облака или специально написанные для Яндекса, а единая система объектного хранения с независимыми инсталляциями, но одинаковым технологическим стеком.

В результате клиенты Yandex.Cloud получают сервис, способный справиться с любой нагрузкой. Его построили для безупречного оказания услуг Яндекса при любых нагрузках, и он показывает отличные тайминги и доступность. Качество Яндекса гарантировано для всех пользователей Yandex.Cloud, в производительности и отказоустойчивости можно не сомневаться.

Технологические решения в области S3 полностью переиспользуются в виде отдельной инсталляции?

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

От теории к практике. Какие возможности S3 чаще всего привлекают клиентов, и какие кейсы они реализуют? Для меня, например, это гарантированная доступность файла в нескольких зонах, в нескольких дата-центрах.

Действительно, это базовое требование к Object Storage, которое важно для всех. Но S3 подходит для любых данных давай поговорим о том, в каких случаях тебе это может потребоваться.

Наиболее популярный кейс это бэкапы. То есть ты можешь сохранять в облаке бэкапы своей базы данных, или логи своего приложения, или любые другие резервные копии. Они будут надежно хранится и будут доступны в любой момент, когда потребуются, хотя насчет бэкапов лучше, чтобы такой момент не наступал. Для задач резервного копирования объектное хранилище, построенное по протоколу S3, подходит очень хорошо.

Есть и другие кейсы. Например, хранение картинок. Это горячий контент, который ты показываешь на сайте, к нему нужен стабильный и быстрый доступ. Более того, ты можешь загрузить по S3 и данные самого сайта, всевозможные js-файлы и статику и показывать пользователям их напрямую из Object Storage.

В отличие от кейса с бэкапами, где в облаке хранятся холодные данные, страницы и графика сайта, это горячие данные, для них выделяется более быстрый пул. В зависимости от скорости и частоты доступа подбирается тариф. Холодное хранение дешевле.

То есть скорость доступа к холодным данным ниже, чем к горячим?

Она может быть ниже. Частота запросов к файлам в холодном хранилище ниже и критичность высокой скорости доступа меньше. Мы сделали такое разделение, так как был пользовательский спрос. Теперь у клиентов есть выбор: для активных файлов, которые часто используются и перезаписываются, выбрать горячее хранилище с высокой скоростью, а для просто хранить холодное, с низкой скоростью и ценой.

Еще один кейс использования объектного хранилища сервис потокового вещания или раздачи видео. В S3 загружаются чанки видео лайвстрима, а затем из него напрямую раздаются и показываются пользователям.

Как это работает есть стандартные решения?

Да, тут есть стандартные решения. У тебя сохраняется некий манифест, из чего состоит потоковое вещание, в нем есть ссылки непосредственно на чанки, которые воспроизводятся у тебя в браузере или в плеере.

При лайв-вещании данные активно загружаются и постоянно скачиваются пользователями, которые смотрят это видео. Это как раз горячее хранение. Но очевидно, что со временем старые данные становятся не так актуальны, эти видео редко смотрят, и их можно переложить в холодное хранилище, как в архив. S3 также предоставляет механизм очистки всего старого. Это один из примеров того, как с помощью удобного API мы стараемся решать наиболее востребованные задачи пользователей.

Меня еще волнует вопрос внезапной пиковой нагрузки. Допустим, видео или картинка завирусилась или на сайт совершена DDoS-атака.

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

На чем сейчас написана реализация S3?

Недавно мы целиком перевели разработку нашего S3 на Go. До этого мы разработали прототип S3 на Python, потому что нужно было быстро запуститься. После этого часть реализовали на плюсах, чтобы выдерживать большие нагрузки от сервисов Яндекса, когда S3 сильно начал расти. Но периодически возникали проблемы.

Поэтому мы решили, что нужно унифицировать процесс нашей разработки и перейти к единому стеку. Выбрали Go, и осенью прошлого года перевели всё на него. Теперь стек технологий, которые используются внутри Яндекса и в Облаке, единый. В результате мы не только привели весь стек к единообразию, но также получили неплохой performance boost.

А когда вообще в Яндексе появился S3?

Первый прототип был создан около четырех лет назад. Еще за год до начала реализации вопрос создания S3 внутри Яндекса регулярно поднимался, но начало работы всегда откладывалось. После успешного внутреннего использования мы запустили в Yandex.Cloud наше решение.

Насколько Object Storage в Yandex.Cloud отличается от реализации S3 в Amazon?

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

Как я уже говорил, просто слепо бежать за Amazon, потому что он это делает так, в какой-то момент станет неактуально. Если мы возьмем готовые библиотеки приложения, то увидим, что они оперируют неким ограниченным набором функциональности, который им нужен от S3. Поэтому многие разработчики, зная, что есть не только S3 от Amazon, берут альтернативные совместимые решения для конкретных задач. Сейчас принято не оперировать огромной кучей фичей, а использовать подмножество S3-API функциональности.

Исходя из этого наш Object Storage обладает функциональностью, которая необходима большинству. И конечно, мы много сил уделили развитию UI. Сейчас наши клиенты получают полноценное продакшен-решение.

P.S.

Самый простой и понятный сценарий, который может использовать любой хаброжитель в своих целях, раздача статического контента с Object Storage, например статического сайта. Это может стать основой вашего Serverless-приложения. О подробностях и нюансах разработки в этой экосистеме можно узнать в сообществе Serverless в Telegram: Yandex Serverless Ecosystem.

На осенней конференции Yandex Scale был анонсирован free tier для сервисов экосистемы бессерверных вычислений. Это специальные тарифы для Serverless-сервисов с уровнем нетарифицируемого использования. Например, для Yandex Object Storage каждый месяц не тарифицируются первые 100 000 операций GET, HEAD и первые 10 000 операций PUT, POST. Более подробно об условиях читайте в разделе.

Подробнее..
Категории: Интервью , S3 , Aws , Yandex.cloud , Serverless

Как выбрать облачную систему хранения данных, чтобы получить лучшую производительность и оптимизировать стоимость

09.03.2021 18:08:33 | Автор: admin

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

Я Хамзет Шогенов, архитектор облачной платформы Mail.ru Cloud Solutions, расскажу о системах хранения данных, доступных на нашей платформе, подробно остановлюсь на их технических характеристиках и оптимальных вариантах использования.

Типы дисков, которые вы можете использовать в облаке

Диски в облаке специально предоставляются в том виде, в котором классическим операционным системам привычнее с ними работать, то есть они имитируют физические носители информации, такие как HDD и SSD. При подключении к инстансам виртуальных машин такие диски можно использовать как обычное блочное устройство с сырым дисковым пространством блоками, на которые разбивается все дисковое пространство, когда оно размечается под ту или иную файловую систему, чтобы уже на размеченном пространстве размещать данные операционных систем и приложений.

Но так как это все-таки виртуальные диски, для них доступны дополнительные возможности, например создание снимков состояния и шаблонов для новых дисков на их основе, смена типов дисков на лету и так далее. Эта функциональность была бы невозможна, если бы вы имели дело с физическим оборудованием, либо возможна, но за счет неоправданно высокой стоимости.

Особенности облачных (блочных) дисков:

  1. Есть определенная гарантированная производительность в единицу времени на единицу объема хранения данных, выражаемая в операциях на диске в секунду (IOPS, пропускная способность).

  2. Широкий выбор типов дисков. Возможность изменения типа диска на лету.

  3. Возможность создания снапшотов и образов (шаблонов) дисков.

  4. Гибкость управления. При масштабировании диска можно не выключать инстанс, к которому он подключен. Можно создавать и подключать к работающей ВМ новые диски.

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

  6. По сравнению с традиционными физическими носителями при использовании дисков в облаке нет необходимости задумываться о типах RAID, количестве шпинделей и прочем. За обслуживание оборудования и программной части отвечают инженеры облачного провайдера.

Отличия облачных (блочных) дисков от других облачных систем хранения данных:

  • Масштабирование производится вручную.

  • Уменьшение размера существующего диска недоступно: потребуется пересоздание.

  • Доступ возможен из любой зоны доступности (AZ), но ресурс локализован в одной AZ. Размещение диска и виртуальной машины в разных ЦОДах не рекомендуется, хотя это и возможно.

  • Непригодны для одновременного доступа при работе как с блочным устройством.

  • Наибольшая стоимость среди всех типов облачных хранилищ, если говорить про наиболее производительные типы дисков.

На нашей платформе поддерживаются несколько типов дисков: HDD, SSD, SSD High IOPS и Low Latency NVMe. Вначале рассмотрим характеристики, которые будут общими для всех дисков, затем остановимся более подробно на каждом из них.

Примечание. Для каждого типа облачных дисков есть ближайший аналог из мира физических устройств. Но это не означает их полного технического соответствия. Также следует учесть, что бюджет на операции ввода-вывода в секунду для облачных дисков всегда определяется на определенный шаг дискового пространства. Я буду приводить значения SLA для 2 Тб пространства. С полным перечнем SLA для различных типов дисков можно ознакомиться здесь.

Общие характеристики для всех типов облачных дисков

  1. Capacity. Рекомендуемый максимальный размер диска 2 Тб.

  2. Масштабирование. Вручную через веб-консоль управления облаком или OpenStack CLI (Command Line Interface). Возможно только увеличение размера диска. Уменьшение недоступно, так как подобная процедура может негативно сказаться на работе файловой системы и целостности данных.

  3. Доступность. Гарантируется SLA, общий для облака, 99,95%.

  4. Бэкапы и восстановление. Для всех дисков поддерживаются снапшоты и резервные копии. Создание снапшотов доступно через консоль управления облаком и OpenStack CLI. Создание бэкапов возможно через встроенный механизм MCS либо с использованием сторонних решений наших партнеров: Acronis и VMware Backup & Replication. Встроенный механизм хорош интеграцией с облачной платформой, сохранением бэкапов в S3, что дешевле, и платой только за хранение данных. Однако в этом случае нет возможности восстановления данных в ту же виртуальную машину и восстановления отдельных файлов.

  5. Границы доступности. Ресурс локализован в рамках одной зоны доступности (AZ, Availability Zone). Чтобы избежать потенциального снижения производительности работы, при создании диска, подключаемого к существующему инстансу, рекомендуется выбирать зону доступности инстанса.

  6. Безопасность. Доступ к данным ограничен механизмами изоляции ресурсов (различные Namespace) проекта.

  7. Механизм расчета стоимости. Цена определяется запрошенным объемом диска. При изменении размера стоимость автоматически пересчитывается.

Диски HDD

Базовые диски, самые недорогие и наименее производительные. В традиционной инфраструктуре этому классу хранения соответствуют обычные диски HDD. Чаще всего используются в качестве загрузочных разделов ОС и файловых хранилищ.

Характеристики, специфичные для HDD

Показатель

Значение

IOPS (количество операций в секунду на 2 Тб пространства)

Показатель SLA для чтения 3002400, для записи 150800. Приложения не всегда могут загрузить диск, это доступно в полной мере только для приложений, которые осуществляют чтение и запись в многопоточном режиме.

Throughput (пропускная способность на 2 Тб пространства при размере блока 1М)

Показатель SLA для чтения 250 Мб/с, для записи 100 Мб/с.

Поведение при выходе физического оборудования из строя

Происходит без прерывания обслуживания и потери данных, так как для дисков обеспечивается двойная и тройная репликация данных.

Диски SSD

Стандартные диски, опережающие HDD по производительности, но обычно более дорогие. По соответствию физическим дискам они существенно быстрее, чем любые HDD, но медленнее SSD, в основном из-за того, что добавляются накладные расходы на сеть и репликацию. Чаще всего используют для хранения СУБД, телеметрии и очередей сообщений.

Характеристики, специфичные для SSD

Показатель

Значение

IOPS (количество операций в секунду на 2 Тб пространства)

Показатель SLA для чтения 100016 000, для записи 500-8000.

Throughput (пропускная способность на 2 Тб пространства, при размере блока 1М)

Показатель SLA для чтения и записи 400 Мб/с.

Поведение при выходе физического оборудования из строя

Происходит без прерывания обслуживания и потери данных, так как для дисков обеспечивается тройная репликация данных.

Диски High IOPS SSD

Быстрые диски, более производительные и дорогие по сравнению с SSD. Соответствуют физическим дискам SSD потребительского класса. Чаще всего используются для хранения файлов в СУБД, аналитике и телеметрии с большими требованиями к производительности, чем у SSD.

Характеристики, специфичные для High IOPS SSD

Показатель

Значение

IOPS (количество операций в секунду на 2 Тб пространства)

Показатель SLA для чтения 10 00045 000, для записи 500030 000.

Throughput (пропускная способность на 2 Тб пространства, при размере блока 1М)

Показатель SLA для чтения и записи 500 Мб/с.

Поведение при выходе физического оборудования из строя

Может случиться временная недоступность данных, сами данные не теряются. Необходимо дополнительно позаботиться о реализации отказоустойчивости на уровне приложения (application-aware). С лучшими практиками по созданию отказоустойчивых приложений можно ознакомиться по ссылке.

Low Latency NVMe

Сверхбыстрые диски с минимальными задержками, доступные на высокочастотных конфигурациях ВМ. Самые производительные и дорогие. Этому классу хранения соответствуют физические диски NVMe. Используются там, где важно обеспечить минимальные задержки: высокопроизводительные СУБД, аналитика, кэш.

Характеристики, специфичные для Low Latency NVMe

Показатель

Значение

IOPS (количество операций в секунду на 2 Тб пространства)

Показатель SLA для чтения 10 00075 000, для записи 5000-50 000.

Throughput (пропускная способность на 2 Тб пространства, при размере блока 1М)

Показатель SLA для чтения 1200 Мб/с, для записи 900 Мб/с.

Latency (задержка)

SLA максимум 0,5 мс. Устойчивы к отказу сети, виртуальная машина с такими дисками максимально близка к bare metal.

Поведение при выходе физического оборудования из строя

Наступает полная недоступность данных, есть риск потери данных. Необходимо дополнительно позаботиться о реализации отказоустойчивости на уровне приложения (application-aware). С лучшими практиками по созданию отказоустойчивых приложений можно ознакомиться по ссылке.

Объектные хранилища S3 еще один тип хранения данных в облаке

В S3 данные хранятся в виде объектов. Объект это некая совокупность данных с уникальным идентификатором и бесконечным количеством метаданных. Для группировки объектов есть дополнительная сущность бакеты. Это контейнеры для объектов, похожие на папки, но не являющиеся их полным аналогом. В проекте может быть один или несколько бакетов.

Лучше всего S3 подходит для хранения неструктурированных данных и обработки большого количества объектов малого и среднего размера, которые редко изменяются и часто требуют параллельного доступа большого числа пользователей. Для обработки больших объектов доступна дополнительная функциональность мультипоточная загрузка.

S3 может выступать в качестве более надежной и дешевой альтернативы дискам HDD для большей части сценариев их использования.

В нашем облаке доступны три класса объектных хранилищ S3, которые различаются по своему назначению и стоимости:

  1. S3 HotBox предназначен для хранения горячих данных с частым доступом. В первую очередь это онлайн-сервисы с повышенной нагрузкой, работа которых требует хранения и раздачи контента: потоковая раздача мультимедиа, хостинг статических сайтов, хранилища для Backend-платформ. Могут также использоваться для анализа данных в Big Data, Data Mining и так далее. В HotBox хранение дороже, а исходящий трафик дешевле, входящий трафик не тарифицируется.

  2. S3 IceBox используют для хранения холодных данных с редким доступом, например несколько раз в месяц. Чаще всего это годовая и месячная отчетность, документы, бэкапы и журналы, к которым периодически нужен быстрый доступ. По сравнению с HotBox в IceBox хранение дешевле, а исходящий трафик дороже, входящий трафик также не тарифицируется.

  3. Glacier подходит для хранения ледяных данных массивных данных (от 100 Тб) с очень редким доступом. Это бэкапы, архивы и логи, к которым доступ может потребоваться несколько раз в год и реже. Из трех типов хранилищ в Glacier самая низкая цена на хранение данных, а весь трафик бесплатный. Такое хранилище подключается по отдельному запросу клиента.

Что хорошего в S3-хранилище:

  • Неограниченный объем хранимых данных (петабайты).

  • Подходит для неструктурированных данных за счет хранения дополнительной информации (метаданных) рядом с объектами.

  • Разграничение доступа за счет ACL и префиксных ключей.

  • Возможность одновременного использования большим количеством приложений.

  • Стабильная скорость раздачи любых объектов независимо от числа одновременных обращений.

  • Автоматическое и виртуально неограниченное масштабирование.

  • Возможность настройки Webhooks для автоматической обработки при создании/удалении объектов (например, автообработка фото и видео).

  • Возможность настройки жизненного цикла объектов. Например, удаление архивных данных по истечении максимального срока их хранения.

  • У S3 нет понятия зоны доступности: это глобальный сервис. Его доступность обеспечивается из пяти ЦОД MCS.

  • Наименьшая стоимость среди всех типов облачных хранилищ.

Основное отличие S3-хранилища от других облачных (блочных) систем хранения блочные хранилища предназначены для использования виртуальными машинами и представляются как диски, а объектное хранилище доступно только по HTTP.

Основные особенности S3

  1. Capacity. Нет ограничений на общий объем можно хранить петабайты данных. В одном хранилище может быть до 25 бакетов, размер одного бакета произвольный. Число объектов в одном бакете не ограничено свыше 1 млрд. Для конкретных объектов действуют следующие рейт-лимиты: 32 ГБ для обычного файла, 320 ТБ для multipart.

  2. IOPS (количество операций в секунду). Действуют следующие рейт-лимиты: для обычных запросов 500 запросов в секунду, 10 млн запросов в день, для запросов на листинг 15 запросов в секунду, 10 млн запросов в день.

  3. Throughput (пропускная способность). Поддерживается скорость передачи объектов 1 Гбит/с. Для быстрой доставки контента хранилище S3 можно интегрировать с сетью доставки контента (CDN, Content Delivery Network), имеющей более 400 точек присутствия во всем мире и емкость канала 1,5 ТБит/с.

  4. Масштабирование. Размер S3 не ограничен. Масштабирование происходит вверх и вниз автоматически, без дополнительных настроек со стороны пользователя.

  5. Доступность. Гарантируется SLA, общий для облака 99,95%. Надежность хранения при этом составляет 99,99999%.

  6. Поведение при выходе физического оборудования из строя. Происходит без прерывания обслуживания и потери данных.

  7. Бэкапы и восстановление. Обеспечивается георепликация данных. В разработке функциональность версионирования объектов с возвратом к определенному номеру версии.

  8. Границы доступности. Возможен доступ из всех зон доступности региона, а также из любого места в интернете с использованием URL объектов.

  9. Протоколы доступа. S3 API. Главная особенность решения от MCS полная совместимость с API S3.

  10. Безопасность. Обеспечивается за счет списков управления доступом (ACL, Access Control Lists) и префиксных ключей. На уровне каждого бакета можно определять, кто может создавать, удалять и перечислять объекты в нем. При организации внешнего доступа вне облака можно использовать HTTPS.

  11. Механизм расчета стоимости. Оплачивается за фактически использованные ресурсы и рассчитывается посекундно. Тарифный план зависит от типа выбранного хранилища: HotBox, IceBox или Glacier.

Файловые хранилища в облаке

Файловое хранилище в облаке представляет собой систему хранения файлов, в которой все данные организованы иерархически в нисходящую сеть папок. На платформе MCS файловое хранилище предоставляется как сервис. С его помощью можно создать удаленную файловую систему, смонтировать ее на виртуальных машинах, а затем читать и записывать данные из инстансов в файловую систему и из нее.

Чаще всего файловое хранилище используют в качестве системы хранения документов, общего пользовательского файлового пространства или общего персистентного хранилища данных для узлов кластера Kubernetes.

Преимущества файловых хранилищ:

  • Есть возможность как увеличения, так и уменьшения размера хранилища.

  • Есть возможность создания снапшотов.

  • Оптимальный вариант хранения для Legacy-приложений, требующих протокола SMB/NFS.

  • Поддерживаются большим количеством классических систем.

Недостатки файловых хранилищ:

  • Масштабирование производится вручную.

  • Одновременный доступ ограничен полосой пропускания стандартного сетевого интерфейса.

  • В настоящий момент для файловых хранилищ нет возможности выбрать другой тип диска, кроме HDD, в будущем появится возможность использовать SSD.

Основные характеристики файловых хранилищ

  1. Capacity. Рекомендуемый максимальный размер хранилища 2 Тб. Максимальный размер файла не больше запрошенного размера хранилища.

  2. IOPS (количество операций в секунду на 2 Тб пространства). Так как файловое хранилище базируется на дисках HDD, показатель SLA будет тем же: для чтения 3002400, для записи 150800.

  3. Throughput (пропускная способность на 2 Тб пространства, при размере блока 1М). Аналогично, как для HDD: для чтения 250 Мб/с, для записи 100 Мб/с.

  4. Масштабирование. Проводится вручную через web-консоль управления облаком или OpenStack CLI (Command Line Interface). В отличие от облачных дисков возможно как увеличение, так и уменьшение размера файлового хранилища.

  5. Доступность. Гарантируется SLA, общий для облака, 99,95%.

  6. Поведение при выходе физического оборудования из строя. При выходе из строя оборудования, с которого предоставляется дисковое пространство, сервис продолжает предоставляться. Но выход из строя компонентов самого сервиса, конечно, ведет к его прерыванию.

  7. Бэкапы и восстановление. Возможно создание снапшотов, бэкапирование из веб-консоли недоступно. Механизмы те же, что и для облачных дисков.

  8. Границы доступности. Доступ осуществим из сетей, которые имеют возможность маршрутизации IP-пакетов с сетью, где размещено файловое хранилище.

  9. Протоколы доступа. Подключить файловое хранилище к инстансам проекта можно по протоколам CIFS (SMB v3) или NFS.

  10. Безопасность. Доступ к файловым хранилищам осуществляется только из виртуальных машин внутри проекта (Namespace) MCS. При этом дается возможность настроить правила доступа к хранилищу в зависимости от IP клиента.

  11. Механизм расчета стоимости. Цена определяется в зависимости от запрошенного при создании объема хранилища. При изменении размера в дальнейшем стоимость автоматически пересчитывается.

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

Преобладающий тип операций (чтение/запись) и их частота. В первую очередь необходимо оценить, каким образом планируется обращаться к хранимым данным.

Объектное хранилище S3 ориентировано на операции WORM. Оно не подойдет для частых модификаций объектов, обладающих большими размерами. Если для таких объектов скорость доступа критична и данные часто модифицируются, следует предпочесть файловые хранилища и, в зависимости от ситуации, облачные диски. Выбор конкретного типа дисков будет зависеть от требуемой производительности.

При выборе S3 необходимо дополнительно определить частоту доступа к данным и выбрать соответствующий тип хранилища: HotBox, IceBox или Glacier.

Требуемая производительность: IOPS, Throughput, Latency. Для систем, требующих низкой задержки и одновременно высокой пропускной способности, рекомендуется использовать блочное хранилище, оно же виртуальный диск. В объектных хранилищах модифицируемый объект перезаписывается целиком, в отличие от обычных дисков, где изменение всегда происходит на уровне конкретного блока данных.

В порядке возрастания производительности диски можно расположить следующим образом: HDD, SSD, SSD High IOPS, Low Latency NVMe. Если требуется обеспечить минимальную задержку, Low Latency NVMe будет лучшим выбором, так как для этого типа диска определено SLA на данный показатель 0,5 мс.

Методы доступа к данным, используемые в классических приложениях (в первую очередь протоколы доступа, так как контроль над интерфейсами напрямую заказчику недоступен). Очень часто при переносе Legacy-приложений клиентов в облако требуется обеспечить конкретные, уже используемые ими протоколы. Конечно, обновление систем возможно, но, как правило, требует дополнительных затрат. В таких случаях выбор облачного хранилища полностью зависит от требований переносимого ПО. Например, файловые хранилища чаще всего выбирают, когда необходимы протоколы SMB/NFS. И это стало в свое время основной причиной того, почему у нас появилось файловое хранилище как сервис.

Требования к организации доступа к данным. Доступ к дисковым и файловым хранилищам возможен из разных AZ, но ресурс локализован в одной AZ, то есть при недоступности этой AZ хранилище тоже может стать недоступным. Поэтому для доступа из нескольких зон доступности или из любой точки интернета вне облака S3 будет лучшим выбором.

Цена. Среди облачных систем хранения данных минимальная стоимость у S3, она может меняться в зависимости от того, какие данные вы будете хранить. Важное преимущество S3 необходимость оплаты только фактически используемых ресурсов.

Для файловых хранилищ и обычных дисков цена определяется запрошенным объемом ресурсов. При этом цена дисков возрастает по мере увеличения их производительности: HDD, SSD, SSD High IOPS, Low Latency NVMe. Рекомендуем выбирать тот тип диска, который при достаточной для вас производительности будет дешевле всего, так как в дальнейшем при необходимости его можно будет изменить на лету.

Схема выбора оптимальной системы хранения данных с учетом описанных параметров в очень упрощенном виде приведена ниже. Она носит рекомендательный характер. Естественно, в зависимости от ситуации данные рекомендации могут быть и абсолютно нерелевантными, но для общих случаев они чаще всего корректны.

Упрощенная схема выбора облачного хранилища

Как показывает опыт, описанные выше требования во многом определяются типом хранимых данных. Поэтому можно выделить наиболее типичные сценарии использования для каждой системы хранения данных. В таблице ниже показано, как выбрать облачное хранилище в зависимости от того, какие данные планируется размещать.

Система хранения данных

Типичные сценарии использования

S3 Glacier

Массивные данные (от 100 Тб) с очень редким доступом: бэкапы, архивы, журналы, системные сообщения, логи.

S3 IceBox

Данные с редким доступом: архивы корпоративных файлов, годовая/месячная отчетность, документы маленьких рабочих групп, бэкапы, системные сообщения, lоg-файлы.

S3 HotBox

Потоковая раздача мультимедиа, хранилища для Backend-платформ, хостинг статических файлов и веб-сайтов, хранение данных для обработки (Big Data, Data Mining).

Файловое хранилище

Файловые хранилища, воссоздание схемы Legacy-приложения, общее персистентное хранилище данных для групп контейнеров.

HDD

Файловые хранилища, загрузочные разделы.

SSD

СУБД, телеметрия, очереди сообщений, загрузочные разделы.

SSD High IOPS

СУБД, аналитика, телеметрия. С большими требованиями к производительности, чем у SSD, но меньшими, чем у Low Latency NVMe.

Low Latency NVMe

Высокопроизводительные СУБД, аналитика, кэш.

Расчет необходимой производительности облачной системы хранения при переносе Legacy-приложений

Один из основных критериев выбора типа облачного хранилища это требуемая производительность для переносимых в облако Legacy-приложений. Как ее правильнее рассчитать?

Предлагаем руководствоваться следующим набором правил:

  1. Определите критерии успешности прохождения тестирования. Это может быть выполнение операций за заданное количество времени, расходование определенного количества ресурсов на заданный набор данных и так далее.

  2. Настройте и запустите процесс снятия метрик на исходном сервере:

    • количество потребляемых ядер;

    • потребляемая частота CPU;

    • количество операций ввода/вывода в секунду;

    • задержки чтения/записи на дисках;

    • эффективность использования ОЗУ.

  3. Подготовьте синтетические данные для нагрузочного тестирования.

  4. Создайте тестовый стенд, выделив на нем минимальное достаточное количество ресурсов в соответствии с расчетами. Расчеты стоит осуществлять с учетом фактического потребления ресурсов в часы наиболее высокой нагрузки. Далее, используя показатели из SLA MCS, пересчитать на ресурсы MCS.

  5. Предварительно настроив сбор данных (в соответствии с шагом 2) на тестовом сервере, выполните нагрузочное тестирование и определите степень достижения показателей, выбранных на шаге 1.

  6. Если показатели успешности не достигнуты, проведите диагностику для выявления узкого места, исходя из анализа данных, собранных на шаге 5. Добавьте необходимый тип ресурса или пересмотрите архитектуру и вновь приступайте к тестированию.

  7. При успешном тестировании можно производить миграцию и начинать промышленную эксплуатацию.

Как сочетать облачные системы хранения между собой

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

Например, возможна следующая комбинация блочных дисков: для ОС использовать HDD, СУБД построить с использованием SSD High IOPS, а для кэша взять Low Latency NVMe. S3 в такую схему можно подключить для размещения медиаконтента (картинок и видео) с возможностью внешнего доступа или для хранения резервных копий.

Если вы используете Kubernetes, S3 подойдет для хранения образов, а файловые хранилища в качестве персистентного хранилища для групп контейнеров. Хотя здесь также в большинстве случаев с минимальными доработками можно научить ПО в контейнерах работать с более надежным и дешевым хранилищем S3.

Варианты комбинации различных типов облачных хранилищ представлены на рисунках ниже.

Вариант комбинации облачных дисков на примере построения инфраструктуры для интернет-магазина с применением облачных сервисов

Вариант комбинации облачных дисков на примере построения инфраструктуры для интернет-магазина с использованием кластера Kubernetes и хранилища S3

Итоговое сравнение облачных систем хранения данных

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

Показатель/Система хранения данных

HDD

SSD

SSD High IOPS

Low Latency NVMe

Файловое хранилище

S3

Тип хранилища

Блочное

Блочное

Блочное

Блочное

Файловое

Объектное

Размер хранилища

Рекомендуемый размер 2 Тб

Рекомендуемый размер 2 Тб

Рекомендуемый размер 2 Тб

Рекомендуемый размер 2 Тб

Рекомендуемый размер 2 Тб

Не ограничен

Максимальный размер файла

Размер диска

Размер диска

Размер диска

Размер диска

Размер хранилища

32 ГБ для обычного файла, 320 ТБ для multipart

IOPS read SLA(на 2 Тб пространства)

300 2400

1 000 16 000

10 000 45 000

10 000 75 000

300 2400

Действуют рейт-лимиты: для обычных запросов 500 запросов/с, 10 000 000 запросов/день для запросов на листинг 15 запросов/с, 10 000 000 запросов/день

IOPS write SLA(на 2 Тб пространства)

150 800

500 8000

5000 30 000

5000 50 000

150 800

Latency SLA

Не предусмотрен SLA

Не предусмотрен SLA

Не предусмотрен SLA

Максимум 0,5 мс

Не предусмотрен SLA

Не предусмотрен SLA

Throughput read SLA(на 2 Тб пространства и размер блока 1 М)

250 Мб/c

400 Мб/c

500 Мб/c

1200 Мб/c

250 Мб/c

Обеспечивается скорость до 1 ГБит/c. При интеграции с CDN: 1,5 ТБит/с

Throughput write SLA(на 2 Тб пространства и размер блока 1 М)

100 Мб/c

400 Мб/c

500 Мб/c

900 Мб/c

100 Мб/c

Масштабирование

Вручную за счет увеличения размера диска

Вручную за счет увеличения размера диска

Вручную за счет увеличения размера диска

Вручную за счет увеличения размера диска

Вручную за счет увеличения/уменьшения размера хранилища

Виртуально не ограничена

Доступность:

  • SLA

  • Поведение при выходе физического оборудования из строя

99,95%

99,95%

99,95%

99,95%

99,95%

99,95%Надежность хранения 99,99999%

Без прерывания обслуживания, данные не теряются (за счет двойных и тройных репликаций)

Без прерывания обслуживания, данные не теряются (за счет двойных и тройных репликаций)

Может возникнуть временная недоступность, данные не теряются (необходимо обеспечить отказоустойчивость на уровне приложения)

Недоступность и риск потери данных (необходимо обеспечить отказоустойчивость на уровне приложения)

Сервис доступен при выходе из строя оборудования, но выход его компонентов из строя ведет к прерыванию сервиса

Без прерывания обслуживания, данные не теряются

Бэкапы и восстановление

Резервные копии, снапшоты

Резервные копии, снапшоты

Резервные копии, снапшоты

Резервные копии, снапшоты

Снапшоты

Георепликация данных, в планах версионность объектов

Границы доступности

Из любой AZ, но ресурс локализован в одной AZ

Из любой AZ, но ресурс локализован в одной AZ

Из любой AZ, но ресурс локализован в одной AZ

Из любой AZ, но ресурс локализован в одной AZ

Из сетей, которые имеют возможность маршрутизации IP-пакетов с сетью, где размещено файловое хранилище

MultiAZ, глобальный

Протоколы доступа

Неприменимо

Неприменимо

Неприменимо

Неприменимо

Ethernet, SMB/NFS

S3 API

Безопасность

Доступ ограничивается namespace проекта

Доступ ограничивается namespace проекта

Доступ ограничивается namespace проекта

Доступ ограничивается namespace проекта

Доступ ограничивается namespace проекта, можно настроить доступ по IP клиента

Возможность ограничения доступа с использованием ACL и префиксных ключей, внешний доступ по HTTPS

Ценообразование

За выделенные ресурсы

За выделенные ресурсы

За выделенные ресурсы

За выделенные ресурсы

За выделенные ресурсы

За фактически использованные ресурсы

Что еще почитать по теме:

  1. Работа с объектным S3-хранилищем Mail.ru Cloud Solutions как с файловой системой.

  2. Принципы организации объектных хранилищ.

  3. Все обновления облачных хранилищ и другие новости наших облачных сервисов в нашем телеграм-канале.

Подробнее..

NestJS. Загрузка файлов в S3 хранилище (minio)

13.08.2020 02:13:49 | Автор: admin
NestJS фреймворк для создания эффективных, масштабируемых серверных приложений на платформе Node.js. Вы можете встретить утверждение, что NestJS является платформо-независимым фреймворком. Имеется в виду, что он может работать на базе одного из двух фрейморков по Вашему выбору: NestJS+Express или NestJS+Fastify. Это действительно так, или почти так. Эта платформо-независимость заканчивается, на обработке запросов Content-Type: multipart/form-data. То есть практически на второй день разработки. И это не является большой проблемой, если Вы используете платформу NestJS+Express в документации есть пример работы для Content-Type: multipart/form-data. Для NestJS+Fastify такого примера нет, и примеров в сети не так уж и много. И некоторые из этих примеров идут по весьма усложненному пути.

Выбирая между платформой NestJS+Fastify и NestJS+Express я сделал выбор в сторону NestJS+Fastify. Зная склонность разработчиков в любой непонятной ситуации вешать на объект req в Express дополнительные свойства и так общаться между разными частями приложения, я твердо решил что Express в следующем проекте не будет.

Только нужно было решить технический вопрос с Content-Type: multipart/form-data. Также полученные через запросы Content-Type: multipart/form-data файлы я планировал сохранять в хранилище S3. В этом плане реализация запросов Content-Type: multipart/form-data на платформе NestJS+Express меня смущала тем, что не работала с потоками.

Запуск локального хранилища S3



S3 это хранилище данных (можно сказать, хотя не совсем строго, хранилище файлов), доступное по протоколу http. Изначально S3 предоставлялся AWS. В настоящее время API S3 поддерживается и другими облачными сервисами. Но не только. Появились реализации серверов S3, которые Вы можете поднять локально, чтобы использовать их во время разработки, и, возможно, поднять свои серверы S3 для работы на проде.

Для начала нужно определиться с мотивацией использования S3 хранилища данных. В некоторых случаях это позволяет снизить затраты. Например, для хранения резервных копий можно взять самые медленные и дешевые хранилища S3. Быстрые хранилища с высоким трафиком (трафик тарифицируется отдельно) на загрузку данных из хранилища, возможно, будут стоить сравнимо с SSD дисками того же объема.

Более весомым мотивом является 1) расширяемость Вам не нужно думать о том, что место на диске может закончиться, и 2) надежность сервера работают в кластере и Вам не нужно думать о резервном копировании, так как необходимое количество копий есть всегда.

Для поднятия реализации серверов S3 minio локально нужен только установленный на компьютере docker и docker-compose. Соответсвующий файл docker-compose.yml:

version: '3'services:  minio1:    image: minio/minio:RELEASE.2020-08-08T04-50-06Z    volumes:      - ./s3/data1-1:/data1      - ./s3/data1-2:/data2    ports:      - '9001:9000'    environment:      MINIO_ACCESS_KEY: minio      MINIO_SECRET_KEY: minio123    command: server http://minio{1...4}/data{1...2}    healthcheck:      test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']      interval: 30s      timeout: 20s      retries: 3  minio2:    image: minio/minio:RELEASE.2020-08-08T04-50-06Z    volumes:      - ./s3/data2-1:/data1      - ./s3/data2-2:/data2    ports:      - '9002:9000'    environment:      MINIO_ACCESS_KEY: minio      MINIO_SECRET_KEY: minio123    command: server http://minio{1...4}/data{1...2}    healthcheck:      test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']      interval: 30s      timeout: 20s      retries: 3  minio3:    image: minio/minio:RELEASE.2020-08-08T04-50-06Z    volumes:      - ./s3/data3-1:/data1      - ./s3/data3-2:/data2    ports:      - '9003:9000'    environment:      MINIO_ACCESS_KEY: minio      MINIO_SECRET_KEY: minio123    command: server http://minio{1...4}/data{1...2}    healthcheck:      test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']      interval: 30s      timeout: 20s      retries: 3  minio4:    image: minio/minio:RELEASE.2020-08-08T04-50-06Z    volumes:      - ./s3/data4-1:/data1      - ./s3/data4-2:/data2    ports:      - '9004:9000'    environment:      MINIO_ACCESS_KEY: minio      MINIO_SECRET_KEY: minio123    command: server http://minio{1...4}/data{1...2}    healthcheck:      test: ['CMD', 'curl', '-f', 'http://localhost:9000/minio/health/live']      interval: 30s      timeout: 20s      retries: 3


Запускаем и без проблем получаем кластер из 4 серверов S3.

NestJS + Fastify + S3



Работу с сервером NestJS опишу с самых первых шагов, хотя часть этого материала отлично описана в документации. Устанавливается CLI NestJS:

npm install -g @nestjs/cli


Создается новый проект NestJS:

nest new s3-nestjs-tut


Инсталлируются необходимые пакеты (включая те что нужны для работы с S3):

npm install --save @nestjs/platform-fastify fastify-multipart aws-sdk sharpnpm install --save-dev @types/fastify-multipart  @types/aws-sdk @types/sharp


По умолчанию в проекте устанавливается платформа NestJS+Express. Как установить Fastify описано в документации docs.nestjs.com/techniques/performance. Дополнительно нам нужно установить плагин для обработки Content-Type: multipart/form-data fastify-multipart

import { NestFactory } from '@nestjs/core';import {  FastifyAdapter,  NestFastifyApplication,} from '@nestjs/platform-fastify';import fastifyMultipart from 'fastify-multipart';import { AppModule } from './app.module';async function bootstrap() {  const fastifyAdapter = new FastifyAdapter();  fastifyAdapter.register(fastifyMultipart, {    limits: {      fieldNameSize: 1024, // Max field name size in bytes      fieldSize: 128 * 1024 * 1024 * 1024, // Max field value size in bytes      fields: 10, // Max number of non-file fields      fileSize: 128 * 1024 * 1024 * 1024, // For multipart forms, the max file size      files: 2, // Max number of file fields      headerPairs: 2000, // Max number of header key=>value pairs    },  });  const app = await NestFactory.create<NestFastifyApplication>(    AppModule,    fastifyAdapter,  );  await app.listen(3000, '127.0.0.1');}bootstrap();


Теперь опишем сервис, загружающий файлы в хранилище S3, сократив код по обработке некоторых видов ошибок (полный текст есть в репозитарии статьи):

import { Injectable, HttpException, BadRequestException } from '@nestjs/common';import { S3 } from 'aws-sdk';import fastify = require('fastify');import { AppResponseDto } from './dto/app.response.dto';import * as sharp from 'sharp';@Injectable()export class AppService {  async uploadFile(req: fastify.FastifyRequest): Promise<any> {    const promises = [];    return new Promise((resolve, reject) => {      const mp = req.multipart(handler, onEnd);      function onEnd(err) {        if (err) {          reject(new HttpException(err, 500));        } else {          Promise.all(promises).then(            data => {              resolve({ result: 'OK' });            },            err => {              reject(new HttpException(err, 500));            },          );        }      }      function handler(field, file, filename, encoding, mimetype: string) {        if (mimetype && mimetype.match(/^image\/(.*)/)) {          const imageType = mimetype.match(/^image\/(.*)/)[1];          const s3Stream = new S3({            accessKeyId: 'minio',            secretAccessKey: 'minio123',            endpoint: 'http://127.0.0.1:9001',            s3ForcePathStyle: true, // needed with minio?            signatureVersion: 'v4',          });          const promise = s3Stream            .upload(              {                Bucket: 'test',                Key: `200x200_${filename}`,                Body: file.pipe(                  sharp()                    .resize(200, 200)                    [imageType](),                ),              }            )            .promise();          promises.push(promise);        }        const s3Stream = new S3({          accessKeyId: 'minio',          secretAccessKey: 'minio123',          endpoint: 'http://127.0.0.1:9001',          s3ForcePathStyle: true, // needed with minio?          signatureVersion: 'v4',        });        const promise = s3Stream          .upload({ Bucket: 'test', Key: filename, Body: file })          .promise();        promises.push(promise);      }    });  }}


Из особенностей следует отметить, что мы пишем входной поток в два выходных потока, если загружается картинка. Один из потоков сжимает картинку до размеров 200х200. Во всех случаях используется стиль работы с потоками (stream). Но для того, чтобы отловить возможные ошибки и вернуть их в контроллер, мы вызываем метод promise(), который определен в библиотеке aws-sdk. Полученные промисы накапливаем в массиве promises:

        const promise = s3Stream          .upload({ Bucket: 'test', Key: filename, Body: file })          .promise();        promises.push(promise);


И, далее, ожидаем их разрешение в методе Promise.all(promises).

Код контроллера, в котором таки пришлось пробросить FastifyRequest в сервис:

import { Controller, Post, Req } from '@nestjs/common';import { AppService } from './app.service';import { FastifyRequest } from 'fastify';@Controller()export class AppController {  constructor(private readonly appService: AppService) {}  @Post('/upload')  async uploadFile(@Req() req: FastifyRequest): Promise<any> {    const result = await this.appService.uploadFile(req);    return result;  }}


Запускается проект:

npm run start:dev


Репозитарий статьи github.com/apapacy/s3-nestjs-tut

apapacy@gmail.com
13 августа 2020 года
Подробнее..
Категории: Node.js , S3 , Nodejs , Docker , Docker-compose , Nestjs , Express , Expressjs , Fastify , Aws-s3 , Minio

CloudWatch и Lambda, или Как я перестал бояться и полюбил AWS

23.04.2021 14:18:12 | Автор: admin

Облачные провайдеры это реактор, где вместо обогащённого урана используется твой кошелёк. В позапрошлом году наша компания начала активно применять облака и мы в полной мере ощутили это на себе: несколько команд разрабатывали отдельные продукты, и для большинства тестов запускались виртуальные мощности в AWS. Мы с коллегами получили сертификаты от Amazon, и это, вместе с наглядностью происходящего, Free Tier и Soft Limitами, создавало ложное чувство спокойствия за свой бюджет. А когда этому чувству поддаёшься, получаешь локальный Чернобыль.

После нескольких превышений бюджета и встреч с начальством из-за забытых виртуалок мне надоело оправдываться, и я решил предотвратить лишние расходы, тем более что было известно, сколько можно потратить. Я использовал связку CloudWatch и Lambda и этот способ себя оправдал.

Почему настройка оповещений это плацебо

Первый способ, на который указывает сам AWS это настройка бюджетов AWS Budgets и оповещений из них. Да, здесь есть очевидные плюсы: это решение очень легко настроить, а сервис предупреждает в случае превышения текущего или прогнозируемого бюджета. С сентября 2020 г. он даже научился прогнозировать потребление и отправку событий в Amazon SNS.

Но объективно это просто ещё одна загорающаяся лампочка: оповещение может прийти на почту в середине ночи, а узнаёшь об этом только в понедельник утром. К тому же, по умолчанию AWS Budgets ничего не делает после того как заметил превышение. Он отлично подходит для контроля конкретных задач (например, отмеряет скорость расходования средств на проде), но среагирует слишком поздно, если у нас рядом будут работать запущенные по ошибке или безымянные виртуальные машины. Не подойдёт.

Как взять под контроль облачный ядерный реактор

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

Я решил, что нужно где-то разместить логику реагирования и дёргать за различные API в облаке. Проще всего это сделать в AWS Lambda, так как Serverless может работать бесплатно, логика реагирования оформляется как на NodeJS, так и на Python, а простота API-вызовов из облака и ролевая модель доступа сокращают время на тестирование такого решения.

Но как тогда нам собирать информацию о запущенных инстансах и их потреблении? Зная время работы и цену инстанса в час, можно соотнести потребление с нашим бюджетом. Цены инстансов статическая и общедоступная информация. Тут главный вопрос как давно стартовал инстанс?

Узнав на него ответ, можно принять решение: выключать инстанс за избыточностью или оставлять, потому что он нам по каким-то причинам нужен или пока не мешает.

Какие есть инструменты для контроля трат на AWS

В AWS есть несколько сервисов, которые помогают контролировать бюджет. В процессе поиска я рассматривал все возможные варианты, так как у каждого из них есть свои сильные стороны и ограничения:

  • Cost Explorer даёт подробную разбивку по сервисам, но единственное, что он показывает, это сколько аккаунт уже успел потратить. Ничего не знает про запущенные в данный момент виртуальные мощности, но с его помощью можно оптимизировать траты.

  • Budgets внутри Cost Explorer. С сентября 2020 г. он научился определять Usage в часах, но всё ещё не даёт ответа, как идентифицировать забытые сущности.

  • CloudTrail мощный инструмент для контроля всего, что происходит в облаке. С его помощью можно раскрывать все сущности, совершившие любое действие, и узнавать точное время. Главный его недостаток для мой задачи он собирает вообще всю информацию в массивные лог-файлы, без конкретного объяснения в какой момент он заканчивает один файл и начинает другой. Писать целый парсер среди множества файлов под одну задачу это как из пушки по воробьям.

  • CloudWatch отличный инструмент, чтобы мониторить состояние уже запущенных инстансов, но информация о запусках находится только в CloudTrail. Кроме сбора информации, умеет запускать события по расписанию.

Таким образом, если нам нужна оперативность, AWS предоставляет достаточно инструментов для обзора прошедших событий. У него огромное количество параметров для контроля здоровья виртуальных машин, когда мы говорим про потребление бюджета. И параметров столько, что разобраться становится практически невозможно.

Накопление данных Lambda + S3

Так как сложные варианты мне не подошли, я остановился на простом: решил накапливать знание о времени работы каждого инстанса самостоятельно, благо хранение файлов это тривиальная задача для Lambda + S3.

Для упрощения тестирования и читаемости кода разобьём задачу между несколькими Lambda:

  • отдельно собираем информацию по запущенным инстансам;

  • решаем, какие инстансы заслуживают выключения, а какие нет;

  • выключаем ненужные инстансы.

Оркестратором простой цепочки Lambda я сделал Step Functions, который может запускаться по расписанию из CloudWatch.

Пример решения

Файлы для Proof of Concept вы можете найти по ссылке на GitHub, а ниже я распишу, что делает этот код. В жизни он дорабатывался под нужды команд и использовался до тех пор, пока мы не обучили достаточное количество сотрудников соблюдать правила работы с облаком.

Что тут происходит?

Каждые 5 минут CloudWatch отправляет заготовленный вызов в Step Functions, который управляет последовательным запуском четырёх Lambda-функций. Каждая Lambda-функция исполняет код на JavaScript (версии Node.js 10.x), использует сервисы EC2, Config или S3, и завершает свою работу передачей JSON в следующую Lambda. Исполнение скрипта завершается записью логов в CloudWatch Logs.

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

CloudWatch Event > Step Function > 4 Lambda functions > CloudWatch Logs

  1. Get List of Working Instances получает список работающих инстансов и передает его через JSON в следующую Lambda-функцию

  2. Update Budget Usage делает много вещей, но главное обновляет данные файла в S3-хранилище.

  3. Terminate Instances выключает инстансы, которые превышают бюджет или нас не устраивают.

Как устанавливать?

После размещения кода и настройки ролей в Lambda-функциях необходимо составить схему работы через Step Functions, а также привязать событие CloudWatch Event Rule, которое будет запускать систему каждые 5 минут.

Как бы я подошёл к этой проблеме сейчас

Облака постоянно развиваются, и завтра может появиться очередная машина Судного дня, которая изменит ландшафт до неузнаваемости. С момента написания кода до публикации этой статьи прошло почти 2 года, в течение которых AWS доработал свой сервис AWS Budgets: теперь он позволяет отслеживать не только затраты, но и потребление с расширенным набором параметров например, по тегам.

В большой организации, где могут быть десятки и сотни виртуальных машин в разных регионах и между разными аккаунтами, теги единственный простой способ определить происхождение и задачу запущенных в облаке мощностей. Теги можно задавать по своим нуждам и привязывать практически к чему угодно. Без них каждый новый человек будет сомневаться: что из запущенного нужно и чьё это?

Если бы я сейчас начал заново решать проблему избыточного времени работы, то в S3 вместо файла бюджета оказался бы файл с договорённостями по использованию тегов. Lambda вместо оценки времени работы немедленно выключала бы все мощности, не подходящие по тегам. А контроль за расходами перешёл бы в связку AWS Budget и SNS с той же Lambda, управляющей их отключением.

Подробнее..

Из песочницы WAL-G бэкапы и восстановление СУБД PostgreSQL

14.06.2020 14:16:56 | Автор: admin
Уже давно известно, что делать бэкапы в SQL-дампы (используя pg_dump или pg_dumpall) не самая хорошая идея. Для резервного копирования СУБД PostgreSQL лучше использовать команду pg_basebackup, которая делает бинарную копию WAL-журналов. Но когда вы начнёте изучать весь процесс создания копии и восстановления, то поймёте что нужно написать как минимум пару трёхколёсных велосипедов, чтобы всё это работало и не вызывало у вас боль как сверху, так и снизу. Дабы облегчить страдания был разработан WAL-G.

WAL-G это инструмент, написанный на Golang для резервного копирования и восстановления PostgreSQL баз данных (а с недавнего времени и MySQL/MariaDB, MongoDB и FoundationDB). Он поддерживает работу с хранилищами Amazon S3 (и аналогами, например, Yandex Object Storage), а также Google Cloud Storage, Azure Storage, Swift Object Storage и просто с файловой системой. Вся настройка сводится к простым шагам, но из-за того что статьи о нём разрозненны по интернету нет полного how-to мануала, который бы включал все шаги от и до (на Хабре есть несколько постов, но многие моменты там упущены).

postgresql backup

Данная статья написана в первую очередь чтобы систематизировать свои знания. Я не являюсь DBA и где-то могу выражаться обывательско-разработческим языком, поэтому приветствуются любые исправления!

Отдельно отмечу что всё нижеприведенное актуально и проверено для PostgreSQL 12.3 на Ubuntu 18.04, все команды должны выполняться от привилегированного пользователя.

Установка


В момент написания данной статьи стабильная версия WAL-G v0.2.15 (март 2020). Её мы и будем использовать (но если вы захотите самостоятельно собрать его из master-ветки, то в репозитории на github есть все инструкции для этого). Для скачивания и установки нужно выполнить:

#!/bin/bashcurl -L "https://github.com/wal-g/wal-g/releases/download/v0.2.15/wal-g.linux-amd64.tar.gz" -o "wal-g.linux-amd64.tar.gz"tar -xzf wal-g.linux-amd64.tar.gzmv wal-g /usr/local/bin/

После этого нужно сконфигурировать вначале WAL-G, а потом сам PostgreSQL.

Настройка WAL-G


Для примера хранения бэкапов будет использоваться Amazon S3 (потому что он ближе к моим серверам и его использование обходится очень дёшево). Для работы с ним нужен s3-бакет и ключи доступа.

Во всех предыдущих статьях о WAL-G использовалось конфигурирование с помощью переменных окружения, но с этого релиза настройки можно расположить в .walg.json файле в домашней директории пользователя postgres. Для его создания выполним следующий bash-скрипт:

#!/bin/bashcat > /var/lib/postgresql/.walg.json << EOF{    "WALG_S3_PREFIX": "s3://your_bucket/path",    "AWS_ACCESS_KEY_ID": "key_id",    "AWS_SECRET_ACCESS_KEY": "secret_key",    "WALG_COMPRESSION_METHOD": "brotli",    "WALG_DELTA_MAX_STEPS": "5",    "PGDATA": "/var/lib/postgresql/12/main",    "PGHOST": "/var/run/postgresql/.s.PGSQL.5432"}EOF# обязательно меняем владельца файла:chown postgres: /var/lib/postgresql/.walg.json

Немного поясню по всем параметрам:

  • WALG_S3_PREFIX путь к вашему S3-бакету куда будут заливаться бэкапы (можно как в корень, так и в папку);
  • AWS_ACCESS_KEY_ID ключ доступа в S3 (в случае восстановления на тестовом сервере данные ключи должны иметь ReadOnly Policy! Об этом подробнее написано в разделе про восстановление);
  • AWS_SECRET_ACCESS_KEY секретный ключ в хранилище S3;
  • WALG_COMPRESSION_METHOD метод компрессии, лучше использовать Brotli (так как это золотая середина между итоговым размером и скоростью сжатия/разжатия);
  • WALG_DELTA_MAX_STEPS количество дельт до создания полного бэкапа (позволяют экономить время и размер загружаемых данных, но могут чуть замедлить процесс восстановления, поэтому не желательно использовать большие значения);
  • PGDATA путь к директории с данными вашей базы (можно узнать, выполнив команду pg_lsclusters);
  • PGHOST подключение к базе, при локальном бэкапе лучше делать через unix-socket как в этом примере.

Остальные параметры можно посмотреть в документации: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Настройка PostgreSQL


Чтобы архиватор внутри базы сам заливал WAL-журналы в облако и восстанавливался из них (в случае необходимости) нужно задать несколько параметров в конфигурационном файле /etc/postgresql/12/main/postgresql.conf. Только для начала вам нужно убедиться, что никакие из нижеприведенных настроек не заданы в какие-то другие значения, чтобы при перезагрузке конфигурации СУБД не упала. Добавить эти параметры можно с помощью:

#!/bin/bashecho "wal_level=replica" >> /etc/postgresql/12/main/postgresql.confecho "archive_mode=on" >> /etc/postgresql/12/main/postgresql.confecho "archive_command='/usr/local/bin/wal-g wal-push \"%p\" >> /var/log/postgresql/archive_command.log 2>&1' " >> /etc/postgresql/12/main/postgresql.confecho archive_timeout=60 >> /etc/postgresql/12/main/postgresql.confecho "restore_command='/usr/local/bin/wal-g wal-fetch \"%f\" \"%p\" >> /var/log/postgresql/restore_command.log 2>&1' " >> /etc/postgresql/12/main/postgresql.conf# перезагружаем конфиг через отправку SIGHUP сигнала всем процессам БДkillall -s HUP postgres

Описание устанавливаемых параметров:

  • wal_level сколько информации писать в WAL журналы, replica писать всё;
  • archive_mode включение загрузки WAL-журналов используя команду из параметра archive_command;
  • archive_command команда, для архивации завершённого WAL-журнала;
  • archive_timeout архивирование журналов производится только когда он завершён, но если ваш сервер мало изменяет/добавляет данных в БД, то имеет смысл выставить тут лимит в секундах, по истечению которого команда архивации будет вызвана принудительно (у меня интенсивная запись в базу каждую секунду, поэтому я отказался от установки этого параметра в продакшене);
  • restore_command команда восстановления WAL-журнала из бэкапа, будет использоваться в случае если в полном бэкапе (base backup) будет недоставать последних изменений в БД.

Подробнее обо всех этих параметрах можно прочитать в переводе официальной документации: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Настройка расписания резервного копирования


Как ни крути, но самым удобным способом для запуска является cron. Именно его мы и настроим для создания резервных копий. Начнём с команды создания полного бэкапа: в wal-g это аргумент запуска backup-push. Но для начала лучше выполнить эту команду вручную от пользователя postgres, чтобы убедиться что всё хорошо (и нет каких-то ошибок доступа):

#!/bin/bashsu - postgres -c '/usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main'

В аргументах запуска указан путь к директории с данными напоминаю что его можно узнать, выполнив pg_lsclusters.

Если всё прошло без ошибок и данные загрузились в хранилище S3, то далее можно настроить периодический запуск в crontab:
#!/bin/bashecho "15 4 * * *    /usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main >> /var/log/postgresql/walg_backup.log 2>&1" >> /var/spool/cron/crontabs/postgres# задаем владельца и выставляем правильные права файлуchown postgres: /var/spool/cron/crontabs/postgreschmod 600 /var/spool/cron/crontabs/postgres

В данном примере процесс бэкапа запускается каждый день в 4:15 утра.

Удаление старых резервных копий


Скорее всего вам не нужно хранить абсолютно все бэкапы с мезозойской эры, поэтому будет полезно периодически подчищать ваше хранилище (как полные бэкапы, так и WAL-журналы). Мы сделаем это всё также через cron задачу:

#!/bin/bashecho "30 6 * * *    /usr/local/bin/wal-g delete before FIND_FULL $(date -d '-10 days' '+%FT%TZ') --confirm >> /var/log/postgresql/walg_delete.log 2>&1" >> /var/spool/cron/crontabs/postgres# ещё раз задаем владельца и выставляем правильные права файлу (хоть это обычно это и не нужно повторно делать)chown postgres: /var/spool/cron/crontabs/postgreschmod 600 /var/spool/cron/crontabs/postgres

Cron будет выполнять эту задачу каждый день в 6:30 утра, удаляя всё (полные бэкапы, дельты и WALы) кроме копий за последние 10 дней, но оставит как минимум один бэкап до указанной даты, чтобы любая точка после даты попадала в PITR.

Восстановление из резервной копии


Ни для кого не секрет что залог здоровой базы в периодическом восстановлении и проверке целостности данных внутри. Как восстановиться с помощью WAL-G расскажу в этом разделе, а о проверках поговорим после.

Отдельно стоит отметить что для восстановления на тестовом окружении (всё то, что не production) нужно использовать Read Only аккаунт в S3, чтобы случайно не перезаписать бэкапы. В случае с WAL-G нужно задать пользователю S3 следующие права в Group Policy (Effect: Allow): s3:GetObject, s3:ListBucket, s3:GetBucketLocation. И, конечно, предварительно не забыть выставить archive_mode=off в файле настроек postgresql.conf, чтобы ваша тестовая база не захотела сбэкапиться по-тихому.

Восстановление производится лёгким движением руки с удалением всех данных PostgreSQL (в том числе пользователей), поэтому, пожалуйста, будьте предельно аккуратны когда будете запускать следующие команды.

#!/bin/bash# если есть балансировщик подключений (например, pgbouncer), то вначале отключаем его, чтобы он не нарыгал ошибок в логservice pgbouncer stop# если есть демон, который перезапускает упавшие процессы (например, monit), то останавливаем в нём процесс мониторинга базы (у меня это pgsql12)monit stop pgsql12# или останавливаем мониторинг полностьюservice monit stop# останавливаем саму базу данныхservice postgresql stop# удаляем все данные из текущей базы (!!!); лучше предварительно сделать их копию, если есть свободное место на дискеrm -rf /var/lib/postgresql/12/main# скачиваем резервную копию и разархивируем еёsu - postgres -c '/usr/local/bin/wal-g backup-fetch /var/lib/postgresql/12/main LATEST'# помещаем рядом с базой специальный файл-сигнал для восстановления (см. https://postgrespro.ru/docs/postgresql/12/runtime-config-wal#RUNTIME-CONFIG-WAL-ARCHIVE-RECOVERY ), он обязательно должен быть создан от пользователя postgressu - postgres -c 'touch /var/lib/postgresql/12/main/recovery.signal'# запускаем базу данных, чтобы она инициировала процесс восстановленияservice postgresql start

Для тех, кто хочет проверять процесс восстановления ниже подготовлен небольшой кусок bash-магии, чтобы в случае проблем в восстановлении скрипт упал с ненулевым exit code. В данном примере делается 120 проверок с таймаутом в 5 секунд (всего 10 минут на восстановление), чтобы узнать удалился ли сигнальный файл (это будет означать что восстановление прошло успешно):

#!/bin/bashCHECK_RECOVERY_SIGNAL_ITER=0while [ ${CHECK_RECOVERY_SIGNAL_ITER} -le 120 ]do    if [ ! -f "/var/lib/postgresql/12/main/recovery.signal" ]    then        echo "recovery.signal removed"        break    fi    sleep 5    ((CHECK_RECOVERY_SIGNAL_ITER+1))done# если после всех проверок файл всё равно существует, то падаем с ошибкойif [ -f "/var/lib/postgresql/12/main/recovery.signal" ]then    echo "recovery.signal still exists!"    exit 17fi

После успешного восстановления не забудьте запустить обратно все процессы (pgbouncer/monit и тд).

Проверка данных после восстановления


Обязательно нужно проверить целостность базы после восстановления, чтобы не возникло ситуации с побитой/кривой резервной копией. И лучше делать это с каждым созданным архивом, но где и как зависит только от вашей фантазии (можно поднимать отдельные сервера на почасовой оплате или запускать проверку в CI). Но как минимум необходимо проверять данные и индексы в базе.

Для проверки данных достаточно прогнать их через дамп, но лучше чтобы при создании базы у вас были включены контрольные суммы (data checksums):

#!/bin/bashif ! su - postgres -c 'pg_dumpall > /dev/null'then    echo 'pg_dumpall failed'    exit 125fi

Для проверки индексов существует модуль amcheck, sql-запрос к нему возьмём из тестов WAL-G и вокруг выстроим небольшую логику:

#!/bin/bash# добавляем sql-запрос для проверки в файл во временной директорииcat > /tmp/amcheck.sql << EOFCREATE EXTENSION IF NOT EXISTS amcheck;SELECT bt_index_check(c.oid), c.relname, c.relpagesFROM pg_index iJOIN pg_opclass op ON i.indclass[0] = op.oidJOIN pg_am am ON op.opcmethod = am.oidJOIN pg_class c ON i.indexrelid = c.oidJOIN pg_namespace n ON c.relnamespace = n.oidWHERE am.amname = 'btree'AND c.relpersistence != 't'AND i.indisready AND i.indisvalid;EOFchown postgres: /tmp/amcheck.sql# добавляем скрипт для запуска проверок всех доступных баз в кластере# (обратите внимание что переменные и запуск команд  экранированы)cat > /tmp/run_amcheck.sh << EOFfor DBNAME in \$(su - postgres -c 'psql -q -A -t -c "SELECT datname FROM pg_database WHERE datistemplate = false;" ')do    echo "Database: \${DBNAME}"    su - postgres -c "psql -f /tmp/amcheck.sql -v 'ON_ERROR_STOP=1' \${DBNAME}" && EXIT_STATUS=\$? || EXIT_STATUS=\$?    if [ "\${EXIT_STATUS}" -ne 0 ]    then        echo "amcheck failed on DB: \${DBNAME}"        exit 125    fidoneEOFchmod +x /tmp/run_amcheck.sh# запускаем скрипт/tmp/run_amcheck.sh > /tmp/amcheck.log# для проверки что всё прошло успешно можно проверить exit code или grepнуть ошибкуif grep 'amcheck failed' "/tmp/amcheck.log"then    echo 'amcheck failed: '    cat /tmp/amcheck.log    exit 125fi

Резюмируя


Выражаю благодарность Андрею Бородину за помощь в подготовке публикации и отдельное спасибо за его вклад в разработку WAL-G!

На этом данная заметка подошла к концу. Надеюсь что смог донести легкость настройки и огромный потенциал для применения этого инструмента у вас в компании. Я очень много слышал о WAL-G, но никак не хватало времени сесть и разобраться. А после того как внедрил его у себя из меня вышла эта статья.

Отдельно стоит заметить, что WAL-G также может работать со следующими СУБД:

Подробнее..

Перевод Оптимизируем затраты с помощью AWS Cost Explorer

15.04.2021 14:13:15 | Автор: admin

У Amazon Web Services отличный бесплатный пакет:хороший набор сервисов и щедрая раздача кредитов для разработчиков. Я был уверен: проблем с оплатой моего окружения не будет, поэтому о расходах не беспокоился. Мое приложение на 100% serverless, и я всегда укладывался в уровень бесплатного использования, так что просто игнорировал вопрос оплаты. В какой-то момент я расслабился и потерял бдительность.

Постепенно мой продукт становился популярнее, пользователей стало больше и я получил счет на 62$. Пережить можно, но я задумался о темпах роста: моё приложение не было оптимизировано для уменьшения затрат, так как я об этом никогда не задумывался. Что ж, пришло время заняться сокращением расходов.

AWS Cost Explorer

Сервис AWS Billing dashboard хорошо подходит для оплаты счетов и показывает график прогноза счетов за текущий месяц. Но этот сервис едва ли претендует на звание лучшего в AWS. Месячный прогноз часто врет, поэтому лучше игнорировать его вовсе.

Помимо Billing Dashboard, соседний Cost Explorer. Он предоставляет очень хорошую детализацию и возможность прогнозирования. Кроме просмотра стандартной разбивки потребления в AWS, можно писать код под Cost Explorer, извлекая много ценной информации. И мне это дело зашло.

Используя Cost Explorer, я смог заранее определить уязвимые места и исправить их задолго до того, как с меня начнут списывать за них деньги. Еще раз спасибо AWS.

Пользовательский интерфейс

Прежде чем начать работать, надо познакомиться со стандартным видом консоли Billing Dashboard. Нужно сначала включить её, что будет стоить денег. Лучше сделать это заранее, чтобы потом не было мучительно больно. У кого много остатку, тот не боится недостатку!

Пользовательский интерфейс интуитивно понятный: здесь мы можем получать подробные отчеты по дням / услуге; или сгруппированные по времени / региону / услуге и множеством других способов.

Это мой график потраченного за последние несколько месяцев.

Отчеты

В разделе reports мы можем получить несколько персональных отчетов. AWS предоставляет неплохой набор готовых шаблонов для отчетов, а также возможность создавать свои с нуля.

Бюджеты

Cost Explorer не ограничиваетсяотчетами и графиками. Чащеначинают осваивать AWS с небольших бюджетов и настроенных под них оповещений, но есть возможность сделать гораздо больше. Можно задать независимые бюджеты (по стоимости и использованию) для отдельных сервисов, и даже для отдельных инстансов или операций внутри сервиса.

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

Обнаружение аномалий

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

Cost Explorer API

Стандартный вид консоли управления меня устраивает но только для эпизодического ознакомления. Для того, чтобы получить нечто большее, AWS предоставляет отличный API. Репозиторий AWS Samples Github дает нам наглядный пример доступа к API Cost Explorer.

Мой код основан на этом примере, и позволяет разработать собственный отчет для Cost Explorera.

Код Lambda функции

import osimport sys# Required to load modules from vendored subfolder (for clean development env)sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "./vendored"))import boto3import datetimeimport loggingimport pandas as pd#For datefrom dateutil.relativedelta import relativedelta#For emailfrom email.mime.application import MIMEApplicationfrom email.mime.multipart import MIMEMultipartfrom email.mime.text import MIMETextfrom email.utils import COMMASPACE, formatdateSES_REGION="ap-south-1"CURRENT_MONTH = True#Default exclude support, as for Enterprise Support#as support billing is finalised later in month so skews trends    INC_SUPPORT = os.environ.get('INC_SUPPORT')if INC_SUPPORT == "true":    INC_SUPPORT = Trueelse:    INC_SUPPORT = FalseTAG_VALUE_FILTER = os.environ.get('TAG_VALUE_FILTER') or '*'TAG_KEY = os.environ.get('TAG_KEY')class CostExplorer:    """Retrieves BillingInfo checks from CostExplorer API    >>> costexplorer = CostExplorer()    >>> costexplorer.addReport(GroupBy=[{"Type": "DIMENSION","Key": "SERVICE"}])    >>> costexplorer.generateExcel()    """        def __init__(self, CurrentMonth=False):        #Array of reports ready to be output to Excel.        self.reports = []        self.client = boto3.client('ce', region_name='us-east-1')        # self.end = datetime.date.today().replace(day=1)        self.riend = datetime.date.today()        self.end = self.riend        # Default is last 12 months        self.start = (datetime.date.today() - relativedelta(months=+12)).replace(day=1) #1st day of month 12 months ago        self.ristart = (datetime.date.today() - relativedelta(months=+11)).replace(day=1) #1st day of month 11 months ago        self.sixmonth = (datetime.date.today() - relativedelta(months=+6)).replace(day=1) #1st day of month 6 months ago, so RI util has savings values        self.accounts = {}    def addRiReport(self, Name='RICoverage', Savings=False, PaymentOption='PARTIAL_UPFRONT', Service='Amazon Elastic Compute Cloud - Compute'): #Call with Savings True to get Utilization report in dollar savings        type = 'chart' #other option table        if Name == "RICoverage":            results = []            response = self.client.get_reservation_coverage(                TimePeriod={                    'Start': self.ristart.isoformat(),                    'End': self.riend.isoformat()                },                Granularity='MONTHLY'            )            results.extend(response['CoveragesByTime'])            while 'nextToken' in response:                nextToken = response['nextToken']                response = self.client.get_reservation_coverage(                    TimePeriod={                        'Start': self.ristart.isoformat(),                        'End': self.riend.isoformat()                    },                    Granularity='MONTHLY',                    NextPageToken=nextToken                )                results.extend(response['CoveragesByTime'])                if 'nextToken' in response:                    nextToken = response['nextToken']                else:                    nextToken = False            rows = []            for v in results:                row = {'date':v['TimePeriod']['Start']}                row.update({'Coverage%':float(v['Total']['CoverageHours']['CoverageHoursPercentage'])})                rows.append(row)              df = pd.DataFrame(rows)            df.set_index("date", inplace= True)            df = df.fillna(0.0)            df = df.T        elif Name in ['RIUtilization','RIUtilizationSavings']:            #Only Six month to support savings            results = []            response = self.client.get_reservation_utilization(                TimePeriod={                    'Start': self.sixmonth.isoformat(),                    'End': self.riend.isoformat()                },                Granularity='MONTHLY'            )            results.extend(response['UtilizationsByTime'])            while 'nextToken' in response:                nextToken = response['nextToken']                response = self.client.get_reservation_utilization(                    TimePeriod={                        'Start': self.sixmonth.isoformat(),                        'End': self.riend.isoformat()                    },                    Granularity='MONTHLY',                    NextPageToken=nextToken                )                results.extend(response['UtilizationsByTime'])                if 'nextToken' in response:                    nextToken = response['nextToken']                else:                    nextToken = False            rows = []            if results:                for v in results:                    row = {'date':v['TimePeriod']['Start']}                    if Savings:                        row.update({'Savings$':float(v['Total']['NetRISavings'])})                    else:                        row.update({'Utilization%':float(v['Total']['UtilizationPercentage'])})                    rows.append(row)                  df = pd.DataFrame(rows)                df.set_index("date", inplace= True)                df = df.fillna(0.0)                df = df.T                type = 'chart'            else:                df = pd.DataFrame(rows)                type = 'table' #Dont try chart empty result        elif Name == 'RIRecommendation':            results = []            response = self.client.get_reservation_purchase_recommendation(                #AccountId='string', May use for Linked view                LookbackPeriodInDays='SIXTY_DAYS',                TermInYears='ONE_YEAR',                PaymentOption=PaymentOption,                Service=Service            )            results.extend(response['Recommendations'])            while 'nextToken' in response:                nextToken = response['nextToken']                response = self.client.get_reservation_purchase_recommendation(                    #AccountId='string', May use for Linked view                    LookbackPeriodInDays='SIXTY_DAYS',                    TermInYears='ONE_YEAR',                    PaymentOption=PaymentOption,                    Service=Service,                    NextPageToken=nextToken                )                results.extend(response['Recommendations'])                if 'nextToken' in response:                    nextToken = response['nextToken']                else:                    nextToken = False            rows = []            for i in results:                for v in i['RecommendationDetails']:                    row = v['InstanceDetails'][list(v['InstanceDetails'].keys())[0]]                    row['Recommended']=v['RecommendedNumberOfInstancesToPurchase']                    row['Minimum']=v['MinimumNumberOfInstancesUsedPerHour']                    row['Maximum']=v['MaximumNumberOfInstancesUsedPerHour']                    row['Savings']=v['EstimatedMonthlySavingsAmount']                    row['OnDemand']=v['EstimatedMonthlyOnDemandCost']                    row['BreakEvenIn']=v['EstimatedBreakEvenInMonths']                    row['UpfrontCost']=v['UpfrontCost']                    row['MonthlyCost']=v['RecurringStandardMonthlyCost']                    rows.append(row)              df = pd.DataFrame(rows)            df = df.fillna(0.0)            type = 'table' #Dont try chart this        self.reports.append({'Name':Name,'Data':df, 'Type':type})    def addReport(self, Name="Default",GroupBy=[{"Type": "DIMENSION","Key": "SERVICE"},],     Style='Total', NoCredits=True, CreditsOnly=False, RefundOnly=False, UpfrontOnly=False, IncSupport=False):        type = 'chart' #other option table        results = []        if not NoCredits:            response = self.client.get_cost_and_usage(                TimePeriod={                    'Start': self.start.isoformat(),                    'End': self.end.isoformat()                },                Granularity='MONTHLY',                Metrics=[                    'UnblendedCost',                ],                GroupBy=GroupBy            )        else:            Filter = {"And": []}            Dimensions={"Not": {"Dimensions": {"Key": "RECORD_TYPE","Values": ["Credit", "Refund", "Upfront", "Support"]}}}            if INC_SUPPORT or IncSupport: #If global set for including support, we dont exclude it                Dimensions={"Not": {"Dimensions": {"Key": "RECORD_TYPE","Values": ["Credit", "Refund", "Upfront"]}}}            if CreditsOnly:                Dimensions={"Dimensions": {"Key": "RECORD_TYPE","Values": ["Credit",]}}            if RefundOnly:                Dimensions={"Dimensions": {"Key": "RECORD_TYPE","Values": ["Refund",]}}            if UpfrontOnly:                Dimensions={"Dimensions": {"Key": "RECORD_TYPE","Values": ["Upfront",]}}            tagValues = None            if TAG_KEY:                tagValues = self.client.get_tags(                    SearchString=TAG_VALUE_FILTER,                    TimePeriod = {                        'Start': self.start.isoformat(),                        'End': datetime.date.today().isoformat()                    },                    TagKey=TAG_KEY                )            if tagValues:                Filter["And"].append(Dimensions)                if len(tagValues["Tags"]) > 0:                    Tags = {"Tags": {"Key": TAG_KEY, "Values": tagValues["Tags"]}}                    Filter["And"].append(Tags)            else:                Filter = Dimensions.copy()            response = self.client.get_cost_and_usage(                TimePeriod={                    'Start': self.start.isoformat(),                    'End': self.end.isoformat()                },                Granularity='MONTHLY',                Metrics=[                    'UnblendedCost',                ],                GroupBy=GroupBy,                Filter=Filter            )        if response:            results.extend(response['ResultsByTime'])            while 'nextToken' in response:                nextToken = response['nextToken']                response = self.client.get_cost_and_usage(                    TimePeriod={                        'Start': self.start.isoformat(),                        'End': self.end.isoformat()                    },                    Granularity='MONTHLY',                    Metrics=[                        'UnblendedCost',                    ],                    GroupBy=GroupBy,                    NextPageToken=nextToken                )                results.extend(response['ResultsByTime'])                if 'nextToken' in response:                    nextToken = response['nextToken']                else:                    nextToken = False        rows = []        sort = ''        for v in results:            row = {'date':v['TimePeriod']['Start']}            sort = v['TimePeriod']['Start']            for i in v['Groups']:                key = i['Keys'][0]                if key in self.accounts:                    key = self.accounts[key][ACCOUNT_LABEL]                row.update({key:float(i['Metrics']['UnblendedCost']['Amount'])})             if not v['Groups']:                row.update({'Total':float(v['Total']['UnblendedCost']['Amount'])})            rows.append(row)          df = pd.DataFrame(rows)        df.set_index("date", inplace= True)        df = df.fillna(0.0)        if Style == 'Change':            dfc = df.copy()            lastindex = None            for index, row in df.iterrows():                if lastindex:                    for i in row.index:                        try:                            df.at[index,i] = dfc.at[index,i] - dfc.at[lastindex,i]                        except:                            logging.exception("Error")                            df.at[index,i] = 0                lastindex = index        df = df.T        df = df.sort_values(sort, ascending=False)        self.reports.append({'Name':Name,'Data':df, 'Type':type})    def generateExcel(self):        # Create a Pandas Excel writer using XlsxWriter as the engine.\        os.chdir('/tmp')        writer = pd.ExcelWriter('cost_explorer_report.xlsx', engine='xlsxwriter')        workbook = writer.book        for report in self.reports:            print(report['Name'],report['Type'])            report['Data'].to_excel(writer, sheet_name=report['Name'])            worksheet = writer.sheets[report['Name']]            if report['Type'] == 'chart':                # Create a chart object.                chart = workbook.add_chart({'type': 'column', 'subtype': 'stacked'})                chartend=13                for row_num in range(1, len(report['Data']) + 1):                    chart.add_series({                        'name':       [report['Name'], row_num, 0],                        'categories': [report['Name'], 0, 1, 0, chartend],                        'values':     [report['Name'], row_num, 1, row_num, chartend],                    })                chart.set_y_axis({'label_position': 'low'})                chart.set_x_axis({'label_position': 'low'})                worksheet.insert_chart('O2', chart, {'x_scale': 2.0, 'y_scale': 2.0})        writer.save()        #Time to deliver the file to S3        if os.environ.get('S3_BUCKET'):            s3 = boto3.client('s3')            s3.upload_file("cost_explorer_report.xlsx", os.environ.get('S3_BUCKET'), "cost_explorer_report.xlsx")        if os.environ.get('SES_SEND'):            #Email logic            msg = MIMEMultipart()            msg['From'] = os.environ.get('SES_FROM')            msg['To'] = COMMASPACE.join(os.environ.get('SES_SEND').split(","))            msg['Date'] = formatdate(localtime=True)            msg['Subject'] = "Cost Explorer Report"            text = "Find your Cost Explorer report attached\n\n"            msg.attach(MIMEText(text))            with open("cost_explorer_report.xlsx", "rb") as fil:                part = MIMEApplication(                    fil.read(),                    Name="cost_explorer_report.xlsx"                )            part['Content-Disposition'] = 'attachment; filename="%s"' % "cost_explorer_report.xlsx"            msg.attach(part)            #SES Sending            ses = boto3.client('ses', region_name=SES_REGION)            result = ses.send_raw_email(                Source=msg['From'],                Destinations=os.environ.get('SES_SEND').split(","),                RawMessage={'Data': msg.as_string()}            )     def lambda_handler(event, context):    costexplorer = CostExplorer(CurrentMonth=False)    #Default addReport has filter to remove Support / Credits / Refunds / UpfrontRI    #Overall Billing Reports    costexplorer.addReport(Name="Total", GroupBy=[],Style='Total',IncSupport=True)    costexplorer.addReport(Name="TotalChange", GroupBy=[],Style='Change')    costexplorer.addReport(Name="TotalInclCredits", GroupBy=[],Style='Total',NoCredits=False,IncSupport=True)    costexplorer.addReport(Name="TotalInclCreditsChange", GroupBy=[],Style='Change',NoCredits=False)    costexplorer.addReport(Name="Credits", GroupBy=[],Style='Total',CreditsOnly=True)    costexplorer.addReport(Name="Refunds", GroupBy=[],Style='Total',RefundOnly=True)    costexplorer.addReport(Name="RIUpfront", GroupBy=[],Style='Total',UpfrontOnly=True)    #GroupBy Reports    costexplorer.addReport(Name="Services", GroupBy=[{"Type": "DIMENSION","Key": "SERVICE"}],Style='Total',IncSupport=True)    costexplorer.addReport(Name="ServicesChange", GroupBy=[{"Type": "DIMENSION","Key": "SERVICE"}],Style='Change')    costexplorer.addReport(Name="Accounts", GroupBy=[{"Type": "DIMENSION","Key": "LINKED_ACCOUNT"}],Style='Total')    costexplorer.addReport(Name="AccountsChange", GroupBy=[{"Type": "DIMENSION","Key": "LINKED_ACCOUNT"}],Style='Change')    costexplorer.addReport(Name="Regions", GroupBy=[{"Type": "DIMENSION","Key": "REGION"}],Style='Total')    costexplorer.addReport(Name="RegionsChange", GroupBy=[{"Type": "DIMENSION","Key": "REGION"}],Style='Change')    if os.environ.get('COST_TAGS'): #Support for multiple/different Cost Allocation tags        for tagkey in os.environ.get('COST_TAGS').split(','):            tabname = tagkey.replace(":",".") #Remove special chars from Excel tabname            costexplorer.addReport(Name="{}".format(tabname)[:31], GroupBy=[{"Type": "TAG","Key": tagkey}],Style='Total')            costexplorer.addReport(Name="Change-{}".format(tabname)[:31], GroupBy=[{"Type": "TAG","Key": tagkey}],Style='Change')    #RI Reports    costexplorer.addRiReport(Name="RICoverage")    costexplorer.addRiReport(Name="RIUtilization")    costexplorer.addRiReport(Name="RIUtilizationSavings", Savings=True)    costexplorer.addRiReport(Name="RIRecommendation") #Service supported value(s): Amazon Elastic Compute Cloud - Compute, Amazon Relational Database Service    costexplorer.generateExcel()    return "Report Generated"

IAM Role

Чтобы запускаться, Lambda функция должна обладать ролью с приведенными ниже правами:

Базовая политика Lambda

{    "Version": "2012-10-17",    "Statement": [        {            "Effect": "Allow",            "Action": [                "logs:CreateLogGroup",                "logs:CreateLogStream",                "logs:PutLogEvents"            ],            "Resource": "*"        }    ]}

Разрешение для записи отчетов в S3 бакет

{    "Version": "2012-10-17",    "Statement": [        {            "Sid": "VisualEditor0",            "Effect": "Allow",            "Action": [                "s3:PutObject",                "s3:GetObject"            ],            "Resource": "arn:aws:s3:::account.admin/*"        }    ]}

Simple Email Service

{    "Version": "2012-10-17",    "Statement": [        {            "Sid": "VisualEditor0",            "Effect": "Allow",            "Action": [                "ses:SendEmail",                "ses:SendRawEmail"            ],            "Resource": "*"        }    ]}

Cost Explorer

{    "Version": "2012-10-17",    "Statement": [        {            "Sid": "VisualEditor0",            "Effect": "Allow",            "Action": "ce:*",            "Resource": "*"        }    ]}

Запуск на Event Bridge

Наконец, мы настраиваем регулярный запуск нашей Lambda функции на Event Bridge, например, 5 числа каждого месяца. В результате работы всех настроек я буду получать email с прикрепленным XLS-отчетом. Также можно настраивать срабатывание еженедельно и даже на определенные дни недели, при необходимости.

Подробнее..

Создание превью картинок в объектном хранилище с помощьюYandex Cloud Functions

17.03.2021 10:08:48 | Автор: admin

Довольно распространенная задача создание превью картинок для сайта из полноразмерных изображений. Автоматизируем этот процесс с помощью триггера для Yandex Object Storage с функцией в Yandex Cloud Functions, которую он будет запускать с наступлением определенного события в бакете в нашем случае, появлением в нем картинки. Функция сделает из нее превью и сохранит в соседний бакет. Возможна вариация сохранения превью в тот же бакет, но с другим префиксом.

Также с помощью триггера в Yandex.Cloud можно задавать действия не только для объектов в хранилище, но и для событий в очереди.

Триггер для Object Storage и Cloud Functions

Для решения этой задачи необходим настроенный бакет в Object Storage, в него будут загружаться файлы и в нем же будет лежать архив с кодом функции. Репозиторий с примером кода можно скачатьтут. После выполнения инструкций из репозитория у вас будет готовая функция, к которой необходимо подключить триггер, срабатывающий на загрузку новых файлов.

В настройках триггера задаем его параметры: название, описание и тип в нашем случае Object Storage. Также указываем бакет, в который будут загружаться картинки. Если дополнительно указать префикс и суффикс объекта, то можно обойтись одним бакетом и для исходных файлов, и для превью.

Далее указываем имя нашей функции, интервал повторных запросов и создаем триггер. Готово, теперь после загрузки картинке будет автоматически создаваться ее превью.

Превью на лету

Но у такого решения есть пара ограничений.

  1. Превью создаются только для новых изображений, если картинка уже лежала в бакете, то для нее превью не будет создано.

  2. Необходимо заранее определять высоту и ширину превью, чтобы внести изменения в соотношение сторон или размер необходимо заново масштабировать все картинки.

Воспользуемся новой возможностью Yandex.Cloud - runtime для функций C#, чтобы доработать нашу функцию на csharp.

Теперь схема будет работать так.

При первичном обращении за превью:

  1. Пользователь запрашивает картинку, указав в URL ее желаемые размеры.

  2. Объектное хранилище не находит нужного файла и согласно настроенным правилам, переадресует запрос в функцию.

  3. Функция идет за оригинальным изображением в объектное хранилище.

  4. Масштабирует его до нужных размеров и сохраняет по ключу, содержащему эти размеры.

  5. Сразу отдает готовый файл пользователю.

При последующих обращениях за файлом пользователь будет сразу получать файл из объектного хранилища без вызова функции.

Особенности технической реализации

Прописываем правила переадресации для бакета, чтобы Object Storage и Cloud Functions смогли работать в связке.

s3.putBucketWebsite({ Bucket: "%bucket_name%", WebsiteConfiguration: { IndexDocument: { Suffix: "index.html" }, RoutingRules: [ { Condition: { HttpErrorCodeReturnedEquals: "404", KeyPrefixEquals: "images/" }, Redirect: { HttpRedirectCode: "302", HostName: "functions.yandexcloud.net", Protocol: "https", ReplaceKeyPrefixWith: "%function_id%?path=", } } ] }})

Если объектное хранилище не найдет (HttpErrorCodeReturnedEquals: "404") запрошенный файл с указанным KeyPrefixEquals, то применит указанный ниже редирект. Подробнее можно посмотреть в документации к AWS S3, а скачать готовый код функции можно в репозитории тут.

В Yandex.Cloud удобно реализовано создание функций с помощью CLI. Вам не надо архивировать код и загружать его в объектное хранилище, достаточно лишь сложить все файлы в директорию и указать на нее при создании версии функции в ключе --source-path. Так же вы можете не передавать все node_modules, а загрузить только package.json и выбрать --runtime nodejs12 или --runtime nodejs14. Все зависимости будут подтянуты в момент создания версии функции.

Обращаться к фалам надо не по обычному хосту %bucket_name%.storage.yandexcloud.net, так как редиректы обрабатываться не будут, а через %bucket_name%.website.yandexcloud.net/PREFIX/%width%x%height%/%path%.

Например, при обращении к %bucket_name%.website.yandexcloud.net/images/500x500/cats/meow.png вы получите картинку которую положили в бакет %bucket_name% по ключу images/cats/meow.png но отмасштабированную до размеров 500x500px.

Чтобы не выстрелить себе в ногу

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

Так же, если у вас очень много изображений, можно задать некоторое значение TTL, создать LRU схему кэширования.

P.S.

Любые вопросы по Serverless и Yandex Cloud Functions обсуждаем у нас в Telegram: Yandex Serverless Ecosystem

Полезные ссылки:

Подробнее..
Категории: C , S3 , Csharp , Yandex.cloud , Serverless , Faas

Видео Databases Meetup Percona, Postgres Pro, Tarantool и MCS

10.07.2020 14:07:02 | Автор: admin


Всем привет! 25 июня прошел второй митап серии @Databases, организованный Mail.ru Cloud Solutions совместно с Tarantool. Переход в онлайн никого не обходит стороной, но даже на удаленке нам удалось собрать вместе более 400 участников, чтобы обсудить актуальные проблемы современных производительных баз данных.

Под катом видео выступлений: Percona о том, как собрать гибридное облако с помощью K8s, которое заменит DBaaS; Postgres Pro сразу с двумя докладами рассказали все о JSON[b] в Postgres, а также поделились стратегическими планами по развитию базы данных; а Mail.ru Cloud Solutions как S3-хранилище эволюционировало за свои три года в проде и вместе с ним менялся подход к Tarantool в его архитектуре.

Как собрать гибридное облако с помощью Kubernetes, которое может заменить DBaaS. Петр Зайцев, генеральный директор Percona



Модель DBaaS для баз данных предпочтительна для многих компаний из-за простоты и удобства. Обычно DBaaS создает вендор-лок и приводит к росту дополнительных расходов, превышающих собственные затраты на инфраструктуру. Это существенный недостаток этой модели.

Петр покажет, как с Kubernetes вы можете собрать гибридное облако, которое заменит DBaaS. Kubernetes позволяет построить полностью OpenSource DBaaS-подобное решение, которое может быть развернуто в любом месте, где работает Kubernetes в общедоступном облаке и в вашем частном дата-центре без привязки к вендору.

Архитектура S3: 3 года эволюции Mail.ru Cloud Storage. Владимир Перепелица, архитектор платформы Mail.ru Cloud Solutions



За три года жизни S3-хранилище MCS выросло до кластера в десятки терабайт в RAM, где лежат метаданные о миллиардах пользовательских объектов. Архитектура такой облачной базы данных требует нетривиального подхода. Например, для нее критически важно уметь линейно масштабироваться.

Владимир расскажет вам историю формирования архитектуры нашего решения, о том, как реализован шардинг, как выполнялась миграция данных без даунтайма и какую важную роль в развитии S3 сыграло использование базы данных Tarantool.

JSON[b] в Postgres: Пора великого объединения. Олег Бартунов, генеральный директор, сооснователь Postgres Professional



Postgres первая значимая реляционная СУБД, в которой появилась поддержка слабоструктурированных данных, в частности, Json. Генеральный директор компании Postgres Professional расскажет про историю создания Json, особенности его реализаций, а также планы по его развитию, включая соответствие SQL-стандарту, улучшение функциональности и производительности.

Эволюция Postgres Pro за годы существования и стратегические планы по развитию. Иван Панченко, сооснователь и заместитель генерального директора Postgres Professional



Postgres Pro коммерческий форк Postgres, выпускающийся компанией Postgres Professional. Форки, в том числе коммерческие, являются важной частью мировой экосистемы Postgres, поставляя в общий котёл передовые разработки. Сооснователь компании Postgres Professional расскажет о том, почему компания пришла к необходимости выпустить свой форк Postgres, каковы основные отличия Postgres Pro от PostgreSQL, а также поделится планами по развитию продукта.

Stay tuned


Следите за анонсами мероприятий Mail.ru Cloud Solutions в нашем канале Telegram: t.me/k8s_mail

А если хотите быть спикером событий серии @Meetup, оставьте заявку на выступление по ссылке: https://mcs.mail.ru/speak
Подробнее..

Архитектура S3 3 года эволюции Mail.ru Cloud Storage

06.08.2020 20:05:17 | Автор: admin


Storage Corridor by St-Pete


Всем привет! Я Mons Anderson, архитектор платформы Mail.ru Cloud Solutions, расскажу, как мы построили наше S3-хранилище, как оно работает, какие решения оказались удачными, а какие стоило изменить, если бы мы начали такой же проект с нуля сейчас.


Статья подготовлена на основе доклада на @Databases Meetup by Mail.ru Cloud Solutions & Tarantool. В статье поговорим:


  • как было устроено хранилище Mail.ru, поверх которого мы строили S3-хранилище;
  • что мы добавили, чтобы сделать Mail.ru Cloud Storage;
  • как работает объектная модель хранения и какие сделаны шаги для выхода в продакшен;
  • про доработки боевой системы: фейловер и масштабирование;
  • как мы реализовали шардирование и решардинг;
  • а также про работу с SSL-сертификатами.

Если не хотите читать, можно посмотреть.


Как было устроено хранилище Mail.ru, поверх которого мы строили S3-хранилище


Разработка нашего S3 началась поверх хранилища Облака Mail.ru, поэтому изначально стоит рассказать, как оно устроено и что умеет.


Хранилище облака Mail.ru состоит из серверов с дисками. В среднем современный storage-сервер это 36 дисков по 1214 терабайт. Раньше диски были меньше, но за три года объемы дисков выросли и сегодня это почти полпетабайта сырых данных.


Диски с разных сервенов хранилища объединяются в так называемые пары (pair). Пара это единичный unit хранения файлов. По сути, это диск, смонтированный в определенный раздел по определенному пути, где могут лежать файлы, идентифицированные хэшами.


Пара это историческое название, оно сохранилось до сегодняшнего дня, хотя сейчас в паре не обязательно только два диска. Дисков может быть три, а также могут быть различные гибридные хранения, например 3/2.



Пары (pair) единицы хранения объектов


Все пары хранятся в PairDB это приложение на базе Tarantool. Все базы в нашем хранилище, начиная с самых первых, это Tarantool, других баз мы не используем.


PairDB хранит все пары, их состояния, свободное место, возможности отказа, последние ошибки. Также она может сама ходить на пары, актуализировать их состояние, проверять, работают они или нет. То есть PairDB такой общий снимок состояния всех дисков нашей системы.



Pair DB: база данных с состоянием пар


На парах хранятся файлы, и, чтобы знать, на какой паре какой файл находится, нужна еще одна база FileDB. Она хранит маппинг, определение соответствия: файл такой-то хранится на паре такой-то, а также небольшое количество нужных атрибутов.


File DB: место, где хранится файл

Еще одно важное звено сервис Nylon, роутер для работы с базами данных. Он является единой точкой входа, позволяет работать через единый интерфейс как с PairDB, так и с FileDB. Это stateless-сервис, он выполняет балансировку запросов, понимает, на какой шард FileDB нужно идти, знает, какие пары активны, какие нет.



Nylon: роутер для работы с базами данных


Также в хранилище нужно как-то помещать контент. Для этого есть сервис Streamer. Он предоставляет два HTTP-метода: метод PUT, чтобы заливать контент в хранилище, и метод GET, чтобы забирать его оттуда. HTTP довольно популярный и удобный протокол для передачи данный.


Когда мы обращаемся к Streamer'y, он через Nylon обращается к PairDB, выясняет, на какую пару можно залить файл, после чего передает данные по WebDAV на эту пару.


По сути, любой storage сервер это nginx плюс диски, которые смонтированы по заданным путям. Мы можем из Streamer залить в хранилище файл, удалить его, переименовать или проверить на целостность. То есть это удобный интерфейс для низкоуровневого взаимодействия с хранилищем.



Streamer: точка входа в хранилище


Что мы добавили, чтобы сделать S3-хранилище


Итак, мы рассмотрели общее базовое устройство хранилища на момент, когда мы собрались запускать S3-хранилище. При помощи метода PUT мы могли поместить туда произвольный контент и получить в качестве идентификатора этих данных хэш. С этим идентификатором впоследствии можно было прийти и забрать исходный файл. Но этого недостаточно для реализации S3. В протоколе S3, помимо самого хранения объектов, есть:


  • хранение метаданных дополнительных свойств объектов;
  • организация доступа к объектам посредством HTTP;
  • группировка объектов в коллекции бакеты;
  • HTTP-S3 Endpoint. S3 организует данные в определенные структуры бакеты, каждый из которых предоставляет точку входа для хранения файлов.

Для реализации этой логики был нужен отдельный сервис. Также хотелось сразу предусмотреть архитектуру для дальнейшего роста сервиса с линейной масштабируемостью.


Первые компоненты


Демон, реализующий S3 API. Это стандартный S3 API Amazon, который поддерживает работу по XML для метаданных и позволяет непосредственно передавать контент. Нам не пришлось что-то изобретать, всё описано и задокументировано.


Также перед сервисом мы поставили Nginx. Его мы использовали для терминации SSL, балансировки нарузки, а также для некоторой логики на Lua (метрики, логгирование и трейсинг).


Для хранения метаданных S3 мы также выбрали Tarantool. В первой версии за метаданными демон S3 ходил в эту базу, сам контент хранил в большом хранилище через Streamer.


Nginx + S3 API + метаданные
Nginx + S3 API + метаданные


Объектная модель хранения


Посмотрим, как работает S3. Пользователь может создать бакет коллекцию объектов. Бакет адресуется именем хоста и является поддоменом сервиса. В рамках бакета пользователь может создавать объекты. Идентификатором объекта будет URL. Содержимое объекта blob, массив двоичных данных, который мы будем хранить в хранилище. Также у объекта есть атрибуты: имя тот самый URL, ACL (список управления доступом), другие дополнительные или произвольные атрибуты всё это сохраняется в метаданных.


Нормализованная схема этих данных может выглядеть так: есть проекты, которым принадлежат бакеты, которым принадлежат объекты и объекты могут быть составные. Поскольку одним из способов объект можно загружать по частям, для загрузки есть две вспомогательные таблицы: uploads и chunks. Также у проектов есть учётные данные для доступа и биллинг.


Схема данных
Схема данных


Поскольку мы делали b2b-сервис с платным доступом, то в этой схеме нужен был биллинг.
Сервис биллинга мы также реализовали на Tarantool.


Биллинг


Доработки S3-хранилища: шаги к продакшену


Мы уже сделали работающую модель, которую можно использовать: объекты и метаданные хранились, но для выхода в продакшн не хватало нескольких моментов.


Во первых система рейт-лимитов. Если запустить сервис без нее, то при пиковой нагрузке мы могли непредсказуемо перегрузить какой-либо участок системы. Рейт-лимит должен работать так: любой S3-запрос приходит на конкретный хост, этот хост является идентификатором бакета, а бакет принадлежит какому-то клиенту. Нам нужно определить некоторую функцию от бакета, которая позволила бы рассчитать рейт-лимит.


Кроме того, система рейт-лимитов должна быть достаточно производительной, чтобы выдерживать нагрузку, которая приходит на S3.


Тут мы снова использовали Tarantool. Рейт-лимиты это кластер из 21 инстанса, инстансы разбиты на группы, разнесены на три физических узла и объединены в большой топологический кластер. По нему автоматически распространяются конфигурационные изменения: задаются рейт-лимиты, дефолты и конфигурация. Каждый бакет обслуживается строго одним инстансом. Когда на конкретный бакет приходит запрос, для него вычисляется инстанс, отвечающий за этот бакет. В рамках этого узла производится подсчет текущего рейта обращений по алгоритму, похожему на Token Bucket. Далее система рейт-лимитов, на основании текущих показателей нагрузки и свойств, установленных для конкретного бакета, говорит, можно ли выполнить запрос или нет. Проверка лимитов выполняется на самой ранней стадии выполнения S3-запроса, защищая все остальные элементы системы от чрезмерной нагрузки.


Система рейт-лимитов


Также под нагрузкой довольно сложно обойтись без кеша. В S3 подразумевается многократное обращение к одним и тем же объектам, то есть это горячее хранилище. В обычном случае обращение к единичному файлу обслуживается полной цепочкой: Streamer, FileDB, PairDB, Storage. Но при многократном обращении к файлы мы оптимизируем доступ к этому контенты при помоди локального кэша.


Кэш многослойный и реализован при помощи nginx, локальных, SSD и RAM-дисков. Тут мы не использовали Tarantool, потому что отдавать объекты удобнее из файловой системы, так мы можем сделать тиринг кэша. Кроме того, у нас крупные объекты с максимальным размером в 32 гигабайта, а в Tarantool кэшировать можно только маленькие объекты.


Кэш данных


Эта первая система, с которой мы запустились, обладала некоторой рассчитанной capacity (емкостью), ее хватило для исследования, понимания продукта, того, что он будет работать.


Доработки боевой системы: фейловер и масштабирование


Система была уже в бою, при этом на старте мы кое-что упустили нужно было добавить фейловер и масштабирование.


Наш S3-демон ходил за метаданными по протоколу Tarantool. На место оригинальной базы мы поставили Tarantool, который выступал в роли прокси-роутера запросов за метаданными. С точки зрения приложения, реализующего API, ничего не поменялось оно продолжило обращаться к базе по протоколу Tarantool, но роутер смог обеспечить активный фейловер. То есть мы смогли проверять доступность узла, выдерживать паузу при переключениях и отказах и так далее. При этом само приложение мы не модифицировали.


Активный фейловер


Подробнее о том, как мы реализовали шардирование


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


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


Вынесли объекты, которые нет смысла шардировать


Также в схеме есть объекты, которые линейно растут сначала их было сотни тысяч, сейчас их количество измеряется несколькими миллиардами. Такие объекты вместе с их частями надо было унести в шардированный кластер.


Растущие объекты унесли в шардированный кластер


Схему мы разделили, но объектам нужно работать с бакетами: объект всегда принадлежит конкретному бакету, плюс на бакете работает ACL. Поэтому для каждого шарда с объектами мы держим теневую копию каждого бакета. Кроме того, во время модификации объектов и выполнения запросов, нужно считать объем для выполнения биллинга, поэтому на каждом шарде есть счетчики от биллинга.


Также мы добавили еще несколько таблиц и компонент:


  • корзину, для уничтожения старых проектов, которые удаляются или замораживаются;
  • очередь для фоновых задач, то есть основное хранилище может выполнять фоновые задачи, которые необходимо делать на кластере;
  • поддержку lifecycle механизма, который позволяет работать с объектами, управлять их жизненным циклом.

Итоговая шардированная схема


Так как часть данных мы унесли на шарды, понадобилась шардирующая прокси. Можно было бы переиспользовать роутер под эту роль, но отдельная шардирующая прокси, отвечающая только за шардирование данных, позволяет из роутера ходить за данными целиком, не думая о шардинге.


Шардирующая прокси


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


Посмотрим, как она устроена. У нас есть 256 доступных шардов. Мы для каждого бакета выделяем диапазон при помощи некой консистентной функции. Это просто так же, как вы при помощи консистентной функции определяете принадлежность к одному шарду, вы определяете стартовый шард и выделяете диапазон:


f(bucket, shards) = subset


То есть если взять бакет, можно сказать, что он и его данные будут всегда лежать на конкретном подмножестве из всех шардов. Это позволяет снизить влияние одних бакетов на другие и упростить работу map-reduce запросов, когда нужно, например, сделать листинг объектов бакета. Для этого надо опросить все шарды, где эти объекты хранятся. Если бы объекты лежали на всех шардах, любой листинг задевал бы систему целиком, а здесь он задевает только конкретное подмножество.


Далее каждый объект принадлежит конкретному бакету, поэтому когда мы обращаемся за объектом, то обращаемся за объектом по имени в конкретном бакете. То есть можем определить функцию для объекта не из всего доступного диапазона шардов, а только из подмножества его бакета:


f(object, subset) = shard


Мы берем конкретный объект, в качестве аргументов функции передаем туда не все шарды, а подмножество его бакета и получаем конкретный шард.


Схема шардирования бакетов


Итак, шардинг реализован, есть шардирующий прокси. Дальше осталось из роутера и базы с метаданными ходить в шардирующий прокси. Например, для создания объектов теневых копий когда мы создаем бакет, основное хранилище должно создать представителя этого бакета на всех шардах, где он должен присутствовать.


Итоговая схема шардирования


Как мы реализовали решардинг


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


Ниже схема нашего кластера, которая получилась после внедрения шардинга. У нас есть nginx, S3 API, роутер, primary-база с проектами, шардирующий прокси и непосредственно шарды


hotbox


Выше я упустил момент, что на определенном этапе проекта была продуктовая задача: Запустить ещё одно хранилище, Айсбокс, как Хотбокс, только для холодных данных. По сути, такое же хранилище, но по другим URL и без кэшей.


Горячее и холодное хранилища


Icebox использовался меньше, чем Hotbox, поэтому он довольно долго обходился без какого-либо шардирования. В итоге мы решили отказаться от него и объединить Hotbox и Icebox в один сервис, просто разделив классы хранения.


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


Для начала мы синхронизировали primary-хранилища. У нас был Tarantool и мы могли при создании объекта сделать так:


  • к базе приходит запрос на создание бакета, например в Hotbox;
  • Tarantool проверяет в другой базе (в этом случае в Icebox), что такого бакета нет;
  • если бакет есть, база говорит, что его создавать нельзя, и он синхронизировался как существующий.
    Синхронизация бакетов
    Синхронизация бакетов

В том хранилище, которое должно было принимать в себя все данные, ввели для проектов и бакетов признак, говорящий, где хранится этот объект. Он мог храниться локально, то есть в Hotbox, в Icebox тогда в новом хранилище от него нет никаких данных, либо мог быть в состоянии миграции.


Если у проекта или бакета стоял признак Migrating, то во время миграции запрос выполнялся сначала в новое хранилище, в котором данные должны находиться, а если их там не было, то запросы пенаправлялись на альтернативное хранилище.


Далее мы переключили трафик. Поскольку API мог обслуживать и запросы Icebox, и запросы Hotbox, мы смогли без даунтайма переключить трафик, просто перенеся хосты и добавив соответствующие записи в Nginx.


После того, как трафик был перенаправлен, Nginx и API от Icebox можно было убрать.
Затем мы убрали Icebox nginx и S3 API и всё заработало:


Переключение входящих запросов


Далее мы запустили фоновый процесс миграции, который работает внутри базы обходит поэлементно все проекты и их бакеты, выставляет для них признак Migrating, переносит данные и по завершению переноса выставляет признак Local.


Миграция данных


После переноса данных нам больше не нужно старое хранилище, и мы убираем оставшиеся части старой системы, а также убираем из кода поддержку статуса миграции.


Очистка Legacy


По тем же принципам был выполнен и решардинг из старого хранилища в шардированное:


  • Пометили все бакеты как Non-sharded. Все запросы к ним шли в оригинальное, нешардированное хранилище.
  • Новые бакеты создавались сразу в статусе Sharded.
  • По одному брали бакеты, ставили статус Migrating и переносили данные.

Запросы при этом обслуживались по принципу:


  • Читаем в новом, потом в старом.
  • Создаём только в новом.
  • Обновляем двухфазно: если в новом нет, переносим из старого в новый, потом обновляем.

Работа с SSL-сертификатами


На фронтенде мы используем Nginx. В нашем случае это не обычный Nginx, а OpenResty, Nginx с поддержкой LuaJIT.

Еще один кусочек системы работа с SSL-сертификатами. В S3-хранилище вы можете установить собственный домен для доступа к конкретному бакету, просто при помощи CNAME. Но без HTTPS на сегодняшний день нельзя: собственный домен подразумевает собственный SSL-сертификат.


Как я уже говорил, за балансировку и терминацию SSL у нас отвечает Nginx. В нашем случае это не обычный Nginx, а OpenResty, Nginx с поддержкой LuaJIT.


Это позволило нам довольно просто научить наш Nginx отдавать произвольные сертификаты. Причём нам было необходимо отдавать сертификаты динамически (без необходимости прописывать их в файл конфигурации). Мы воспользовались расширением ssl_certificate_by_lua, которое позволяет прочесть сертификат из произвольного источника непосредственно во время TLS-хэндшейка. В качестве хранилища сертификатов мы также взяли Tarantool: это позволяет управлять сертификатами извне и обеспечивает крайне быструю отдачу.


Также был реализован отдельный демон, в задачу которого входит регулярное обновление сертификатов, которые были выписаны при помощи Let's Encrypt.


Хранилище SSL сертификатов


Что бы я сохранил, а что сделал по-другому, если разрабатывать хранилище заново


Что нужно было использовать с самого начала


Шардинг сразу. Довольно много проблем доставил решардинг. Его легко сделать, но, тем не менее, если начинать проекты, которые нужно масштабировать, лучше сразу брать шардированный кластер, пусть даже и на минимуме узлов. Реализация шардинга на старте почти бесплатная в сравнении с внедрением шардинга в рабочую систему.


Работа с Tarantool через балансировщики. Сейчас мы все новые базы сразу подключаем в работу через балансировщики. Это позволяет расширять функциональность, добиваться более высокой отказоустойчивости.


Автофейловер. Установил бы все инструменты, которые требуются для автофейловера, так как первые неудачи после запуска были связаны с его отсутствием. После опыта с S3 все последующие продукты запускались с учётом этого.


Фича S3 Версионирование. Изначально показалось, что это не очень востребованная функциональность. Встраивать эту возможность в архитектуру работающей системы крайне сложно.


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


Что было удачным решением


Модель данных. История показала, что по мере развития сервиса мы довольно точно совпадаем с моделью данных Amazon, поэтому можем реализовывать те фичи, которые есть там.


Схема шардирования. Я бы поддержал такие же диапазонные шардинги по бакетам, поскольку это позволяет хорошо распределять запросы от разных бакетов по большому кластеру.


Использование Tarantool. Tarantool сильно помог при развитии сервиса и его модификации, мы легко работали с данными, трансформировали и шардировали хранилище, при этом не понадобилось подниматься на слой приложения.


Этот доклад впервые прозвучал на @Databases Meetup by Mail.ru Cloud Solutions&Tarantool. Смотрите видео других выступлений и подписывайтесь на анонсы мероприятий в Telegram Вокруг Kubernetes в Mail.ru Group.

Также вы можете, посмотреть мой старый доклад про S3 или почитать статью моего коллеги про блочное хранилище.


Подробнее..

Как использовать объектное S3-хранилище Mail.ru Cloud Solutions для хранения бэкапов Veeam

07.12.2020 18:15:24 | Автор: admin
LogiMap ASRS Unit by Vidom

Veeam Backup & Replication коммерческая платформа для резервного копирования и управления данными облачной, виртуальной и физической среды. Она поддерживает разные сценарии хранения данных, в том числе использование S3-совместимых объектных хранилищ для хранения бэкапов, об одном из которых они сами недавно писали: про подключение к хранилищу MinIo.

Между тем, есть смысл использовать S3 в виде готового облачного сервиса, когда вы не хотите сами заниматься вопросами обслуживания и масштабирования хранилища. Сервис-провайдер даёт дополнительные функциональности (надёжность, безопасность и так далее) по стоимости ниже, чем большинство собственных инсталляций, за счёт массовости предоставления услуги.

Я покажу подключение Veeam к облачному объектному S3-хранилищу на примере Mail.ru Cloud Storage. Потому что я из этой команды :) Так что буду рад обратной связи по этому сценарию и самому сервису в комментариях.

Выбор в сторону объектного хранилища S3 в качестве хранилища бэкапов был вполне очевидным, так как:

  1. Стандарт де-факто.
  2. Многие производители уже реализовали в своих системах резервного копирования поддержку S3.
  3. Надежность.
  4. Низкая стоимость.

Тестовый стенд


Тестовый стенд:

  1. Виртуальная машина с Windows (установлена Windows-Server-2019Std-ru.202006) для установки Veeam.
  2. Виртуальная машина с Ubuntu 20.04 в качестве инстанса для бэкапа.
  3. Настроенный доступ и бакет в хранилище MCS S3.

Установку Veeam опустим, она хорошо описана в официальной документации. Я использовал для установки версию Veeam Backup & Replication Community Edition, с выписанной пробной лицензией на месяц. Community Edition не поддерживает создание Scale-Out Backup Repository.

Далее перейдем к настройке.

Подключение Object Storage


  1. Нажмите на Backup Repositories, далее правой кнопкой мыши вызовите Add Backup Repository:


  2. Выберите Object Storage:


  3. Выберите S3 Compatible:


  4. Введите имя хранилища и нажмите Next:


  5. В качестве Service point введите hb.bizmrg.com, регион default. Напротив Credentials нажмите Add:


  6. Введите свой Access Key ID и Secret Access Key для доступа к S3 MCS, нажмите OK:


  7. Выберите бакет и нажмите Browse напротив Folder для выбора папки, где в бакете будут храниться бэкапы:


  8. Выберите существующую папку или создайте новую и нажмите ОК:


  9. Если необходимо установить лимит места, которое могут занимать бэкапы, поставьте галочку Limit object storage consumption и установите лимит. Галочку immutable не ставьте, MCS S3 пока не поддерживает эту функцию. Нажмите Next:


  10. Создание объектного стораджа завершено:



Подключение Scale Out Backup Repository (SOBR)


Логика работы Veeam такова, что использовать напрямую объектное хранилище S3 нельзя, необходимо собрать расширяемое хранилище, состоящее из локального репозитория бэкапов и хранилища S3, куда будут переноситься бэкапы из локального хранилища, в соответствии с настроенной политикой переноса. Обратите внимание, что SOBR-хранилище недоступно в Community-версии приложения, необходима лицензия.

  1. Выберите в левой колонке Scale-Out Repositories и нажмите в поле справа правую кнопку мыши. Выберите Add scale-out backup repository:


  2. Введите имя репозитория и нажмите Next:


  3. Нажмите Add и добавьте локальное хранилище:


  4. Выберите политику размещения бэкапов либо хранение полных и инкрементальных бэкапов вместе, либо разнесение их по хранилищам. Нажмите Next:


  5. Поставьте галочку Extend scale-out backup repository capacity with object storage и добавьте созданное ранее объектное хранилище. Ниже настраивается политика работы как видно на скриншоте, у нас настроено сразу же переносить вновь созданные бэкапы в S3, а также переносить уже существующие бэкапы старше 7 дней. Нажмите Apply:


  6. SOBR-хранилище успешно создано:



Настройка бэкапа Linux-сервера


Настроим бэкап тестового сервера Linux. Для бэкапа Veeam должен установить свой агент на Linux-сервер.

  1. Добавим данные сервера Linux, который мы хотим бекапить. Нажмите в нижнем левом углу экрана на Home, в верхнем левом углу на Backup Job и выберите Linux computer:


  2. Выберите Server и Managed by backup server. Настраивать задания можно как централизованно, так и на серверах, которые мы хотим бэкапить, в настройках Veeam-агента. В данном случае настроим управление со стороны сервера Veeam:


  3. Выберите название нового задания и нажмите Next:


  4. Нажмите на Add и выберите Individual computer:


  5. Введите имя или IP-адрес Linux-сервера, который будем бэкапить, напротив Credentials нажмите Add. В выпадающем меню выберите тип авторизации по паролю или по ключу. В нашем случае по ключу:


  6. Введите имя пользователя, выберите на диске предварительно сохраненный файл с приватным ключом к серверу. Приватный ключ формируется при заказе инстанса в панели управления MCS. Установите галочку Elevate account privileges automatically, нажмите OK:


  7. Выберите, что бэкапить в данном случае весь сервер. Нажмите Next:


  8. Выберите хранилище, куда будут помещены бэкапы. В нашем случае настроенное ранее Scale-Out Backup Repository. Нажмите Next:


  9. Настройте расписание запуска и нажмите Apply:


  10. Если необходим немедленный запуск задания, установите галочку Run the job when I click Finish. Нажмите Finish:



После окончания настройки Veeam сразу запустит проверку наличия агента на сервере, который мы добавили и установит его при необходимости. После этого начнется первый бэкап.

Прогресс бэкапа можно увидеть, если нажать на Last 24 Hours/Running и выбрать созданное нами бэкап-задание Ubuntu1:



После окончания бэкапа в web-интерфейсе S3 MCS можно увидеть, что Veeam создал папку с бэкапом сервера:



На этом настройка закончена, удачи!

В нашем телеграм-канале новости об этом и других сервисах на облачной платформе Mail.ru Cloud Solutions.

Еще про использование S3-хранилища:

Подробнее..

Из песочницы Как разместить статический сайт с помощью Yandex.Cloud Object Storage

20.07.2020 12:12:02 | Автор: admin
Привет, Хабр!

В этой статье, я расскажу как легко и просто разместить статический сайт с помощью технологий Яндекса, а именно Object Storage.


В конце у вас будет размещенный в сети сайт, который будет доступен по внешней ссылке.


Эта статья будет полезна, если вы


  • Начинающий разработчик, который только обучается программированию;
  • Разработчик, который сделал портфолио и хочет разместить его в открытом доступе, чтобы показать друзьям и работодателям.

О себе


Недавно, я разрабатывал SaaS сервис, подобие маркетплейса, где люди находят спортивных тренеров для персональных тренировок. Использовал стек Amazon Web Services (далее AWS). Но чем глубже погружался в проект тем больше нюансов узнавал о разных процессах организации стартапа.


Я столкнулся с следующими проблемами:


  • AWS потреблял много денег. Поработав 3 года в Enterprise компаниях, я привык к таким радостям, как Docker, Kubernetes, CI/CD, blue green deployment, и, как начинающий программист-стартапер, захотел реализовать тоже самое. В итоге пришел к тому, что ежемесячно AWS потреблял по 300-400 баксов. Самым дорогим оказался Kubernetes, около 100 баксов, при минималке с одним кластером и одной нодой.
    P.S. На старте не нужно так делать.
  • Далее, задумавшись о юридической стороне, я узнал про закон 152-ФЗ, в котором говорилось примерно следующее: "Персональные данные граждан РФ должны храниться на территории РФ", иначе штрафы, чего мне не хотелось. Я решил заняться этими вопросами, пока "сверху" мне не прилетело :).

Вдохновленный статьей о мигрировании инфраструктуры из Amazon Web Services в Яндекс.Облако, я решил изучить стек Яндекса подробнее.


Для меня ключевыми особенностями Яндекс.Облака было следующее:



Я изучал других конкурентов этого сервиса, но на тот момент Яндекс выигрывал.


О себе рассказал, можно и перейти к делу.


Шаг 0. Подготовим сайт


Для начала нам понадобится сайт, который мы хотим разместить в интернете. Так как я Angular разработчик, я сделаю простой шаблон SPA приложения, который далее размещу в интернете.


P.S. Кто разбирается в Angular или знает про его документацию https://angular.io/guide/setup-local, переходите к Шагу 1.


Установим Angular-CLI чтобы создавать SPA-сайты на Ангуляре:


npm install -g @angular/cli

Создадим Angular приложение с помощью следующей команды:


ng new angular-habr-object-storage

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


cd angular-habr-object-storageng serve --open

Статическое SPA-приложение на Angular


Приложение создано, но пока не готово к размещению на хостинге. Соберем приложение в небольшой билд (Production), чтобы убрать все лишнее и оставить только необходимые файлы.
В ангуляре это можно сделать следующей командой:


ng build --prod

В результате этой команды в корне приложения появилась папочка dist с нашим сайтом.


Работает. Теперь переходим к хостингу.



Шаг 1.


Переходим на сайт https://console.cloud.yandex.ru/ и жмем на кнопку "Подключиться".


Примечание:


  • Для пользования сервисом Яндекса может понадобится почта Яндекса (но это не точно)
  • Для некоторых функций придется положить деньги на счет в личном кабинете (минимум 500 рублей).

После успешной регистрации и авторизации, мы в личном кабинете.


Интерфейс личного кабинета Yandex.Cloud


Далее слева в меню нужно найти сервис "Object Storage", который мы как раз будем использовать для хостинга сайта.


Коротко по терминам:


  • Object Storage это хранилище файлов, совместимое с аналогичной технологией Амазона AWS S3, у которого также есть свой API для управления хранилищем из кода и его также как и AWS S3 можно использовать для размещения статического сайта.
  • В Object Storage мы создаем "бакеты" (bucket / Корзина), которые являются отдельными хранилищами наших файлов.

Интерфейс сервиса Yandex.Cloud Object Storage


Создадим один из них. Для этого в консоли сервиса жмем на кнопку "Создать бакет".


Интрфейс создания бакета в Yandex.Cloud


В форме создания бакета есть следующие поля, пробежимся по ним:


  • Имя бакета. Для простоты, назовем так же как и ангуляр проект angular-habr-object-storage
  • Макс. размер. Ставим столько, сколько у нас весит сайт, так как сайт хранится не бесплатно и за каждый выделенный гигабайт, мы будем платить Яндексу копеечку.
  • Доступ для чтения объектов. Ставим "Публичный", так как пользователь должен получать каждый файл нашего статического сайта, чтобы на нем правильно отрисовывалась верстка, отрабатывали скрипты и тд.
  • Доступ к списку объектов и Доступ на чтение настроек. Оставляем "Ограниченный". Это нужно для того, чтобы использовать бакет как внутреннее хранилище файлов для приложений.
  • Класс хранилища. Оставляем "Стандартный". Это означает, что наш сайт часто будут посещать, а значит и часто скачивать файлы, составляющие сайт. Плюс пункт влияет на производительность и оплату (вставить ссылку).

Жмем "Создать бакет" и бакет создан.


Yandex.Cloud Бакет создан


Теперь нужно загрузить наш сайт в бакет. Самый простой способ открыть рядом папочку dist нашего сайта и ручками перетащить прямо на страницу. Это удобнее, чем жать на кнопку "Загрузить объекты", потому что в таком случае папки не переносятся и их придется создавать ручками в правильной последовательности.


Загрузили в бакет наш сайт


Итак, сайт загружен в хранилище, тем можем предоставить пользователям возможность обращаться к хранилищу, как к сайту.
Для этого слева в меню жмем на вкладку "Веб-сайт".


Настройка бакета под сайт


На странице настройки бакета как сайта, выбираем таб "Хостинг". Здесь указываем главную страницу сайта, обычно это index.html. Если у вас SPA приложение, то вероятно все ошибки обрабатываются также на главной странице, поэтому укажем на странице ошибки также index.html.


Мы сразу видим, по какой ссылке будет доступен наш сайт. Жмем сохранить.


Через минут 5, перейдя по ссылке мы видим что теперь наш сайт доступен всем желающим.


Хостинг Angular приложения с помощью Yandex.Cloud Object Storage


Спасибо всем кто дочитал до конца! Это моя первая статья, планирую дальше описать другие сервисы Яндекса и их интеграцию с frontend и backend технологиями.


Напишите в комментариях насколько интересно вам узнать про другие сервисы Яндекса или про использование Angular в современной разработке.

Подробнее..

Из песочницы AWS сколько нужно сервисов, чтобы поднять веб-приложение?

03.09.2020 16:16:34 | Автор: admin

Так получилось, что до 2020 года я не имел дело с облаками. Когда же, наконец, углубился в эту тему, то немного потерялся от обилия сервисов, предлагаемых в AWS.


Необходимо было создать приложение с такими фичами:


  • Авторизацией через facebook или google.
  • Возможностью загрузки и отображения медиа-файлов.
  • Получением событий с сервера в реальном времени.

В этой статье описаны сервисы, которые понадобились мне для реализации проекта и ощущения от процесса.
image


Архитектура приложения


Веб-сервис


Бизнес-логика нашего приложения реализована в виде веб-сервиса и помещена в докер-контейнер. Для запуска контейнеров в AWS можно использовать два сервиса Fargate и Elastic Beanstalk.


Fargate


Модный PaaS поверх ECS или EKS (EKS-Elastic Kubernetes Service, ECS-Elastic Container Service проприетарная альтернатива kubernetes). Конфигурация выполняется путем описания высокоуровневой абстракции задачи (Task), в которой указывается необходимое количество вычислительных ресурсов для ваших контейнеров.


Elastic Beanstalk


Появился задолго до Fargate. Позволяет запустить приложение на виртуалках (EC2). В том числе есть конфигурация и для старта докер-контейнера. Из недостатков сложен в конфигурировании, виртуалки медленно создаются. Из преимуществ цена. Elastic Beanstalk значительно дешевле при небольших средних нагрузках.


EC2 Базовая утилизация процессора Цена за час Fargate Цена за час Отношение цен
t2.micro 10% $0.0134 1vCPU,1GB $0.05167 3.85
t2.medium 20% $0.0536 2vCPU,4GB $0.11356 2.12
t2.xlarge 22.5% $0.2144 4vCPU,16GB $0.268 1.25

Еще один небольшой плюс Elastic Beanstalk возможность мониторить сетевой трафик виртуалок. В Fargate я так и не разобрался как это сделать.


Application Load Balancer


И Fargate, и Elastic Beanstalk предусматривают возможность горизонтального масштабирования. В обоих случаях балансировщик нагрузки конфигурируется автоматически. Предполагается, что вы не меняете настройки балансировщика напрямую. В Fargate масштабирование описывается в Task Definition, Elastic Beanstalk создает Auto Scaling Group.


Application Load Balancer работает на уровне HTTP. В принципе он может обрабатывать и HTTPS, но, так как он находится за CloudFront, то нам это не требуется. От ALB на инстансы идет уже только HTTP.


Состояние


Для хранения состояния используются три сервиса.


DynamoDB


NoSql база данных. Работает очень быстро (в пределах 1020 миллисекунд на запрос). Поддерживает условные обновления, что позволяет организовать оптимистическую транзакционность.


S3


Хранилище файлов. Практически безразмерное. Позволяет открыть прямой доступ из интернета. Таким образом пользователи могут заливать и скачивать контент без использования вычислительных ресурсов сервиса нашего приложения.


Parameters Store


Хранилище настроек сервиса. Позволяет хранить ключи в зашифрованном виде.


Фасад


Технически, в облаке каждый сервис выставлен в интернет, так что, при наличии прав, клиент может обращаться к нему напрямую. Например, в нашем приложении, клиент напрямую заливает медиа-файлы в S3.


В остальных же случаях взаимодействие клиента с бэкендом идет через описанные ниже сервисы.


Route53


DNS от AWS.


CloudFront


CDN от AWS. В настройках CloudFront задаются правила по перенаправлению запросов к медиа-контенту на S3, а api-вызовов на наш веб-сервис. Также здесь можно настроить редирект с Http на Https (понадобится отдельный публичный S3 bucket с правилом редиректа).


AppSync


AppSync часть большой перспективной концепции AWS Amplify. Позволяет быстро разрабатывать мобильные приложения с serverless-бэкендом или даже no-code-бэкендом. Наше приложение не такое модное, но мы не могли пройти мимо AppSync, так это чуть ли не единственный сервис AWS, позволяющий публиковать и подписываться на сообщения.


Cognito


Отвечает в aws за регистрацию и авторизацию пользователей. Позволяет создать User Pool c привязкой к аккаунтам в Google, Amazon, Facebook и не только.


DevOps сервисы


Сервисы для базовых девелоперских операций представлены на следующей схеме.
image
Управление доступом к сервисам и ресурсам осуществляется с помощью IAM-Identity and Access Management.


Для автоматизации развертывания ресурсов используется CloudFormation, как с помощью шаблонов, так и из программного кода, с помощью SDK. Единица управления ресурсами называется стеком.


Конвейер обновления версий кода создается с помощью группы сервисов:


  • CodeCommit менеджер git-репозиториев, может быть безболезненно заменен на github.
  • CodeBuild сборка и публикация артефактов. Докеровские образы сохраняются в ECR-Elastic Container Repository.
  • CodeDeploy поставка, в нашем случае в Fargate или Elastic Beanstalk.
  • CodePipeline оркестрация конвейера.

Все логи накапливаются в CloudWatch. Там же, в пару кликов мыши, можно настроить алерты и дашборды для мониторинга.


Ответ на заглавный вопрос


Итак, для того, чтобы запустить веб-приложение, понадобилось 9 сервисов AWS, а для того, чтобы настроить девелоперские операции еще 8 сервисов.


Следует отдать должное AWS, они максимально уменьшили порог вхождения, документации и примеров предостаточно, но все же 17 раз пришлось разбираться с логикой проприетарного сервиса!


Когда я начинал, то не думал, что мне это может понравится. Я сопротивлялся. Так, например, закодил самостоятельно механизм jwt-аутентификации, вместо того, чтобы использовать Cognito.


Но я поменял свое мнение. Все же приятно сбросить с плеч груз инфраструктурного кода и сконцентрироваться на бизнес-логике. Немного освоившись, я начал испытывать чувства ребенка в парке аттракционов. Каждый новый сервис новое веселье. Я рад, что этот парк пока не исследован до конца. Для себя я принял решение, что я здесь надолго.

Подробнее..

Перевод Новые метрики объектных хранилищ

12.10.2020 18:16:09 | Автор: admin
Flying Fortress by Nele-Diel

Команда объектного S3-хранилища Mail.ru Cloud Storage перевела статью о том, какие критерии важны при выборе объектного хранилища. Далее текст от лица автора.

Когда заходит речь об объектном хранилище, как правило, люди думают только об одной характеристике цене за ТБ/ГБ. Конечно, эта метрика важна, но она делает подход однобоким и приравнивает объектное хранилище к инструменту для хранения архивов. Плюс такой подход уменьшает важность объектного хранилища для технологического стека предприятия.

Выбирая объектное хранилище, стоит обращать внимание на пять характеристик:

  • производительность;
  • масштабируемость;
  • совместимость с S3;
  • реакция на сбои;
  • целостность.

Эти пять характеристик новые метрики объектного хранилища, наравне со стоимостью. Рассмотрим их все.

Производительность


Традиционные объектные хранилища не отличаются производительностью. Поставщики услуг постоянно жертвовали ей в погоне за низкими ценами. Однако с современными объектными хранилищами всё иначе.

Скорость различных хранилищ приближается к Hadoop или даже превосходит ее. Современные требования к скорости чтения и записи: от 10 ГБ/с для жестких дисков, до 35 ГБ/с для NVMe.

Такой пропускной способности достаточно для Spark, Presto, Tensorflow, Teradata, Vertica, Splunk и других современных вычислительных фреймворков в стеке аналитики. Тот факт, что MPP-базы данных настраивают на объектные хранилища, говорит о том, что оно всё чаще используется как основное хранилище.

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

Это ключевой момент: новый показатель производительности пропускная способность, а не задержка. Она требуется для масштабируемых данных, и это норма в современной инфраструктуре данных.

И хотя тесты производительности хороший способ определения производительности, ее невозможно точно измерить до запуска приложения в среде. Только после него можно сказать, где именно узкое место: в программном обеспечении, дисках, сети или на уровне вычислений.

Масштабируемость


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

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

Когда у стандартных блоков небольшие размеры, их легче оптимизировать, то есть обеспечить безопасность, контроль доступа, управление политиками, жизненным циклом и обновлениями без прерывания работы. И в итоге обеспечить производительность. Размер строительного блока функция управляемости области отказа, именно так строятся высокоустойчивые системы.

У мультиклиентности множество характеристик. Хотя параметр говорит о том, как организации предоставляют доступ к данным и приложениям, он также относится и к самим приложениям, и к логике их изоляции друг от друга.

Характеристики современного подхода к мультиклиентности:

  • За короткое время количество клиентов может вырасти с нескольких сотен до нескольких миллионов.
  • Клиенты полностью изолированы друг от друга. Это позволяет им запускать разные версии одного и того же ПО и хранить объекты с различными конфигурациями, разрешениями, функциями, уровнями безопасности и обслуживания. Это необходимо, когда масштабируются новые серверы, обновления и географические регионы.
  • Хранилище эластично масштабируется, ресурсы предоставляются по запросу.
  • Каждая операция управляется API и автоматизируется без участия человека.
  • ПО можно разместить в контейнерах и использовать стандартные системы оркестрации, например Kubernetes.

Совместимость с S3


Amazon S3 API фактически стандарт для объектных хранилищ. Каждый поставщик ПО для объектного хранилища заявляет о совместимости с ним. Совместимость с S3 двоичная: либо она реализована в полном объеме, либо ее нет.

На практике возможны сотни и тысячи пограничных сценариев, когда при использовании объектного хранилища что-то идет не так. Особенно у поставщиков проприетарного ПО и услуг. Его основные сценарии использования прямое архивирование или резервное копирование, поэтому причин для вызова API немного, варианты использования однородны.

Значительные преимущества у ПО с открытым исходным кодом. Оно покрывает большинство пограничных сценариев, учитывая размер и разнообразие приложений, операционных систем и аппаратной архитектуры.

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

Открытый исходный код означает: приложения не привязаны к поставщику и более прозрачны. Это обеспечивает долгий жизненный цикл приложения.

И еще несколько замечаний насчет открытого исходного кода и S3.

Если вы запускаете приложение для работы с большими данными, S3 SELECT на порядок повышает производительность и эффективность. Это происходит за счет использования SQL для извлечения из хранилища только тех объектов, которые вам необходимы.

Ключевой момент поддержка уведомлений бакетов. Бакетные уведомления упрощают бессерверные вычисления важный компонент любой микросервисной архитектуры, которая предоставляется как услуга. Учитывая, что объектное хранилище фактически облачное хранилище, эта возможность становится решающей, когда объектное хранилище используют облачные приложения.

Наконец, реализация S3 должна поддерживать Amazon S3 API-интерфейсы шифрования на стороне сервера: SSE-C, SSE-S3, SSE-KMS. Еще лучше, если S3 поддерживает защиту от несанкционированного доступа, которая действительно безопасна.

Реакция на сбои


Показатель, который, вероятно, часто упускают из виду, то, как система обрабатывает сбои. Неудачи случаются по разным причинам, и объектное хранилище должно обрабатывать их все.

Например, есть единственная точка отказа, метрика этого равна нулю.

К сожалению, многие системы хранения объектов используют специальные узлы, которые должны быть активированы для правильной работы кластера. К ним относят узлы имен или серверы метаданных это создает единую точку отказа.

Даже там, где предусмотрено несколько точек отказа, первостепенное значение имеет способность выдерживать катастрофические отказы. Диски выходят из строя, серверы выходят из строя. Ключевой момент создание программного обеспечения, предназначенного для обработки сбоев как нормального состояния. При выходе из строя диска или узла такое ПО продолжит работать без изменений.

Встроенная защита от стирания и деградации данных гарантирует: вы можете потерять столько дисков или узлов, сколько у вас есть блоков четности обычно это половина дисков. И только тогда ПО не сможет возвращать данные.

Отказ редко проверяется под нагрузкой, но такая проверка обязательна. Моделирование сбоя под нагрузкой покажет совокупные затраты, понесенные после сбоя.

Консистентность


Показатель консистентности в 100% также называют строгой консистентностью. Консистентность ключевой компонент любой системы хранения, но строгая консистентность встречается довольно редко. Например, Amazon S3 ListObject не является строго консистентным, он консистентен только в конце.

Что подразумевается под строгой консистентностью? Для всех операций после подтвержденной операции PUT должно выполняться следующее:

  • Обновленное значение видно при чтении с любого узла.
  • Обновление защищено резервированием от сбоя узла.

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

Заключение


Это новые метрики объектного хранилища, которые отражают модели использования в современных организациях, где производительность, консистентность, масштабируемость, домены отказа и совместимость с S3 строительные блоки для облачных приложений и аналитики больших данных. Рекомендую использовать этот список в дополнение к цене при создании современных стеков данных.

Об объектном хранилище Mail.ru Cloud Solutions: Архитектура S3. 3 года эволюции Mail.ru Cloud Storage.

Что еще почитать:

  1. Пример event-driven приложения на основе вебхуков в объектном S3-хранилище Mail.ru Cloud Solutions.
  2. Больше чем Ceph: блочное хранилище облака MCS
  3. Работа с объектным S3-хранилищем Mail.ru Cloud Solutions как с файловой системой.
  4. Наш канал в Телеграме с новостями об обновлениях S3-хранилища и других продуктов.
Подробнее..

Перевод Apache Ozone следующее поколение хранилища для платформы больших данных

16.03.2021 10:10:28 | Автор: admin

Распределенная файловая система Apache Hadoop (HDFS) де-факто является файловой системой для больших данных. При этом легко забыть, насколько масштабируемая и надежная HDFS в реальном мире. Наши клиенты управляют кластерами с тысячами узлов; в этих кластерах хранится более 100 петабайт данных, обслуживающих тысячи одновременных клиентов.

Верная своим корням big data, HDFS работает лучше всего, когда большинство файлов имеют большой размер - от десятков до сотен мегабайт. HDFS страдает от известного ограничения небольших файлов, а производительность начинает ухудшаться при более чем 350 миллионах файлов. Существует повышенный спрос на HDFS-подобные системы хранения данных, которые могут масштабироваться до миллиардов маленьких файлов.

Ozone - это распределенное объектное хранилище, которое может управлять как малыми, так и большими файлами. В то время как HDFS предоставляет семантику, подобную POSIX, Ozone архитектурно выглядит и ведет себя как Object Store. Ozone разрабатывается и внедряется командой инженеров и архитекторов, имеющих значительный опыт управления большими кластерами Apache Hadoop. Это дало нам представление о том, что HDFS делает хорошо, и о некоторых вещах, которые можно делать по-другому. Эти уроки повлияли на дизайн и эволюцию Ozone.

При проектировании Озона мы руководствовались следующими принципами:

Strongly Consistent
Строгая консистенция упрощает проектирование приложений. Озон разработан для обеспечения строгой серийности.

Архитектурная простота
Простая архитектура проще в рассуждениях и проще в отладке, когда что-то идет не так. Мы постарались сделать архитектуру Ozone простой даже ценой потенциальной масштабируемости. Однако Ozone не сдаёт позиции, когда речь заходит о масштабировании. Мы разработали его для хранения более 100 миллиардов объектов в одном кластере.

Многослойная архитектура
Для достижения масштабов современных систем хранения данных Ozone является многоуровневой файловой системой. Она отделяет управление пространством имён от слоя управления блоками и узлами, что позволяет пользователям независимо масштабировать по обеим осям.

Безболезненное восстановление
Ключевым преимуществом HDFS является то, что она может эффективно восстанавливаться после таких катастрофических событий, как отключение электроэнергии по всему кластеру без потери данных и без дорогостоящих шагов по восстановлению. Потери в стойке и в узле - относительно незначительные. Озон является таким же надежным перед лицом сбоев.

Открытые исходные коды в Apache
Мы считаем, что сообщество Apache с открытым исходным кодом имеет решающее значение для успеха Ozone. Весь дизайн и разработка Ozone осуществляется в сообществе Apache Hadoop.

Взаимодействие с экосистемой Hadoop
Озон должен использоваться существующей экосистемой Apache Hadoop и связанными с ней приложениями, такими как Apache Hive, Apache Spark и традиционные задания MapReduce. Следовательно, Озон поддерживает:
- Hadoop Compatible FileSystem API (он же OzoneFS). Это позволяет Hive, Spark и т.д. использовать Ozone в качестве хранилища с нулевыми модификациями.
- Data Locality (Локальность данных) была ключом к оригинальной архитектуре HDFS/MapReduce, позволяя планировать задачи на тех же узлах, что и данные. Ozone также поддерживает локализацию данных для приложений.
- Совместное развертывание рядом с HDFS. Ozone может быть установлен в существующем кластере Hadoop и может совместно использовать диски хранения данных с HDFS.

Озон состоит из знакомых для S3 хранилищ понятий - Volumes, Buckets и keys.
Volumes - Volumes аналогичны аккаунтам. Они могут быть созданы или удалены только администраторами. Администратор обычно создает том для всей организации или команды.

Buckets - Том может содержать ноль и более корзин. Корзины Озона похожи на корзины Amazon S3.

Keys - Ключи уникальны внутри данной корзины и похожи на Объекты в S3. Имя ключа может быть любой строкой. Значения представляют собой данные, которые вы храните внутри этих ключей, в настоящее время Ozone не применяет верхний предел размера ключа.

Чтобы масштабировать Ozone до миллиардов файлов, нам нужно было решить два узких места, которые существуют в HDFS:
Масштабируемость пространства имён
Мы больше не можем хранить все пространство имен в памяти одного узла. Ключевым моментом является то, что пространство имен имеет опорную точку, поэтому мы можем хранить только рабочий набор в памяти. Пространство имен управляется сервисом под названием Ozone Manager.

Масштабируемость карты блоков
Эту проблему решить сложнее. В отличие от пространства имён, карта блоков не имеет опорного местоположения, так как узлы хранения (DataNodes) периодически посылают отчеты о каждом блоке в системе. Озон делегирует эту проблему на общий уровень хранилища под названием Hadoop Distributed DataStore (HDDS).

Основные блоки архитектуры:

  • Storage Containers - Контейнеры для хранения, построенные с использованием готовой key-value БД (RocksDB). Типичный размер контейнера - 5 ГБ. Единица репликации, реплики синхронизированы через консенсус-протокол RAFT. Хранится на DataNodes, управляется Storage Container Manager. Эквивалент менеджера блоков HDFS.

  • Консенсусный протокол RAFT через Apache Ratis. RAFT - это консенсус-протокол, похожий по духу на Paxos, но предназначенный для простоты понимания и реализации. Apache Ratis - это Java-реализация с открытым исходным кодом RAFT, оптимизированная для высокой пропускной способности.

  • Storage Container Manager (SCM) - сервис, который управляет жизненным циклом реплицируемых контейнеров. Масштабируется, не отслеживая отдельные блоки данных. Вместо этого он отслеживает контейнеры, которые являются аггрегатами информации о блоках. Storage Container Manager выступает в качестве кластер менеджера, центра сертификации, блок-менеджера и менеджера по репликам. SCM выделяет блоки и распределяет их по узлам. Клиенты читают и записывают эти блоки напрямую. SCM отслеживает все реплики блоков.

  • Hadoop Distributed Data Store (HDDS) - общий уровень распределенного хранения для блоков, не предоставляющих пространства имен.

  • Ozone Manager (OM) - менеджер пространства имен, реализующий архитектуру ключ-значение. Пространство имён Озона - это коллекция томов, каждый из которых состоит из корзин и ключей. OM ведет список томов, корзин и ключей. Ozone Manager будет использовать Apache Ratis для воспроизведения состояния OM. Это обеспечивает высокую доступность для Ozone.

Основные свойства Озона, которые помогают ему достичь масштабируемости:

  • Пространство имён (namespace) в Ozone сохраняется в локальную RocksDB, при таком дизайне может быть легко настроен баланс между производительностью (сохранение всего в памяти) и масштабируемостью (сохранение на диске менее используемых метаданных).

  • Управление пространством имён и блоками разделено на два разных сервиса OzoneManager(OM) и StorageContainerManager(SCM) соответственно. Каждый из них может масштабироваться независимо друг от друга.

  • В отличие от HDFS, отчеты по блокам Ozone передаются через контейнерные отчеты, которые агрегируют отчеты по нескольким блокам в контейнере.

Бенчмаркинг Ozone

Apache Ozone был разработан для преодоления ограничений масштабирования HDFS при небольших файлах и большом общем количестве объектов файловой системы. На текущем оборудовании центра обработки данных ограничение HDFS составляет около 350 миллионов файлов и 700 миллионов объектов файловой системы. Архитектура Ozone устраняет эти ограничения. Ниже сравнивается производительность Ozone при работе с HDFS.

Для тестирования мы выбрали широко используемый эталонный тест TPC-DS и обычный стек Hadoop, состоящий из Hive, Tez, YARN и HDFS наряду с Ozone. В соответствии с текущей потребностью отрасли в разделении вычислений и хранилищ данных, когда используются высокоплотные узлы хранения и эластичные вычисления, мы выполняем эти тесты, используя отдельные DataNodes и NodeManagers. Фундаментальная цель данного исследования и последующих усилий по оптимизации продукта - сделать его сопоставимым по стабильности и производительности с HDFS. И мы хотели бы отметить невероятный объем работы, проделанной сообществом за последние несколько месяцев для достижения этой цели.

Hive на Ozone работает быстрее

Следующие измерения были получены путем создания двух независимых наборов данных по 100 ГБ и 1 ТБ в кластере с 12 выделенными узлами для хранения и 12 выделенными вычислительными узлами. В последнем разделе этой статьи будет представлена более подробная информация о конфигурации.

Следующие иллюстрации показывают, что, учитывая общее время выполнения наших 99 тестовых запросов, на обоих наборах данных Ozone превзошел HDFS в среднем на 3,5%.

Чтобы лучше понять подробные результаты, мы разделили наши запросы на три группы:

1. Более быстрые запросы (запросы, в которых Ozone превосходит HDFS).

2. Незначительно более медленные запросы (запросы, в которых Ozone уступает HDFS на 25% или меньше).

3. Выбросы - запросы, в которых Ozone уступает HDFS более чем на 25%).

Когда данные находятся в Ozone, более чем в 70% случаев запросы выполняются быстрее по сравнению с HDFS. Усилия сообщества, направленные на стабилизацию и улучшение производительности, похоже, окупаются. Но есть еще куда расти.

На следующем графике разброса показана средняя разница во времени выполнения между Ozone и HDFS для каждого отдельного запроса TPC-DS и каждого набора данных. Каждый запрос на графике, который колеблется в районе 0%, показывает незначительную разницу в производительности между Ozone и HDFS. Чтобы нормализовать дисперсию из-за шума, числа были усреднены для каждого запроса за 10 последовательных прогонов.

Значения по оси Y представляют собой разницу во времени выполнения по сравнению со временем выполнения запроса в HDFS. Так, например, 50% означает, что разница составляет половину времени выполнения в HDFS. Это фактически означает, что в Ozone запрос выполнялся в 2 раза быстрее, а -50% (отрицательное значение) означает, что для выполнения запроса в Ozone потребовалось в 1,5 раза большее время, чем в HDFS.

Заключение

Тестовые прогоны показывают, что Ozone с небольшим отрывом лидирует - немногим более чем на 70% запросов TPC-DS. Однако есть несколько выбросов, которые мы активно изучаем, чтобы найти узкие места и сгладить их с наивысшим приоритетом.

В настоящее время Ozone выпущен в GA-версии, готов к работе в продуктиве и включен в дистрибутив Cloudera Data Platform (CDP), мы постоянно находимся в процессе сбора отзывов и продолжения развития распределенной системы хранения больших данных следующего поколения. В рамках платформы Озон интегрирован с Ranger и поддерживает шифрование данных как в пути, так и в покое.
Сообщество пользователей активно растёт, так например Tencent использует Озон в продуктиве на около 1000 узлах, также в списке активных пользователей Bloomberg и Target. Доступны или готовятся к выходу референсные архитектуры от Cisco, Dell и HP, а также материалы от Клаудеры. Из основных планов на ближайшее будущее: реализация HA Storage Container Manager, поддержка Erasure Coding, интеграция с Atlas, NiFi/Kafka/Flink коннекторы и тул для миграции HDFS->Ozone.

Подробная информация о среде

Кластер состоит из 28 однородных физических узлов, оснащенных 20-ядерными процессорами Intel Xeon, 128 ГБ оперативной памяти, 4 дисками по 2 ТБ и сетью 10 ГБ/с. Узлы, работающие под управлением CentOS 7.4 и Cloudera Runtime 7.0.3, которые содержат Hive 3, Hadoop 3, Tez 0.9 и Ozone, созданы из основной ветки Apache с начала января 2020 г.

4 главных узла, один - для Cloudera Manager, Prometheus и Zookeeper, другой - для YARN, третий - для Hive и четвертый - для HDFS и Ozone, поскольку мы используем их в разное время.

12 узлов хранения, с 2 выделенными дисками для хранения данных и 12 вычислительных узлов с 3 дисками, выделенными для YARN и журналов (логов).

Шифрование SSL/TLS было отключено, и использовалась простая аутентификация.

Ozone был настроен на использование одного тома с одним бакетом для имитации семантики HDFS.

Высокая доступность не была включена ни для одной из служб.

Внутренняя сеть кластера используется совместно с другими хостами, и мы знаем, что трафик может быть нестабильным - от довольно низкого до пикового. Отчасти это причина того, что мы усреднили результаты десяти полных тестовых прогонов как для HDFS, так и для Ozone, прежде чем прийти к окончательному результату.

Кэширование результатов в Hive было отключено для всех запросов, чтобы все данные считывались из файловой системы.

Инструменты, которые мы использовали для тестирования, также доступны и имеют открытый исходный код - если хотите, попробуйте его в своей среде. Все инструменты можно найти в этом репозитории.

Дополнительная литература

1. Толеранция аварий в Озоне

2. Apache Hadoop Ozone - обзор хранилища объектов

3. 1 миллиард файлов в Озон

4. Apache Hadoop Ozone - архитектура хранилища объектов

Подробнее..

Сам себе DevOps строим cloud-only CI для веб приложения

22.05.2021 18:12:52 | Автор: admin

Привет, Хабр! Сегодня мы немного поговорим о DevOps и самоорганизации на примере одного из наших проектов.

Начнем с фразы, с которой не соглашается добрая половина разработчиков в индустрии: "каждый разработчик должен быть сам себе DevOps". Кто-то считает, что этим должен заниматься отдельно выделенный человек, чтобы у разработчика оставалась забота только о качестве кода. А кому-то свойственно думать о конвейере доставки кода в той же степени, как и о самом коде. Я же считаю, что в современных реалиях рынка и избытке инструментов/знаний разработчик должен уметь настроить и обслуживать конвейер быстрой и предсказуемой доставки артефакта в нужную ему среду. В отличие от мобильных разработчиков, для которых вопросы инфраструктуры и доставки приложения в большей степени решены самим вендором (Google и Apple), backend и web разработчики должны если не владеть, то хотя бы интересоваться практиками доставки кода.

И речь не идет о настройке каких-то больших и громоздких билд-систем, для которых обычно приносится в жертвую целая штатная единица. Нет. DevOps - не человек, а система ежедневных маленьких привычек, основанных на самоорганизации. Понятие, взрастающее снизу вверх, а не сверху или в бок. И если вы, как разработчик, смогли ускорить поток артефактов (любимое американцами понятие "Value Stream") на небольшой процент, то поздравляем - это уже DevOps way. Рекомендуем прочесть книгу "DevOps Handbook" by Gene Kim - лучшая книга для понимания этого концепта (ссылка в конце статьи).

В этой статье мы представим вам маленькую историю зарождения DevOps в нашей команде, позволившую нам ускорить разработку проекта. Эта история применима как к разработчику-одиночке, так и к большой команде.

Кто

Одна из наших команд занимается разработкой системы интернет-банкинга для одного крупного банка. Команда достаточно большая, но речь сегодня пойдет о конкретных персонажах:

  • 3 фронтенд разработчика с кучей пулл реквестов в день

  • 2 тестировщика, бастующие за улучшение QX (QA experience)

Что

Клиентское и администраторское web-приложения на Angular 9.0, собираемые из одного репозитория.

Где

Моя команда известна как ярый адепт продуктов Atlassian, поэтому вся экосистема нашего проекта живет в "австралийских облаках":

  • задачи и релизы в Jira

  • код в Bitbucket

  • CI в Bitbucket Pipelines

  • подробная документация в Confluence.

Наша команда использует стандартный план Bitbucket за $4/чел, включающий в себя 1500 минут сборки в Bitbucket Pipelines. О нем в сегодняшней статье и пойдет речь. Принцип работы и синтаксис настройки на 90 процентов похожи на Gitlab CI, поэтому любому пользователю Gitlab вся схема работы будет максимально понятной.

Сама система интернет банкинга разбита на микросервисы и работает в контейнерах на серверах Банка. Но в этой статье речь будет идти не о контейнерах, хотя настройка CI с помощью Docker-образов звучит очевидным.

Немного контекста

Первые наши шаги в DevOps и конкретно в улучшении QX (QA experience) мы начали задолго до этого в проектах мобильных приложений. Мы интегрировали между собой Jira, Bitbucket и сервис Bitrise.io во всех наших пулл-реквестах, что позволило иметь на выходе конкретный билд на каждый коммит по конкретной задаче. Для наглядности: тестировщик понимал, что пулл реквест 30 выдает билд приложения 170, в которой нужно тестировать Jira-задачу 500. Если вкратце описать процесс пулл-реквестов, то обязательными требованиями к слиянию пулл-реквеста являются

  • Зеленый билд на последнем коммите

  • Добро от разработчика-ревьюера

  • Добро от тестировщика

Если один из этих шагов давал красный свет, то пулл-реквест проходит все шаги заново.
Такой процесс позволяет нам обеспечить высокое качество кода и продукта в стабильной ветке репозитория. Мы с высокой долей уверенности можем релизить приложение, собранное с master (мы начали работать по trunk-based development и поэтому master наша стабильная ветка).

В данном проекте для мобильных приложений мы применили ту же самую рабочую схему для мобильных приложений и начали выпускать и проверять тестовые приложения с первого дня.

Подобного механизма пулл-реквестов никогда не существовало в web приложениях. Мы всегда делали приемку задач после слияния пулл-реквестов в стабильную ветку, из-за чего каждый третий коммит в ней был дефектным. Настроить такой же процесс приема пулл реквестов, как в мобилке, было для нас очевидным шагом. Сделать CI окружение для web приложения на инфраструктуре Банка было для нас слишком долгой историей, потому что хотелось настроить и поехать очень быстро. А все, кто работал с большими банками, почувствовал "скорость" продвижения задач по железу. Все процессы, что мы опишем в этой статье, мы планируем воссоздать в инфраструктуре банка с помощью оркестратора (Kubernetes или OpenShift, на усмотрение заказчика), но это уже другая история. В тот момент нам нужно было как можно быстрее начать работать правильно.

Первый очевидный вопрос: куда доставлять? Мы начали присматриваться к разным вариантам: Heroku, AWS, Netlify, Surge итд. В итоге остановились на использовании AWS S3. Для тех, кто думал, что S3 это всего лишь файловое хранилище - S3 может работать как сайт и его можно привязать к доменному имени. Подробнее об этом можно прочитать на страничке AWS.

Так почему же AWS?

  • Доступная цена. При всей репутации AWS как дорогой экосистемы, ежемесячные счета за S3 выходят в среднем 2 доллара при следующих метриках:

    • Новых ПР в день ~ 2

    • Пайплайнов в день ~ 12

    • Кол-во единовременно существующих бакетов ~ 5

    • Средний размер бакета = 13 Mb

  • У AWS отличный API и CLI. у "Surge" и других легковесных сервисов хостинга не настолько качественный и полноценный тулинг, как у Amazon AWS. Надо отметить, что CLI и документация Heroku не уступает Амазону, но высокий на наш взгляд порог вхождения и специфика работы Heroku Dynos заставили нас отойти от его выбора.

  • У команды уже был опыт работы с продуктами AWS.

Можно было бы настроить весь этот процесс в контейнерах в самом Amazon, но это повлечет за собой запуск EC2 машин. Даже с использованием Docker Hub вместо Elastic Container Registry, прогноз затрат вываливался у нас за $100 в месяц. В конечном итоге у нас получилась именно та схема работы с пулл-реквестами, которую мы представляли себе в самом начале. Но давайте проанализируем каждую ступень нашей эволюции и посмотрим на принятые решения.

Уровень 1: создание S3 бакета

Мы начали с того, что создали по одному выделенному S3 bucket для хостинга клиентского и админского приложений. Настроили конфигурацию сборки нашего проекта (bitbucket-pipelines.yml), чтобы он собирал приложения (html/css/js/img) и заливал их в соответствующий S3 bucket. В начале был использован AWS CLI, но, как оказалось, Bitbucket предоставляет набор готовых официальных Pipes (аналог Github actions), среди которых оказался Pipe для выгрузки файлов в S3 bucket. В итоге: тестировщик имеет сайт, на котором он может проверить реализацию задачи пулл-реквеста с постоянной условной ссылкой web.s3-website.ap-northeast-2.amazonaws.com.

Обязательным предварительным шагом при создании бакета через консоль AWS является включение опции "Enable static hosting" в настройках бакета. Без этой опции bucket является просто файловым хранилищем.

- step:      name: Build and deploy webadmin PR version into AWS for QA      caches:        - node      script:        # начальная конфигурация        - apk update && apk add git        - npm install        # сборка        - npm run build:admin        - cd dist/admin        # загрузка в S3        - pipe: atlassian/aws-s3-deploy:0.2.4          variables:            AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID            AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY            AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION            S3_BUCKET: $S3_WEBADMIN_BUCKET_NAME            DELETE_FLAG: 'true'            LOCAL_PATH: $(pwd)            ACL: 'public-read'

Но данная схема оказалась совершенно не масштабируемой по следующей причине: запущенный пайплайн пулл-реквеста от второго автора перетирала наработки первого. С этой примитивной схемой команда жила месяц.

Оценка:

  • за старания - четверка

  • за QX - двойка

Уровень 2: выделение S3 bucket под каждого автора

В ответ на обратную связь от тестировщиков командой было решено выделить по одному S3 bucket на каждого фронтенд разработчика. В нашем проекте были разработчики Манар, Миша - следовательно были созданы условные S3 бакеты jsn-web-manar и jsn-web-michael. В bitbucket-pipelines.yml в step для пулл-реквестов была добавлена логика определения конечного S3 бакета в зависимости от PR автора.

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

  1. Гонка пулл-реквестов одного автора. Если один и тот же разработчик создаст 3 параллельных пулл-реквеста, то все они вызовут запуск пайплайна сборки. Мы не можем точно знать, какой из пайплайнов закончится быстрее. Команде, в частности тестировщику, без использования консоли Chrome сложно понять, какой из пулл-реквестов сейчас развернут на S3 бакете разработчика Михаила.

  2. Появление нового автора. В наших репозиториях создавать пулл-реквест может любой член команды, поэтому эта схема сломалась ровно в тот момент, когда ПР создал кто-то, кроме фронтенд-разработчиков. По нашей тривиальной логике определения бакета его запущенный пайплайн "угонит" S3 бакет одного из разработчиков. В итоге другой тестировщик может потерять version-under-test сайт прям в момент тестирования.

  3. Смена никнейма. Наши разработчики забавы ради любят менять свои git author name время от времени. Для нас это никогда не являлось проблемой до того, как мы применили логику с бакетами на каждого автора. К сожалению, Bitbucket Pipelines из коробки не предоставляют возможности определения автора по его Jira account, поэтому в логике присвоения бакета пришлось оперировать стандартным commit git author. Как вы и сами догадались, при смене имени с "Manar Kurmanov" на "Dark Lord" повторилась ситуация из пункта 2 - был угнан бакет другого разработчика.

С этой шаткой схемой команда прожила еще несколько месяцев.

Оценка:

  • за старания - четверка

  • за QX - тройка

Уровень 3: добавление штампа авторства в web приложение

Команда решила проблему гонки пулл-реквестов добавлением пояснительного текста в footer сайта:

Каждый пайплайн добавлял в футер сайта название ветки, автора и timestamp. Таким образом решалась проблема параллельных пулл-реквестов от одного автора - тестировщик четко понимает, какая Jira-задача разработчика Георгия представлена в бакете.

Фрагмент из bitbucket-pipelines.yml

- step:    name: Build PR version    caches:      - node    script:      # initial configuration      - apk update && apk add git      - npm install      # preparing site footer text      - TIMESTAMP_FILE="./src/app/some/folder/copyright.timestamp.html"      - GIT_AUTHOR=$(git log -n 1 --format=format:'%an')      - PR_URL="$BITBUCKET_GIT_HTTP_ORIGIN/pull-requests/$BITBUCKET_PR_ID"      - BRANCH_TEXT="PR branch <a href=\\"$PR_URL\\">$BITBUCKET_BRANCH</a><br>"      - echo $BRANCH_TEXT >> $TIMESTAMP_FILE      - echo "Author $GIT_AUTHOR<br>" >> $TIMESTAMP_FILE      - echo "Built at $(TZ=UTC-6 date '+%d-%m-%Y %H:%M') <br>" >> $TIMESTAMP_FILE      - echo "</small>" >> $TIMESTAMP_FILE      - cat $TIMESTAMP_FILE > src/app/target/folder/copyright.component.html      # building artefacts      - npm run build    artifacts:      paths:        # кеширование артефактов для следующего Build Step         - dist/web/**

Казалось бы, +100 к QX, куда еще прозрачнее. Но поставьте себя на место тестировщика в ежедневной работе и вы поймете еще одно скрытое неудобство. Допустим, что разработчик создал 3 параллельных пулл-реквеста и тестировщик проверил сайт на S3 бакете. Что он должен делать дальше? Тестировщику не очевидно, что он находится в ситуации очереди ПР-ок на один и тот же S3 бакет. После он должен зайти в странице Pipelines, найти нужную ветку и сделать ручной Rerun.

Мы поняли, что проблема с гонкой пулл-реквестов принципиально не решена и нашу схему тестирования нельзя назвать масштабируемой. Нужно пересмотреть процесс.

Оценка:

  • за старания - четверка

  • за QX - тройка с плюсом

Уровень 4: динамичные бакеты под каждый пулл реквест

Мы решил копнуть глубже в возможности AWS API и воссоздать поведение динамических сред для тестировщиков и разработчиков. Какие были требования:

  • Каждый пулл реквест должен породить свой отдельный S3 бакет и задеплоить сайт туда.

  • Нужно, чтобы в комментарий к пулл-реквесту писалась ссылка на этот бакет при каждом новом билде.

  • Автоматика должна уметь подчищать за собой неиспользуемые бакеты

Для реализации этих требований не хватало стандартных Bitbucket Pipes, поэтому нужно было писать кастомные скрипты для взаимодействия с AWS S3. К счастью Bitbucket Pipelines, как и многие CI системы, является cloud-first и предоставляет возможность запускать свои пайплайны на базе любого публичного Docker образа. Мы использовали официальный образ aws-cli, включающий в себя AWS CLI и все базовые утилиты командной строки (curl, sed, xargs).

Ниже фрагмент из bitbucket-pipelines.yml по загрузке статики сайта в динамический бакет. NOTE: в скрипте используются ключи и секреты из учетной записи AWS S3, их можно сгенерировать по официальной инструкции.

- step:    name: Deploy PR version into AWS bucket for QA    image:      name: amazon/aws-cli    script:      # 1. Настройка сессии в aws cli с помощью ключей      - aws configure set aws_access_key_id=$AWS_ACCESS_KEY_ID aws_secret_access_key=$AWS_SECRET_ACCESS_KEY      # 2. определяем название для динамического бакета      - export BUCKET_NAME=web-pullrequest-$BITBUCKET_PR_ID      # 3. если в AWS нету бакета с таким названием, создаем его с нужными флагами      - if [ -z $(aws s3 ls | grep $BUCKET_NAME) ]; then aws s3api create-bucket --bucket $BUCKET_NAME --acl public-read --region ap-northeast-2 --create-bucket-configuration LocationConstraint=ap-northeast-2; fi      # 4. задаем это бакету настройку статичного хостинга      - aws s3api put-bucket-website --website-configuration "{\\"ErrorDocument\\":{\\"Key\\":\\"error.html\\"},\\"IndexDocument\\":{\\"Suffix\\":\\"index.html\\"}}" --bucket $BUCKET_NAME      # 5. очищаем содержимое бакета      - aws s3 rm s3://$BUCKET_NAME --recursive       # 5. заливаем в него собранные html/css/js      - aws s3 cp dist/web s3://$BUCKET_NAME --acl public-read --recursive      # 6. Пишем коммент со ссылкой от имени сервисной учетки в нужный пулл реквест      - export PR_API_URL=https://api.bitbucket.org/2.0/repositories/$BITBUCKET_REPO_FULL_NAME/pullrequests/$BITBUCKET_PR_ID/comments      - export BUCKET_PUBLIC_URL=http://$BUCKET_NAME.s3-website.ap-northeast-2.amazonaws.com      - curl $PR_API_URL -u $CI_BB_USERNAME:$CI_BB_APP_PASSWORD --request POST --header 'Content-Type:application/json' --data "{\\"content\\":{\\"raw\\":\\"[http://$BUCKET_NAME.s3-website.ap-northeast-2.amazonaws.com](http://personeltest.ru/away/$BUCKET_NAME.s3-website.ap-northeast-2.amazonaws.com)\\"}}"

В качестве автора комментарий в пулл реквест мы использовали нашу сервисную учетную запись для CI с использованием App-specific password. В этой статье от Atlassian можно узнать, как создать такой пароль.

Данная схема может обслуживать и двоих и сотню разработчиков - мы обеспечили масштабируемость и прозрачность процесса тестирования.

"Единственный ручной процесс в этой схеме - чистка неиспользуемых S3 бакетов раз в неделю. Зачем это автоматизировать?" - подумали мы. Но по закону жанра команда благополучно забывала подчищать бакеты и вспомнила об этом только после того, как бухгалтер показал счет на 25 долларов от AWS из-за скопившихся бакетов.

В итоге мы решили добавить логику чистки неиспользуемых бакетов в пайплайн при слиянии пулл-реквеста.

- step:    name: Remove dangling s3 buckets left after PR merges    image:        name: amazon/aws-cli    script:      # 1. Запросить список 10 последних MERGED пулл реквестов      - export API_URL="<https://api.bitbucket.org/2.0/repositories/$BITBUCKET_REPO_FULL_NAME/pullrequests?state=MERGED>"      - curl "$API_URL" -u $CI_BB_USERNAME:$CI_BB_APP_PASSWORD > pr_list.json      # 2. выделить бакеты, соответствующие спец-формату       - aws s3 ls | grep -o '[a-zA-Z\\-]\\+pullrequest\\-[0-9]\\+' > buckets.txt- set +e      # очистить все бакеты с номер ПР-ок, которые уже MERGED      # (AWS API требует очистки бакета перед его полным удалением)      - echo "$(cat pr_list.json | grep -o '"id":\\s[0-9]\\+')" | sed 's/[^0-9]//g' | xargs -I{} grep {} buckets.txt | xargs -I{} aws s3 rm s3://{} --recursive      # удалить все бакеты с номер ПР-ок, которые уже MERGED      - echo "$(cat pr_list.json | grep -o '"id":\\s[0-9]\\+')" | sed 's/[^0-9]//g' | xargs -I{} grep {} buckets.txt | xargs -I{} aws s3api delete-bucket --bucket {}

Оценка:

  • За старания пятерочка

  • за QX - четверка с плюсом. Почему не пять? Потому что на своей шкуре мы поняли, что улучшение любого X (QX, DevX, HX) - это бесконечный процесс

Технические ремарки

Есть несколько важных моментов, которые стоит отметить для всех желающих опробовать данную схему.

#1: По поводу CORS

Так как API запросы совершаются с одного хоста (.amazonaws.com) на другой хост (*.somebank.com), по умолчанию они будут блокироваться браузером из-за настроек CORS (cross origin resource sharing) сервера. Если вкратце, то браузер позволяет отправлять запросы только из того же хоста, откуда сайт был запрошен. Для примера, API на api.server.com будет принимать запросы только с сайта server.com. При попытке сделать GET запрос с сайта another.com браузер сначала совершит "pre-flight" запрос на сервер и поймет, что сервер строго выдерживает правило "same-origin-policy".

Для того, чтобы запросы со статичного сайта S3 бакета проходили в ваш API, вы должны добавить хост бакета в серверные настройки Headers.

Access-Control-Allow-Origin: <http://bucket.s3-website.amazonaws.com># илиAccess-Control-Allow-Origin: *

Во всех популярных фреймворках есть поддержка управления Cross Origin.

#2: По поводу расходов

В уровне 4 в скрипте присутствует строка очистки содержимого бакета:

aws s3 rm s3://$BUCKET_NAME --recursive 

Это микро оптимизация расходов компании на AWS. Мы на каждом запуске ПР пайплайна очищаем предыдущее содержимое бакета, чтобы в нем не скопились файлы от 4 предыдущих сборок одного и того же пулл реквеста.

Если этого не делать, то размер бакета будет увеличиваться пропорционально кол-ву пайплайнов на 1 ПР. В масштабах 3 разработчиков это экономит нам пару центов, но в масштабе десяток разработчиков и долгих ПР - это десятки долларов. Мы считаем, что это полезное упражнение как минимум с точки зрения практики владения AWS API.

ВАЖНО! Если в вашем проекте будет использоваться долгоживущий S3 bucket и вы будете использовать официальный aws-s3-deploy pipe, то убедитесь, что вы используете DELETE_FLAG. Этот флаг очищает bucket перед очередной выгрузкой файлов. Во время уровня #1 наша команда об этом флаге не знала в течение 2 месяцев и узнала только после обнаружения нескольких тысяч файлов в одном бакете. Поэтому парочку десяток американских долларов было сожжено во имя наших познаний.

# вызов пайпа загрузки файлов в S3 с флагом DELETE_FLAG- pipe: atlassian/aws-s3-deploy:0.2.4    variables:      AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID      AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY      AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION      S3_BUCKET: $S3_WEBADMIN_BUCKET_NAME      DELETE_FLAG: 'true' # не забыть этот флаг      LOCAL_PATH: $(pwd)      ACL: 'public-read'

Вывод

Эта история проб и ошибок одного отдельного процесса позволила нам не только улучшить конкретно этот процесс, а посеяла в нас зерно DevOps ментальности и дала настрой на мини улучшения в других местах проекта и продукта. Мы рекомендуем всем, кто еще не погружался в практики CI/CD, изучить и отточить это направление в своей карьере.

Финальную версию bitbucket-pipelines.yml можно посмотреть в github репозитории.

Материалы к прочтению

Подробнее..
Категории: S3 , Angular , Devops , Frontend , Amazon web services , Aws , Cicd , Pipelines , Bitbucket

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru