如何在AWS Lambda中正确加载gem扩展

7

我在 AWS Lambda 上遇到了一个gem加载错误的问题。

{
  "errorMessage": "LoadError: libpq.so.5: cannot open shared object file: No such file or directory - /var/task/vendor/bundle/ruby/2.5.0/gems/pg-1.1.4/lib/pg_ext.so",
  "errorType": "Function<Sequel::AdapterNotFound>",
  "stackTrace": [
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/pg-1.1.4/lib/pg.rb:4:in `<top (required)>'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/sequel-5.16.0/lib/sequel/adapters/postgres.rb:6:in `<top (required)>'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/lang/lib/ruby/2.5.0/rubygems/core_ext/kernel_require.rb:59:in `require'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/sequel-5.16.0/lib/sequel/database/connecting.rb:88:in `load_adapter'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/sequel-5.16.0/lib/sequel/database/connecting.rb:17:in `adapter_class'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/sequel-5.16.0/lib/sequel/database/connecting.rb:45:in `connect'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/sequel-5.16.0/lib/sequel/core.rb:121:in `connect'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/sequel-5.16.0/lib/sequel/core.rb:399:in `adapter_method'",
    "/var/task/vendor/bundle/ruby/2.5.0/gems/sequel-5.16.0/lib/sequel/core.rb:406:in `block (2 levels) in def_adapter_method'",
    "/var/task/lib/warehouse/loader.rb:5:in `connection'",
    "/var/task/lib/warehouse/loader.rb:24:in `initialize'",
    "/var/task/lib/warehouse/update.rb:43:in `new'",
    "/var/task/lib/warehouse/update.rb:43:in `block in handle'",
    "/var/task/lib/warehouse/update.rb:42:in `each'",
    "/var/task/lib/warehouse/update.rb:42:in `handle'",
    "/var/task/lambda.rb:11:in `handler'"
  ]
}

我正在使用Sequel库从AWS Lambda建立PSQL连接,但似乎该函数无法找到so文件。 我已经将依赖项打包在vendor / bundle中,在CodeBuild上在Ubuntu中构建,并验证了.so文件存在于上传到lambda的结果工件中。 我还编辑了$ LOAD_PATH,但似乎没有帮助。
有其他人遇到这种困难吗? 有任何进一步解决或调试的提示吗?

稍微详细一点:我打印了LOAD_PATH的内容。我清楚地看到了./vendor/bundle/ruby/2.5.0/gems/pg-1.1.4/lib,但上面的错误引用了/var/task/vendor/bundle/ruby/2.5.0/gems/pg-1.1.4/lib/pg.rb。我认为这是相同的路径...但我需要更明确吗? - 1ijk
4个回答

15

您的 lib 文件夹中是否有 libpq.so.5

您的错误说明没有在 $PATH 中找到 libpq.so.5,在 AWS Lambda 中,文件夹 lib 会自动加载到路径中,因此您只需要在那里放置这个文件。

在 Lambda 环境之外创建的可执行文件无法在 Lambda 上运行,此外,您需要在 Lambda 映像上自行编译可执行文件。以下是如何执行的示例:

Gemfile

source "https://rubygems.org"

gem "pg"
gem "mysql2"

handler.rb

require 'pg'
require 'mysql2'

def run(event:, context:)
  {
    postgres_client_version: PG.library_version,
    mysql_client_version: Mysql2::VERSION
  }
end

Dockerfile

:Docker的构建文件。
FROM lambci/lambda:build-ruby2.5

RUN yum install -y postgresql postgresql-devel mysql mysql-devel
RUN gem update bundler

ADD Gemfile /var/task/Gemfile
ADD Gemfile.lock /var/task/Gemfile.lock

RUN bundle install --path /var/task/vendor/bundle --clean

这将构建您的镜像,然后运行它以生成PG和MYSQL可执行文件,然后将其复制到您的lib文件夹中。

build.sh

#!/bin/bash -x
set -e

rm -rf lib && rm -rf vendor && mkdir lib && mkdir vendor

docker build -t pg_mysql_layer -f Dockerfile .

CONTAINER=$(docker run -d pg_mysql_layer false)

docker cp \
    $CONTAINER:/var/task/vendor/ \
    ./

