Подключение AWS SAM Local с динамодабом в докере
Я настроил пару 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
но все равно получаю ту же ошибку
Сэм местный распечатывает 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()
}
и проверяя DynamodBclient мой код создает шоу
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 и в веб-оболочке
7 ответов
Большое спасибо Heitor Lessa, который ответил мне в Twitter с примером репо
Который указал мне на ответ...
Контейнер Docker DynamodB находится на 127.0.0.1 из контекста моей машины (именно поэтому я мог взаимодействовать с ним)
Докерский контейнер SAM local находится на 127.0.0.1 из контекста моей машины
Но они не на 127.0.0.1 друг от друга
Итак: https://github.com/heitorlessa/sam-local-python-hot-reloading/blob/master/users/users.py
Указал мне на изменение моего кода подключения на:
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'
}
из контекста локального Docker-контейнера 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 имя
Если вы используете sam-local на Mac, как многие разработчики, вы можете просто использовать
options.endpoint = "http://docker.for.mac.localhost:8000"
Или на более новых установках докера https://docs.docker.com/docker-for-mac/release-notes/
options.endpoint = "http://host.docker.internal:8000"
Вместо того, чтобы выполнять несколько команд, как показывал Пол выше (но это может быть более независимым от платформы?).
Другие ответы были слишком сложными / неясными для меня. Вот что я придумал.
Шаг 1: используйте docker-compose, чтобы локально запустить DynamoDB в пользовательской сети
докер-compose.yml
Обратите внимание на имя сети abp-sam-backend
, наименование услуги dynamo
и это dynamo
Сервис использует backend
сеть.
version: '3.5'
services:
dynamo:
container_name: abp-sam-nestjs-dynamodb
image: amazon/dynamodb-local
networks:
- backend
ports:
- '8000:8000'
volumes:
- dynamodata:/home/dynamodblocal
working_dir: /home/dynamodblocal
command: '-jar DynamoDBLocal.jar -sharedDb -dbPath .'
networks:
backend:
name: abp-sam-backend
volumes:
dynamodata: {}
Запустите локальный контейнер DyanmoDB через:
docker-compose up -d dynamo
Шаг 2: Напишите свой код для обработки локальной конечной точки DynamoDB
import { DynamoDB, Endpoint } from 'aws-sdk';
const ddb = new DynamoDB({ apiVersion: '2012-08-10' });
if (process.env['AWS_SAM_LOCAL']) {
ddb.endpoint = new Endpoint('http://dynamo:8000');
} else if ('local' == process.env['APP_STAGE']) {
// Use this when running code directly via node. Much faster iterations than using sam local
ddb.endpoint = new Endpoint('http://localhost:8000');
}
Обратите внимание, что я использую псевдоним имени хоста dynamo
, Этот псевдоним автоматически создается для меня докером внутри abp-sam-backend
сеть. Псевдоним - это просто имя службы.
Шаг 3: Запустите код через sam local
sam local start-api -t sam-template.yml --docker-network abp-sam-backend --skip-pull-image --profile default --parameter-overrides 'ParameterKey=StageName,ParameterValue=local ParameterKey=DDBTableName,ParameterValue=local-SingleTable'
Обратите внимание, что я говорю sam local
использовать существующую сеть abp-sam-backend
это было определено в моем docker-compose.yml
Сквозной пример
Я сделал рабочий пример (плюс кучу других функций), которые можно найти по адресу https://github.com/rynop/abp-sam-nestjs
SAM
запускает докер контейнер 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 при запуске контейнера DynamodB. Хотя это довольно просто, но это не работает во всех операционных системах. Для получения более подробной информации об этой функции, пожалуйста, проверьте докер документацию
Как упомянул @Paul, речь идет о настройке вашей сети между контейнерами Docker - лямбда и база данных.
Другой подход, который работал для меня (с помощью docker-compose).
докер-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-й
- Docker-compose: 1.21.1
- MacOS HighSierra 10.13.6
Если вы используете 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 может привести к различным идентификаторам.
Это может быть полезно для тех, кто все еще сталкивается с той же проблемой:
Недавно я столкнулся с той же проблемой. Я выполнил все шаги, упомянутые rynop (Спасибо @rynop)
Я исправил проблему (в своих окнах), заменив конечную точку (http: // localhost:8000) на мой (частный) IP-адрес (например, http://192.168.8.101:8000) в следующем коде:
import { DynamoDB, Endpoint } from 'aws-sdk';
const ddb = new DynamoDB({ apiVersion: '2012-08-10' });
if (process.env['AWS_SAM_LOCAL']) {
ddb.endpoint = new Endpoint('http://dynamo:8000');
} else if ('local' == process.env['APP_STAGE']) {
// Use this when running code directly via node. Much faster iterations than using sam local
ddb.endpoint = new Endpoint('http://localhost:8000');
}