Статьи

Микросервис с использованием AWS API Gateway, AWS Lambda и Couchbase

Этот блог объяснил следующие концепции для безсерверных приложений:

В третьем блоге, посвященном бессерверной серии, будет рассказано, как создать простой микросервис с использованием Amazon API Gateway, AWS Lambda и Couchbase .

Прочитайте предыдущие блоги, чтобы узнать больше о AWS Lambda.

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

Вот ключевые компоненты этой архитектуры:

  • Клиент может быть curl, AWS CLI, клиентом Postman или любым другим инструментом / API, который может вызывать конечную точку REST.
  • API Gateway используется для предоставления API. Ресурс верхнего уровня доступен в path /books . Методы HTTP GET и POST публикуются для ресурса.
  • Каждый API запускает лямбда-функцию. Созданы две лямбда-функции: функция списка книг для перечисления всех доступных книг и функция создания книги для создания новой книги.
  • Couchbase используется в качестве постоянного хранилища в EC2. Все документы JSON хранятся и извлекаются из этой базы данных.

Давайте начнем!

Создать роль IAM

Роли IAM будут иметь политики и доверительные отношения, которые позволят использовать эту роль в API Gateway и выполнять функцию Lambda.

Давайте создадим новую роль IAM:

1
2
3
aws iam create-role \
--role-name microserviceRole \
--assume-role-policy-document file://./trust.json

--assume-role-policy-document определяет --assume-role-policy-document доверительных отношений, который предоставляет объекту разрешение на принятие роли. trust.json находится на github.com/arun-gupta/serverless/blob/master/aws/microservice/trust.json и выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "lambda.amazonaws.com",
          "apigateway.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Эти доверительные отношения позволяют функциям Lambda и API-шлюзу выполнять эту роль во время выполнения.

Связать политики с этой ролью как:

1
2
3
4
aws iam put-role-policy \
--role-name microserviceRole \
--policy-name microPolicy \
--policy-document file://./policy.json

policy.json находится на github.com/arun-gupta/serverless/blob/master/aws/microservice/policy.json и выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:*"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "apigateway:*"
      ],
      "Resource": "arn:aws:apigateway:*::/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "execute-api:Invoke"
      ],
      "Resource": "arn:aws:execute-api:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
          "lambda:*"
      ],
      "Resource": "*"
    }
  ]
}

Эта щедрая политика разрешает любые разрешения для журналов, созданных в CloudWatch для всех ресурсов. Кроме того, он разрешает все разрешения Lambda и API Gateway для всех ресурсов. В целом, только требуемая политика будет предоставлена ​​конкретным ресурсам.

Создать лямбда-функции

Подробные шаги по созданию лямбда-функций описаны в разделе «Бессерверный FaaS с AWS Lambda и Java» . Давайте создадим две лямбда-функции, как требуется в нашем случае:

01
02
03
04
05
06
07
08
09
10
11
12
aws lambda create-function \
--function-name MicroserviceGetAll \
--role arn:aws:iam::598307997273:role/microserviceRole \
--handler org.sample.serverless.aws.couchbase.BucketGetAll \
--zip-file fileb:///Users/arungupta/workspaces/serverless/aws/microservice/microservice-http-endpoint/target/microservice-http-endpoint-1.0-SNAPSHOT.jar \
--description "Microservice HTTP Endpoint - Get All" \
--runtime java8 \
--region us-west-1 \
--timeout 30 \
--memory-size 1024 \
--environment Variables={COUCHBASE_HOST=ec2-52-53-193-176.us-west-1.compute.amazonaws.com} \
--publish

Пара ключевых моментов, которые следует отметить в этой функции:

  • Роль IAM microserviceRole созданная на предыдущем шаге, здесь явно указана
  • Обработчиком является класс org.sample.serverless.aws.couchbase.BucketGetAll . Этот класс запрашивает базу данных Couchbase, определенную с помощью переменной среды COUCHBASE_HOST .

Создайте вторую лямбда-функцию:

