4

I have Puma installed and running as a web server for a production Rails site, using Nginx as a reverse proxy.

I want to use init.d to manage the services for both Nginx and Puma. It appears to me like I've got it setup to do this, however when restarting Puma following an application change, the old changes are still being served.

What's the right was to both hot and cold restart Puma to serve new application changes?

The only way I can figure out to actually get new changes to apply is rebooting the server.

Obviously, that's not right, there's a command where, but when I run sudo /etc/init.d/puma restart, it looks like it restarts (with a new PID and everything) but still no application changes are visible.

OS: Ubuntu 16.04
Nginx: 1.12.1
Puma: 3.11.0
Ruby: 2.5.0

Rails config

/path/to/app/Gemfile (excerpt)

gem 'puma', "~> 3.10"

/path/to/app/config/puma.rb

# Change to match your CPU core count
workers Integer(ENV['WEB_CONCURRENCY'] || 2)

# Min and Max threads per worker
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count

app_dir = File.expand_path("../..", __FILE__)
# shared_dir = "#{app_dir}/shared"

# Default to production
rails_env = ENV['RAILS_ENV'] || "production"
environment rails_env

# Set up socket location
bind "unix://#{app_dir}/tmp/sockets/myapp.sock"

# Logging
# stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true

# Set master PID and state locations
# pidfile "#{shared_dir}/pids/puma.pid"
# state_path "#{shared_dir}/pids/puma.state"
activate_control_app

on_worker_boot do
  require "active_record"
  require 'erb'
  begin
    ActiveRecord::Base.connection.disconnect!
  rescue
    ActiveRecord::ConnectionNotEstablished
  end
  ActiveRecord::Base.establish_connection(YAML.safe_load(ERB.new(File.read("/path/to/app/config/database.yml")).result, [], [], true)[rails_env])
end

Puma Jungle

https://github.com/puma/puma/tree/master/tools/jungle/init.d

I ran the following to setup init.d with puma, following the instructions in the link above:

  cd ~
  wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/init.d/puma
  # Copy the init script to services directory
  sudo mv puma /etc/init.d
  sudo chmod +x /etc/init.d/puma

  # Make it start at boot time.
  sudo update-rc.d -f puma defaults

  wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/init.d/run-puma
  sudo mv run-puma /usr/local/bin
  sudo chmod +x /usr/local/bin/run-puma

  # Create an empty configuration file
  sudo touch /etc/puma.conf
  sudo /etc/init.d/puma add /path/to/app deploy /path/to/app/config/puma.rb /path/to/app/log/puma.log

  touch /path/to/app/tmp/sockets/myapp.sock
  mkdir -p /path/to/app/tmp/puma

nginx config

  upstream myapp {
    server unix:///path/to/app/tmp/sockets/myapp.sock;
  }

  server {
    listen 80 default_server;
    listen [::]:80 default_server ipv6only=on;
    server_name example.com;

    root /path/to/app/public;

    location / {
      proxy_pass          http://myapp;
      proxy_set_header    X-Real-IP $remote_addr;
      proxy_set_header    Host $http_host;
      proxy_set_header    X-Forwarded-Proto $scheme;
      proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_redirect      off;
      proxy_next_upstream error timeout invalid_header http_502;
    }
  }

Here's some of the puma output from init.d status and the puma.log:

sudo /etc/init.d/puma status

● puma.service - LSB: Puma web server
   Loaded: loaded (/etc/init.d/puma; bad; vendor preset: enabled)
   Active: active (running) since Mon 2018-01-15 23:01:50 UTC; 25min ago
     Docs: man:systemd-sysv-generator(8)
  Process: 1119 ExecStart=/etc/init.d/puma start (code=exited, status=0/SUCCESS)
    Tasks: 36
   Memory: 309.6M
      CPU: 11.541s
   CGroup: /system.slice/puma.service
           ├─1240 puma 3.11.0 (unix:///path/to/app/tmp/sockets/myapp.sock) [myapp]
           ├─2013 puma: cluster worker 0: 1240 [myapp]
           └─2017 puma: cluster worker 1: 1240 [myapp]

Jan 15 23:01:50 web2 systemd[1]: Starting LSB: Puma web server...
Jan 15 23:01:50 web2 puma[1119]:  * => Running the jungle...
Jan 15 23:01:50 web2 puma[1119]:  * --> Woke up puma /path/to/app
Jan 15 23:01:50 web2 puma[1119]:  * user deploy
Jan 15 23:01:50 web2 puma[1119]:  * log to /path/to/app/log/puma.log
Jan 15 23:01:50 web2 systemd[1]: Started LSB: Puma web server.

/path/to/app/log/puma.log

[3014] Puma starting in cluster mode...
[3014] * Version 3.11.0 (ruby 2.5.0-p0), codename: Love Song
[3014] * Min threads: 5, max threads: 5
[3014] * Environment: production
[3014] * Process workers: 2
[3014] * Phased restart available
[3014] * Listening on unix:///path/to/app/tmp/sockets/myapp.sock
[3014] Use Ctrl-C to stop
[3014] * Starting control server on unix:///tmp/puma-status-1515998276357-3014
[3014] - Worker 1 (pid: 3123) booted, phase: 0
[3014] - Worker 0 (pid: 3119) booted, phase: 0
[9913] Puma starting in cluster mode...
[9913] * Version 3.11.0 (ruby 2.5.0-p0), codename: Love Song
[9913] * Min threads: 5, max threads: 5
[9913] * Environment: production
[9913] * Process workers: 2
[9913] * Phased restart available
[9913] * Listening on unix:///path/to/app/tmp/sockets/myapp.sock
bundler: failed to load command: puma (/home/deploy/.rbenv/versions/2.5.0/bin/puma)
[3014] - Gracefully shutting down workers...
[1240] Puma starting in cluster mode...
[1240] * Version 3.11.0 (ruby 2.5.0-p0), codename: Love Song
[1240] * Min threads: 5, max threads: 5
[1240] * Environment: production
[1240] * Process workers: 2
[1240] * Phased restart available
[1240] * Listening on unix:///path/to/app/tmp/sockets/myapp.sock
[1240] Use Ctrl-C to stop
[1240] * Starting control server on unix:///tmp/puma-status-1516057427440-1240
[1240] - Worker 1 (pid: 2017) booted, phase: 0
[1240] - Worker 0 (pid: 2013) booted, phase: 0
Turgs
  • 285
  • 4
  • 9

1 Answers1

5

My solution was to switch to systemd as suggested in one of the comments above.

Remove the init.d for puma

sudo rv /etc/init.d/puma
sudo update-rc.d -f puma remove
sudo rm /usr/local/bin/run-puma
sudo rm /etc/puma.conf

Use systemd for puma

Create service file:

sudo sh -c "cat << EOF > /etc/systemd/system/puma.service
[Unit]
Description=Puma HTTP Server
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/path/to/app
ExecStart=/home/deploy/.rbenv/shims/bundle exec puma -e production -C /path/to/app/config/puma.rb /path/to/app/config.ru
PIDFile=/path/to/app/tmp/pids/puma.pid
Restart=always

[Install]
WantedBy=multi-user.target
EOF

Then enable it all:

# After installing or making changes to puma.service
sudo systemctl daemon-reload

# Enable so it starts on boot
sudo systemctl enable puma.service

# Initial start up.
sudo systemctl start puma.service

# Check status
sudo systemctl status puma.service

Now running restart does an immediate hot/phased restart just like I was looking for:

sudo systemctl restart puma.service
Turgs
  • 285
  • 4
  • 9