docker cp \
    $CONTAINER:/usr/lib64/libpq.so.5.5 \
    lib/libpq.so.5

docker cp \
    $CONTAINER:/usr/lib64/mysql/. \
    lib/

docker rm $CONTAINER

运行./build.sh之后,将生成具有所需内容的文件夹libvendor,现在您只需要部署lambda函数即可。要在本地测试,可以运行:docker run --rm -it -v $PWD:/var/task -w /var/task lambci/lambda:ruby2.5 handler.run。它会返回类似于这样的内容:Lambda execution。参考资料: https://www.stevenringo.com/ruby-in-aws-lambda-with-postgresql-nokogiri/https://www.reddit.com/r/ruby/comments/a3e7a1/postgresql_on_aws_lambda_ruby/

使用 sam cli,这个命令对我无效:sam build --debug -t template.yaml --use-container - prcoder
@prcoder,请提供更多信息。这里的问题是关于在AWS Lambda上加载gem扩展,没有涉及到CLI。 - Ruan Carlos
嘿Ruan,真抱歉。问题出在使用SAM的AWS Lambda层上。这个解决方案不适用于此。我尝试了将所有必需的本地依赖项放到代码中的常规方法,它是有效的。感谢您提供的解决方案。 - prcoder
2
Ruby 2.7 的示例代码:https://github.com/proton/lambda-ruby-pg - proton

2

有几个很好的插件可以管理AWS Lambda的依赖项。对于Ruby,可以使用serverless-ruby-layer,对于Python,可以使用serverless-python-requirements

对于您的Ruby案例,您只需将插件相关配置添加到您的serverless.yml中即可使用serverless-ruby-layer。

service: using-docker-yums

plugins:
  - serverless-ruby-layer

custom:
  rubyLayer:
    use_docker: true
    docker_yums:
      - postgresql-devel
    native_libs:
      - /usr/lib64/libpq.so.5

provider:
  name: aws
  runtime: ruby2.5

functions:
  hello:
    handler: handler.hello

您需要在无服务器项目文件夹内使用以下命令安装插件:

sls plugin install -n serverless-ruby-layer

现在运行 sls deploy 将自动部署宝石和库到层中。

请查看文档中的示例


1

2022年更新:使用更新的pg gem版本:

FROM public.ecr.aws/lambda/ruby:2.7

# Get PostgreSQL 10 available in amazon-linux-extras
RUN yum install -y amazon-linux-extras
RUN amazon-linux-extras enable postgresql10
RUN yum install -y postgresql postgresql-devel

ADD . ${LAMBDA_TASK_ROOT}

ENV GEM_HOME=${LAMBDA_TASK_ROOT}
RUN bundle install

CMD [ "functions.my_function" ]

现在您需要在lambda/ruby:2.7 docker镜像中安装amazon-linux-extras,这将使postgresql10软件包可用。启用它,然后安装postgresql-devel。如果您尝试安装postgresql-devel而没有指定至少postgresql9.3或更高版本,则它将尝试安装PostgreSQL9.2,并且pg gem将引发一个错误,指出postgres客户端的版本已过时。


1
这里有一个类似于@ruan-carlos的解决方案,但是只需一步即可,并且更容易理解。
#!/bin/bash

set -e

cat >Dockerfile <<EOF
FROM public.ecr.aws/lambda/ruby:2.7
RUN yum install -y postgresql-libs
EOF

rm -rf lib

docker build -t pg-layer-source  .

mkdir lib

docker create -ti --name dummy pg-layer-source bash

docker cp dummy:/usr/lib64/libpq.so.5.5 lib/libpq.so.5
docker cp dummy:/usr/lib64/libldap_r-2.4.so.2.10.7 lib/libldap_r-2.4.so.2
docker cp dummy:/usr/lib64/liblber-2.4.so.2.10.7 lib/liblber-2.4.so.2
docker cp dummy:/usr/lib64/libsasl2.so.3.0.0 lib/libsasl2.so.3
docker cp dummy:/usr/lib64/libssl3.so lib
docker cp dummy:/usr/lib64/libsmime3.so lib
docker cp dummy:/usr/lib64/libnss3.so lib

docker rm -f dummy

zip -r $(date +%Y-%m-%d-%s)-lib-pg-layer.zip lib

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接