подключение AWS SAM Local с dynamodb в докере

Я настроил пару API-шлюз /AWS-лямбда с помощью AWS sam local и подтвердил, что могу успешно вызвать ее после запуска

sam local start-api

Затем я добавил локальный экземпляр DynamodB в Docker-контейнер и создал на нем таблицу с помощью AWS Cli

Но, добавив код в лямбду для записи в экземпляр Dynamodb, я получаю:

2018-02-22T11:13: 16.172Z ошибка ed9ab38e-fb54-18a4-0852-db7e5b56c8cd: не удалось записать в таблицу: {"message": "connect ECONNREFUSED 0.0.0.0:8000","code":"NetworkingError", "ERRNO": "ECONNREFUSED", "системный вызов": "подключиться", "адрес": "0.0.0.0", "порт": 8000, "регион": "ес-запад-2", "имя хоста": "0,0.0.0 "," retryable ": true," time ":" 2018-02-22T11:13: 16.165Z "} событие записи из команды: {" name ":" test "," geolocation ":" xyz "," тип ":" createDestination "} END RequestId: ed9ab38e-fb54-18a4-0852-db7e5b56c8cd

В Интернете я увидел, что вам может понадобиться подключиться к той же сети докеров, поэтому я создал сеть сетевых docker network create lambda-local и изменил мои команды запуска на:

sam local start-api --docker-network lambda-local

а также

docker run -v "$PWD": /dynamodb_local_db -p 8000:8000 --network=lambda-local cnadiminti/dynamodb-local:latest

но все равно получаю ту же ошибку

sam local печатает 2018/02/22 11:12:51 Connecting container 98b19370ab92f3378ce380e9c840177905a49fc986597fef9ef589e624b4eac3 to network lambda-local

Я создаю DynamodBclient с помощью:

const AWS = require('aws-sdk')
const dynamodbURL = process.env.dynamodbURL || 'http://0.0.0.0:8000'
const awsAccessKeyId = process.env.AWS_ACCESS_KEY_ID || '1234567'
const awsAccessKey = process.env.AWS_SECRET_ACCESS_KEY || '7654321'
const awsRegion = process.env.AWS_REGION || 'eu-west-2'

console.log(awsRegion, 'initialising dynamodb in region: ')

let dynamoDbClient
const makeClient = () => {
  dynamoDbClient = new AWS.DynamoDB.DocumentClient({
    endpoint: dynamodbURL,
    accessKeyId: awsAccessKeyId,
    secretAccessKey: awsAccessKey,
    region: awsRegion
  })
  return dynamoDbClient
}

module.exports = {
  connect: () => dynamoDbClient || makeClient()
}

и проверка динамбклиента мой код создает шоу

DocumentClient {
  options:
   { endpoint: 'http://0.0.0.0:8000',
     accessKeyId: 'my-key',
     secretAccessKey: 'my-secret',
     region: 'eu-west-2',
     attrValue: 'S8' },
  service:
   Service {
     config:
      Config {
        credentials: [Object],
        credentialProvider: [Object],
        region: 'eu-west-2',
        logger: null,
        apiVersions: {},
        apiVersion: null,
        endpoint: 'http://0.0.0.0:8000',
        httpOptions: [Object],
        maxRetries: undefined,
        maxRedirects: 10,
        paramValidation: true,
        sslEnabled: true,
        s3ForcePathStyle: false,
        s3BucketEndpoint: false,
        s3DisableBodySigning: true,
        computeChecksums: true,
        convertResponseTypes: true,
        correctClockSkew: false,
        customUserAgent: null,
        dynamoDbCrc32: true,
        systemClockOffset: 0,
        signatureVersion: null,
        signatureCache: true,
        retryDelayOptions: {},
        useAccelerateEndpoint: false,
        accessKeyId: 'my-key',
        secretAccessKey: 'my-secret' },
     endpoint:
      Endpoint {
        protocol: 'http:',
        host: '0.0.0.0:8000',
        port: 8000,
        hostname: '0.0.0.0',
        pathname: '/',
        path: '/',
        href: 'http://0.0.0.0:8000/' },
     _clientId: 1 },
  attrValue: 'S8' }

Должна ли эта настройка работать? Как мне заставить их говорить друг с другом?

---- редактировать ----

Основываясь на разговоре в Твиттере, стоит упомянуть (возможно), что я могу взаимодействовать с DynamodB в CLI и в веб-оболочке.

dynamo db at the CLI

dynamodb web shell

Ответы

Ответ 1

Большое спасибо Heitor Lessa, который ответил мне на Twitter с примером репо

Который указал мне на ответ...

  • контейнер docker dynamodb находится на 127.0.0.1 из контекста моей машины (именно поэтому я мог взаимодействовать с ним)

  • Локальный контейнер-док-станция SAM находится на 127.0.0.1 из контекста моей машины

  • Но они не на 127.0.0.1 друг от друга контекста

Итак: https://github.com/heitorlessa/sam-local-python-hot-reloading/blob/master/users/users.py#L14

Направил меня на изменение кода подключения:

const AWS = require('aws-sdk')
const awsRegion = process.env.AWS_REGION || 'eu-west-2'

let dynamoDbClient
const makeClient = () => {
  const options = {
    region: awsRegion
  }
  if(process.env.AWS_SAM_LOCAL) {
    options.endpoint = 'http://dynamodb:8000'
  }
  dynamoDbClient = new AWS.DynamoDB.DocumentClient(options)
  return dynamoDbClient
}

