部署 Ruby on Rails 部署方案 Nginx + Mina + Puma

gamefu · 2015年06月22日 · 最后由 feng88724 回复于 2016年04月26日 · 7197 次阅读

搭建工具介绍

Ruby on Rails作为一款十分优秀的 web 开发框架,在当前 web 领域中慢慢占据了越来越重要,秉承 rails 快速开发的特点,很多快速部署 rails 的方案也越来越多。这篇文章中所选的方案是我个人认为十分优秀的部署方案。这套部署方案的结构是,nginx作为反向代理服务器负责负载均衡,mina作为自动化部署工具,puma作为 rails 的 web 服务器

nginx

nginx是一款优秀的代理服务器,其高效的性能已经得到了业界的广泛认可,相信作为 web 开发人员不会没听说过他的大名

mina

mina是一款由 ruby 开发的自动化部署工具,其目的是为了简化每次 rails 代码提交时的部署,一键完成部署,杜绝了提交到 git 服务器后,又去服务器上 git pull 的情况

puma

puma是一款专门针对 rails 的并发服务器,相对于passengerpuma可配置面更广,而且性能比passenger更高,是 rails web 服务器的不二之选

部署前言

由于这篇文章需要很多铺垫,包括 rails 的安装下载,git 的配置等等,需要读者自己去查阅资料或者查阅之前我写过的一些文章,如果期间有什么问题,请留言。。

mina

首先在你的 rails 项目的Gemfile中加上

gem mina

运行bundle 安装 mina,接着在你的 rails 项目根目录初始化mina

mina init

这是在你项目的 config 目录下会有一个deploy.rb,配置deploy.rb,列出重点部分,每一行的解释会附在代码的注释里


#服务器地址,是使用ssh的方式登录服务器
set :domain, '[email protected]'
#服务器中项目部署位置
set :deploy_to, '/var/www/ruby_sample'
#git代码仓库
set :repository, 'https://github.com/gameFu/ruby_sample.git'
#git分支
set :branch, 'master'

# 中括号里的文件 会出现在服务器项目附录的shared文件夹中,这里加入了secrets.yml,环境密钥无需跟开发计算机一样
set :shared_paths, ['config/database.yml', 'log', 'config/secrets.yml']

