From 0389a1b9dda12b73b3de5f465efb77fc515ea44a Mon Sep 17 00:00:00 2001 From: Graeme Porteous Date: Fri, 10 Feb 2023 14:53:28 +0000 Subject: [PATCH] Add puma webserver over thin --- .ruby-style.yml | 2 + Gemfile | 2 +- Gemfile.lock | 10 ++--- bin/puma | 27 ++++++++++++ bin/pumactl | 27 ++++++++++++ bin/thin | 16 ------- config/puma.rb | 43 +++++++++++++++++++ config/puma.service.example | 23 ++++++++++ config/sysvinit-thin.example | 82 ------------------------------------ lib/tasks/config_files.rake | 6 +-- tmp/pids/.keep | 0 11 files changed, 129 insertions(+), 109 deletions(-) create mode 100755 bin/puma create mode 100755 bin/pumactl delete mode 100755 bin/thin create mode 100644 config/puma.rb create mode 100755 config/puma.service.example delete mode 100755 config/sysvinit-thin.example create mode 100644 tmp/pids/.keep diff --git a/.ruby-style.yml b/.ruby-style.yml index 9d65691cc6..a50880c0c8 100644 --- a/.ruby-style.yml +++ b/.ruby-style.yml @@ -27,6 +27,8 @@ AllCops: - vendor/**/* - ".git/**/*" - bin/bundle + - bin/puma + - bin/pumactl - bin/rubocop - bin/sidekiq - bin/sidekiqmon diff --git a/Gemfile b/Gemfile index df22d7dbb2..2f96eeacdb 100644 --- a/Gemfile +++ b/Gemfile @@ -109,6 +109,7 @@ gem 'maxmind-db', '~> 1.2.0' gem 'mahoro', '~> 0.5' gem 'nokogiri', '~> 1.16.7' gem 'open4', '~> 1.3.0' +gem 'puma', '~> 6.4.2' gem 'rack', '~> 2.2.9' gem 'rack-utf8_sanitizer', '~> 1.9.1' gem 'recaptcha', '~> 5.17.0', require: 'recaptcha/rails' @@ -128,7 +129,6 @@ gem 'strip_attributes', git: 'https://github.com/mysociety/strip_attributes.git' gem 'stripe', '~> 5.55.0' gem 'syck', '~> 1.4.1', require: false gem 'syslog_protocol', '~> 0.9.0' -gem 'thin', '~> 1.8.2' gem 'vpim', '~> 24.2.20' gem 'will_paginate', '~> 4.0.1' gem 'xapian-full-alaveteli', '~> 1.4.22.1' diff --git a/Gemfile.lock b/Gemfile.lock index 1405e46bee..ac898bb2fa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -200,7 +200,6 @@ GEM bigdecimal rexml crass (1.0.6) - daemons (1.4.1) dalli (3.2.8) dante (0.2.0) date (3.3.4) @@ -210,7 +209,6 @@ GEM rake (>= 12.0.0, < 14.0.0) docile (1.4.0) erubi (1.13.0) - eventmachine (1.2.7) exception_notification (4.5.0) actionmailer (>= 5.2, < 8) activesupport (>= 5.2, < 8) @@ -392,6 +390,8 @@ GEM coderay (~> 1.1) method_source (~> 1.0) public_suffix (6.0.0) + puma (6.4.2) + nio4r (~> 2.0) racc (1.8.1) rack (2.2.9) rack-test (2.1.0) @@ -539,10 +539,6 @@ GEM syck (1.4.1) syslog_protocol (0.9.2) text (1.3.1) - thin (1.8.2) - daemons (~> 1.0, >= 1.0.9) - eventmachine (~> 1.0, >= 1.0.4) - rack (>= 1, < 3) thor (1.3.1) tilt (2.0.10) timeout (0.4.1) @@ -649,6 +645,7 @@ DEPENDENCIES open4 (~> 1.3.0) pg (~> 1.5.7) pry (~> 0.14.2) + puma (~> 6.4.2) rack (~> 2.2.9) rack-utf8_sanitizer (~> 1.9.1) rails (~> 7.0.8) @@ -679,7 +676,6 @@ DEPENDENCIES stripe-ruby-mock! syck (~> 1.4.1) syslog_protocol (~> 0.9.0) - thin (~> 1.8.2) turbo-rails (~> 2.0.6) uglifier (~> 4.2.0) unicode (~> 0.4.4) diff --git a/bin/puma b/bin/puma new file mode 100755 index 0000000000..01a92a3242 --- /dev/null +++ b/bin/puma @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'puma' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +bundle_binstub = File.expand_path("bundle", __dir__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("puma", "puma") diff --git a/bin/pumactl b/bin/pumactl new file mode 100755 index 0000000000..c93cff2107 --- /dev/null +++ b/bin/pumactl @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# +# This file was generated by Bundler. +# +# The application 'pumactl' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) + +bundle_binstub = File.expand_path("bundle", __dir__) + +if File.file?(bundle_binstub) + if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") + load(bundle_binstub) + else + abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. +Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") + end +end + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("puma", "pumactl") diff --git a/bin/thin b/bin/thin deleted file mode 100755 index 80d4e14101..0000000000 --- a/bin/thin +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env ruby -# -# This file was generated by Bundler. -# -# The application 'thin' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require 'pathname' -ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", - Pathname.new(__FILE__).realpath) - -require 'rubygems' -require 'bundler/setup' - -load Gem.bin_path('thin', 'thin') diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 0000000000..8db51afc80 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,43 @@ +require 'concurrent-ruby' + +require File.expand_path('load_env.rb', __dir__) + +# This configuration file will be evaluated by Puma. The top-level methods that +# are invoked here are part of Puma's configuration DSL. For more information +# about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. + +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +max_threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } +min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } +threads min_threads_count, max_threads_count + +# Specifies that the worker count should equal the number of processors in +# production. +if ENV['RAILS_ENV'] == 'production' + concurrency = ENV.fetch('WEB_CONCURRENCY') do + Concurrent.physical_processor_count + end + worker_count = Integer(concurrency) + workers worker_count if worker_count > 1 +end + +# Specifies the `worker_timeout` threshold that Puma will use to wait before +# terminating a worker in development environments. +worker_timeout 3600 if ENV.fetch('RAILS_ENV', 'development') == 'development' + +# 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' } + +# Specifies the `pidfile` that Puma will use. +pidfile ENV.fetch('PIDFILE') { 'tmp/pids/server.pid' } + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart diff --git a/config/puma.service.example b/config/puma.service.example new file mode 100755 index 0000000000..d93fcf7bb0 --- /dev/null +++ b/config/puma.service.example @@ -0,0 +1,23 @@ +[Unit] +Description=<%= daemon_name %> +After=network.target + +[Service] +Type=notify + +WatchdogSec=10 + +User=<%= user %> +Group=<%= user %> +UMask=0002 + +WorkingDirectory=<%= vhost_dir %>/<%= vcspath %> + +ExecStart=/bin/bash -lc 'bundle exec puma -C config/puma.rb' +ExecStop=/bin/bash -lc 'bundle exec pumactl stop -F config/puma.rb' + +RestartSec=1 +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/config/sysvinit-thin.example b/config/sysvinit-thin.example deleted file mode 100755 index f42ba66152..0000000000 --- a/config/sysvinit-thin.example +++ /dev/null @@ -1,82 +0,0 @@ -#! /bin/sh -# -### BEGIN INIT INFO -# Provides: application-thin-<%= site %> -# Required-Start: $local_fs $network -# Required-Stop: $local_fs $network -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Starts the Thin app server for the "<%= site %>" site -# Description: The Thin app server for the "<%= site %>" site -### END INIT INFO - -# This example sysvinit script is based on the helpful example here: -# http://richard.wallman.org.uk/2010/02/howto-deploy-a-catalyst-application-using-fastcgi-and-nginx/ - -PATH=/usr/local/bin:/usr/bin:/bin -NAME=<%= site %> -SITE_HOME=<%= vhost_dir %>/<%= vcspath %> -DESC="Alaveteli app server" -USER=<%= user %> -CPUS=<%= cpus %> - -<% unless rails_env_defined? %> -RAILS_ENV=<%= rails_env %> -export RAILS_ENV -<% end %> - -CMD="bundle exec thin" - -<% if use_rbenv? %> -RBENV_ROOT="/home/<%= user %>/.rbenv" -PATH="$RBENV_ROOT/bin:$RBENV_ROOT/shims:$PATH" -CMD="cd <%= vhost_dir %>/<%= vcspath %>; rbenv rehash; rbenv local <%= ruby_version %>; $CMD" -<% else %> -PATH="/home/<%= user %>/.gem/ruby/<%= ruby_version %>/bin:$PATH" -CMD="cd <%= vhost_dir %>/<%= vcspath %>; $CMD" -<% end %> - -start_daemon() { - echo -n "Starting $DESC: " - su - $USER -c "$CMD \ - --environment=$RAILS_ENV \ - --user="$USER" \ - --group="$USER" \ - --address=127.0.0.1 \ - --daemonize \ - --servers="$CPUS" \ - --quiet \ - start || true" - echo "$NAME." -} - -stop_daemon() { - echo -n "Stopping $DESC: " - su - $USER -c "$CMD --quiet --servers="$CPUS" stop || true" - echo "$NAME." -} - -restart_daemon() { - echo -n "Restarting $DESC: " - su - $USER -c "$CMD --onebyone --quiet restart || true" - echo "$NAME." -} - -case "$1" in - start) - start_daemon - ;; - stop) - stop_daemon - ;; - reload|restart|force-reload) - restart_daemon - ;; - *) - N=/etc/init.d/$NAME - echo "Usage: $N {start|stop|reload|restart|force-reload}" >&2 - exit 1 - ;; -esac - -exit 0 diff --git a/lib/tasks/config_files.rake b/lib/tasks/config_files.rake index aa2bd8a0d7..0c38018518 100644 --- a/lib/tasks/config_files.rake +++ b/lib/tasks/config_files.rake @@ -51,9 +51,9 @@ namespace :config_files do def daemons [ { - path: '/etc/init.d', - name: 'thin', - template: 'config/sysvinit-thin.example', + path: '/etc/systemd/system', + name: 'puma.service', + template: 'config/puma.service.example', condition: -> { ENV['RAILS_ENV'] == 'production' } }, { diff --git a/tmp/pids/.keep b/tmp/pids/.keep new file mode 100644 index 0000000000..e69de29bb2