module.exports = {
  connect: () => dynamoDbClient || makeClient()
}

с важными линиями:

if(process.env.AWS_SAM_LOCAL) {
  options.endpoint = 'http://dynamodb:8000'
}

из контекста локального контейнера док-станции SAM контейнер dynamodb открывается через его имя

Мои две команды запуска оказались такими:

docker run -d -v "$PWD": /dynamodb_local_db -p 8000:8000 --network lambda-local --name dynamodb cnadiminti/dynamodb-local

а также

AWS_REGION=eu-west-2 sam local start-api --docker-network lambda-local

с единственным изменением здесь, чтобы дать контейнеру dynamodb имя

Ответ 2

Если вы используете sam-local на Mac, как многие разработчики, вы можете просто использовать

options.endpoint = "http://docker.for.mac.localhost:8000"

Или на более новых установках докера https://docs.docker.com/docker-for-mac/release-notes/#docker-community-edition-18030-ce-mac59-2018-03-26

options.endpoint = "http://host.docker.internal:8000"

Вместо того, чтобы выполнять несколько команд, как показывал Пол выше (но это может быть более независимым от платформы?).

Ответ 3

Как отметил @Paul, речь идет о настройке вашей сети между контейнерами докеров - лямбдой и базой данных.

Другой подход, который работал для меня (с использованием докеры-компоновки).

докер-Compose:

version: '2.1'

services:
  db:
    image: ...
    ports:
      - "3306:3306"
    networks:
      - my_network
    environment:
      ...
    volumes:
      ...

networks:
  my_network:

Затем, после docker-compose up, работающая docker network ls покажет:

NETWORK ID          NAME                        DRIVER              SCOPE
7eb440d5c0e6        dev_my_network              bridge              local

Имя моего контейнера-докера - dev_db_1.

Мой код js:

const connection = mysql.createConnection({
    host: "dev_db_1",
    port: 3306,
    ...
});

Затем запустите команду sam:

sam local invoke --docker-network dev_my_network -e my.json

стек:

  • Докер: 18.03.1-ce
  • Docker-compose: 1.21.1
  • MacOS HighSierra 10.13.6

Ответ 4

Если вы используете LocalStack для запуска DynamoDB, я считаю, что правильная команда для использования сети LocalStack для SAM:

sam local start-api --env-vars env.json --docker-network localstack_default

И в вашем коде имя хоста LocalStack должно быть localstack_localstack_1

const dynamoDbDocumentClient = new AWS.DynamoDB.DocumentClient({
  endpoint: process.env.AWS_SAM_LOCAL ?
    'http://localstack_localstack_1:4569' :
    undefined,
});

Однако я запустил LocalStack, используя docker-compose up. Использование инструмента pip CLI для запуска LocalStack может привести к различным идентификаторам.

Ответ 5

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

Предположим, DynamodB (обратите внимание --name, это конечная точка сейчас)

docker run -d -p 8000:8000 --name DynamoDBEndpoint amazon/dynamodb-local

Это приведет к чему-то вроде этого

0e35b1c90cf0....

Чтобы узнать, какая сеть была создана внутри:

docker inspect 0e35b1c90cf0

Это должно дать вам что-то вроде

...
Networks: {
     "services_default": {//this is the <<myNetworkName>>

....

Если вы знаете свои сети и хотите поместить Docker-контейнер в определенную сеть, вы можете сохранить описанные выше шаги и сделать это одной командой при запуске контейнера с использованием --network

docker run -d -p 8000:8000 --network myNetworkName --name DynamoDBEndpoint amazon/dynamodb-local

Важно: у вашего лямбда-кода теперь должна быть конечная точка для динамо в DynamoDBEndpoint

Сказать например:

if(process.env.AWS_SAM_LOCAL) {
  options.endpoint = 'http://DynamoDBEndpoint:8000'
}

Тестирование всего:

Использование lambci:lambda

Это должно только перечислить все таблицы внутри вашего другого контейнера DynamodB

docker run -ti --rm --network myNetworkName lambci/lambda:build-go1.x \
   aws configure set aws_access_key_id "xxx" && \
   aws configure set aws_secret_access_key "yyy" &&  \
   aws --endpoint-url=http://DynamoDBEndpoint:4569 --region=us-east-1 dynamodb list-tables

Или для вызова функции: (Go Example, как NodeJS)

#Golang
docker run --rm -v "$PWD":/var/task lambci/lambda:go1.x handlerName '{"some": "event"}'
#Same for NodeJS 
docker run --rm -v "$PWD":/var/task lambci/lambda:nodejs10.x index.handler

Больше информации о lambci/lambda можно найти здесь

Используя SAM (который использует тот же контейнер lmabci/lambda):

sam local invoke --event myEventData.json --docker-network myNetworkName MyFuncName

Вы всегда можете использовать --debug если хотите увидеть больше деталей.

Кроме того, вы также можете использовать http://host.docker.internal:8000 без хлопот с докером, этот URL-адрес зарезервирован для внутреннего использования и дает вам доступ к вашему хост-компьютеру, но убедитесь, что вы открываете порт 8000 при запуске динамод контейнер. Хотя это довольно просто, но это не работает во всех операционных системах. Для получения более подробной информации об этой функции, пожалуйста, проверьте докер документацию