# 这个块里面的代码表示运行 mina setup时运行的命令
task :setup => :environment do

  # 在服务器项目目录的shared中创建log文件夹
  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/log"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/log"]

  # 在服务器项目目录的shared中创建config文件夹 下同
  queue! %[mkdir -p "#{deploy_to}/#{shared_path}/config"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/#{shared_path}/config"]

  queue! %[touch "#{deploy_to}/#{shared_path}/config/database.yml"]
  queue! %[touch "#{deploy_to}/#{shared_path}/config/secrets.yml"]

  # puma.rb 配置puma必须得文件夹及文件
  queue! %[mkdir -p "#{deploy_to}/shared/tmp/pids"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/tmp/pids"]

  queue! %[mkdir -p "#{deploy_to}/shared/tmp/sockets"]
  queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/tmp/sockets"]

  queue! %[touch "#{deploy_to}/shared/config/puma.rb"]
  queue  %[echo "-----> Be sure to edit 'shared/config/puma.rb'."]

  # tmp/sockets/puma.state
  queue! %[touch "#{deploy_to}/shared/tmp/sockets/puma.state"]
  queue  %[echo "-----> Be sure to edit 'shared/tmp/sockets/puma.state'."]

  # log/puma.stdout.log
  queue! %[touch "#{deploy_to}/shared/log/puma.stdout.log"]
  queue  %[echo "-----> Be sure to edit 'shared/log/puma.stdout.log'."]

  # log/puma.stdout.log
  queue! %[touch "#{deploy_to}/shared/log/puma.stderr.log"]
  queue  %[echo "-----> Be sure to edit 'shared/log/puma.stderr.log'."]

  queue  %[echo "-----> Be sure to edit '#{deploy_to}/#{shared_path}/config/database.yml'."]
end

#这个代码块表示运行 mina deploy时执行的命令
desc "Deploys the current version to the server."
task :deploy => :environment do
  to :before_hook do
  end
  deploy do
    #重新拉git服务器上的最新版本,即使没有改变
    invoke :'git:clone'
    #重新设定shared_path位置
    invoke :'deploy:link_shared_paths'
    invoke :'bundle:install'
    invoke :'rails:db_migrate'
    invoke :'rails:assets_precompile'
    invoke :'deploy:cleanup'

    to :launch do
      queue "mkdir -p #{deploy_to}/#{current_path}/tmp/"
      # queue "chown -R www-data #{deploy_to}"
      queue "touch #{deploy_to}/#{current_path}/tmp/restart.txt"
    end
  end
end

这样一来 mina 的基本配置就完成,接下来只要将你开发环境的项目上传到 git 服务器,然后运行下面的命令就完成了

mina deploy

完成部署后,你就可以在指定的服务器目录下看到你的项目,目录结构如下

  • current -当前版本目录也就是项目目录
  • last_version -版本号
  • releases/ -过去的版本
  • scm/
  • shared/ 先前 shared_path 所设定另外拉出来的文件都在这里
  • tmp/

这里需要注意的几点 1.shared_path 里面的文件不仅仅是表示这些文件会在服务器目录中出现在另外的目录里,也表示这些文件或者目录不会受到 git 版本库的控制,也就是说这些文件的配置必须在你服务器中手动去配置,这两个文件包括 database.yml 和 secrets.yml,在 shared/config 目录下 2.针对 deploy 最好在服务器创建一个使用者,并针对他创建一个 ssh authorized_keys,这里直接使用了 root 身份,参考centos7 服务器部署 ssh 证书授权登录,这样做能避免每次部署的时候都需要输入服务器账号密码

可能会遇到的问题

由于生产环境一般会搭配类似于postgresql等成熟数据库,这里我就举出一个搭建postgresql,首先是启动数据库时(centos 7 下),如果遇到问题请使用下面的命令就能看到详细的错误信息

systemctl status postgresql-9.4.service -l 

然后在跑mina deploy时可能会报类似于这样的一个错误

Gem::LoadError: Specified 'postgresql' for database adapter, but the gem is not loaded. Add `gem 'pg'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord).

从错误信息上能很明显的看出是因为没有安装 pg 这个包导致的,但是有一种情况是明明在项目的Gemfile上写上了 pg 但还是跑不过,造成这个的原因,可能是由于你的服务器环境缺少了 pg 的头文件导致的,如果是在 centos 下,只需要执行下面命令就能解决

yum install postgresql-libs
yum install postgresql-devel

Puma

首先在你的Gemfile里加上


gem puma

然后在 config 目录下手动创建一个puma.rb文件,配置puma.rb文件


#!/usr/bin/env puma

#rails的运行环境
environment 'production'
threads 2, 64
workers 4

#项目名
app_name = "ruby_sample"
#项目路径
application_path = "/var/www/#{app_name}"
#这里一定要配置为项目路径下地current
directory "#{application_path}/current"

#下面都是 puma的配置项
pidfile "#{application_path}/shared/tmp/pids/puma.pid"
state_path "#{application_path}/shared/tmp/sockets/puma.state"
stdout_redirect "#{application_path}/shared/log/puma.stdout.log", "#{application_path}/shared/log/puma.stderr.log"
bind "unix://#{application_path}/shared/tmp/sockets/#{app_name}.sock"
activate_control_app "unix://#{application_path}/shared/tmp/sockets/pumactl.sock"

#后台运行
daemonize true
on_restart do
  puts 'On restart...'
end
preload_app!

这里需要注意的地方

  • threads - puma的线程数,第一个参数是最小的线程数,第二个参数是最大线程数
  • bind - 这个指定的是puma运行时产生的socket,后面nginx会用到
  • 这里所有对应的目录是在deploy配置中配置的,如果需要更改配置目录,deploy.rb也需要相应的更改

Nginx

下载安装nginx后,打开nginx的配置文件nginx.conf进行配置

worker_processes  1;

    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;

    events {
        worker_connections  1024;
    }

    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        sendfile        on;
        #tcp_nopush     on;

        keepalive_timeout  65;

        #gzip  on;

        #include /etc/nginx/conf.d/*.conf;
        upstream deploy {
                server unix:///var/www/ruby_sample/shared/tmp/sockets/ruby_sample.sock;
        }

        server {
            listen 80;
            server_name your.server.domain.ip; # change to match your URL
            root /var/www/ruby_sample/current/public; # I assume your app is located at this location

            location / {
                proxy_pass http://deploy; # match the name of upstream directive which is defined above
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            }

            location ~* ^/assets/ {
                # Per RFC2616 - 1 year maximum expiry
                expires 1y;
                add_header Cache-Control public;
                        # Some browsers still send conditional-GET requests if there's a
                # Last-Modified header or an ETag header even if they haven't
                # reached the expiry date sent in the Expires header.
                add_header Last-Modified "";
                add_header ETag "";
                break;
            }
     }
    }

这里只需要注意的是

  • upstream 中 server 要配置成你在 puma 中 bind 的 socket 就行了
  • root 要设置成你服务器项目的根目录,也就是puma.rb中的 directory

接下里只需要重启 nginx 服务器,整个 rails 的环境就搭建完成了


nginx -s reload

如果完成了配置后访问站点是 504,那么可能是两种情况,一是服务器防火墙问题,二是 rails 环境密钥的问题,请在使用 passenger 在 Centos7 部署 nginx+Ruby on Rails中寻找答案

虽然我和你用一样的解决方案,但“终极部署方案”措辞是不是有那么点不恰当呢

#1 楼 @peter 确实如此,谢谢提醒

用 puma 的话,进程是不是要做下监控?

#3 楼 @smallx 海南 ruby 者?我也是海南的。。现在我做的部署主要还是先让他跑起来,其他的我暂时还没想那么多,有相关资料嘛?

#4 楼 @gameFu 哦,老乡啊,你好!资料网上搜呗,我没有做笔记的习惯。

有多台设备的 b 部署实例吗

我执行脚本启动,报错了,不知道如何季节,初次接触 ruby /mnt/script/puma.sh start Starting puma... /mnt/webserver/www//releases/4/vendor/bundle/ruby/2.2.0/gems/puma-2.15.3/lib/puma/cli.rb:195:in chdir': No such file or directory @ dir_chdir - /mnt/webserver/www/current (Errno::ENOENT) from /mnt/webserver/www//releases/4/vendor/bundle/ruby/2.2.0/gems/puma-2.15.3/lib/puma/cli.rb:195:inrun' from /mnt/webserver/www//releases/4/vendor/bundle/ruby/2.2.0/gems/puma-2.15.3/bin/puma:10:in <top (required)>' from /mnt/webserver/wwwreleases/4/vendor/bundle/ruby/2.2.0/bin/puma:23:inload' from /mnt/webserver/www//releases/4/vendor/bundle/ruby/2.2.0/bin/puma:23:in `

'

楼主,请教两个问题: 1、database.yml 为什么要放 shared_path 里? 2、上传至 public 的静态资源没有处理?

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