Блог Слёрм

Бессерверные вычисления на основе OpenWhisk, часть 3

Эта статья продолжает цикл переводных заметок об OpenWhisk от автора Priti Desai. Сегодня рассмотрим примеры развертывания Zip-функций, зависимости GitHub, а также подробнее опишем синхронизацию объектов между клиентом и сервером OpenWhisk.

Zip-функции

OpenWhisk поддерживает создание функции из единственного файла с исходным кодом, как это было [показано ранее](). Он же поддерживает создание функции с использованием нескольких файлов с исходным кодом и набором пакетов, от которых функция зависит. Этот вариант использования функций называется zip-функцией. Давайте попробуем развернуть zip-функцию с помощью wskdeploy.

Шаг первый

Создаем файл-манифест:

packages:
         zipaction:
                actions:
                my-zip-action:
                        function: actions/my-zip-action
                        runtime: nodejs:6
                        inputs:
                               name: Amy

Считаем, что my-zip-action имеет такую структуру каталога, содержащую исходный код функции:

$ ls actions/my-zip-action
index.js
package.json

Содержимое файла index.js:

function helloworld(params) {
       var format = require('string-format');
       var name = params.name || 'Stranger';
       payload = format('Hello, {}!', name)
       return { message: payload };
}

exports.main = helloworld;

Содержимое файла package.json:

{
        "name": "my-zip-action",
        "description": "Node OpenWhisk zip action to demo Whisk Deploy",
        "license": "Apache-2.0",
        "version": "1.0.0",
        "main": "index.js",
        "dependencies": {
                "string-format": "0.5.0"
         }
}

Шаг второй


Запускаем npm install для установки string-format:

cd actions/my-action
npm install --production

Шаг третий

Разворачиваем zip-функцию:


Скопировать код

Мы развернули zip-функцию my-zip-action с зависимым модулем string-format. При указании каталога в манифесте по ключу function создается zip-архив из этого каталога, а также — функция из этого архива, так что не надо искать его по файловой системе. После развертывания можно работать с новой функцией точно так же, как и с другими функциями.

Include и exclude для файлов в zip-функциях

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

Включение файлов

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

$ cd actions/
$ ls -1 ./
common/
greeting1/
greeting2/
manifest.yaml
$ ls -1 common/
utils.js
$ ls -1 greeting1/
index.js
package.json
$ ls -1 greeting2/
index.js
package.json

Можно видеть файл index.js в каталоге greeting1, а также еще один index.js в каталоге greeting2, и оба ссылаются на utils.js, расположенный в common/.

Содержимое файла index.js, расположенного в actions/greeting1/:


Скопировать код

Содержимое файла index.js, расположенного в actions/greeting2/:


Скопировать код

Внутри ключа include содержится список файлов или каталогов, которые должны быть включены в функцию. Каждый элемент этого списка должен иметь source и\или destination, к примеру:

include:
     - [source]
     - [source, destination]

Примечания:
  • source содержит относительный путь от каталога, содержащего manimanifest.yaml. destination подразумевает относительный путь от каталога с функцией, к примеру actions/greeting1 и actions/greeting2 в следующем примере.
  • Если параметр destination не задается — считается что он будет такой же, как и source.

Содержимое файла-манифеста:


Скопировать код

include работает с различными сочетаниями source и destination:

  • просто source:

include:
     - [actions/common/utils.js]

При такой записи utils.js будет скопирован в actions/greeting/actions/common/utils.js, а index.js может сослаться на него так:

var utils = require('./actions/common/utils.js')

  • include с переименованием:

include:
     - ["actions/common/utils.js", "./common/myUtils.js"]

С таким определением utils.js будет помещен по пути actions/greeting/common/myUtils.js, а index.js будет ссылаться на него так:

var utils = require('./common/myUtils.js')

  • include с другим путём:

include:
      - ["actions/common/utils.js", "./common/utility/utils.js"]

В таком случае utils.js будет скопирован в actions/greeting/common/utility/utils.js, со ссылкой из index.js:

var utils = require('./common/utility/utils.js')

  • include с символом *:

include:
      - ["actions/common/*.js", "./common/"]

В этом варианте utils.js вместе с другими файлами с расширением .js будет скопирован в каталог actions/greeting/common/, а в index.js эти файлы будут подключаться так:

var utils = require('./common/utils.js')

Включение каталогов

В include можно прописать каталог, который будет рекурсивно скопирован в указанное место перед включением в архив. Например libs`` содержит список библиотек, на которые ссылаетсяindex.jsв подкаталогеgreeting3/```:

$ cd actions/
$ ls -1
libs/
greeting3/
manifest.yaml
$ ls -1 libs/
lib1/
lib2/
lib3/
$ ls -1 libs/lib1/
utils.js
$ ls -1 libs/lib2/
utils.js
$ ls -1 libs/lib3/
utils.js
$ ls -1 greeting3/
index.js
package.json

Содержимое index.js в каталоге actions/greeting3/:


Скопировать код

Содержимое файла-манифеста:

packages:
        zipactionwithinclude:
               actions:
                     greeting3:
                             function: actions/greeting3
                             runtime: nodejs:6
                             include:
                                   - ["actions/libs/*", "libs/"]

В этом примере каталог libs целиком рекурсивно копируется в actions/greeting3/libs/.

Подключение каталогов с символом *:

  • пример 1:

include:
      - ["actions/libs/*/utils.js", "libs/"]

При таком написании будут скопированы из libs все подкаталоги, содержащие utils.js. Ссылки из index.js будут выглядеть так:

var lib1 = require('./libs/lib1/utils.js')
var lib2 = require('./libs/lib2/utils.js')
var lib3 = require('./libs/lib3/utils.js')

  • пример 2:

include:
      - ["actions/*/*/utils.js"]

При такой записи будут скопированы все подкаталоги, подходящие под маску и содержащие utils.js. Доступ из index.js будет таким:

var lib1 = require('./actions/libs/lib1/utils.js')
var lib2 = require('./actions/libs/lib2/utils.js')
var lib3 = require('./actions/libs/lib3/utils.js')

  • пример 3:

include:
      - ["actions/*/*/utils.js", "actions/"]

В этом примере явно указано, куда все скопируется. Доступ из index.js будет таким же, как и в предыдущем примере.

Исключение

Ключевое слово exclude может использоваться в виде списка файлов и каталогов, разрешено использовать маску в виде символа *. Пример использования:

exclude:
       - actions/common/*.js
       - actions/libs/*/utils.js

Общий пример совместного использования include и exclude:


Скопировать код

Функции с GitHub зависимостями


OpenWhisk поддерживает зависимости, так что можно описать другие пакеты OpenWhisk, от которых зависит наш проект. При наличии таких зависимостей OpenWhisk автоматически развернет и зависимые пакеты. Любой пакет с manifest.yaml и\или deployment.yaml можно рассматривать как зависимый пакет, который может быть указан в манифесте нашего проекта. Можно описать такую зависимость в манифесте в разделе dependencies:


Скопировать код

В этом примере helloworlds является внешним пакетом, размещенным в репозитории GitHub по адресу https://github.com/apache/incubator-openwhisk-test. Пакет helloworlds будет развернут, исходя из его файлов для развертывания в каталоге packages/helloworlds — при развертывании нашего проекта RootProject. Также есть возможность смены имени зависимого пакета, например вместо helloworlds задать ChildProject:


Скопировать код

Можно добавить несколько зависимостей к нескольким пакетам:


Скопировать код

Как работает синхронизация проектов OpenWhisk между клиентом и сервером

Для ответа надо запустить развертывание в режиме managed deployment. В этом режиме OpenWhisk производит развертывание всех объектов из манифеста, а также присоединяет к каждому из них скрытое описание, т.н. managed. Это описание можно посмотреть по ссылке.

Вариант 1: Если __OW_PROJECT_HASH совпадает на клиенте и сервере, т.е. если нет изменений проекта на клиентской стороне, то проект на сервере остается как есть, за исключением развертывания через wskdeploy новых объектов из манифеста для получения любых изменений в файле deployment.yaml.

Вариант 2: Если __OW_PROJECT_HASH не совпадает, т.е. имеются изменения на стороне клиента, то wskdeploy выполняет развертывание всех объектов из манифеста, а затем обновляет их __OW_PROJECT_HASH на сервере. Также wskdeploy выполняет поиск всех объектов: включая функции, последовательности и условные срабатывания, у которых такой же __OW_PROJECT_NAME, — т.е. принадлежащих тому же проекту, но имеющих другой __OW_PROJECT_HASH, поскольку они могли быть удалены из манифеста на клиенте. Имя проекта в манифесте является обязательным для синхронизации проекта между клиентом и сервером:

project:
       name: MyProjectName
       packages:
               package1:
                        ....

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

project:
      name: MyProjectName

Давайте посмотрим на пример проекта для понимания managed deployment. В этом репозитории можно посмотреть проект с различными манифестами, которые показывают managed deployment.

Шаг первый

Выполняем развертывание MyFirstManagedProject, используя режим managed deployment:

$wskdeploy -m tests/src/integration/managed-deployment/manifest.yaml --managed
Deployment completed successfully.

Список объектов, развернутых на сервере OpenWhisk

Описание объекта


Шаг второй

Синхронизируем клиент и сервер — удаляем ManagedPackage-2: https://xpaste.pro/p/5xm4eUso

Список объектов после удаления в MyFirstManagedProject


Шаг третий

Синхронизируем клиент и сервер — удаляем последовательность ManagedSequence-2: https://xpaste.pro/p/0E82P8r4

Список объектов после удаления

Шаг четвертый

Удаляем функцию Helloworld-3: https://xpaste.pro/p/xjm1tvPT

Список объектов после удаления

Шаг пятый

Удаляем ManagedPackage-1: https://xpaste.pro/p/JZAfllFV

Остальные объекты в MyFirstManagedProject


Другие статьи цикла

Бессерверные вычисления на основе OpenWhisk, часть 1
Бессерверные вычисления на основе OpenWhisk, часть 2
Бессерверные вычисления на основе OpenWhisk, часть 4
DevOps