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

Облачные решения сегодня предлагают несколько вариантов запуска контейнеров, и serverless-подход — один из них. Если разместить код приложения в Serverless Containers, облако само запустит нужный контейнер с вашим сервисом тогда, когда появится потребность в его вызове. Разница не только в тарификации по времени работы контейнера, но и в эластичности. Если нагрузка резко возрастёт, то сервис запустит дополнительные экземпляры контейнера. Но и здесь есть свои ограничения. 

В этой статье покажем, какие есть способы запуска контейнеров в Yandex Cloud, и расскажем, как и когда лучше запускать контейнеры в Serverless Containers. Материал может пригодиться бэкенд-разработчикам, DevOps-инженерам и системным администраторам.
image


Способы запуска контейнеров в Yandex Cloud


Итак, у нас есть контейнеры, которые мы хотим запустить в облаке. Кратко напомним, какие возможности есть у разных способов запуска. 
  1. Запуск контейнера в арендуемой виртуальной машине (ВМ). Это самый очевидный способ, и в нём нет никаких ограничений для запуска Docker-контейнеров — можно делать это вручную или с помощью инструмента для оркестрации контейнеров. Подойдёт тем, кому нужен контроль инфраструктуры и полная свобода выбора запускаемого ПО.

    Главное ограничение — это уже упомянутое отсутствие эластичности по умолчанию: нужно заранее определить параметры ВМ, причём с запасом. Ещё одно ограничение связано с отказоустойчивостью — аренда только одной ВМ по умолчанию не может считаться отказоустойчивой. А с появлением трёх и более ВМ неизбежно возникают вопросы балансировки нагрузки, которые в IaaS нужно решать самому. Такой вариант подразумевает высокий порог входа: если в качестве оркестратора выбран Kubernetes, администрировать кластер k8s также нужно будет самостоятельно.
  2. Использование Managed Kubernetes. В этом сервисе уже можно в автоматическом режиме развертывать и масштабировать приложения на основе Kubernetes-спецификаций, а также управлять ими. При этом специалисты облака берут на себя обслуживание и обновление компонентов инфраструктуры кластера: не нужно думать о бэкапах, обновлениях, синхронизации данных в распределённой базе конфигураций etcd и так далее. С точки зрения масштабирования это более гибкий вариант: если нужно увеличить нагрузку, Autoscaler создаст новую виртуальную машину с учётом заданных в настройках лимитов и квот. И наоборот: если нагрузка на узлы недостаточная, число узлов в группе будет постепенно уменьшаться до указанного минимума. Также есть горизонтальное и вертикальное автомасштабирование подов.

    Но при этом тарификация по-прежнему не близка к модели Pay as you go: клиент платит за работу ВМ, на которой развёрнут контейнер, а также отдельно за использование мастера кластера Kubernetes. Помимо сравнительно высокой стоимости ресурсов, у этого сервиса всё ещё остаётся довольно высокий порог входа: масштабирование и другие параметры необходимо настроить.
  3. Запуск в Container Solution. В Marketplace есть готовый образ виртуальной машины, это позволяет быстро создать ВМ и запустить в ней Docker-контейнер или даже группу контейнеров. В этом случае можно работать с Docker-спецификациями и прописывать, какие образы и с какими параметрами будут запускаться на машине при старте Docker’a. Порог входа не такой высокий, как в случае Managed Kubernetes, но масштабирование и другие параметры всё же нужно будет реализовать самостоятельно. 

    В этом сервисе тоже тарифицируется арендованная ВМ, а если используются Docker-образы из сервиса Container Registry — будет тарифицироваться объём хранилища образов и исходящий трафик. 
  4. Serverless containers. Здесь клиент приносит Docker-контейнер, и когда приложение его вызывает, сервис в бессерверном режиме запускает контейнер самостоятельно. Это подходит для приложений, архитектура которых следует принципам событийно-ориентированного подхода.

    Так как serverless предполагает, что время работы контейнера не должно превышать минуты, а в идеальном случае — секунды или доли секунд, необходимо как-то решать вопрос «слушающей» части приложения. Для этой цели отлично подходит API Gateway, который всегда готов принимать входящие запросы, чтобы по цепочке передать их дальше в контейнер для обработки. Для того чтобы ещё больше повысить надежность обработки запроса, в эту цепочку могут включить очередь сообщений. Тогда запрос не будет считаться выполненным, пока контейнер не вернёт сообщение об успешной обработке запроса обратно в очередь. Контейнеры могут запускаться по триггеру на события в других сервисах облака, например, при добавлении объекта в объектном хранилище. 

    Serverless Containers тарифицируется по модели Pay as You Go: клиент платит за вызовы контейнера и время использования CPU, RAM и сетевых ресурсов, пока контейнеры запущены, а также за хранение образа в Container Registry — если для работы используется этот реестр. 

    У сервиса есть свои ограничения:
    • Docker-контейнеры в Serverless Containers не работают в фоновом режиме, а только во время выполнения запроса. 
    • Отсутствует состояние контейнера: если трафика в контейнер нет, если события не наступают, то платформа «тушит» контейнер и его состояние исчезает из памяти. Причём она может делать это после каждого обращения к контейнеру, если между вызовами произошел довольно большой временной интервал. 
    • Бессерверные контейнеры не подходят для обслуживания произвольных сетевых протоколов. Сервис поддерживает обработку только HTTP-вызовов, событий в очередях и других сервисах Yandex Cloud. Но в нём не получится развернуть контейнер, обслуживающий, например, произвольный TCP-based протокол.


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

