company_banner

Строим собственный serverless на основе Fn

Автор оригинала: Nicolas Fränkel
  • Перевод


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


Другая общая черта — тенденция к минимизации и фокусировании кода, поэтому бессерверные вычисления иногда называют "функция как услуга" (FaaS).


Исторически первым поставщиком облачных услуг, предложившим FaaS с AWS Lambda, был Amazon, откуда и пошло это название. Другие облачные поставщики услуг также предлагают аналоги:


  • Cloud Functions от компании Google
  • Azure Functions от Microsoft

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


  • Платформу Apache OpenWhisk, разрабатываемую в инкубаторе компанией IBM,
  • Spring Cloud Functions, как часть достаточно богатой экосистемы Spring Framework, которая также может быть использована как фасад AWS Lambda, Azure Functions и OpenWhisk,
  • Проект Fn, поддерживаемый компанией Oracle.

Все они полностью независимы от облаков, то есть устанавливаются в любое облако, включая ваше собственное, публичное или частное, и конечно же в Exoscale.


Как проект Fn устроен


Fn полностью основан на Docker, состоит из двух основных компонентов:


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

Функции, разворачиваемые в Fn, так же выполняются в отдельных контейнерах, что позволяет поддерживать весьма много языков для программирования, например… Clojure!


Аргументы функций передаются на стандартный ввод (STDIN), результаты пишутся на стандартный вывод (STDOUT). Ели аргументы или возвращаемые значения не являются простыми значениями (например, объект JSON) — они могут быть преобразованы с помощью слоя абстракции, предоставляемого самим Fn в виде комплекта разработки функций (FDK).


Для удобства предлагаются встроенные наборы шаблонов, облегчающих разворачивание FaaS на обширном списке разных языков и их версий (Go, разные версии Java, Python и т.д.).


Создать FaaS просто, следуя этой схеме:


  • Разворачиваем функцию с помощью CLI Fn: создается конфигурационный файл приложения для Fn, основанный на выбранном шаблоне.
  • Выкатываем собственную функцию, опять же с помощью CLI Fn: образ контейнера помещается в некий репозиторий, после чего сервер уведомляется о существовании и размещении этого образа.


Принцип поставки функций в Fn


Локальная установка и тестирование бессерверных функций


Приступим к установке Fn на локальной машине. Сначала устанавливается Docker, как того требует Fn. Предполагается, что мы на Debian/Ubuntu:


$ sudo apt-get update
$ sudo apt-get install docker.io

Ну или используйте менеджер пакетов/ сборку Docker согласно вашей системе. Затем можно перейти прямиком к установке Fn CLI. К примеру, с помощью curl:


$ curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh

Если вы работаете на OSX с установленным Homebrew, можно пойти другим путем:


$ brew install fn

==> Downloading https://homebrew.bintray.com/bottles/fn-0.5.8.high_sierra.bottle.tar.gz
==> Downloading from https://akamai.bintray.com/b1/b1767fb00e2e69fd9da73427d0926b1d1d0003622f7ddc0dd3a899b2894781ff?__gda__=exp=1538038849~hmac=c702c9335e7785fcbacad1f29afa61244d02f2eebb
######################################################################## 100.0%
==> Pouring fn-0.5.8.high_sierra.bottle.tar.gz
  /usr/local/Cellar/fn/0.5.8: 5 files, 16.7MB

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


$ fn init --runtime node --trigger http hellonode

Creating function at: /hellonode
Function boilerplate generated.
func.yaml created.

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


# Каталог с node выглядит так:

   hellonode
   ├── func.js
   ├── func.yaml
   └── package.json

# Свежеустановленное окружение Java11 такое:

   hellojava11
   ├── func.yaml
   ├── pom.xml
   └── src
       ├── main
       │   └── java
       │       └── com
       │           └── example
       │               └── fn
       │                   └── HelloFunction.java
       └── test
           └── java
               └── com
                   └── example
                       └── fn
                           └── HelloFunctionTest.java

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


В случае среды выполнения Node это означает:


$ cat hellonode/func.js

const fdk=require('@fnproject/fdk');

fdk.handle(function(input){
  let name = 'World';
  if (input.name) {
    name = input.name;
  }
  return {'message': 'Hello ' + name}
})

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


Для начала мы запустим сервер Fn. Как уже было сказано, сервер Fn представляет собой Docker контейнер, следовательно он после запуска пойдет и возьмет образ из реестра Docker.


$ fn start -d                    # запускаем локальный сервер в фоне

Unable to find image 'fnproject/fnserver:latest' locally
latest: Pulling from fnproject/fnserver
ff3a5c916c92: Pull complete
1a649ea86bca: Pull complete
ce35f4d5f86a: Pull complete

...

Status: Downloaded newer image for fnproject/fnserver:latest
668ce9ac0ed8d7cd59da49228bda62464e01bff2c0c60079542d24ac6070f8e5

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


