Хабрахабр

Мал, да удал. Анбоксинг микровиртуалки Firecracker

Записывайте рецепт микровиртуалок Firecracker. Берем два популярных метода изоляции многопользовательской нагрузки — виртуальные машины и контейнеры. Выжимаем лучшее из обоих подходов, максимально упрощаем, тестируем на настоящем хайлоаде. В итоге получаем непробиваемую изоляцию виртуалок, которые можно запускать за сотни миллисекунд. Именно это решение работает под капотом AWS Lambda и Fargate, запуская в облаке миллионы serverless-функций и контейнеров каждую секунду. Оно называется Firecracker.

Если ваши задачи требуют мульти-тенантной изоляции, (ну, например, вы решили сделать собственное облако), Firecracker — это то, что надо. Этот инструмент микровиртуализации доступен в OpenSource.

Василий Пантюхин, архитектор Amazon Web Services, расскажет об архитектуре Firecracker, о том, как он используется AWS Lambda, сравнит его с альтернативными решениями и приведет примеры интеграции.

Дисклеймер: всё, что ниже — это личное мнение Василия, и оно может не совпадать с позицией Amazon Web Services.

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

Одно из фундаментальных свойств публичных облаков — мультитенантность.

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

Еще одно свойство, которое присуще публичным облакам — это хайлоад.

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

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

Виртуальные машины или контейнеры?

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

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

  • безопасности, когда одна виртуалка не имеет возможности получить доступ к данным в другой машине;
  • ресурсов, когда я заказал 8 ГБ памяти, то надеюсь, что эта RAM будет моей и никто не сможет претендовать на ресурс, за который я заплатил.

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

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

Я считаю, что на сегодняшний день правильно «сваренный» контейнер дает достаточный уровень безопасности.

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

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

В том самом хайлоаде критически важных сервисов, например, AWS Lambda (serverless-функции) и AWS Fargate (serverless-контейнеры). Firecracker уже используется в продакшн.

Проблемы старой AWS Lambda

AWS Lambda — это сервис serverless-функций. Берем функцию на Java, Go, Python или другом языке, кидаем ее в Lambda, и она волшебным образом исполняется. Выделять и управлять ресурсами не нужно. Как это было реализовано раньше?

Такая виртуалка потребляет ресурсы, даже когда ничего не делает. Для каждого аккаунта AWS выделяли одну или несколько отдельных виртуальных машин EC2 для изоляции функций, которые принадлежат разным тенантам. Получается, что в течение часа EC2-машина используется всего лишь несколько секунд. Предположим, что мы запускаем функцию раз в 10 минут, а она выполняется за 200 мс как обычная Lambda. Это бешено невыгодно. При этом даже во время исполнения она не потребляет все доступные ресурсы — утилизация ниже плинтуса.

Как решили проблему утилизации?

Разработали с нуля свое собственное решение Firecracker. Это очевидно из названия доклада:)

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

  • Работает на KVM в качестве гипервизора. Кто работает с AWS, знает, что у нас два любимых гипервизора. Виртуальные машины предыдущих поколений работают на Xen. С конца 2017 г. под капотом всех машин живет KVM.
  • Запускается максимально быстро. На эталонном железе требованием было полноценная загрузка микровиртуалки за 125 мс.
  • Минимальные накладные расходы на виртуализацию. В эталонной архитектуре одна микровиртуалка Firecracker дополнительно потребляет всего лишь 5 MB памяти. 
  • Возможность максимально плотного запуска. Расчетные параметры — полная загрузка 5 микровиртуалок на ядро в секунду. Это требование принципиально именно для таких сервисов как AWS Lambda. Функции должны быстро взлетать, отрабатывать и умирать, освобождая ресурсы для следующих.
  • Возможность переподписки. Именно возможность — не обязательно ее использовать. По сути, это выделение виртуальных ресурсов в большем объеме, чем они физически доступны. Это значит, что на сервере есть 16 ГБ RAM, и вы на нем одновременно запускаете 4 виртуальные машины, каждая из которых уверена, что ей доступно 8 ГБ памяти. 

AWS Lambda с Firecracker под капотом

Что изменилось в новой версии AWS Lambda? С точки зрения конечного пользователя ничего не поменялось. Миграция на обновленную архитектуру в продакшн прошла полностью прозрачно и незаметно для потребителей. Функции Lambda короткоживущие — сделать это было нетрудно.