Запуск контейнера с помощью Serverless Containers


Для нашего эксперимента возьмём docker-образ mongo-express, который развернёт административную панель над MongoDB. Он достаточно популярен (более ста миллионов скачиваний) и кроме того, удовлетворяет ограничениям Serverless Containers: не имеет своего состояния и служит довольно тонкой прослойкой для существующего кластера Mongo.

Перед запуском нужно подготовиться и установить интерфейс командной строки Yandex Cloud CLI: здесь инструкция для разных ОС

Шаг 1. Заводим реестр в сервисе Container Registry Yandex Cloud и загружаем в него docker-образ с приложением. Serverless-контейнер разворачивается из образов, которые добавлены в Container Registry, поэтому начнём с создания реестра:
yc container registry create --name my-first-registry

Получаем ID для обращения к созданному реестру:
..done
id: crpc9qeoft236r8tfalm
folder_id: b1g0itj57rbjk9thrinv
name: my-first-registry 
status: ACTIVE
created_at: "2018-12-25T12:24:56.286Z"

Не забудем аутентифицироваться в Container Registry с помощью Docker Credential helper: cконфигурируем Docker
yc container registry configure-docker

и проверим, что в конфигурационном файле /home/<user>/.docker/config.json появилась строка:
"cr.yandex": "yc"

Нам необходимо загрузить в реестр образ mongo-express с Docker Hub. Для этого присвоим скачанному образу тег вида cr.yandex/<ID реестра>/<имя Docker-образа>:<тег>.
docker tag mongo-express \ 
cr.yandex/crpc9qeoft236r8tfalm/mongo-express:hello

Отправим образ в реестр Container Registry.
docker push cr.yandex/crpc9qeoft236r8tfalm/mongo-express:hello 

Шаг 2. Создаём контейнер. Для этого можно воспользоваться консолью управления, CLI, Terraform или API.  

В консоли просто выбираем каталог, в котором нужно создать контейнер, переходим в сервис Serverless Containers, нажимаем Создать контейнер, вводим имя и описание контейнера. 

То же самое в командной строке
yc serverless container create --name <имя_контейнера>

Здесь по умолчанию используется каталог, указанный в профиле CLI, но можно указать другой с помощью параметра --folder-name или --folder-id.

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

Шаг 3. Создаём сервисные аккаунты для использования образа и работы с секретами, назначаем роли. Подробнее о работе с сервисными аккаунтами и особенностях назначения прав поговорим в следующий раз. А пока лишь отметим, что на этом шаге нам нужен сервисный аккаунт для использования образа из Container Registry, потому что по умолчанию он приватный. 

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

То же самое в командной строке
yc iam service-account create --name <имя аккаунта>



Чтобы назначить сервисному аккаунту роль на текущий каталог, нажмём Добавить роль и выберем роль: serverless.containers.invoker и container-registry.images.puller на созданный нами реестр — они будут нужны для создания триггера, который запустит контейнер. Также создадим и добавим аккаунту роли, которые пригодятся на следующих шагах:
  • lockbox.payloadViewer для работы с секретами;
  • kms.keys.encrypterDecrypter на ключ шифрования, если секрет создан с использованием ключа Yandex Key Management Service.

Назначение ролей на ресурсы в командной строке
yc resource-manager <категория_ресурса> add-access-binding <имя ресурса>|<идентификатор ресурса> \
 --role <идентификатор_роли> \
 --subject serviceAccount:<идентификатор_сервисного_аккаунта>


Назначить права доступа на секрет и ключ шифрования разным аккаунтам также поможет документация сервиса Yandex Lockbox.

Шаг 4. Настраиваем переменные окружения. В консоли управления для Serverless Containers выбираем наш контейнер, переходим на вкладку Редактор. В блоке Параметры образа укажем переменную окружения и нажмём Добавить.

В нашем случае указываем IP-адрес сервера MongoDB, над которым мы разворачиваем административную панель и порт через который приложение будет доступно. 

В командной строке эти же действия мы будем делать через создание ревизии контейнера. Создавать новую ревизию необходимо при загрузке новой версии Docker-образа в Container Registry и при изменении настроек. Подробнее о других параметрах настройки рекомендуем почитать в документации

