如何使用Puma和Foreman设置零停机Rails部署

来自菜鸟教程
跳转至:导航、​搜索

介绍

Puma 是一个高效的 Ruby Web 服务器,可以很好地处理 Rack 应用程序(例如 Rails)。 Puma 使用 线程和/或工作者 提供并发性。 您可以使用 Puma 的集群模式(使用工作人员)来部署应用程序而无需停机。 Puma会一一重启worker,其他worker会在这段时间内继续处理。 这很好,因为当您将更新部署到应用程序时,您的用户将不再看到延迟响应或错误页面。

在本指南中,我们将使用以下内容:

  • Puma 作为我们的网络服务器
  • Foreman 来管理我们的应用程序(这不是绝对必要的,但它确实让生活更轻松一些)
  • Capistrano 进行部署(部署说明相当通用,可以轻松地重新应用于其他方法)

本指南假定您有一个将要使用的现有 Rails 应用程序。 如果您还没有达到那里,请参阅 Rails 入门 指南。 本指南还假设您使用的是 Ubuntu; 阅读如何设置它here。 我们将在本指南中使用 Upstart

安装彪马

Puma 可通过 RubyGems 安装:

gem install puma

安装后,您可以使用 Puma 启动应用程序,只需调用:

puma config.ru # or whatever your *.ru file is called

但相反,我们将使用 Foreman 来管理我们的应用程序。 继续阅读。

安装工头

Foreman 可以使用 RubyGems 安装:

gem install foreman

使用 Puma 和 Foreman 配置 Rails

首先,将 puma 和 foreman 添加到您的 Gemfile

# Gemfile
gem 'puma'
gem 'foreman'

并安装:

bundle install

Foreman 需要一个 Procfile 用于指定要运行的进程。 您可以给进程名称,例如 webworker,然后您可以使用它们来区分要运行的进程类型。 接下来我们要做的是创建我们的 Procfile

echo "web: bundle exec puma -e $RAILS_ENV -p 5000 -S ~/puma -C config/puma.rb" >> Procfile

您可以将端口号更改为您喜欢的任何值,我们将使用 5000。 接下来,创建一个 puma 配置文件并将其编辑为如下所示:

# config/puma.rb
threads 1, 6
workers 2

on_worker_boot do
  require "active_record"
  cwd = File.dirname(__FILE__)+"/.."
  ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
  ActiveRecord::Base.establish_connection(ENV["DATABASE_URL"] || YAML.load_file("#{cwd}/config/database.yml")[ENV["RAILS_ENV"]])
end

正确配置线程和工作线程的数量很重要,Puma README 提供了一些关于如何最好地做到这一点的建议。 要在集群模式下运行 Puma,您将需要 至少两个工人 。 通常,您应该匹配 VPS 上可用的核心数量。 在 Ubuntu 上,您可以使用以下命令:

grep -c processor /proc/cpuinfo

检查你有多少核心。 或者,您可以根据您正在使用的 液滴 类型来判断。 另请注意,每个工作人员将使用给定数量的线程,因此使用上面的配置,最少有 2 个,最多有 12 个线程。

on_worker_boot 块在工作程序启动时建立 ActiveRecord 连接(即。 在部署期间)。 如果您使用不同的 ORM,您将需要在此处进行适当的连接。

配置 Procfilepuma.rb 后,您应该能够运行您的应用程序。 为此,只需运行以下命令:

foreman start

加载后,您的应用程序应该可以在 localhost:5000(或您在 Procfile 中指定的任何端口)访问。

部署

Foreman 可以导出为其他流程管理格式; 我们将使用 Upstart,它是 Ubuntu 进程管理器。 您可以通过运行以下命令使用 Foreman 导出与 Ubstart 兼容的脚本:

sudo foreman export upstart /etc/init -a puma-test-app -u puma-test-user -l /var/puma-test-app/log

在此示例中,puma-test-apppuma-test-user 将替换为适当的应用程序名称和系统用户。 不过,我们不想在本地执行此操作 - 我们想做的是自动创建 upstart 脚本作为我们部署的一部分,以便我们始终正确启动应用程序并让 Upstart 确保它继续运行。

