AWS Lambda 函数无法使用 R 脚本找到容器化图像的引导文件

问题描述 投票:0回答:0

我已经设置了一个推送到 ECR 的图像,然后由我的 lambda 函数使用。我的脚本包括 R 中的自定义运行时、我的 lambda 函数和一个用于将工作目录更改为 $LAMBDA_TASK_ROOT 的引导文件。

我是使用 AWS 云应用程序和开发供 lambda 使用的容器化图像的初学者。我一直在使用此链接作为指南:https://mdneuzerling.com/post/r-on-aws-lambda-with-containers/

这是我目前通过AWS接口测试函数的错误。由于工作计算机的一些限制,我无法在本地测试功能和 dockerfile。

/lambda-entrypoint.sh: line 14: /var/runtime/bootstrap: No such file or directory
/lambda-entrypoint.sh: line 14: /var/runtime/bootstrap: No such file or directory
START RequestId: a0fea004-ed78-46a5-972f-5463e89ccad2 Version: $LATEST
RequestId: a0fea004-ed78-46a5-972f-5463e89ccad2 Error: Runtime exited with error: exit status 127
Runtime.ExitError
END RequestId: a0fea004-ed78-46a5-972f-5463e89ccad2
REPORT RequestId: a0fea004-ed78-46a5-972f-5463e89ccad2  Duration: 22.76 ms  Billed Duration: 23 ms  Memory Size: 128 MB Max Memory Used: 3 MB   

这是我的 dockerfile

FROM public.ecr.aws/lambda/provided:al2

ENV R_VERSION=4.3.0

RUN yum -y install amazon-linux-extras && \
    yum clean all

RUN amazon-linux-extras install kernel-5.15 && \
    yum clean all

RUN yum -y install wget-1.14 && \
    yum -y install glib2-2.56.1-9.amzn2.0.3 && \
    yum -y install libssh2-1.4.3-12.amzn2.2.4 && \
    yum clean all

RUN yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \
  && wget --no-verbose https://cdn.rstudio.com/r/centos-7/pkgs/R-${R_VERSION}-1-1.x86_64.rpm \
  && yum -y install R-${R_VERSION}-1-1.x86_64.rpm \
  && rm R-${R_VERSION}-1-1.x86_64.rpm && \
  yum clean all

ENV PATH="${PATH}:/opt/R/${R_VERSION}/bin/"