01
02
03
04
05
06
07
08
09
10
11
12
aws lambda create-function \
--function-name MicroservicePost \
--role arn:aws:iam::598307997273:role/microserviceRole \
--handler org.sample.serverless.aws.couchbase.BucketPost \
--zip-file fileb:///Users/arungupta/workspaces/serverless/aws/microservice/microservice-http-endpoint/target/microservice-http-endpoint-1.0-SNAPSHOT.jar \
--description "Microservice HTTP Endpoint - Post" \
--runtime java8 \
--region us-west-1 \
--timeout 30 \
--memory-size 1024 \
--environment Variables={COUCHBASE_HOST=ec2-52-53-193-176.us-west-1.compute.amazonaws.com} \
--publish

Обработчик этой функции — класс org.sample.serverless.aws.couchbase.BucketPost . Этот класс создает новый документ JSON в базе данных Couchbase, идентифицируемый переменной среды COUCHBASE_HOST .

Полный исходный код для этих классов находится по адресу github.com/arun-gupta/serverless/tree/master/aws/microservice/microservice-http-endpoint .

Ресурс шлюза API

Создайте API с помощью Amazon API Gateway, протестируйте его и создайте API для предоставления лямбда-функции, предоставьте подробные шаги и объяснение того, как использовать API-шлюз и лямбда-функции для создания мощных внутренних систем. Этот блог поможет вам быстро выполнить все шаги на случай, если вы захотите сократить погоню.

Давайте создадим ресурсы API Gateway.

  • Первым шагом является создание API:
1
2
3
aws apigateway \
create-rest-api \
--name Book

Это показывает вывод как:

1
2
3
4
5
{
    "name": "Book",
    "id": "lb2qgujjif",
    "createdDate": 1482998945
}

Значением атрибута id является API ID. В нашем случае это lb2qgujjif .

  • Найдите ROOT ID созданного API, поскольку это требуется для следующего вызова интерфейса командной строки AWS:
1
aws apigateway get-resources --rest-api-id lb2qgujjif

Это показывает вывод:

1
2
3
4
5
6
7
8
{
    "items": [
        {
            "path": "/",
            "id": "hgxogdkheg"
        }
    ]
}

Значением атрибута id является ROOT ID. Это также идентификатор PARENT для ресурса верхнего уровня.

  • Создать ресурс
1
2
3
4
aws apigateway create-resource \
--rest-api-id lb2qgujjif \
--parent-id hgxogdkheg \
--path-part books

Это показывает вывод:

1
2
3
4
5
6
{
    "path": "/books",
    "pathPart": "books",
    "id": "vrpkod",
    "parentId": "hgxogdkheg"
}

Значением атрибута id является RESOURCE ID.

Идентификатор API и идентификатор RESOURCE используются для последующих вызовов интерфейса командной строки AWS.

Метод POST для шлюза API

Теперь, когда ресурс создан, давайте создадим метод HTTP POST для этого ресурса.

  • Создать метод POST
1
2
3
4
5
aws apigateway put-method \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method POST \
--authorization-type NONE

чтобы увидеть ответ:

1
2
3
4
5
{
    "apiKeyRequired": false,
    "httpMethod": "POST",
    "authorizationType": "NONE"
}
  • Установите лямбда-функцию в качестве пункта назначения метода POST:
1
2
3
4
5
6
7
aws apigateway put-integration \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method POST \
--type AWS \
--integration-http-method POST \
--uri arn:aws:apigateway:us-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-1:<act-id>:function:MicroservicePost/invocations

Обязательно замените <act-id> на свой идентификатор учетной записи AWS. Здесь также используются API ID и RESOURCE ID из предыдущего раздела. --uri используется для указания URI входных данных интеграции. Формат URI фиксированный. Этот CLI покажет результат как:

1
2
3
4
5
6
7
8
{
    "httpMethod": "POST",
    "passthroughBehavior": "WHEN_NO_MATCH",
    "cacheKeyParameters": [],
    "type": "AWS",
    "uri": "arn:aws:apigateway:us-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-1:<act-id>:function:MicroservicePost/invocations",
    "cacheNamespace": "vrpkod"
}
  • Установите content-type ответа метода POST:
1
2
3
4
5
6
aws apigateway put-method-response \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method POST \
--status-code 200 \
--response-models "{\"application/json\": \"Empty\"}"