我们将使用 Capistrano 进行部署。 如果您还没有这样做,请通过运行以下命令设置您的应用程序以使用 Capistrano:

capify .

在您的应用程序的根目录中。 接下来,在 config/deploy.rb 创建的文件中,添加以下内容:

# config/deploy.rb
set :app_name, "puma-test-app"
set :user, "puma-test-user"

namespace :foreman do
  desc "Export the Procfile to Ubuntu's upstart scripts"
  task :export, :roles => :app do
    run "cd #{current_path} && #{sudo} foreman export upstart /etc/init -a #{app_name} -u #{user} -l /var/#{app_name}/log"
  end
  
  desc "Start the application services"
  task :start, :roles => :app do
    run "#{sudo} service #{app_name} start"
  end
 
  desc "Stop the application services"
  task :stop, :roles => :app do
    run "#{sudo} service #{app_name} stop"
  end
 
  desc "Restart the application services"
  task :restart, :roles => :app do
    run "#{sudo} service #{app_name} start || #{sudo} service #{app_name} restart"
  end
end

foreman:export 任务将更新 Ubuntu 的 Upstart 脚本,我们将在部署时调用它。 其他任务管理 Upstart 支持的服务。 您的 Procfile 不会定期更改,但如果发生更改,则需要重新启动应用程序服务以注册这些更改; 你现在可以通过运行来做到这一点:

cap foreman:restart

但是,在大多数情况下,我们要做的就是告诉 Puma 分阶段重启。 为此,我们将使用 Capistrano deploy:restart 任务,该任务作为标准 Capistrano 部署的一部分自动运行。 将以下内容添加到您的 config/deploy.rb

namespace :deploy do
  task :restart, :roles => :app do
    foreman.export

    # on OS X the equivalent pid-finding command is `ps | grep '/puma' | head -n 1 | awk {'print $1'}`
    run "(kill -s SIGUSR1 $(ps -C ruby -F | grep '/puma' | awk {'print $2'})) || #{sudo} service #{app_name} restart"

    # foreman.restart # uncomment this (and comment line above) if we need to read changes to the procfile
  end
end

deploy:restart 任务做的第一件事是调用 foreman:export 来更新我们的 Upstart 脚本。 然后,我们向所有正在运行的 Puma 实例发送 SIGUSR1 信号。 它通过查找 Puma 主进程的进程 ID,然后向其发送适当的信号来做到这一点。 注释掉的命令会在 OS X 上找到 PID,如果您需要在本地进行测试,将两者都放在手边可能会有所帮助。 当 Puma 收到 SIGUSR1 信号时,它将开始分阶段重启。 如果由于某种原因我们无法发送 SIGUSR1 信号,我们会退回到以老式方式重新启动服务。

如果您需要注册对 Procfile 的更改,则必须调用 foreman:restart 任务。 为此,注释掉以 run 开头的行,并取消注释 foreman.restart 行。 您不需要定期执行此操作,因为理想情况下您的 Procfile 在部署之间将保持不变。

测试

在测试之前,请确保使用上述说明在您的 Ubuntu VPS 上安装了 Foreman。

如果您还没有,您需要设置您的 VPS 以使用 Capistrano。 首先,为您的 Web 服务器、存储库等填写 config.deploy.rb 中的空白。 然后通过运行配置您的 VPS:

cap deploy:setup

然后你可以运行:

cap deploy

进行第一次部署。 在此部署期间,您的应用程序将作为 VPS 上的 Ubuntu 服务启动。 在所有后续部署中,Puma 将收到 SIGUSR1 信号,该信号将触发分阶段重启; 您应该能够在整个重启过程中继续使用您的应用程序。

数据库迁移

当 Puma 进行分阶段重启时,您的应用程序将运行两个不同的代码库。 因此,您需要确保两个代码库都与您现有的数据库模式一起工作,如果您必须进行迁移,这可能会很棘手。 (如果您要进行大量迁移并且需要使站点离线,您可以通过调用新的 foreman:stop Capistrano 任务来实现,然后调用 foreman:start 任务以稍后重新在线。 )