【问题标题】:connecting AWS SAM Local with dynamodb in docker在 docker 中将 AWS SAM Local 与 dynamodb 连接
【发布时间】:2021-11-04 17:36:11
【问题描述】:

我已经使用 AWS sam local 设置了一个 api gateway/aws lambda 对,并确认我可以在运行后成功调用它

sam local start-api

然后,我在 docker 容器中添加了一个本地 dynamodb 实例,并使用 aws cli 在其上创建了一个表

但是,将代码添加到 lambda 以写入我收到的 dynamodb 实例:

2018-02-22T11:13:16.172Z ed9ab38e-fb54-18a4-0852-db7e5b56c8cd 错误: 无法写入表:{“消息”:“连接 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","re​​tryable":true,"time":"2018-02-22T11:13:16.165Z"} 从命令写入事件: {"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 和 web shell 中与 dynamodb 交互

【问题讨论】:

    标签: amazon-web-services docker amazon-dynamodb aws-sam-cli


    【解决方案1】:

    非常感谢 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 容器命名

    【讨论】:

    • 对于任何被抓到的人的小补充 - 您需要事先专门创建 lambda-local 网络:docker network create lambda-local
    • 有人可以澄清为什么 sam 本地容器无法通过默认桥与 dynamodb 容器通信?
    • 但是...现在您无法与 dynamodb shell 交互
    【解决方案2】:

    如果你像很多开发者一样在 Mac 上使用 sam-local,你应该可以使用

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

    或者在新安装的 docker 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"

    不必像上面显示的 Paul 那样执行多个命令(但这可能与平台无关?)。

    【讨论】:

    【解决方案3】:

    其他答案对我来说过于复杂/不清楚。这是我想出的。

    第 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。这个别名是 docker 在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 使用在我的docker-compose.yml 中定义的现有网络abp-sam-backend

    端到端示例

    我做了一个可以在https://github.com/rynop/abp-sam-nestjs找到的工作示例(以及许多其他功能)

    【讨论】:

      【解决方案4】:

      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示例,同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 是内部保留的,可让您访问您的主机,但请确保在使用时公开端口 8000启动 dynamodb 容器。虽然这很容易,但它并不适用于所有操作系统。有关此功能的更多详细信息,请查看docker documentation

      【讨论】:

      • 我遇到了类似 host.docker.internal\' 的错误。此服务可能在 `us-east-1\' 中不可用。我正在运行 Docker 工具箱。这应该工作吗?
      • http://host.docker.internal:8000 不起作用,即使使用 docker run -p 8000:8000 ....
      【解决方案5】:

      正如@Paul 提到的,它是关于在 docker 容器 - lambda 和数据库之间配置您的网络。

      另一种对我有用的方法(使用 docker-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
      

      我的 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-compose: 1.21.1
      • MacOS HighSierra 10.13.6

      【讨论】:

      • 是否可以从docker-compose 中启动 sam 本地容器(即使用 start-api cmd)?而不是通过 cli 的手动命令单独执行?
      • @Kwhitejr 在他的博客@pauldambra.github.io/2018/02/serverless-3.html 中提到,您需要使用 docker-network 参数运行命令,例如sam local start-api --docker-network lambda-local
      【解决方案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 可能会产生不同的标识符。

      【讨论】:

        【解决方案7】:

        这可能对仍然面临相同问题的人有所帮助:

        我最近也遇到了同样的问题。我按照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');
        }
        

        【讨论】:

          猜你喜欢
          • 2023-01-23
          • 2020-07-10
          • 2022-11-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-06-27
          • 1970-01-01
          • 2019-11-18
          相关资源
          最近更新 更多