These are my notes from deploying a boilerplate Rails app to AWS Lambda (yeah, you read that right) using Lamby. Basically this is me going through the Quick Start guide. (Here is my production endpoint--not sure how long this will be up.)
Related links:
- https://aws.amazon.com/lambda/
- https://lamby.custominktech.com
- https://lamby.custominktech.com/docs/quick_start
Follow me on Twitter: https://twitter.com/heyjoshwood
A few unanswered questions I have:
- What about Lambda's 250MB max function size? Will this work with a large Rails app?
- What about cold starts? Even a boilerplate Rails app takes a long time to boot.
- Why is Docker so slow on macOS ðŸ˜
Update: Ken Collins, the creator of the Lamby gem, responded to some of my questions on Reddit. Check out the thread.
- Generate the initial Rails app:
brew install awscli jq
brew tap aws/tap
brew install aws-sam-cli
asdf shell ruby 2.7.1
gem install rails -N
rails new lamby_rails \
--skip-action-mailer --skip-action-mailbox --skip-action-text \
--skip-active-record --skip-active-storage --skip-puma \
--skip-action-cable --skip-spring --skip-listen --skip-turbolinks \
--skip-system-test --skip-bootsnap
cd lamby_rails
git add .
git commit -m 'initial'
- Edit
app/controllers/application_controller.rb
:
class ApplicationController < ActionController::Base
def index
render html: "<h1>Hello Lamby</h1>".html_safe
end
end
Edit config/routes.rb
:
Rails.application.routes.draw do
root to: "application#index"
end
- Save progress:
git add -p
git commit -m 'hello lamby'
- Install lamby gems:
bundle add lamby aws-sdk-ssm
- Edit
Gemfile
:
- gem "lamby", "~> 2.0"
+ gem "lamby", "~> 2.0", require: false
- Finish installing lamby:
./bin/rake -r lamby lamby:install
git add -p
git status
git add .
git commit -m 'install lamby'
- I set up my AWS credentials here:
# ~/.aws/credentials
[lamby_rails]
aws_access_key_id = VALUE
aws_secret_access_key = SECRET_VALUE
- And the region config here:
# ~/.aws/config
[lamby_rails]
output = json
region = us-west-1
- I set my AWS_PROFILE using direnv. Edit
.envrc
:
export AWS_PROFILE=lamby_rails
then run:
direnv allow
- Configure SSM w/ Rails master key:
aws ssm put-parameter \
--name "/config/lamby_rails/env/RAILS_MASTER_KEY" \
--type "SecureString" \
--value $(cat config/master.key)
- Edit
app.rb
and add this line right afterrequire 'lamby'
:
ENV['RAILS_MASTER_KEY'] =
Lamby::SsmParameterStore.get!('/config/lamby_rails/env/RAILS_MASTER_KEY')
- Edit
template.yaml
CloudFormation/SAM file by adding this to theProperties
section of yourRailsFunction
. This addition allows your Lambda's runtime policy to read configs from SSM Parameter store (see fulltemplate.yml
):
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ssm:GetParameter
- ssm:GetParameters
- ssm:GetParametersByPath
- ssm:GetParameterHistory
Resource:
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/config/lamby_rails/*
- Save progress:
git add -p
git commit -m 'configure lamby'
- Edit
.envrc
(change the bucket name):
export CLOUDFORMATION_BUCKET=lamby-rails-josh
export AWS_DEFAULT_REGION=us-west-1
don't forget:
direnv allow
- Now run:
aws s3 mb "s3://$CLOUDFORMATION_BUCKET"
./bin/deploy
- This uses Docker to bake the Rails app Lambda function. That can take a long time on macOS. Go get some coffee or tea. :)
$ time ./bin/deploy
Successfully created/updated stack - lambyrails-production-us-west-1 in None
./bin/deploy 17.45s user 9.86s system 5% cpu 9:04.13 total
(that's the build/deploy time on an 8-core MacBook Pro 🤔)
Top comments (1)
"Why is Docker on Mac so slow?" = There are several reasons, but the main one is that Docker on Mac requires an additional layer of virtualization vs. Docker on Linux.
github.com/EugenMayer/docker-sync can help, but yeah... it's a problem.
Regarding Lamby, It's interesting, but I'm struggling to think of a real-world use case. I'm seeing some references in the docs to a Lambda/Rails proxy, which doesn't make much sense to me. A preferable pattern for something async would be Lambda -> SQS -> Rails worker/sidekiq, I would think. A real-time API using this seems like it would be fragile/slow/difficult-to-troubleshoot. I may be missing something. Have you come across any examples where folks were using this for something concrete?