从 AWS Lambda 触发 AWS EMR 流程步骤时超时

Timeout while triggering AWS EMR flow step from AWS Lambda

我正在尝试 运行 JavaScript 中的 AWS lambda 应用程序,但无法使其正常工作。 我在 JS 配置和触发方面没有遇到任何问题(我成功 运行 获得了一个 hello world 应用程序),但我遇到了 aws-sdk 库的问题。老实说,我不知道这是与网络配置还是 IAM 配置有关的问题,但我很确定这不是脚本问题,因为我可以 运行 在我的本地没有任何问题电脑。 我遇到的主要问题是,当 lambda 应用程序调用 AWS EMR API 时,出现超时错误。这就像 lambda 无法与 EMR 通信。

这里可以看到emr客户端(console.log(emr_client)):

  emr: Service {
    config: 
     Config {
       credentials: 
        EnvironmentCredentials {
          expired: false,
          expireTime: null,
          accessKeyId: 'XXXXXXXXXXXXXXXX',
          sessionToken: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
          envPrefix: 'AWS' },
       credentialProvider: CredentialProviderChain { providers: [Array] },
       region: 'us-west-2',
       logger: null,
       apiVersions: {},
       apiVersion: '2009-03-31',
       endpoint: 'elasticmapreduce.us-west-2.amazonaws.com',
       httpOptions: { timeout: 120000 },
       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: 'v4',
       signatureCache: true,
       retryDelayOptions: {},
       useAccelerateEndpoint: false,
       accesKeyId: 'XXXXXXXXXXXXXXXX' },
    isGlobalEndpoint: false,
    endpoint: 
     Endpoint {
       protocol: 'https:',
       host: 'elasticmapreduce.us-west-2.amazonaws.com',
       port: 443,
       hostname: 'elasticmapreduce.us-west-2.amazonaws.com',
       pathname: '/',
       path: '/',
       href: 'https://elasticmapreduce.us-west-2.amazonaws.com/' },
    _clientId: 1 
    }

一些 AWS 配置信息:

  1. 我在我的 EMR 集群所在的位置创建了一个 VPC,位于 us-west-2 区域,我正在那里触发 lambda 函数(因为我可以确认安慰 process.env.AWS_REGION) .

  2. 我设置了一个之前在同一个 VPC 中创建的子网。 EMR 集群在其中,Lambda 函数可以访问它。

  3. 我在同一个 VPC 中设置了一个安全组,允许所有 inbounds/outbounds(所有端口进出 0.0.0.0/0),看看我是否有配置问题。

  4. 我设置了一个执行角色,附加了以下策略并将其与我的 lambda 函数链接:

AWSLambdaFullAccess

AmazonElasticMapReduceFullAccess

AWSLambdaExecute

AWSLambdaVPCAccessExecutionRole

AWSLambdaRole

AWSLambdaENIManagementAccess

最后,我的代码:

const AWS = require('aws-sdk');

exports.handler = (event, context, callback) => {
  const emr = new AWS.EMR({
    apiVersion:'2009-03-31',
    region: process.env.AWS_REGION,
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
  });

  const flowSteps = {
    JobFlowId: process.env['JOB_FLOW_ID'],
    Steps: [{
      Name: "my_beautiful_step",
      ActionOnFailure: "CANCEL_AND_WAIT",
      HadoopJarStep: {
        Jar: "command-runner.jar",
        Args: [
          "spark-submit",
          "--master"," yarn",
          ...
          ...
          ...
        ]
      }
    }]
  };

  emr.addJobFlowSteps(flowSteps, (err, data) => {
    if (err) {
      console.log('ERROR', err, err.stack);
    } else {
      console.log('NO ERROR', data);
    }
  });

};

编辑: 我尝试与 s3 通信(获取存储桶位置)只是为了测试问题是否仅与 EMR 有关,但该功能也会超时。

好吧,我解决了我的问题。基本上,如果您无法访问互联网,则无法在 VPC 内调用 AWS API 端点,因为大多数 aws 服务都有 public URL,例如 https://elasticmapreduce.us-west-2.amazonaws.com.当您控制台 EMR 客户端对象时,您可以清楚地看到这一点(这也适用于我验证过的其他客户端对象,例如 S3)

Service {
  config: 
   Config {
     ...
     ...
     region: 'us-west-2',
     logger: null,
     apiVersions: {},
     apiVersion: null,
     endpoint: 'elasticmapreduce.us-west-2.amazonaws.com',
     httpOptions: { timeout: 120000 },
     maxRetries: undefined,
   },
  endpoint: 
   Endpoint {
     protocol: 'https:',
     host: 'elasticmapreduce.us-west-2.amazonaws.com',
     port: 443,
     hostname: 'elasticmapreduce.us-west-2.amazonaws.com',
     pathname: '/',
     path: '/',
     href: 'https://elasticmapreduce.us-west-2.amazonaws.com/' 
    },
  ...
}

无论如何,AWS 在 vpcs VPC Endpoints 中提供了一些本地端点,这样您就可以在不访问 Internet 的情况下访问 VPC 中的那些服务端点。另一种情况,你必须设置一个NAT网关+互联网网关(~u$30/月)才能访问其他服务,如EMR。