在 docker 中将 AWS SAM Local 与 dynamodb 连接
connecting AWS SAM Local with dynamodb in docker
我已经使用 AWS sam local 设置了一个 api gateway/aws lambda 对,并确认我可以在 运行
之后成功调用它
sam local start-api
然后我在 docker 容器中添加了一个本地 dynamodb 实例,并使用 aws cli
在其上创建了一个 table
但是,将代码添加到 lambda 以写入我收到的 dynamodb 实例:
2018-02-22T11:13:16.172Z ed9ab38e-fb54-18a4-0852-db7e5b56c8cd error:
could not write to table: {"message":"connect ECONNREFUSED
0.0.0.0:8000","code":"NetworkingError","errno":"ECONNREFUSED","syscall":"connect","address":"0.0.0.0","port":8000,"region":"eu-west-2","hostname":"0.0.0.0","retryable":true,"time":"2018-02-22T11:13:16.165Z"}
writing event from command:
{"name":"test","geolocation":"xyz","type":"createDestination"} END
RequestId: ed9ab38e-fb54-18a4-0852-db7e5b56c8cd
我在网上看到您可能需要连接到同一个 docker 网络,所以我创建了一个网络 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()
}
并检查我的代码正在创建的 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' }
这个设置应该有效吗?我如何让他们互相交谈?
----编辑----
基于 Twitter 对话,值得一提的是(也许)我可以在 CLI 和网络上与 dynamodb 交互 shell
非常感谢 Heitor Lessa who answered me on Twitter 以及示例 repo
这让我找到了答案...
dynamodb 的 docker 容器在我的上下文中位于 127.0.0.1
机器(这就是我可以与之交互的原因)
SAM 本地的 docker 容器在我的上下文中位于 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 本地 docker 容器的上下文中,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 容器一个名字
如果你像很多开发者一样在 mac 上使用 sam-local,你应该可以只使用
options.endpoint = "http://docker.for.mac.localhost:8000"
的较新安装
options.endpoint = "http://host.docker.internal:8000"
不必像 Paul 上面显示的那样执行多个命令(但这可能与平台无关?)。
如@Paul 所述,它是关于在 docker 容器 - lambda 和数据库之间配置网络。
另一种对我有用的方法(使用 docker-compose)。
docker-撰写:
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
我的 docker 容器名称是 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
堆栈:
- Docker: 18.03.1-ce
- Docker-撰写: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,
});
但是,我使用 docker-compose up
启动了 LocalStack。使用 pip
CLI 工具启动 LocalStack 可能会产生不同的标识符。
SAM
在后台启动一个 docker 容器 lambci/lambda
,如果您有另一个容器托管 dynamodb
或您想要连接的任何其他服务你的 lambda,所以你应该在 同一个网络
假设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
重要提示: 您的 lambda 代码现在应该有端点到 dynamo 到 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 的更多信息 here
使用SAM
(使用相同的容器lmabci/lambda
):
sam local invoke --event myEventData.json --docker-network myNetworkName MyFuncName
如果您想查看更多详细信息,您可以随时使用 --debug
选项。
或者,你也可以使用http://host.docker.internal:8000
而不用玩docker,这个URL是内部保留的,给你访问您的主机,但确保在启动 dynamodb 容器时公开端口 8000。虽然它很容易,但它并不适用于所有操作系统。有关此功能的更多详细信息,请查看 docker documentation
其他答案对我来说过于复杂/不清楚。这是我想出的。
第 1 步:使用 docker-compose 在自定义网络上获取本地 DynamoDB 运行
docker-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
网络中的 docker 为我自动创建的。别名就是服务名称。
第 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
使用在我的 docker-compose.yml
[=25= 中定义的现有网络 abp-sam-backend
]
端到端示例
我制作了一个可以在 https://github.com/rynop/abp-sam-nestjs
中找到的工作示例(加上许多其他功能)
这可能对仍然面临相同问题的人有所帮助:
我最近也遇到了同样的问题。我遵循了 rynop 提到的所有步骤(感谢@rynop)
我通过在以下代码中将端点 (http://localhost:8000) 替换为我的(私有)IP 地址(即 http://192.168.8.101:8000)解决了问题(在我的 windows 上) :
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');
}
我已经使用 AWS sam local 设置了一个 api gateway/aws lambda 对,并确认我可以在 运行
之后成功调用它sam local start-api
然后我在 docker 容器中添加了一个本地 dynamodb 实例,并使用 aws cli
在其上创建了一个 table但是,将代码添加到 lambda 以写入我收到的 dynamodb 实例:
2018-02-22T11:13:16.172Z ed9ab38e-fb54-18a4-0852-db7e5b56c8cd error: could not write to table: {"message":"connect ECONNREFUSED 0.0.0.0:8000","code":"NetworkingError","errno":"ECONNREFUSED","syscall":"connect","address":"0.0.0.0","port":8000,"region":"eu-west-2","hostname":"0.0.0.0","retryable":true,"time":"2018-02-22T11:13:16.165Z"} writing event from command: {"name":"test","geolocation":"xyz","type":"createDestination"} END RequestId: ed9ab38e-fb54-18a4-0852-db7e5b56c8cd
我在网上看到您可能需要连接到同一个 docker 网络,所以我创建了一个网络 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()
}
并检查我的代码正在创建的 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' }
这个设置应该有效吗?我如何让他们互相交谈?
----编辑----
基于 Twitter 对话,值得一提的是(也许)我可以在 CLI 和网络上与 dynamodb 交互 shell
非常感谢 Heitor Lessa who answered me on Twitter 以及示例 repo
这让我找到了答案...
dynamodb 的 docker 容器在我的上下文中位于 127.0.0.1 机器(这就是我可以与之交互的原因)
SAM 本地的 docker 容器在我的上下文中位于 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 本地 docker 容器的上下文中,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 容器一个名字
如果你像很多开发者一样在 mac 上使用 sam-local,你应该可以只使用
options.endpoint = "http://docker.for.mac.localhost:8000"
options.endpoint = "http://host.docker.internal:8000"
不必像 Paul 上面显示的那样执行多个命令(但这可能与平台无关?)。
如@Paul 所述,它是关于在 docker 容器 - lambda 和数据库之间配置网络。
另一种对我有用的方法(使用 docker-compose)。
docker-撰写:
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
我的 docker 容器名称是 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
堆栈:
- Docker: 18.03.1-ce
- Docker-撰写: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,
});
但是,我使用 docker-compose up
启动了 LocalStack。使用 pip
CLI 工具启动 LocalStack 可能会产生不同的标识符。
SAM
在后台启动一个 docker 容器 lambci/lambda
,如果您有另一个容器托管 dynamodb
或您想要连接的任何其他服务你的 lambda,所以你应该在 同一个网络
假设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
重要提示: 您的 lambda 代码现在应该有端点到 dynamo 到 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 的更多信息 here
使用SAM
(使用相同的容器lmabci/lambda
):
sam local invoke --event myEventData.json --docker-network myNetworkName MyFuncName
如果您想查看更多详细信息,您可以随时使用 --debug
选项。
或者,你也可以使用http://host.docker.internal:8000
而不用玩docker,这个URL是内部保留的,给你访问您的主机,但确保在启动 dynamodb 容器时公开端口 8000。虽然它很容易,但它并不适用于所有操作系统。有关此功能的更多详细信息,请查看 docker documentation
其他答案对我来说过于复杂/不清楚。这是我想出的。
第 1 步:使用 docker-compose 在自定义网络上获取本地 DynamoDB 运行
docker-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
网络中的 docker 为我自动创建的。别名就是服务名称。
第 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
使用在我的 docker-compose.yml
[=25= 中定义的现有网络 abp-sam-backend
]
端到端示例
我制作了一个可以在 https://github.com/rynop/abp-sam-nestjs
中找到的工作示例(加上许多其他功能)这可能对仍然面临相同问题的人有所帮助:
我最近也遇到了同样的问题。我遵循了 rynop 提到的所有步骤(感谢@rynop)
我通过在以下代码中将端点 (http://localhost:8000) 替换为我的(私有)IP 地址(即 http://192.168.8.101:8000)解决了问题(在我的 windows 上) :
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');
}