чтобы увидеть ответ:

1
2
3
4
5
6
{
    "responseModels": {
        "application/json": "Empty"
    },
    "statusCode": "200"
}
  • Установите content-type ответа интеграции метода POST:
1
2
3
4
5
6
aws apigateway put-integration-response \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method POST \
--status-code 200 \
--response-templates "{\"application/json\": \"Empty\"}"

чтобы увидеть ответ:

1
2
3
4
5
6
{
    "statusCode": "200",
    "responseTemplates": {
        "application/json": "Empty"
    }
}
  • Разверните API
1
2
3
aws apigateway create-deployment \
--rest-api-id lb2qgujjif \
--stage-name test

чтобы увидеть ответ

1
2
3
4
{
    "id": "9wi991",
    "createdDate": 1482999187
}
  • Предоставьте разрешение API-шлюзу на вызов лямбда-функции:
1
2
3
4
5
6
aws lambda add-permission \
--function-name MicroservicePost \
--statement-id apigateway-test-post-1 \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-1:<act-id>:lb2qgujjif/*/POST/books"

Также предоставьте разрешение развернутому API:

1
2
3
4
5
6
aws lambda add-permission \
--function-name MicroservicePost \
--statement-id apigateway-test-post-2 \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-1:<act-id>:lb2qgujjif/test/GET/books"
  • Проверьте метод API:
1
2
3
4
5
6
aws apigateway test-invoke-method \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method POST \
--path-with-query-string "" \
--body "{\"id\": \"1\", \"bookname\": \"test book\", \"isbn\": \"123\", \"cost\": \"1.23\"}"

чтобы увидеть ответ:

01
02
03
04
05
06
07
08
09
10
{
    "status": 200,
    "body": "Empty",
    "log": "Execution log for request test-request\nThu Dec 29 08:16:05 UTC 2016 : Starting execution for request: test-invoke-request\nThu Dec 29 08:16:05 UTC 2016 : HTTP Method: POST, Resource Path: /books\nThu Dec 29 08:16:05 UTC 2016 : Method request path: {}\nThu Dec 29 08:16:05 UTC 2016 : Method request query string: {}\nThu Dec 29 08:16:05 UTC 2016 : Method request headers: {}\nThu Dec 29 08:16:05 UTC 2016 : Method request body before transformations: {\"id\": \"1\", \"bookname\": \"test book\", \"isbn\": \"123\", \"cost\": \"1.23\"}\nThu Dec 29 08:16:05 UTC 2016 : Endpoint request URI: https://lambda.us-west-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-1:598307997273:function:MicroservicePost/invocations\nThu Dec 29 08:16:05 UTC 2016 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=****************************************************************************************************************************************************************************************************************************************************************************************************************************************c8bb85, X-Amz-Date=20161229T081605Z, x-amzn-apigateway-api-id=lb2qgujjif, X-Amz-Source-Arn=arn:aws:execute-api:us-west-1:598307997273:lb2qgujjif/null/POST/books, Accept=application/json, User-Agent=AmazonAPIGateway_lb2qgujjif, Host=lambda.us-west-1.amazonaws.com, X-Amz-Content-Sha256=559d0296d96ec5647eef6381602fe5e7f55dd17065864fafb4f581d106aa92f4, X-Amzn-Trace-Id=Root=1-5864c645-8494974a41a3a16c8d2f9929, Content-Type=application/json}\nThu Dec 29 08:16:05 UTC 2016 : Endpoint request body after transformations: {\"id\": \"1\", \"bookname\": \"test book\", \"isbn\": \"123\", \"cost\": \"1.23\"}\nThu Dec 29 08:16:10 UTC 2016 : Endpoint response body before transformations: \"{\\\"cost\\\":\\\"1.23\\\",\\\"id\\\":\\\"1\\\",\\\"bookname\\\":\\\"test book\\\",\\\"isbn\\\":\\\"123\\\"}\"\nThu Dec 29 08:16:10 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=0b25323b-cd9f-11e6-8bd4-292925ba63a9, Connection=keep-alive, Content-Length=78, Date=Thu, 29 Dec 2016 08:16:10 GMT, Content-Type=application/json}\nThu Dec 29 08:16:10 UTC 2016 : Method response body after transformations: Empty\nThu Dec 29 08:16:10 UTC 2016 : Method response headers: {X-Amzn-Trace-Id=Root=1-5864c645-8494974a41a3a16c8d2f9929, Content-Type=application/json}\nThu Dec 29 08:16:10 UTC 2016 : Successfully completed execution\nThu Dec 29 08:16:10 UTC 2016 : Method completed with status: 200\n",
    "latency": 5091,
    "headers": {
        "X-Amzn-Trace-Id": "Root=1-5864c645-8494974a41a3a16c8d2f9929",
        "Content-Type": "application/json"
    }
}

Значение атрибута status равно 200 и указывает, что это был успешный вызов. Значение атрибута log показывает запись журнала из CloudWatch Logs. Подробные журналы также можно получить с помощью aws logs filter-log-events --log-group /aws/lambda/MicroservicePost .

  • Эта команда сохраняет один документ JSON в Couchbase. Это можно легко проверить с помощью Couch Casebase CLI Tool. Подключитесь к серверу Couchbase следующим образом:
1
cbq -u Administrator -p password -e="http://<COUCHBASE_HOST>:8091"

Создайте первичный индекс в default по default поскольку это необходимо для запроса блока без разделов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
cbq> create primary index default_index on default;
{
    "requestID": "13b539f9-7fff-4386-92f4-cea161a7aa08",
    "signature": null,
    "results": [
    ],
    "status": "success",
    "metrics": {
        "elapsedTime": "1.917009047s",
        "executionTime": "1.916970061s",
        "resultCount": 0,
        "resultSize": 0
    }
}
  • Напишите запрос N1QL для доступа к данным:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cbq> select * from default limit 10;
{
    "requestID": "d7b1c3f9-6b4e-4952-9a1e-9faf5169926e",
    "signature": {
        "*": "*"
    },
    "results": [
        {
            "default": {
                "bookname": "test",
                "cost": "1.23",
                "id": "1",
                "isbn": "123"
            }
        }
    ],
    "status": "success",
    "metrics": {
        "elapsedTime": "24.337755ms",
        "executionTime": "24.289796ms",
        "resultCount": 1,
        "resultSize": 175
    }
}

Результаты показывают документ JSON, который был сохранен нашей функцией Lambda.

API GET Метод GET

Давайте создадим метод HTTP GET для ресурса:

  • Создайте метод GET :
1
2
3
4
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method GET \
--authorization-type NONE
  • Установите правильную лямбда-функцию в качестве пункта назначения GET:
1
2
3
4
5
6
7
aws apigateway put-integration \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method GET \
--type AWS \
--integration-http-method POST \
--uri arn:aws:apigateway:us-west-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-1:598307997273:function:MicroserviceGetAll/invocations
  • Установите content-type ответа метода GET:
1
2
3
4
5
6
aws apigateway put-method-response \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method GET \
--status-code 200 \
--response-models "{\"application/json\": \"Empty\"}"
  • Установите content-type ответа интеграции метода GET:
1
2
3
4
5
6
aws apigateway put-integration-response \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method GET \
--status-code 200 \
--response-templates "{\"application/json\": \"Empty\"}"
  • Предоставьте разрешение API-шлюзу на вызов лямбда-функции
1
2
3
4
5
6
aws lambda add-permission \
--function-name MicroserviceGetAll \
--statement-id apigateway-test-getall-1 \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-1:598307997273:lb2qgujjif/*/GET/books"
  • Предоставьте разрешение на развернутый API:
