Cannot load file mysql2 on AWS Lambda

Viewed 1800

Trying to have a Lambda connect to an RDS database but can't get the mysql2 gem to load. Tried the pristine instruction but that didn't resolve the issue.

I've got the built mysql2 gem in the vendor directory. Did this using bundle install --deployment.

Presumably this is a problem because of the compiled extensions used by mysql2. Not sure how I can sort this for AWS Lambda though. Thoughts?

Here's the log output:

START RequestId: 62f35c49-039f-11e9-be04-1fd1111df42b Version: $LATEST
Ignoring mysql2-0.5.2 because its extensions are not built. Try: gem pristine mysql2 --version 0.5.2
Init error when loading handler lambda_function.lambda_handler
{
  "errorMessage": "cannot load such file -- mysql2",
  "errorType": "Init<LoadError>",
  "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/lambda_function.rb:3: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'"
  ]
}
END RequestId: 62f35c49-039f-11e9-be04-1fd1111df42b
REPORT RequestId: 62f35c49-039f-11e9-be04-1fd1111df42b  Duration: 1439.17 ms    Billed Duration: 1500 ms    Memory Size: 128 MB Max Memory Used: 17 MB  
Unknown application error occurred
Init<LoadError>

Here's my Gemfile:

source 'https://rubygems.org'

gem 'mysql2', '~> 0.5.2'
gem 'sequel', '~> 5.15.0'

Gemfile.lock

GEM
remote: https://rubygems.org/
specs:
  mysql2 (0.5.2)
  sequel (5.15.0)

PLATFORMS
  ruby

DEPENDENCIES
  mysql2 (~> 0.5.2)
  sequel (~> 5.15.0)

BUNDLED WITH
  1.17.2

Here's the top of my lambda_function.rb file

require 'json'
require 'logger'
require 'mysql2'
require 'sequel'
3 Answers

Dhanabal's answer works but lets me elaborate more since the answer is too procedural for me.

Basically, a gem with native extension should build on the same environment as AWS Lambda.
So we need to build native extension inside lambci/lambda:build-ruby2.5 docker image that mimics Lambda environment.

Also, we need to copy dynamic libraries into the load path.
First, let's find out where can we place the libraries.
echo $LD_LIBRARY_PATH in the container and you'll get /var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib.
So, I simply copy the library from /usr/lib64/mysql to [function dir]/lib and deploy.

That's it.

Prerequesties: * Docker

Create a docker file like this

    FROM lambci/lambda:build-ruby2.5
    RUN yum -y install mysql-devel
    RUN gem update bundler
    CMD "/bin/bash"

Build the docker

    docker build -t lambda-ruby2.5-mysqldep .

Docker run in your source code folder with the below command

    docker run --rm -it -v $PWD:/var/task -w /var/task lambda-ruby2.5-mysqldep

It will get you into the bash Inside the bash

  • move the mysql folder from /usr/lib64/mysql to /usr/mysql
  • mkdir -p /var/task/lib
  • cp -a /usr/mysql/.so. /var/task/lib/
  • bundle config --local build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
  • bundle install with and without deployment

Then exit the bash. And create a lambda deployment package and upload it to AWS. It worked for me

2 things about Dhanabal's answer

1) cp -a /usr/mysql/.so step is missing a * 2) Make sure there are no sym links in the lib directory, you need actual files because symlinks break after they are zipped up.

Related