Fn СLI будет искать файл func.yaml в текущем каталоге, который будет применяться для настройки функции. Так что сначала надо перейти в наш каталог hellonode.


$ cd hellonode
$ fn deploy --app fnexo --local  # выкатываем функцию локально, имя приложения - fnexo.
                                 # параметр local не заливает образ в удаленный реестр,
                                 # запуская его напрямую

Deploying hellonode to app: fnexo
Bumped to version 0.0.2
Building image nfrankel/hellonode:0.0.3 .
Updating function hellonode using image nfrankel/hellonode:0.0.3...
Successfully created app:  fnexo
Successfully created function: hellonode with nfrankel/hellonode:0.0.3
Successfully created trigger: hellonode-trigger

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


  • используя команду Fn invoke
  • вызывая напрямую через http

Вызов invoke через Fn просто эмулирует работу по HTTP для тестов, что удобно для быстрой проверки:


$ fn invoke fnexo hellonode      # вызываем функцию hellonode приложения fnexo

{"message":"Hello World"}

Для того чтобы вызвать функцию напрямую, надо знать полный URL:


$ curl http://localhost:8080/t/fnexo/hellonode-trigger

{"message":"Hello World"}

Сервер Fn предоставляет свои функции через порт 8080, и, как кажется, URL функции соответствует схеме t/app/function, но не полностью. Через HTTP функция вызывается не напрямую, а через так называемый trigger, который согласно своему названию "запускает" вызов функции. Триггеры определяются в `func.yml проекта:


schema_version: 20180708
name: hellonode
version: 0.0.3
runtime: node
entrypoint: node func.js
format: json
triggers:
- name: hellonode-trigger
  type: http
  source: /hellonode-trigger    # URL триггера

Можем поменять имя триггера, чтобы оно соответствовало имени функции, это все упростит:


triggers:
- name: hellonode-trigger
  type: http
  source: /hellonode    # совпадает с именем функции

Затем запускаем поставку функции еще раз и вызываем ее из нового триггера:


$ fn deploy --app fnexo hellonode --local
$ curl http://localhost:8080/t/fnexo/hellonode

{"message":"Hello World"}

Все работает! Самое время перейти к натурным экспериментам и опубликовать наш FaaS на сервере!


Установка сервисов бессерверных функций на собственной инфраструктуре


Давайте по-быстрому установим виртуальную машину используя CLI Exoscale. Если вы ее еще не настроили — можно воспользоваться нашим руководством для быстрого запуска. Это крутой инструмент, который еще больше повысит вашу продуктивность. Не забывайте о том, что надо настроить правило для открытия порта 8080 в Security Group! Следующие команды запустят чистую виртуальную машину, готовую для размещения наших функций:


$ exo firewall create fn-securitygroup
$ exo firewall add fn-securitygroup ssh --my-ip
$ exo firewall add fn-securitygroup -p tcp -P 8080-8080 -c 0.0.0.0/0
$ exo vm create fn-server -s fn-securitygroup

Затем можно зайти по ssh на виртуальную машину и установить удаленный сервер Fn:


$ exo ssh fn-server

The authenticity of host '185.19.30.175 (185.19.30.175)' can't be established.
ECDSA key fingerprint is SHA256:uaCKRYeX4cvim+Gr8StdPvIQ7eQgPuOKdnj5WI3gI9Q.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '185.19.30.175' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04 LTS (GNU/Linux 4.15.0-20-generic x86_64)

Затем устанавливаем Docker и сервер Fn также, как это уже делалось на локальной машине, запускаем сервер:


$ sudo apt-get update
$ sudo apt-get install docker.io
$ sudo systemctl start docker
$ curl -LSs https://raw.githubusercontent.com/fnproject/cli/master/install | sh
$ sudo fn start

...

    ______
   / ____/___
  / /_  / __ \
 / __/ / / / /
/_/   /_/ /_/
    v0.3.643

Fn готов для получения функций! Для целевой передачи функций на удаленный сервер будем использовать команду deploy с локального компьютера, опуская флаг --local.


Кроме этого, Fn требует указать размещение сервера Fn и реестра Docker. Эти параметры могут быть установлены через переменные окружения FN_API_URL и FN_REGISTRY соответственно, но предлагается и более удобный способ для легкого управления созданием и управлением конфигураций для развертывания.


В терминах Fn конфигурация для развертывания называется context. Следующая команда создаст контекст:


$ fn create context exoscale --provider default --api-url http://185.19.30.175:8080 --registry nfrankel

Вы можете просмотреть доступные контексты так:


$ fn list contexts

CURRENT NAME      PROVIDER      API URL                      REGISTRY
    default       default       http://localhost:8080/
    exoscale      default       http://185.19.30.175:8080    nfrankel

А переключиться на контекст, который только что был создан, так:


 $ fn use context exoscale

 Now using context: exoscale

Начиная с этого места поставка функций Fn будет загружать образы Docker, используя выбранную учетную запись на DockerHub (в моем случае — nfrankel), после чего уведомлять удаленный сервер (в этом примере — http://185.19.30.175:8080) о местонахождении и версии последнего образа, содержащего вашу функцию.


$ fn deploy --app fnexo .   # выполняется на локальной машине из каталога hellonode

Deploying function at: /.
Deploying hellonode to app: fnexo
Bumped to version 0.0.5
Building image nfrankel/hellonode:0.0.5 .

Наконец:


$ curl http://185.19.30.175:8080/t/fnexo/hellonode

{"message":"Hello World"}


Жизненный цикл функции в бессерверных вычислениях на основе Fn


Преимущества бессерверных вычислений на своих мощностях


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


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


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


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


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

Southbridge
527,59
Обеспечиваем стабильную работу highload-проектов
Поделиться публикацией

Комментарии 14

    +1
    А есть реальные случаи удобного использования serverless?
      0
      Да сколько угодно — от «формы обратной связи» для полностью статического сайта на голом html до крупного портала, где какие-то вещи обёрнуты в API и вынесены наружу, в serverless. Например загрузка и ресайз картинок/видео, лента комментариев, краулинг внешних ресурсов итд.

      aws.amazon.com/ru/getting-started/projects/build-serverless-web-app-lambda-apigateway-s3-dynamodb-cognito
        0
        С одной стороны да, с другой делать FaaS ради формы обратной связи? :)

        Просто я не пойму смысла в этом всем. Если вы можете в FaaS, то что мешает сделать реплицируемый сервис для ресайза картинок? Будет, скорее всего, дешевле и предсказуемей.
          0
          Скорее тут больше тема про MVP, когда подготовка инфраструктуры по времени сопоставима со временем начала разработки. Например, надо под какое-то событие быстренько бота набросать. И непонятно, сколько там пользователей будет, чтобы определить, куда его выделить, сколько мощности выделить, чтобы в случае хабраэффекта он не упал, и прочее. А тут быстренько получается скалируемая площадка, где о непосредственном скалировании задумываться просто не нужно.
            0
            делать FaaS ради формы обратной связи? :)

            Хм, вы спросили — «для чего нужен serverless». Я ответил, подразумевая публичные сервисы, такие как Lambda. Вопрос о необходимости построения собсвтенной FaaS-платформы вроде не прозвучал?
              0
              Вопрос скорее о реальных кейсах. Не «давайте сделаем serverless чтобы было», а «мы используем для ...».
                0
                — обработчик события в с3 бакете, например, загрузка картинки,
                — обработчик события в cloudwatch,
                — автоматизация инфраструктуры, например прицепить еластик ип на другую году
                — и тп
                вы, конечно, можете поставить одну или больше нод с воркерами, которые будут обрабатывать эти или подобные события, но это будет неэффективно и дороже.
                и кстати, родной faas рулит, благодаря тесной интеграции с инфраструктурой.
                  0

                  Cloud Guru полностью serverless кажется с 2015 года

            0

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


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


            IMHO, это хайп, популярность которого начала снижаться, к тому же.

              0
              Вот в этом и дело. Я тоже поигрался. Ну прикольно, да. Но в облаках выходит дорого, а на своем железе бессмысленно. Пока реальных решений не видел, все только в теории.
            0
            Было бы интересно сопоставить финансовые затраты в сравнении с FaaS от того же Amazon. Ведь в варианте самостоятельной подготовки и поддержки инфраструктуры нужен, как минимум, хороший инженер, тогда как Lambda вполне работает без него.

            Я могу ошибаться, но свой собственный FaaS будет востребован либо для очень больших компаний, где внутри IT есть несколько слоёв сервисов, оказывающих друг другу услуги, либо для компаний закрытых, которые вот совсем никак не доверяют вендорам.
              0
              не представляю себе кошмар дебаггинга чего-то построенного на куче серверлесс функциях связанных друг с другом.
              выглядит либо как императивное программирование на клауд-функциях либо как обернуть монолит в FaaS и задеплоить в продакшн поскорее
                0

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

                0

                Очень интересно сравнение с другими фреймворками. Lambda, serverless, openwhisk etc.


                С функциями еще связан такой интересный момент… что с ними реально просто разрабатывать, но эксплуатация становится сложнее.


                1. это новая и непонятная технология. Сыроватая.
                2. у нас вместо условных 10 контейнеров получается условные 100 функций — гораздо больше элементов
                3. как следствие — кошмар с обновлениями, логированием и пр.
                4. облачные (лямбда) функции часто бывают ДОРОЖЕ, чем такое же решение на базе контейнеров
                5. ограничения технологии — функции четко лимитированы по объему используемых ресурсов (память, диск, время выполнения).
                6. весь персистенс (сохранение данных) — только во внешних хранилищах (которые еще нужно затащить в свое облако, или использовать готовые S3/RDS etc)

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

                Самое читаемое