I have an Rails 5 API which I am trying to deploy(correctly) on Elastic Beanstalk.
Here is my initial config/puma.rb
file which I use:
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests, default is 3000.
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
environment ENV.fetch("RAILS_ENV") { "development" }
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
I get the following socket error:
2015/11/24 06:44:12 [crit] 2689#0: *4719 connect() to unix:///var/run/puma/my_app.sock failed (2: No such file or directory) while connecting to upstream
To fix this I tried adding below lines and got it to work:
rails_env = ENV['RAILS_ENV'] || "production"
if rails_env == "production"
bind "unix:///var/run/puma/my_app.sock"
pidfile "/var/run/puma/my_app.sock"
end
My real question is, is this the right way to do it? If anyone has done it before can you point me to it? Is there a way to do this via docker containers?
You can deploy your Rails app as a Rails - Puma app to Elastic Beanstalk or Docker as well. The answer will be more general and rather points where to start than provides complete solution.
Ruby - Puma
This can be a quite tricky: If you create new Elastic Beanstalk Environment for Ruby via Console (in Web Browser), it can set Passenger platform by default, instead of Puma platform. And probably you can't change it in console:
To create new environment with Puma, use eb cli
. Nice walkthrough here. However, before you run eb create
, you have to do one more thing - select platform:
$ eb platform select
It appears you are using Python. Is this correct?
(y/n): n
Select a platform.
1) Go
2) Node.js
3) PHP
4) Python
5) Ruby
6) Tomcat
7) IIS
8) Docker
9) Multi-container Docker
10) GlassFish
11) Java
(default is 1): 5
Select a platform version.
1) Ruby 2.3 (Puma)
2) Ruby 2.2 (Puma)
3) Ruby 2.1 (Puma)
4) Ruby 2.0 (Puma)
5) Ruby 2.3 (Passenger Standalone)
6) Ruby 2.2 (Passenger Standalone)
7) Ruby 2.1 (Passenger Standalone)
8) Ruby 2.0 (Passenger Standalone)
9) Ruby 1.9.3
(default is 1):
If you want to create Elastic Beanstalk's Worker instead of Web Server, run:
$ eb create -t worker
You can use Console (Web Browser) or eb cli
(docs) to set other configuration.
Docker
Following post maybe useful how to setup Rails + Puma + Nginx + Docker:
http://codepany.com/blog/rails-5-and-docker-puma-nginx/
This is multicontainer configuration, where Nginx is binded to port 80 and streams request to puma via socket. In your case it would be: "unix:///var/run/puma/my_app.sock"
To upload Dockers, you can use AWS ECR to store Docker images. You have to create Dockerrun.aws.json file (is quite similar to docker-compose.yml file), which you can than deploy via AWS Console (web browser) to your environment.
EDIT
Here is the puma.rb
configuration file:
threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }
threads threads_count, threads_count
bind "unix:///var/run/puma.sock?umask=0000"
stdout_redirect "/var/log/puma.stdout.log", "/var/log/puma.stderr.log", true
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch('RAILS_ENV') { 'development' }
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
Some settings may vary, but the point is that I bind there a puma server to unix socket and it connects with NGINX. The NGINX configuration file:
user root;
error_log /var/log/app-nginx-error.log;
pid /var/run/app-nginx.pid;
events {
worker_connections 8096;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/app-nginx-access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 10;
upstream appserver {
server unix:///var/run/puma.sock;
}
server {
listen 80 default_server;
root /var/www/public;
client_max_body_size 16m;
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri @appserver;
location @appserver {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Client-IP $remote_addr;
proxy_pass http://appserver;
}
access_log /var/log/app-nginx-access.log;
error_log /var/log/app-nginx-error.log debug;
error_page 500 502 503 504 /500.html;
}
}
The most important part in NGINX configuration file is:
upstream appserver {
server unix:///var/run/puma.sock;
}