Как же выглядит современная архитектура?

На самом нижнем уровне теперь находится не виртуальная машина, а физическая bare-metal.

Такие серверы позволяют полноценно использовать все функции CPU, например, Intel VT. Это дает дополнительные преимущества при использовании виртуализации на вышестоящих уровнях.

Выше запускаются микровиртуалки Firecracker со своими собственными гостевыми ОС. Поверх железки стоит хостовая операционка с модулем KVM в ядре. Ну а в них уже реализованы компоненты самого сервиса AWS Lambda.

Новый подход позволяет запускать легковесную микровиртуалку, только когда это действительно нужно. Раньше под каждого заказчика, который использует функции Lambda, мы выделяли отдельные EC2 виртуальные машины с низкой утилизацией. Утилизация ресурсов принципиально улучшилась. Изоляция ресурсов Firecracker друг от друга позволяет нам делать это на общем железе с максимальной плотностью.

Что в коробке?

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

Принципы дизайна

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

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

Избавьтесь от всего лишнего и сфокусируйтесь на одной задаче.

Упростить то, что осталось. Даже то, что осталось, должно быть простым, насколько это возможно.

На чем писали?

Firecracker написан на модном Rust, потому что:

  • он позволяет писать более безопасный код, в частности, с точки зрения памяти;
  • Производительность и накладные расходы сопоставимы с современными С++;
  • Rust давно используется, с 2015 года на нем написано много классных хайлоад-решений.

Железо

Повторю — Firecracker требует установки на реальное железо. В качестве эталонной конфигурации была выбрана bare-metal машина i3.metal.

Примечание: доклад был в начале апреля 2019. На тот момент поддерживалась только Intel платформа. В мае добавили альфа поддержку AMD, а в июне ARM. AMD, возможно, будет немного дешевле, чем Intel, а поддержка ARM открывает интересные возможности для работы с мультитенантным IoT. 

Поэтому, если решитесь установить Firecracker на них, то не забудьте после экспериментов обязательно погасить эти машины. Если заказывать i3.metal или другую bare-metal машину в AWS, то для экспериментов это будет слишком мощная и дорогая конфигурация. Иначе в конце месяца придет приличный счет.

Да, можно загружать Firecracker в виртуальном окружении. Есть ли вариант дешевле? Зато это можно делать в GCP, Azure, DigitalOcean или использовать Proxmox, Parallels, VMware Fusion. Но уже не в AWS — мы принципиально не поддерживаем на EC2 вложенную (nested) виртуализацию. Все будет работать, если придерживаться требований, в частности, по версии ядра гостевой ОС.

Ядро

Принципиальный элемент решения — модуль ядра KVM.

На всякий случай в качестве отступления опишу, что такое KVM. Это не гипервизор целиком, а только его часть. Ее основные задачи — это настройка виртуального процессора (vCPU) и старт виртуальной машины.

Кроме процессора нужны еще какие-то устройства. Но этого не хватит для полноценной работы. Их эмуляция происходит в user space.

VMM

Чтобы появились хотя бы базовые девайсы, нужен еще один компонент — Virtual Machine Manager (VMM). До Firecracker основным VMM-вариантом был QEMU.

Для этого было несколько причин. Мы серьезно рассматривали возможность использования QEMU, но решили разработать свой «велосипед».

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

Именно поэтому у него достаточно большая поверхность атаки. Безопасность. QEMU умеет эмулировать много девайсов. С точки зрения безопасности мы обязаны поверхность атаки минимизировать. Наша задача — сделать микровиртуалку.

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

Устройства

Firecracker реализует VMM, который используется для эмуляции устройств. Мы эмулируем следующие устройства:

  • Консоль. Полезная и нужная вещь, хотя ее можно и отключать.
  • Клавиатура. Мы сделали хитрую клавиатуру — всего лишь с одной кнопкой «Reset». Мы просто не доверяем программному «Reset» тех ОС, которые потенциально могут запускаться в Firecracker. Поэтому сделали железный.
  • Драйверы Virtio для диска и сети. Virtio — это очень примитивное устройство. По сути, это «ring buffer». Гостевая система что-то записала в буфер, нажала на звонок, а хостовая система прочитала данные из этого буфера через файл. 

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