1
2
3
4
5
6
aws lambda add-permission \
--function-name MicroserviceGetAll \
--statement-id apigateway-test-getall-2 \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn "arn:aws:execute-api:us-west-1:598307997273:lb2qgujjif/test/GET/books"
  • Проверьте метод:
1
2
3
4
aws apigateway test-invoke-method \
--rest-api-id lb2qgujjif \
--resource-id vrpkod \
--http-method GET

чтобы увидеть результат:

01
02
03
04
05
06
07
08
09
10
{
    "status": 200,
    "body": "Empty",
    "log": "Execution log for request test-request\nSat Dec 31 09:07:48 UTC 2016 : Starting execution for request: test-invoke-request\nSat Dec 31 09:07:48 UTC 2016 : HTTP Method: GET, Resource Path: /books\nSat Dec 31 09:07:48 UTC 2016 : Method request path: {}\nSat Dec 31 09:07:48 UTC 2016 : Method request query string: {}\nSat Dec 31 09:07:48 UTC 2016 : Method request headers: {}\nSat Dec 31 09:07:48 UTC 2016 : Method request body before transformations: \nSat Dec 31 09:07:48 UTC 2016 : Endpoint request URI: https://lambda.us-west-1.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-1:598307997273:function:MicroserviceGetAll/invocations\nSat Dec 31 09:07:48 UTC 2016 : Endpoint request headers: {x-amzn-lambda-integration-tag=test-request, Authorization=******************************************************************************************************************************************************************************************************************************************************************************************************6de147, X-Amz-Date=20161231T090748Z, x-amzn-apigateway-api-id=lb2qgujjif, X-Amz-Source-Arn=arn:aws:execute-api:us-west-1:598307997273:lb2qgujjif/null/GET/books, Accept=application/json, User-Agent=AmazonAPIGateway_lb2qgujjif, X-Amz-Security-Token=FQoDYXdzEHEaDEILpsKTo45Ys1LrFCK3A+KOe5HXOSP3GfVAaRYHe1pDUJGHL9MtkFiPjORLFT+UCKjRqE7UFaGscTVG6PZXTuSyQev4XTyROfPylCrtDomGsoZF/iwy4rlJQIJ7elBceyeKu1OVdaT1A99PVeliaCAiDL6Veo1viWOnP+7c72nAaJ5jnyF/nHl/OLhFdFv4t/hnx3JePMk5YM89/6ofxUEVDNfzXxbZHRpTrG/4TPHwjPdoR5i9dEzWMU6Eo5xD4ldQ/m5B3RmrwpaPOuEq39LhJ8k/Vzo+pAfgJTq5ssbNwYOgh0RPSGVNMcoTkCwk0EMMT5vDbmQqZ2dW1a1tmQg9N2xR+QQy+RKMFgO5YY8fMxHnRSdMuuipxl79G1pktc [TRUNCATED]\nSat Dec 31 09:07:48 UTC 2016 : Endpoint request body after transformations: \nSat Dec 31 09:07:53 UTC 2016 : Endpoint response body before transformations: \"[{\\\"default\\\":{\\\"cost\\\":\\\"1.23\\\",\\\"id\\\":\\\"1\\\",\\\"bookname\\\":\\\"test book\\\",\\\"isbn\\\":\\\"123\\\"}}]\"\nSat Dec 31 09:07:53 UTC 2016 : Endpoint response headers: {x-amzn-Remapped-Content-Length=0, x-amzn-RequestId=99ab09b2-cf38-11e6-996f-f5f07af431af, Connection=keep-alive, Content-Length=94, Date=Sat, 31 Dec 2016 09:07:52 GMT, Content-Type=application/json}\nSat Dec 31 09:07:53 UTC 2016 : Method response body after transformations: Empty\nSat Dec 31 09:07:53 UTC 2016 : Method response headers: {X-Amzn-Trace-Id=Root=1-58677564-66f1e96642b16d2db703126e, Content-Type=application/json}\nSat Dec 31 09:07:53 UTC 2016 : Successfully completed execution\nSat Dec 31 09:07:53 UTC 2016 : Method completed with status: 200\n",
    "latency": 4744,
    "headers": {
        "X-Amzn-Trace-Id": "Root=1-58677564-66f1e96642b16d2db703126e",
        "Content-Type": "application/json"
    }
}

Еще раз код состояния 200 показывает успешный вызов. Подробные журналы можно получить с помощью aws logs filter-log-events --log-group /aws/lambda/MicroservicePost .

Этот блог показывает только один простой метод POST и GET. Другие методы HTTP также могут быть легко включены в этот микросервис.

API Gateway и лямбда-ссылки