From 404f9df820c04be914dda04620bfb03932479bca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Lecour?= Date: Sat, 24 Aug 2019 11:26:49 +0200 Subject: [PATCH] Better Puma configuration + documentation for systemd unit --- INSTALL.md | 28 +++++++++++-- config/deploy.rb | 4 ++ config/deploy/puma-chexpire@.service | 21 ++++++++++ config/puma.rb | 59 +++++++++++++++++----------- 4 files changed, 85 insertions(+), 27 deletions(-) create mode 100644 config/deploy/puma-chexpire@.service diff --git a/INSTALL.md b/INSTALL.md index aa14382..37e5093 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -11,7 +11,7 @@ We are usually running Chexpire on typical POSIX servers like : - Linux Debian 9, Ruby 2.5.4, NodeJS 8.11 and MariaDB 10.1 - macOS High Sierra, Ruby 2.5.4, NodeJS 10.2.1 and MariaDB 10.2 -It probably works on any system that supports Ruby >2.3, NodeJS >6 and MySQL >5.5. Feel free to report any unexpected incompatibilities. +It probably works on any system that supports Ruby >= 2.5.4, NodeJS >= 6 and MySQL >= 5.5. Feel free to report any unexpected incompatibilities. If you use rbenv, chruby or RVM, you can set your prefered Ruby version in the `.ruby-version` file at the root of the project. @@ -34,7 +34,7 @@ If you want to do manual installations, you can use our Wiki documentations for Execute `# bundle install` to install Ruby gems (including Rails itself). -Execute `# yarn install` to install Javascript/NodeJS packages. +Execute `# yarn install --check-files` to install Javascript/NodeJS packages. Depending on what is already installed on your OS or not, you might need to install a few system packages to be able to have everything working. @@ -45,15 +45,21 @@ To use elliptic curve SSH keys, we need to have `libsodium` and its headers. * on macOS with Homebrew : `# brew install libsodium`. -## Rails configuration +## Application configuration After cloning this repository, you have to create and edit a few files for your local development/test configuration. Theses files will be ignored by git. +### Environment variables + +A handful of settings can be set by environment variables. If you use Heroku-like platforms they offer a simple way to set them. + +If you use Rbenv, there is the `rbenv-vars` plugin. That is what we recommend on POSIX servers. You have to put an `.rbenv-vars` file at the root of the project. If you use Capistrano, put it in the shared directory and have it linked in the `current` directory at deploy time. + ### Database configuration Create the file if missing : `cp config/database.example.yml config/database.yml`. If you change the settings in the `defaults` section it applies to the `development` and `test` sections. More information is available at "guides.rubyonrails.org":https://guides.rubyonrails.org/configuring.html#configuring-a-database -Note that on Debian 9 with MariaDB, the database socket is at `/var/run/mysqld/mysqld.sock`, which is not the default in the configuration file. +Note that on Debian 9+ with MariaDB, the database socket is at `/var/run/mysqld/mysqld.sock`, which is not the default in the configuration file. ### Rails secrets @@ -113,3 +119,17 @@ You can use the `script/to_staging` and/or `script/to_production` scripts. * with `to_production` you deploy the `master` branch to production. On the remote servers – where the application will be deployed – you have to copy the configuration files just as you've just did for your development setup. The files has to go in the `shared/config/` directory, relative to your `deploy_to` path. They will be symlinked to the proper destination by Capistrano. +If an `.rbenv-vars` file is found in the shared directory, it will be linked to help loading environment files (by Ruby via Rbenv, systemd…). + +### systemd + +If you want to use systemd to manage your Puma process, there are [a few different ways](https://github.com/puma/puma/blob/master/docs/systemd.md). We've prepared a systemd unit file (`config/deploy/puma-chexpire@.service`) but you can adjust to better suit your needs. + +If you deploy your application to `/home/chexpire_`, the systemd unit can be used as a template, for example : `puma-chexpire@production.service`. This template is compatible with systemd actions like `systemctl stop puma-chexpire@production.service` and also with Capistrano tasks like `cap production puma:stop`. + +To install the systemd unit : + +``` +$ cp config/deploy/puma-chexpire@.service /etc/systemd/system/puma-chexpire@.service +$ systemctl enable puma-chexpire@.service +``` diff --git a/config/deploy.rb b/config/deploy.rb index 5fdad3c..68d7e83 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -24,12 +24,16 @@ set :repo_url, "https://github.com/Evolix/chexpire.git" # Default value for :linked_files is [] append :linked_files, + ".rbenv-vars", "config/chexpire.yml", "config/database.yml", "config/secrets.yml" # Default value for linked_dirs is [] # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system" +append :linked_dirs, + "tmp/pids", + "tmp/sockets" # Default value for default_env is {} # set :default_env, { path: "/opt/ruby/bin:$PATH" } diff --git a/config/deploy/puma-chexpire@.service b/config/deploy/puma-chexpire@.service new file mode 100644 index 0000000..d7bc357 --- /dev/null +++ b/config/deploy/puma-chexpire@.service @@ -0,0 +1,21 @@ +[Unit] +Description=Puma Server for Chexpire (%i) +After=network.target + +[Service] +Type=forking +User=chexpire_%i +EnvironmentFile=/home/chexpire_%i/www/current/.rbenv-vars +Environment=RAILS_ENV=%i +WorkingDirectory=/home/chexpire_%i/www/current/ +PIDFile=/home/chexpire_%i/www/shared/tmp/pids/puma.pid + +ExecStart=/home/chexpire_%i/.rbenv/bin/rbenv exec bundle exec puma -C /home/chexpire_%i/www/current/config/puma.rb --daemon +ExecStop=/home/chexpire_%i/.rbenv/bin/rbenv exec bundle exec pumactl -F /home/chexpire_%i/www/current/config/puma.rb stop +ExecReload=/home/chexpire_%i/.rbenv/bin/rbenv exec bundle exec pumactl -F /home/chexpire_%i/www/current/config/puma.rb phased-restart + +Restart=no +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/config/puma.rb b/config/puma.rb index 5ed4437..60081a8 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -8,31 +8,44 @@ 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 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" } +rails_env = ENV.fetch("RAILS_ENV") { "development" } +environment rails_env -# Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +app_dir = File.expand_path("../..", __FILE__) +directory app_dir +shared_dir = "#{app_dir}/tmp" -# Specifies the number of `workers` to boot in clustered mode. -# Workers are forked web server processes. If using threads and workers together -# the concurrency of the application would be max `threads` * `workers`. -# Workers do not work on JRuby or Windows (both of which do not support -# processes). -# -# workers ENV.fetch("WEB_CONCURRENCY") { 2 } +if %w[production staging].member?(rails_env) + # Logging + stdout_redirect "#{app_dir}/log/puma.stdout.log", "#{app_dir}/log/puma.stderr.log", true -# Use the `preload_app!` method when specifying a `workers` number. -# This directive tells Puma to first boot the application and load code -# before forking the application. This takes advantage of Copy On Write -# process behavior so workers use less memory. -# -# preload_app! + # Set master PID and state locations + pidfile "#{shared_dir}/pids/puma.pid" + state_path "#{shared_dir}/pids/puma.state" -# Allow puma to be restarted by `rails restart` command. -plugin :tmp_restart + # Change to match your CPU core count + workers ENV.fetch("WEB_CONCURRENCY") { 2 } + + preload_app! + + # Set up socket location and port + bind "unix://#{shared_dir}/sockets/puma.sock" + port ENV.fetch("PORT") { 3000 } + + before_fork do + ActiveRecord::Base.connection_pool.disconnect! + end + + on_worker_boot do + ActiveSupport.on_load(:active_record) do + db_url = ENV.fetch('DATABASE_URL') + # puts "puma: connecting to DB at #{db_url}" + ActiveRecord::Base.establish_connection(db_url) + end + end +elsif rails_env == "development" + # Specifies the `port` that Puma will listen on to receive requests; default is 3000. + port ENV.fetch("PORT") { 3000 } + plugin :tmp_restart +end