# System requirements for R packages
RUN yum -y install openssl-devel && \
    Rscript -e "install.packages(c('httr', 'jsonlite', 'logger', 'logging', 'sp', 'raster', 'parallel'), Ncpus=2L, INSTALL_opts=c('--no-docs', '--no-help','--no-html', '--no-multiarch', '--no-test-load'), dependencies=TRUE, repos=c('https://artifactory.wma.chs.usgs.gov/artifactory/r-cran-mirror/','https://RForge.net'))" && \
    Rscript -e "install.packages('aws.s3', Ncpus=2L, INSTALL_opts=c('--no-docs', '--no-help','--no-html', '--no-multiarch', '--no-test-load'), repos = c('https://RForge.net'))" && \
    yum install -y \
    openldap-2.4.44-25.amzn2.0.5 && \
    yum clean all && rm -rf /var/lib/apt/lists/*

COPY runtime.r process_landsat_temp.r ${LAMBDA_TASK_ROOT}/
RUN chmod 755 -R "${LAMBDA_TASK_ROOT}"

WORKDIR /var/runtime
COPY bootstrap bootstrap
RUN chmod +x bootstrap

我的运行时 R 脚本。

library(httr)
library(logger)
log_formatter(formatter_paste)
log_threshold(INFO)

#' Convert a list to a single character, preserving names
#' prettify_list(list("a" = 1, "b" = 2, "c" = 3))
#' # "a=5, b=5, c=5"
prettify_list <- function(x) {
  paste(
    paste(names(x), x, sep = "="),
    collapse = ", "
  )
}

# error handling with http codes
# from http://adv-r.had.co.nz/Exceptions-Debugging.html
condition <- function(subclass, message, code, call = sys.call(-1), ...) {
  structure(
    class = c(subclass, "condition"),
    list(message = message, code = code, call = call),
    ...
  )
}
stop_api <- function(message, code = 500, call = sys.call(-1), ...) {
  stop(condition(c("api_error", "error"), message, code = code, call = call,
                 ...))
}

log_debug("Deriving lambda runtime API endpoints from environment variables")
lambda_runtime_api <- Sys.getenv("AWS_LAMBDA_RUNTIME_API")
if (lambda_runtime_api == "") {
  error_message <- "AWS_LAMBDA_RUNTIME_API environment variable undefined"
  log_error(error_message)
  stop(error_message)
}
next_invocation_endpoint <- paste0(
  "http://", lambda_runtime_api, "/2018-06-01/runtime/invocation/next"
)
initialisation_error_endpoint <- paste0(
  "http://", lambda_runtime_api, "/2018-06-01/runtime/init/error"
)

tryCatch(
  {
    log_debug("Determining handler from environment variables")
    handler <- Sys.getenv("_HANDLER")
    if (is.null(handler) || handler == "") {
      stop_api("_HANDLER environment variable undefined")
    }
    log_info("Handler found:", handler)
    handler_split <- strsplit(handler, ".", fixed = TRUE)[[1]]
    file_name <- paste0(handler_split[1], ".R")
    function_name <- handler_split[2]
    log_info("Using function", function_name, "from", file_name)
    
    log_debug("Checking if", file_name, "exists")
    if (!file.exists(file_name)) {
      stop_api(file_name, " doesn't exist in ", getwd())
    }
    source(file_name)
    
    log_debug("Checking if", function_name, "is defined")
    if (!exists(function_name)) {
      stop_api("Function name ", function_name, " isn't defined in R")
    }
    log_debug("Checking if", function_name, "is a function")
    if (!is.function(eval(parse(text = function_name)))) {
      stop_api("Function name ", function_name, " is not a function")
    }
  },
  api_error = function(e) {
    log_error(as.character(e))
    POST(
      url = initialisation_error_endpoint,
      body = list(
        statusCode = e$code,
        error_message = as.character(e$message)),
      encode = "json"
    )
    stop(e)
  }
)

handle_event <- function(event) {
  status_code <- status_code(event)
  log_debug("Status code:", status_code)
  if (status_code != 200) {
    stop_api("Didn't get status code 200. Status code: ", status_code,
             code = 400)
  }
  event_headers <- headers(event)
  
  # HTTP headers are case-insensitive
  names(event_headers) <- tolower(names(event_headers))
  log_debug("Event headers:", prettify_list(event_headers))
  
  aws_request_id <- event_headers[["lambda-runtime-aws-request-id"]]
  if (is.null(aws_request_id)) {
    stop_api("Could not find lambda-runtime-aws-request-id header in event",
             code = 400)
  }
  
  # According to the AWS guide, the below is used by "X-Ray SDK"
  runtime_trace_id <- event_headers[["lambda-runtime-trace-id"]]
  if (!is.null(runtime_trace_id)) {
    Sys.setenv("_X_AMZN_TRACE_ID" = runtime_trace_id)
  }
  
  # we need to parse the event in four contexts before sending to the lambda fn:
  # 1a) direct invocation with no function args (empty event)
  # 1b) direct invocation with function args (parse and send entire event)
  # 2a) api endpoint with no args (parse HTTP request, confirm null request
  #   element; send empty list)
  # 2b) api endpoint with args (parse HTTP request, confirm non-null request
  #   element; extract and send it)
  
  unparsed_content <- httr::content(event, "text", encoding = "UTF-8")
  # Thank you to Menno Schellekens for this fix for Cloudwatch events
  is_scheduled_event <- grepl("Scheduled Event", unparsed_content)
  if(is_scheduled_event) log_info("Event type is scheduled")
  log_debug("Unparsed content:", unparsed_content)
  if (unparsed_content == "" || is_scheduled_event) {
    # (1a) direct invocation with no args (or scheduled request)
    event_content <- list()
  } else {
    # (1b, 2a or 2b)
    event_content <- jsonlite::fromJSON(unparsed_content)
  }
  
  # if you want to do any additional inspection of the event body (including
  # other http request elements if it's an endpoint), you can do that here!
  
  # change `http_req_element` if you'd prefer to send the http request `body` to
  # the lambda fn, rather than the query parameters
  # (note that query string params are always strings! your lambda fn may need to
  # convert them back to numeric/logical/Date/etc.)
  is_http_req <- FALSE
  http_req_element <- "queryStringParameters"
  
  if (http_req_element %in% names(event_content)) {
    is_http_req <- TRUE
    if (is.null(event_content[[http_req_element]])) {
      # (2a) api request with no args
      event_content <- list()
    } else {
      # (2b) api request with args
      event_content <- event_content[[http_req_element]]
    }
  }
  
  result <- do.call(function_name, event_content)
  log_debug("Result:", as.character(result))
  response_endpoint <- paste0(
    "http://", lambda_runtime_api, "/2018-06-01/runtime/invocation/",
    aws_request_id, "/response"
  )
  # aws api gateway is a bit particular about the response format
  body <- if (is_http_req) {
    list(
      isBase64Encoded = FALSE,
      statusCode = 200L,
      body =  as.character(jsonlite::toJSON(result, auto_unbox = TRUE))
    )
  } else {
    result
  }
  POST(
    url = response_endpoint,
    body = body,
    encode = "json"
  )
  rm("aws_request_id") # so we don't report errors to an outdated endpoint
}

log_info("Querying for events")
while (TRUE) {
  tryCatch(
    {
      event <- GET(url = next_invocation_endpoint)
      log_debug("Event received")
      handle_event(event)
    },
    api_error = function(e) {
      log_error(as.character(e))
      aws_request_id <-
        headers(event)[["lambda-runtime-aws-request-id"]]
      if (exists("aws_request_id")) {
        log_debug("POSTing invocation error for ID:", aws_request_id)
        invocation_error_endpoint <- paste0(
          "http://", lambda_runtime_api, "/2018-06-01/runtime/invocation/",
          aws_request_id, "/error"
        )
        POST(
          url = invocation_error_endpoint,
          body = list(
            statusCode = e$code,
            error_message = as.character(e$message)),
          encode = "json"
        )
      } else {
        log_debug("No invocation ID!",
                  "Can't clear this request from the queue.")
      }
    },
    error = function(e) {
      log_error(as.character(e))
      aws_request_id <-
        headers(event)[["lambda-runtime-aws-request-id"]]
      if (exists("aws_request_id")) {
        log_debug("POSTing invocation error for ID:", aws_request_id)
        invocation_error_endpoint <- paste0(
          "http://", lambda_runtime_api, "/2018-06-01/runtime/invocation/",
          aws_request_id, "/error"
        )
        POST(
          url = invocation_error_endpoint,
          body = list(error_message = as.character(e)),
          encode = "json"
        )
      } else {
        log_debug("No invocation ID!",
                  "Can't clear this request from the queue.")
      }
    }
  )
}

还有我的引导程序文件

#!/bin/sh Rscript
cd $LAMBDA_TASK_ROOT
Rscript runtime.r

r linux amazon-web-services aws-lambda
© www.soinside.com 2019 - 2024. All rights reserved.