While on a project I encountered the following problem. The application was a Ruby program. It utilized some external gems (modules) and packaging it up was quite a feat even with the documentation available. Especially because we encountered following problems
As a good DevOps I read the documentation before doing stupid things. Yes, I wasn’t planning to waste a full day on experimenting instead of just reading the official guidelines.
Which brought me to the following AWS Documentation page “Updating a function with additional dependencies”
What I didn’t know was that step 1 would already produce some very shitty bug if you copy each line separately.
This command is meant to tell the bundle packager that he should not
use some global path for the gemfiles but instead do it locally in the
root folder. After that you do a bundle install
and you see
the vendor/bundle
folder being created with all your
dependencies! Good job!
Except that the first command could possible create a wrong
.bundle/config
with a whitespace at the end if you just
copy it line per line.
Which would generate the following config file for bundle
---
BUNDLE_PATH: "vendor/bundle "
Now, the problem that arises here is that when you do
bundle install
a folder will be created with the following
path vendor/bundle
. If you don’t see it, there is a space
at the end! Which your favorite IDE/ISE will probably just show as… a
space. Oh what do I hate spaces in paths as they are invisible for most
of the time.
So if you receive errors that your gemfiles/modules/… aren’t found… Check if your bundle config file doesn’t have a space at the end. And shame on you for not just copying all lines and pasting them in your console!
Fix for this: Edit the .bundle/config
file and remove
the whitespace at the end of BUNDLE_PATH or execute the first line
without the \
at the end.
The error would show like
"errorMessage": "libwhateveri.so.8: cannot open shared object file: No such file or directory - /var/task/vendor/bundle/ruby/2.7.0/gems/libwhateveri-1.15.3/lib/libwhateveri_c.so",
But after checking the zip file you see it does exist. The file is there, the directory is correct. You’ve checked the permissions and user settings with all sort of tricks but in the end the error was just a very confusing error.
The file is actually there. It is just that you compiled it under the wrong *NIX and now it tries to find libraries with other paths. Fix for that? Spin up a Docker Lambda and compile everything in there. For our beloved Ruby that would be the following Dockerfile
FROM lambci/lambda:build-ruby2.7
RUN yum -y install clang make ruby-dev
RUN gem update bundler
CMD "/bin/bash"
Now build the Docker image with the command
docker build -t lambda-ruby-worker .
This image will spin up a “development environment” for the moment.
It will run until you exit that bash shell. If you want to use this for
your CICD, just remove the CMD with /bin/bash
and use other
commands to build, package and send your code to AWS.
To enter this development environment you use the following docker command
This will mount your current directory inside /var/task
.
That way you can still easily code with your favorite IDE/ISE but
run/install in a Lambda environment. Do a bundle install
in
that environment and zip everything up and send it to your AWS
environment. Suddenly your compiled libraries will work just fine!
Hooray!
Ok, we have our development Docker and it is cool, very cool. But I hate sending my code to Lambda to test it, can’t I just locally develop it before sending it to AWS Lambda?
Yes you can! As any Lambda enthusiast knows, you must define a Lambda
handler somewhere that AWS can call. But we can call that handler as
well! For example, if your handler is in run.rb
and looks
like the following
def handler(event:, context:)
{ event: JSON.generate(event), context: JSON.generate(context.inspect) }
end
than you can call it on your dev environment as
And that is it! That is how the Lambda Handler
run.handler
looks like.