За это отвечает rate limiter. Ну и еще нам нужна возможность контроля потребления ресурсов.

Но как ими управлять и настраивать конфигурацию микровиртуалок? Девайсы есть. Здесь нам помогут API управления.

Управление

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

Мы придерживаемся Open API спецификации, так что можно пользоваться Swagger.

Метаданные

Есть такое зло — hard coding. Это когда прямо в код зашиваются какие-то данные, например, логин и пароль доступа к другому ресурсу. Это, конечно, не допустимо. Периодически нам нужно передавать какие-то данные внутрь микровиртуалки. Это делается через сервис метаданных.

Чтобы получить эту информацию внутри микровиртуалки, нужно по REST API запросить http://169. Требуемую информацию через сокет мы передаем в трэд метаданных IMDS. 169. 254. Пользователи AWS уже знают этот «волшебный» IP. 254/latest/meta-data. Точно также микровиртуалки могут получить детальную информацию о конфигурации самих себя.

Логи

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

Дополнительная изоляция

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

Это стандартные меры предосторожности:

  • cgroups для ограничения ресурсов;

  • namespace для изоляции процессов;
  • seccomp — аналог файрвола для системных вызовов;
  • и, конечно, всеми любимый chroot.

Интеграция с другими сервисами

Есть ли уже готовые решения на базе Firecracker? Конечно, да.

OSV

Эта операционная система заточена под запуск всего одного приложения. За счет этого у нее предельно просто организованы работа с памятью и планировщик. Производительная и легкая в управлении ОС теперь работает и поверх Firecracker.

Containerd

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

Он уже управляет runc, через агента, который установлен внутри микровиртуалки. Вместо обычного shim, containerd обращается к Firecracker shim. Проверено, работает.

Kata Containers

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

Почему вы их не использовали? — Механизм изоляции контейнеров уже придуман — это Kata Containers. Поэтому мы решили сварить свое. Зачем разрабатывать свое?
— Kata работает с QEMU, а мы не хотим работать с QEMU.

Разработчики из Kata встретились с разработчиками Firecracker и договорились об интеграции. Но получилось интересно. Kata Containers v1. Потому что все видят в этом огромный плюс. 5 уже поддерживает и QEMU, и Firecracker.

В Kubernetes с v1. Получается, что мы не конкурируем, а гармонично дополняем друг друга. 12 можно определить runtimeClassName как Kata FC или Kata QEMU, и запускать свои контейнеры в соответствующей изоляции.

Мое личное мнение, что здесь принципиально то, какое у вас приложение — pets или cattle? Как же выбрать изоляцию, которая подходит для вашей задачи — Firecracker или QEMU?

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

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

Что в итоге?

Мы начали с противоречий. Кажется, что разрешение противоречия — это подход «и нашим и вашим», что-то среднее, что удовлетворит всех. Но опыт говорит, что компромисс не обязателен.

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

Главный плюс, который мы получили — улучшение утилизации сервисов. Мы уже используем Firecracker в продакшн нескольких наших сервисов. Частью этой экономии мы поделились с заказчиками. Это позволило значительно сэкономить. Выгода видна невооруженным глазом, поэтому нет никаких сомнений, что мы будем и дальше развивать этот продукт и делиться наработками с Open Source. Например, после внедрения Firecracker цены на serverless-контейнеры AWS Fargate в январе 2019 упали на 35-50%. То, что Firecracker уже начали интегрировать с другими решениями говорит о растущем интересе со стороны комьюнити.

Если у вас идея или готовый продукт в описании которого есть слова «мультитенантная изоляция» — это явный индикатор того, что стоит поэкспериментировать с микровиртуалками Firecracker.

И дело не только в возможном языковом барьере, а в привычной нам культуре делиться техническими деталями. Этот доклад отлично иллюстрирует принцип, которого мы стараемся придерживаться на конференциях Онтико, — получать мировой опыт от русскоговорящих специалистов. Будут у нас и доклады на английском языке, подпишитесь на рассылку — будем рассказывать, когда к сорока принятым докладам добавятся новые. В ноябре на HighLoad++ Василий выступит снова, а компанию ему составят, например, Артемий Колесников из Facebook, Vittorio Cioe из Oracle и, конечно, Петр Зайцев из Percona.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть