Статьи

Подход управления версиями API с помощью AWS API Gateway

Аннотация

В этой статье я попытался описать шаги и артефакты, необходимые для реализации стратегии управления версиями REST API на основе Amazon API Gateway.

Вступление

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

Поскольку мы реализуем наш API с помощью сервиса Amazon API Gateway, и мы хотим реализовать рекомендуемый наилучший подход. Я пытался найти лучшую стратегию управления версиями с использованием AWS API Gateway и Lambda. Проведя день в поиске, я понимаю, что существует три грубых стратегии для версии REST API. 1) Создайте совершенно новый API с добавлением номера версии в конце (например, www.mydomain.com/ordersV1, www.mydomain.com/ordersV2 примерно так), 2) Поместите индикатор версии в путь к ресурсу (например, www .mydomain.com / api / v1 / xxx, www.mydomain.com/api/v2/xxx) — это традиционный подход. и 3) Создать совершенно новый домен для новой версии (v1.api.mysite.com, v2.api.mysite.com). Мы должны выбрать наш подход из этих трех стратегий.

Решение Подход

В AWS API Gateway есть опция с именем Stage Variables. В представлении Integration Request мы можем предоставить имя переменной рабочей области (  ${stageVariables.<stage variable name> } ) вместо фактического имени функции Lambda. Во время развертывания переменная stage может содержать другую версию Lambda (или псевдоним), так что в зависимости от активированного ресурса (/ api / v1 / getOrders), промежуточной среды (dev, prod и т. Д.) И лямбда-имени с версией, определенной в stage Переменная API-шлюз может выбрать соответствующую функцию для вызова. Мы решили использовать эту функцию в соответствии с традиционным подходом к управлению версиями, то есть к управлению версиями на основе пути к ресурсам. В следующем разделе объясняются подробности этапов реализации этого подхода вручную.

Детали решения

Я создал одну простую лямбда-функцию NodeJs с двумя версиями, а затем создал ресурсы API шлюза AWS для запуска этих версий. Все описанные здесь шаги выполняются вручную. Позже мы установим AWS CodePipeline для развертывания версии (или псевдонима) Lambda, но в соответствии с текущим планом мы не будем автоматизировать развертывание API. Это будет сделано вручную в соответствии с дорожной картой.

1. Создайте одну лямбда-функцию nodejs getOrders.

2. Сгенерируйте две версии функции с некоторыми изменениями в ответе JSON.

3. Создайте тестовое событие для проверки лямбды. Поскольку у меня есть план по созданию LAMBDA_PROXYинтеграции типов в API Gateway, я выбираю  шаблон API Gateway AWS Proxy . Запрос JSON тестового события приведен ниже.

{
  "body": "{\"test\":\"body\"}",
  "resource": "/dev/v1/orders",
  "requestContext": {
    "resourceId": "123456",
    "apiId": "1234567890",
    "resourcePath": "/dev/v1/orders",
    "httpMethod": "GET",
    "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
    "accountId": "123456789012",
    "identity": {
      "apiKey": null,
      "userArn": null,
      "cognitoAuthenticationType": null,
      "caller": null,
      "userAgent": "Custom User Agent String",
      "user": null,
      "cognitoIdentityPoolId": null,
      "cognitoIdentityId": null,
      "cognitoAuthenticationProvider": null,
      "sourceIp": "127.0.0.1",
      "accountId": null
    },
    "stage": "dev"
  },
  "queryStringParameters": {
    "start":"2015-10-01T00:00:00Z",
    "end":"2015-10-04T00:00:00Z"
  },
  "headers": {
    "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
    "Accept-Language": "en-US,en;.8",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Mobile-Viewer": "false",
    "X-Forwarded-For": "127.0.0.1, 127.0.0.2",
    "CloudFront-Viewer-Country": "US",
    "Accept": "text/html,application/xhtml+xml,application/xml;.9,image/webp,*/*;.8",
    "Upgrade-Insecure-Requests": "1",
    "X-Forwarded-Port": "443",
    "Host": "1234567890.execute-api.us-east-2.amazonaws.com",
    "X-Forwarded-Proto": "https",
    "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
    "CloudFront-Is-Tablet-Viewer": "false",
    "Cache-Control": "",
    "User-Agent": "Custom User Agent String",
    "CloudFront-Forwarded-Proto": "https",
    "Accept-Encoding": "gzip, deflate, sdch"
  },
  "pathParameters": {
    "proxy": "path/to/resource"
  },
  "httpMethod": "GET",
  "stageVariables": {
    "v1fn": "getOrders:1",
    "v2fn":"getOrders:2"
  },
  "path": "/path/to/resource"
}

4. Следуя минимальным изменениям, которые я сделал для проверки лямбда-функции:

  • Обновление resourceи resourcePathс запланированным путем ресурса шлюза API ( /dev/v<#>/orders).
  • httpMethodбудет GETдля этого POC.
  • stageбудет dev.
  • queryStringParametersесть startи endотметка времени.
  • У меня есть план создать две версии API, каждая из которых будет указывать на соответствующую версию Lambda в среде разработки. Для этого я создал два stageVariables( v1fnи v2fn) с <имя функции>: <номер версии> ( getOrders:1& getOrders:2)

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

5. Теперь из меню Amazon API Gateway создайте API со следующим путем для двух версий API. Для версии 1 это так /v1/orders, а для версии 2 это так /v2/orders.

6. Тип запроса интеграции LAMBDA_PROXYдолжен пройти queryStringParametersбез проблем.

7. Чтобы реализовать LAMBDA_PROXYтип интеграции, нам нужно выполнить следующую настройку в представлении  запроса интеграции .

Примечание. Здесь вместо того, чтобы давать имя лямбда-функции, я указываю ${stageVariables.<stage-variable-name>}динамически выбирать другую версию функции (или псевдоним).

8. Такая же настройка необходима для /v2/ordersресурса.

9. Теперь мы можем протестировать оба API-интерфейса из мастера тестирования API.

Примечание: здесь мы должны ввести значение переменной stage во время теста (например, для v2fnнего getOrders:2), иначе API будет работать некорректно.

10. Теперь разверните API на стадии разработки. URI API для разных версий:

  • https://xxxxxxxxxx.execute-api.xxxxxxxxx.amazonaws.com/dev/v1/orders
  • https://xxxxxxxxxx.execute-api.xxxxxxxxx.amazonaws.com/dev/v2/orders

Где ресурсы /dev/v1/ordersи /dev/v2/ordersсоответственно.

Примечание. У нас есть план развертывания лямбда-кода с использованием AWS CodePipeline. Поскольку у нас ограниченное количество API, мы вручную создадим API из консоли Amazon API Gateway.

Заключение

Ниже приведены общие проблемы, которые мы рассмотрели в ходе этой реализации.

вопросы

Как мы обращаемся

Нет изоляции. Если у нас есть ошибка в коде v1, которая может быть использована, все наши экземпляры уязвимы.

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

Ограничения развития. Нам нужно поддерживать всю кодовую базу, чтобы убедиться, что «v1» и «v2» прекрасно живут вместе. Рассмотрим ситуацию, когда некоторая зависимость используется как в «v1», так и в «v2», но требует определенных разных версий.

Коды лямбда-функций v1 и v2 и соответствующие им зависимости являются взаимоисключающими, поэтому конфликта не будет.

Технология блокировки. Нельзя или очень сложно иметь «v1» в C # и «v2» в Python.

Это может быть невозможно при использовании лямбда-реализации, потому что между лямбда-функцией и языком программирования существует соотношение 1: 1.

Планирование и мониторинг мощностей. Трудно понять, сколько ресурсов потребляют вызовы «v1» против «v2».

Мы можем осуществлять мониторинг на основе полного и регулярного пути к ресурсам на основе выражений.

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