Создание ревизии с переменными окружения в командной строке
yc serverless container revision deploy \
 --container-name <имя_контейнера> \
 --image cr.yandex/crpc9qeoft236r8tfalm/mongo-express:hello \
 --cores 1 \
 --memory 1GB \
 --service-account-id <идентификатор_сервисного_аккаунта> \
 --environment ME_CONFIG_MONGODB_PORT=27017     


Названия других необходимых переменных окружения можно взять из документации mongo-express на docker-hub.

Шаг 5. Настраиваем работу с секретами через сервис Yandex Lockbox. Для начала создаём секрет с credentials от MongoDB. Это можно сделать прямо из UI: на вкладке Редактор в блоке Параметры образа заполняем поле Секрет Yandex Lockbox.
То же самое в командной строке
yc serverless container revision deploy \
 --container-name <имя_контейнера> \
 --image cr.yandex/crpc9qeoft236r8tfalm/mongo-express:hello \
 --cores 1 \
 --memory 1GB \
 --service-account-id <идентификатор_сервисного_аккаунта> \
 --secret environment-variable=KEY,id=fc3q4a***,version-id=fc3gvv**,key=key-id


Шаг 6. Поставим тайм-аут на работу контейнера. Рекомендуем взять значение с запасом, потому что мы не знаем, сколько времени могут занимать запросы к mongo-express. Например, 10 секунд. 
В консоли управления этот параметр задаётся на вкладке Редактор для контейнера, всё в том же блоке Параметры образа.  

В командной строке этот параметр задаётся через execution timeout
yc serverless container revision deploy \
 --container-name <имя_контейнера> \
 --image cr.yandex/crpc9qeoft236r8tfalm/mongo-express:hello \
 --cores 1 \
 --memory 1GB \
 --concurrency 1 \
 --execution-timeout 10s \
 --service-account-id <идентификатор_сервисного_аккаунта>


Шаг 7. Создаём последнюю ревизию контейнера. 

Работа по запуску контейнера закончена, теперь мы можем дать к нему публичный доступ и перейти по ссылке, чтобы увидеть административную панель, запущенную на техническом домене Serverless Containers. Здесь можно создавать таблицы, добавлять и удалять документы. А логи работы приложения будут записываться в сервис Yandex Cloud Logging.

Шаг 8. Настраиваем авторизацию через API Gateway. Мы протестировали работу приложения, теперь настроим работу через API-шлюз, ограничив к приложению доступ по IP-адресу. Для этого добавим возможность авторизации в приложении через интеграцию API Gateway с специальной функцией-авторайзером. 

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

Подведём итог


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

Комментарии (5)


  1. kulaginds
    07.09.2023 13:58
    +2

    Спасибо за туториал.

    Я использую Serverless Containers в своем прод-сервисе, который генерирует картинку на основе архива с данными.

    Сам генератор выполняется в группе ВМ и там настроено авто масштабирование при увеличении очереди. И сделано это из-за ограничения с диском в Serverless Container (его там нет, есть только tmpfs на 500 мб).

    Все лето я потратил на тесты Serverless YDB, Serverless Containers и Message Queue и пришел к нескольким выводам по этой связке:

    1. В базе могут быть долгие ответы (выше 1 секунды на селект по первичному ключу). В условиях 5 секунд на запрос, можно не успеть. Лучше работает Postgres в виртуалке 20% cpu (см. п. 3 про http веб-сервер). Держать pet-project на этой базе можно смело. Нагрузка меньше 1qps.

    2. В очереди иногда долго отвечает SendMessage. Изначально в очередь писал http-сервис из Serverless Container. Потом перенес http-сервис в постоянную виртуалку с 20% cpu и стало реже долго отвечать SendMessage, но проблема полностью не ушла. Уже общаемся с поддержкой по этому поводу.

    3. http веб-сервер работает хорошо, но иногда бывают то ли проблемы с сетью (она инициализируется медленнее, чем http веб-сервер на Golang), то ли с ресурсами (в логах биллится 5 сек, а первого сообщения в логах из функции main нет). Если вам критично, чтобы запрос отдавался за условные 5-10 сек, то Serverless Container не подойдет. Даже с подготовленным экземпляром (а он по цене примерно так же, как ВМ с 20% cpu). Для фоновых задач по крону или из очереди подходит на отлично.

    С нетерпением жду фичу по монтированию s3 бакета в Serverless Container.


    1. kulaginds
      07.09.2023 13:58
      +2

      Спасибо ребятам, за то, что создали такое. Мощная работа. В РФ аналогов пока не нашел.


    1. dpivovarov Автор
      07.09.2023 13:58

      Спасибо за обратную связь! Про монтирование бакета - скоро будут апдейты, подключайтесь к трансляции нашего трека на https://scale.yandex.ru/


  1. Naglec
    07.09.2023 13:58
    +1

    Насколько это дороже арендуемых VM? Спотовых?


    1. dpivovarov Автор
      07.09.2023 13:58

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

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