Погружение в Serverless. Функции как основной элемент системы

0f2e4d16de77edee1c02bb4d6733907a.png

Внимание на Serverless я обратил с подачи @eapotapov. Мне довелось с ним беседовать в качестве ведущего в подкасте «The Art Of Programming». И меня безумно захватила эта идея. Погрузившись в мир «Бессерверных вычислений», я познакомился с массой интересных людей, занятых созданием экосистемы Serverless в Yandex.Cloud. И после недолгих метаний решил устроить серию интервью, открывающих этот удивительный мир для вас и написанных в дружеской неофициальной манере вечерних посиделок за бокальчиком пенного. Первый мой гость, Глеб Борисов, наверно, один из главных людей, занятых созданием инструментов для запуска функций в Yandex.Cloud.

Вот мы с тобой сидим, идет 2021 год, и у меня стойкое дежавю. Как будто бы идея Serverless — это повторение идеи, с которой я сталкивался, когда только появился интернет. Тогда тоже не было серверов, а были только CGI-скрипты, которые я писал, складывал на чей-то хостинг, и они выполнялись.

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

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

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

Понять можно единственным образом — попробовать. По-другому никак.

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

Yandex.Cloud предоставляет довольно стандартный набор программного обеспечения, без кастомных патчей и каких-то странных изменений. Это стандартное, современное, но проверенное временем Linux-ядро с пользовательским окружением Ubuntu 18.04, LTS-версия которого также давно проверена в бою.

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

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

Больше не надо иметь на работе универсального солдата, который и ядро умеет пересобирать, и на Node.js писать. Достаточно только писать на Node.js, а ядро пересобирает платформа. 

Когда я попробовал сделать функцию, то увидел свежую версию Python с возможностью подложить свои библиотеки. Но меня насторожила необходимость предоставления исходного кода. Python всегда публикуется в открытом коде, но если мы будем использовать другие языки, например Go или Java, то моя интеллектуальная собственность может оказаться скомпрометирована. Я предоставляю ее в открытом виде платформе, и мне важно, как организована ее безопасность. 

Безопасность — это комплекс мер у всех облачных провайдеров. Yandex.Cloud регулярно проходит сертификации и аудиты по различным стандартам информационной безопасности. Это ФЗ-152, семейство ISO 27017, 27018 и другие. Также мы первые в России, кто прошел сертификацию на соответствие требованиям PCI DSS. Потому с точки зрения информационной безопасности Yandex.Cloud проходит любой необходимый контроль или аудит, это правильные и признаваемые подходы к процессам. Беспокоиться о том, что кто-то потенциально может посмотреть в код, точно не стоит.

Для компилируемых языков программирования есть возможность предоставлять не исходный код, а уже скомпилированный программой. Например, это работает для Golang, но следует обратить внимание, что для Golang необходимо обеспечивать совместимость версий компилятора, причем довольно тесную совместимость с платформой. Для Java и C# требования проще, они обусловлены самим языком программирования. Можно принести JAR-файл и не предоставлять платформе доступ к исходному коду. 

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

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

Самый первый для меня сценарий использования функций, особенно если приложение уже разработано в микросервисной архитектуре, — это приделать еще один маленький микросервис сбоку, закрыв фасад некоторым API Gateway. Только написать его не в моей экосистеме, а уже в виде функции. Насколько быстро можно масштабировать этот микросервис при необходимости? Сколько времени занимает подготовка виртуальных машин? На каждый вызов функции создается отдельная виртуальная машина?

Не совсем. Разберемся на примере с Node.js. Время требуется на скачивание кода, старт виртуальной машины, запуск Node.js-интерпретатора, загрузку кода, получение запроса и ответ на этот запрос. Полный цикл.

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

Получается, что если микросервис достаточно «горячий», то виртуальная машина почти никогда не останавливается?

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

А что происходит с виртуальной машиной между запусками?

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

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

Это звучит как описание работы с пулами и подключение к database. Сколько времени потребовалось, чтобы написать первую версию такого планировщика?

Сложный вопрос. Это был итеративный процесс. Первую версию, прототип, я написал за пару недель, но потом потребовался год работы «с напильником».

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

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

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

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

После использования по запросу планировщика удаляем машину, а пул в фоне пополняется новой с заданными параметрами. 

Конвейер такой получается.

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

Python медленнее Node.js, а Golang быстрый, потому что это бинарный код, и ему не нужен интерпретатор. Java и C# тоже быстрее, чем Node.js, из-за своего технологического устройства. Время очень сильно зависит от языка.

