部署 在 Ubuntu 14.04 上使用 Puma 和 Nginx 部署 Rails App

ff4415 · 2017年03月05日 · 最后由 kooglezhang 回复于 2017年03月22日 · 7828 次阅读
本帖已被管理员设置为精华贴

原文链接: how-to-deploy-a-rails-app-with-puma-and-nginx-on-ubuntu-14-04

简介

在部署自己的 Rails on Ruby 应用时,有很多有效的安装包可以考虑。这篇教程旨在帮助你用 PostgreSQL 作为数据库,在 Ubuntu14.04 下面使用 Pumma 和 Ngnix 部署 Ruby on Rails 的生产环境。

Puma是一个应用服务器(application server),与Passenger或者Unicon一样,允许你的RAILS应用处理并发请求。由于Puma不是为用户直接访问而设计,我们需要使用Nginx作为反向代理,用于在用户和你的应用之间缓存请求响应

要求

本教程假设你拥有一台 Ubuntu 14.04 服务器,并在需要部署应用的用户下面安装了以下软件:

  • [在 Ubuntu 14.04 上使用 rbenv 安装 Ruby on Rails] [1]
  • [在 Ubuntu 14.04 下使用 PostgreSQL 部署 Ruby on Rails] [2]

如果你还没有以上配置,遵循以上链接的教程。我们假设你的用户账号deploy

以及,这篇教程没有包含 如何配置开发和测试环境。如果你有这方面的需求,遵循 [PostgreSQL with Rails tutorial][2] 中的例子。

创建 Rails 应用

理想情况下,你已经有一个想要部署的 Rails 应用。如果是这样,你可以跳过这一节,在接下来使用合适的代替品。如果不是,第一步就是使用 PostgreSQL 作为数据库创建一个新的 Rails 应用。

这个命令将会创建一个新的 Rails 应用,名为appname.并使用 PostgreSQL 作为数据库。高亮的"appname"可以随便修改:

rails new appname -d postgresql

然后跳转到用户目录:

cd appname

让我们花点时间创建一个PostgreSQL用户给你的 Rails 应用的生产环境。

创建生产数据库用户

为了让事情简单,我们把生产数据库用户名和你的应用名字保持一致。比如,如果你的应用叫做"appname",你就像这样创建 PostgreSQL 用户:

sudo -u postgres createruser -s appname

我们想要为此数据库用户设置密码,像这样进入 PostgresSQL 控制台:

sudo -u postgres psql

为数据库用户"appname"设置密码:

password appname

输入你想要的密码然后确认。

退出 PostSQL 控制台:

\q

现在我们准备好用这些数据库连接信息配置你的应用了。

配置数据库连接

确定你是在你的应用的根目录(cd ~/appname)。

用你喜好的文本编辑器打开你的应用的数据库配置文件。我们用vi:

vi config/database.yml

更新production 段落,让它看起来像这样:

production:
  <<: *default
  host: localhost
  adapter: postgresql
  encoding: utf8
  database: appname_production
  pool: 5
  username: <%= ENV['APPNAME_DATABASE_USER'] %>
  password: <%= ENV['APPNAME_DATABASE_PASSWORD'] %>

注意数据库用户名和密码配置成通过环境变量读取,'APPNAME_DATABASE_USER' 和'APPNAME_DATABASE_PASSWORD'。这被认为是保证你的生产密码密文(secrets) 在应用代码之外的最佳实践,否则在使用类似 GIT 这样的版本控制系统时容易把它们暴露出去。接下来我们就会介绍 如何配置数据库权限和环境变量

Save and exit.

安装 rbenv-vars 插件

在部署 Rails生产应用(production Rails application) 之前,你需要使用环境变量设置生产安全密文(production secret key) 和数据库密码。作为一种管理环境变量的简单方式,可以使用 rbenv-vars 插件 来实时加载密码密文到我们的应用里面。

