402v /posts/railsxiang-mu-da-jian-er-di-ge-railsxiang-mu

Rails开发指南 - Nginx+Unicorn的服务器配置

From where:Rails开发指南 - 第一个Rails项目

服务端访问rails项目的实现一般是基于nginx+unicorn,nginx监听80端口,收到对应域的请求之后通过socket或者端口转发给unicorn处理。How unicorn talks to nginx这篇文章把这个转发过程介绍的非常好,建议后续找时间读一下;

#Nginx+Unicorn的服务器配置

一个注意事项是:log、pid等文件的访问权限问题,配置出现问题首先要想到的就是对某个文件是否没有访问权限,后面配置cap也一样。

这部分的内容需要在Mac OSX本地和Ubuntu服务器上都有操作,注意区分当前所在的环境。如Unicorn gem的配置是本地开发之后提交到github上去的,Nginx配置肯定是在服务器上,这里特别强调一下开发的方式:推荐在本地开发,通过git同步代码,服务器上只做git pull。其实在后面的介绍中有了capistrano这个神器之后git pull都省了,完全不需要登录服务器就可以完成网站的发布

###安装Unicorn 在Gemfile中添加gem 'unicorn',保存退出运行bundle或者bundle install更新gem。

###新建Unicorn配置文件 在config路径下新建unicorn.rb文件,模板内容如下,针对自己的环境做些配置调整,一定要注意访问路径的权限问题,仔细阅读配置文件,如果只是复制粘贴,有任何一条配置不理解的话,以后遇到错误都是要重新阅读查阅的,还不如一次性的都了解清楚。为了叙述上的连贯性,直接在注释部分解释配置目的和注意事项:

# 没有指定环境的话默认在开发环境下运行
env = ENV["RAILS_ENV"] || "development"

# 需要开启的unicorn worker进程数,详见:http://unicorn.bogomips.org/Unicorn/Configurator.html.
worker_processes 4

# app相关设置
# app工作路径,默认向上找两级路径,即projects/config/unicorn.rb
app_dir = File.expand_path("../..", __FILE__)

# 应用名称(项目根目录名)
app_name = "projects"

# 保存unicorn进程id的文件路径,重要,确保当前用户有访问此文件的权限
pid_path = "#{app_dir}/tmp/pids/unicorn.#{app_name}.pid"

# nginx与unicorn进行socket通信的文件路径,重要,确保当前用户有访问此文件的权限
sock_path = "#{app_dir}/tmp/sockets/unicorn.#{app_name}.sock"

# 监听socket文件,也可以在此处指定一个端口号
# 减小backlog的长度以得到更快的速度(这里理解的不深,先把原文留下了:we use a shorter backlog for quicker failover when busy)
listen sock_path, :backlog => 64

# 销毁worker的时间改为30s,默认为60
timeout 30

# 指定pid保存路径
pid pid_path

# Production环境下的特殊配置,即网站发布之后的服务器端配置
if env == "production"
  # Help ensure your application will always spawn in the symlinked
  # "current" directory that Capistrano sets up.

  # 指定production下的工作路径为current,项目自动发布(deploy)到服务器上的时候,capistrano会将当前工作的站点放在current路径下,在后续deploy的时候会详细讲到。
  working_directory "#{app_dir}/current"

  # production环境下运行的用户和组,确保这个用户有访问app_dir、socket、pid路径的权限
  user 'deploy', 'deploy'

  # share目录,每次发布时不需要改变或者无法改变的文件会放在这个目录下,如log等,deploy中会讲到
  shared_path = "#{app_dir}/current/shared"

  # unicorn的错误和输出日志
  stderr_path "#{shared_path}/log/unicorn.stderr.log"
  stdout_path "#{shared_path}/log/unicorn.stdout.log"
end

# 在创建fork process之前预加载app以得到更快的启动速度,但这样的话必须保证其他的连接(如数据库连接)都被正确的关闭和重启,于是就用到了before_fork和after_fork
preload_app true

# unicorn通过fork processes来实现多进程,因此需要在before_fork和after_fork配置数据库连接的关闭和重启。
before_fork do |server, worker|
  # the following is highly recomended for Rails + "preload_app true"
  # as there's no need for the master process to hold a connection
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.connection.disconnect!
  end

  # 杀掉位于 .oldbin 这个PID上的master进程.
  # deploy的时候很有用,可以实现rails服务的zero downtime重启
  old_pid = "#{pid_path}.oldbin"
  if File.exists?(old_pid) && server.pid != old_pid
    begin
      Process.kill("QUIT", File.read(old_pid).to_i)
    rescue Errno::ENOENT, Errno::ESRCH
      # someone else did our job for us
    end
  end
end

after_fork do |server, worker|
  # the following is *required* for Rails + "preload_app true",
  if defined?(ActiveRecord::Base)
    ActiveRecord::Base.establish_connection
  end

  # if preload_app is true, then you may also want to check and
  # restart any other shared sockets/descriptors such as Memcached,
  # and Redis.  TokyoCabinet file handles are safe to reuse
  # between any number of forked children (assuming your kernel
  # correctly implements pread()/pwrite() system calls)
end

###启动Unicorn 启动Unicorn的方式有几种:

  1. 在服务端命令行手动启动和停止,伪代码如下:

     # 启动unicorn
     cd #{current_path} && #{fetch(:unicorn_binary)} -c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D
    
     # 关闭unicorn
     kill `cat #{fetch(:unicorn_pid)}`
    
  2. 生成一个unicorn的服务放在/etc/init.d下,通过service unicorn_appname start等命令控制unicorn进程。

    • 运行sudo vi /etc/init.d/unicorn_appname创建shell文件,

    • 在文件内指定服务的参数,shell文件的具体内容参见这里

    • 权限配置

        sudo chmod 755 /etc/init.d/unicorn_appname
        sudo update-rc.d unicorn_appname defaults
      
    • 之后就可以运行start、stop、restart等命令运行unicorn了。

        sudo service unicorn_appname start
      
  3. 最后一种方式也是最实用的方式是启动unicorn的操作放到capistrano或者mina等自动化发布工具上去做,也是本文所采用的方式,后面在讲capistrano的deploy配置时会详细说。

###Nginx配置 nginx的目录配置(如:site-avaliables、site-enables)可以参见:Ghost博客搭建系列之三 - 多重域名访问b,这里默认本地nginx畅通,那么我们需要做的是添加一个nginx配置文件,内容如下(具体说明在注释中):

upstream app {
    # unicorn.rb中设置的unicorn的socket文件路径 unix:/home/deploy/apps/projects/current/tmp/sockets/unicorn.projects.sock fail_timeout=0;
}

server {
    listen 80;
    server_name {my-domain}.com www.{my-domain}.com;

    # rails的public文件路径
    root /home/deploy/apps/{my-app}/current/public;

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

    # location到app这个upstream
    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;
    }

    # 资源路径
    location ~ ^/assets/ {
      expires 1y;
      add_header Cache-Control public;

      add_header ETag "";
      break;
    }

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

配置成功后重启nginx:service nginx restart,如果发生错误运行nginx -t快速测试查看原因。

这个时候如果nginx的root对应路径有项目文件的话,运行unicorn已经可以访问到网站。

可以先git clone一个项目到/home/deploy/apps/{my-app}/current/public,手动启动unicorn测试一下效果。或者直接阅读下面的Cap自动化部署。

#参考连接 3. Nginx:worker_connections are not enough while connecting to upstream


Where to go:Rails开发指南 - Capistrano 3实现Rails自动化部署

评论 · 0

还没有评论。