Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to deploy Rails + Puma + Postgres app to Elastic beanstalk?

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?

like image 577
aks Avatar asked Dec 02 '16 17:12

aks


1 Answers

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:

enter image description here

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;
}
like image 98
nicq Avatar answered Sep 27 '22 23:09

nicq