要安装 rbenv-rars 插件,只用跳转到.rbenv/plugins目录然后 clone it from GitHub. 比如,如果 rbenv 安装在你的home目录,运行这些命令:

cd ~/.rbenv/plugins
git clone https://github.com/sstephenson/rbenv-vars.git

设置环境变量

现在 rbenv-vars 插件已经安装了。让我们来配置需要的环境变量。

首先,生成密文 (secret key),用于校验单个 cookie 的完整性:

cd ~/appname
rake secret

copy 生成的密文 (secret key),然后打开.rbenv-vars 文件:

vi .rbenv-vars

在这里设置的任何环境变量都会被你的 Rails 应用读取。

备注:这里实际是在你的程序根目录里新建一个.rbenv-vars 文件,然后写如环境变量

首先,设置 SECRET_KEY_BASE 变量 (替换高亮部分为你自己刚刚生成并拷贝的密文):

SECRET_KEY_BASE=your_generated_secret

然后,设置 APPNAME_DATABASE_USER 变量 (替换高亮的'APPNAME'为你自己的应用名称,'appname'是你生产数据库的用户名):

APPNAME_DATABASE_USER=appname

最后,设置 APPNAME_DATABASE_PASSWORD 变量 (替换高亮的'APPNAME'为你自己的应用名称,'prod_db_pass'是你生产数据库的用户密码):

APPNAME_DATABASE_PASSWORD=prod_db_pass

Save and exit.

你可以查看那些环境变量被 rbenv-vars 插件设置于你的应用:

rbenv vars

如果你改变你的密文或数据库密码,更新.rbenv-vars 文件。注意保持这个文件的私有性,不要把它包含在你的公开代码库里。

创建生产数据库

现在你的应用已经配置好可以与你的 PostgreSQL 对话了。让我们来创建数据库:

RAILS_ENV=production rake db:create

Generate a Controller

如果你依照的是这个例子,我们将生成一个 scaffold controller 这样我们的应用就有点东西可以看了:

rails generate scaffold Task title:string note:text

现在运行命令来更新这个生产数据库:

RAILS_ENV=production rake db:migrate

你还需要预编译assets:

RAILS_ENV=production rake assets:precompile

要测试你的应用是否工作,可以运行这个生产环境,并绑定你的服务器的公共 IP (替换为你自己的服务器的公共 IP 地址):

RAILS_ENV=production rails server --binding=server_public_IP

现在到浏览器访问这个 URL:

http://server_public_IP:3000/tasks

如果工作正常,你将看到这个页面:

page

回到你的 Rails server, Ctrl-c 停止应用。

安装 Puma

现在准备安装 Puma。

一个简单的办法是把它加入你应用的 Gemfile。打开 Gemfile(确定你是在你的应用的根目录):

vi Gemfile

在文件末尾,加入这一行:

gem 'puma'

Save and exit.

要安装 Puma,以及相关的依赖,run Bundler:

bundle

现在 puma 已经安装了,不过我们还需要配置它。

配置 Puma

配置 Puma 之前,你要看一下你的服务器的 CPU 核的数量。用这个命令可以轻易做到:

grep -c processor /proc/cpuinfo

现在加入 Puma 配置到 config/puma.rb。打开文件:

vi config/puma.rb

拷贝粘贴这些配置到文件里:

# Change to match your CPU core count
workers 2

# Min and Max threads per worker
threads 1, 6

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://#{shared_dir}/sockets/puma.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"
  ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
  ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env])
end

workers的数量改为你的服务器的 CPU 核心的数量。

Save and exit. 这会给 puma 配置你应用的位置,Puma 的 socket、logs、PIDs 的位置。可以自由修改这个文件,加入其他你需要的选项。

现在创建配置文件里引用到的目录:

mkdir -p shared/pids shared/sockets shared/log

创建 Puma Upstart Script

让我们创建一个 Upstart init script ,这样就能简单的开始和停止 Puma,并确保在服务器启动时同时启动。