Ты упомянул, что можно всю разработку вести в консоли. В некотором смысле она заменяет рабочее место. Но мне милее работать в любимой IntelliJ IDEA. У меня здесь отладка, связь с билдтулами и другие организованные процессы. С некоторым удовольствием обнаружил, что вы добавили поддержку Serverless Framework. Это история про полный цикл деплоя только для JS?

Serverless Framework — инструмент для управления облачным провайдером, он не сильно привязан к языку. Можно писать функции на Python, но при этом сам Serverless Framework написан на JavaScript.

Да, меня это смутило. 

Это Command Line Tool — после запуска она идет в облако и создает, изменяет, удаляет объекты так, чтобы привести состояние системы в нужный вид. Если сильно смущает JavaScript, всегда можно попробовать Terraform. Поддержка Terraform появилась в Yandex.Cloud раньше, чем поддержка Serverless Framework. Terraform более широко охватывает сервисы Yandex.Cloud, при этом имеет примерно такое же удобство по использованию, как Serverless Framework. И для твоей любимой среды разработки IntelliJ IDEA в Terraform есть отличный плагин, который позволяет редактировать и применять план.

Помимо озвученного сценария — «микросервис, который дописывается сбоку» — какие еще есть интересные сценарии для Serverless с точки зрения внедрения в существующие проекты?

Разделим на несколько категорий. Первая и самая простая, с простым порогом входа, — это автоматизация инфраструктуры. У нас есть клиенты, которые делают бэкапы виртуальных машин, есть клиенты, которые автоматически переключают мастера в своем Postgres-кластере или в Managed PostgreSQL переключают availability-зону на нужную в данный момент. Можно, например, удалять ненужные виртуальные машины по таймингу, если ты знаешь, что у тебя в окружении только тестинг, и виртуальные машины станут не нужны через сутки. Такие сценарии просто решаются интеграцией с облачным SDK, интеграцией с системой аутентификации и авторизации.

При этом разработка не сильно отличается от написания скрипта локально, ты пишешь скрипт на Python или на Node.js, он очень легко портируется в функции. Тут нет ничего специфического.

Вторая категория — это размещение сайтов и веб-сервисов. Здесь к Serverless добавляется API Gateway, он позволяет создать фасад для сервиса. Свой домен, терминация TLS, интеграция c Let«s Encrypt, метрика, мониторинг и другие возможности. Статичный контент размещаем в Object Storage, а бэкенд может быть написан на Cloud Functions. Есть несколько хороших примеров от наших клиентов. Например, существующие приложения на Node.js портировали на Cloud Functions с минимумом изменений благодаря middleware-врапперу вокруг стандартной библиотеки. К тому же можно очень экономично настроить потребление ресурсов.

Последняя категория — самый тяжелый и сложный случай, но она дает самую большую экономию средств и времени на разработку — это обработка данных. Если у тебя есть большой массив или поток данных, который надо регулярно или в режиме реального времени обрабатывать, то Cloud Functions, триггеры и другие сервисы Yandex.Cloud могут в этом отлично помочь. Ты, например, можешь сложить данные в очередь сообщений и подписать функцию. Появились данные в очереди сообщений, функция вызвалась, что-то с ними сделала. 

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

Вот еще момент, который меня заботит. Когда слышу в разговорах про использование микросервисов, Docker, Kubernetes, Prometheus, я понимаю, что эти технологии пришли надолго, на 2–3 года точно. А что с Serverless-решениями и их подходом?

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

На конференциях было приведено множество примеров, когда e-commerce-компании строят свою систему Kubernetes, тратя на это очень много ресурсов. Вся эта работа по амортизации деплоя и эксплуатации очень затратна с точки зрения ресурсов. Их можно и нужно перебросить на разработку бизнес-ценности, добавить новые возможности для пользователей, оптимизировать какие-то процессы, повысить выручку и в целом маржинальность бизнеса. Но вместо этого компаниям приходится заниматься всем подряд. В этом плане я считаю, что за Serverless будущее.

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

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

P.S. На осенней конференции Yandex.Scale был анонсирован free tier для сервисов экосистемы бессерверных вычислений. Другими словами, специальные тарифы для Serverless-сервисов с уровнем нетарифицируемого использования. Например, для Yandex Cloud Functions каждый месяц не тарифицируется первый 1 000 000 вызовов функций, а для Yandex API Gateway не тарифицируются первые 100 000 запросов к API-шлюзам. Более подробно об условиях читайте тут.

Как по мне, это прекрасная возможность попробовать Yandex Cloud Functions, как минимум в своих pet-project. А о подробностях и нюансах можно узнать в комьюнити Serverless в телеграме — Yandex Serverless Ecosystem.

© Habrahabr.ru