从 Puma GitHub 库下载 Jungle Upstart tool 到你的home目录:

cd ~
wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma-manager.conf
wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma.conf

现在打开提供的 puma.conf 文件,这样我们能配置 Puma 开发用户:

vi puma.conf

找到指定setuidsetuid的两行,然后用你的开发用户和组的名字替换"apps"。比如,如果你的开发用户叫做"deploy",这几行看起来像这样:

setuid deploy
setgid deploy

Save and exit.

拷贝 scripts 到 Upstart servives 目录:

sudo cp puma.conf puma-manager.conf /etc/init

puma-manager.conf 脚本引用了 它管理的应用的/etc/puma.conf。现在创建并编辑这个编录文件:

sudo vi /etc/puma.conf

文件里的每一行就是你希望 puma-manager 管理的一个应用的路径。现在加入你的应用的路径。比如:

/home/deploy/appname

Save and exit.

现在你的应用已经配置为随服务器引导 (boot time) 而启动。你的应用在你的服务器 reboot 的时候重新启动。

手动启动 Puma 应用

要启动所有被管理的 Puma 应用,运行命令:

sudo start puma-manager

或者你想使用 puma Upstart script 启动单个 Puma 应用:

sudo start puma app=/home/deploy/appname

可以使用restartstop 来控制应用:

sudo stop puma-manager
sudo restart puma-manager

现在你的 Rails 应用的生产环境运行在 puma 下了,并被shared/sockets/puma.socksocket 监听。在你的应用被外部用户访问之前,你需要设置 Nginx 反向代理。

安装和配置 Nginx

使用 apt-get 安装 Nginx :

sudo apt-get install nginx

现在打开默认服务器块:

sudo vi /etc/nginx/sites-available/default

用下面的代码块替换文件内容。确定使用相应的用户名应用名字替换deployappname部分:

upstream app {
    # Path to Puma SOCK file, as defined previously
    server unix:/home/deploy/appname/shared/sockets/puma.sock fail_timeout=0;
}

server {
    listen 80;
    server_name localhost;

    root /home/deploy/appname/public;

    try_files $uri/index.html $uri @app;

    location @app {
        proxy_pass http://app;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

Save and exit.

这个配置 Nginx 作为反向代理,这样 HTTP 请求通过 Unix socket 抵达 Puma 应用服务器。随意修改配置到你感到满意为止。

重启 Nginx 使修改生效:

sudo service nginx restart

现在你的应用的生产环境已经可以通过你的服务器的公共 IP 地址或者 FQDN 访问了。要访问我们之前创建的 Tasks controller,在 web 浏览器里面访问:

http://server_public_IP/tasks

你会看到和第一次测试时同样的页面,不过现在被架设在了 nginx 和 Puma 上。

结论

恭喜! 你已经在 Nginx 和 Puma 上部署了你的 Rails on Ruby 应用的生产环境。

如果你希望增强你在生产环境下的 Rails 应用部署的能力,你可以查看系列教程 How To Use Capistrano to Automate Deployments

这个系列基于 CentOS,不过对于自动部署你的应用也有帮助。

[1]: https://ruby-china.org/topics/32449 [2]: https://ruby-china.org/topics/32450

huacnlee 将本帖设为了精华贴。 03月05日 22:33

很强势,毕业论文准备搭建在 Ubuntu 上面,这个教程真是及时雨啊

最近也是使用 rbenv 搭建 ruby 环境.rvm 搭建并不顺畅。

4 楼 已删除

应该结合 Capistrano 使用,更加方便

经过自己多次实践,感觉安装 nginx 还是通过解压包命令行编译来的痛快和干净利落,后期需要什么模块也都好往上,比如 https module,ruby 版本管理用 rvm,安装由于网络问题,我都是直接下载安装包放到 rvm 的 archive 下

需要 登录 后方可回复, 如果你还没有账号请 注册新账号