Nginx + Unicorn + Rails

April 21, 2013 13:28


一直以来部署都是用别人写的代码,配置也是直接复制以前的,昨天在自己电脑上弄了下, 半天才用运行起来。其实也就是为了开发用,不喜欢打localhost:xxx了,于是把nginx弄起来。 然后自动生成nginx config,再自动在/etc/hosts中加入定义好的域名

我用的系统是Fedora 17, 至从Ubuntu换到Fedora来后,基本上两三天一次系统更新, 一直折腾。呃,跑题了。

先安装Nginx, sudo yum install nginx, 这个没什么说的了,然后项目里Gemfile加入: gem 'unicorn',再去github把example的config复制下来。。

$ curl -o config/unicorn.rb https://raw.github.com/defunkt/unicorn/master/examples/unicorn.conf.rb

然后修改之,我只把修改的写出来了

require File.expand_path("../../lib/deploy", __FILE__)

worker_processes Deploy::CONFIG["worker_processes"] || 4

working_directory Deploy::APP_PATH # available in 0.94.0+

listen Deploy::SOCK_PATH, :backlog => 64
# listen 8080, :tcp_nopush => true

pid Deploy::APP_PATH + "/tmp/pids/unicorn.pid"

stderr_path Deploy::APP_PATH + "/log/unicorn.stderr.log"
stdout_path Deploy::APP_PATH + "/log/unicorn.stdout.log"

说明一下,首先把一些要用到的配置、路径写到一个module里面,我就是放在lib/deploy里, 然后require 这个模板,这样其它地方也要用了,修改时不用到处改。这里主要就写了些主要配置

下面是lib/deploy.rb文件:

require 'yaml'
require 'erb'

require 'pry'


module Deploy
  APP_ENV = ENV['RAILS_ENV'] || 'development'
  APP_PATH = defined?(Rails) ? Rails.root.to_s : File.expand_path("../..", __FILE__)
  CONFIG = YAML.load_file(APP_PATH + "/config/config.yml")[APP_ENV]
  NAME = CONFIG["app_name"] || File.basename(APP_PATH)
  HOST = CONFIG["app_host"] || (NAME.gsub(/_/, "-") + ".dev")
  SOCK_PATH = APP_PATH + "/tmp/sockets/#{NAME}_unicorn.sock"
  NGINX_TEMPLATE_PATH = APP_PATH + "/config/deploy/nginx.conf.erb"
  NGINX_CONFIG_PATH = APP_PATH + "/tmp/nginx.conf"

  `mkdir -p #{APP_PATH}/tmp`


  # generate nginx config
  def self.generate_nginx_config
    server = {
      "sock_path" => "unix:" + SOCK_PATH,
      "upstream_name" => NAME,
      "name" => "127.0.0.1 #{HOST}",
      "public" => APP_PATH + "/public"
    }

    File.open(NGINX_CONFIG_PATH, "w+") do |f|
      f.write ERB.new(File.read(NGINX_TEMPLATE_PATH)).result(binding)
    end
  end
end

服务器的配置都放到这里来了,然后nginx的配置文件也在这里生成,用了一个简单的模板, 写在config/deploy/nginx.conf.erb中,用erb模板来渲染

upstream <%= server['upstream_name'] %> {
  server <%= server['sock_path'] %> fail_timeout=0;
}

server {
  listen 80;
  server_name <%= server["name"] %>;

  root <%= server["public"] %>;

  location ~* ^(/assets|/favicon.ico) {
    access_log off;
    expires max;
  }

  location / {
    # proxy_redirect off;
    proxy_set_header Host $host;
    # proxy_set_header X-Forwarded-Host $host;
    # proxy_set_header X-Forwarded-Server $host;
    # proxy_set_header X-Real-IP $remote_addr;
    # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    # proxy_buffering on;
    proxy_pass http://<%= server['upstream_name'] %>;
  }
}

恩,这样就保证了unicorn的配置和nginx的配置对上了,生成的配置于项目的tmp/nginx.conf, 然后就是配置文件写入与项目的启动了,我写了几task,于lib/tasks/server.rake:

namespace :server do
  task :start do
    require 'deploy'

    Deploy.generate_nginx_config
    ln_nginx_config
    write_localhosts_config

    `unicorn_rails -c #{Rails.root + "config/unicorn.rb"} -E #{Rails.env} -D`

    `sudo service nginx restart` if Deploy::ENV == "development"
  end

  task :stop do
    pid = `cat #{Rails.root + "tmp/pids/unicorn.pid"}`
    `kill -9 #{pid}` if `ps -A | grep #{pid}`.length > 0
  end

  task :restart do
    Rake::Task["server:stop"].invoke
    Rake::Task["server:start"].invoke
  end



  def ln_nginx_config
    cmd = %Q{\
      sudo ln -fs #{Deploy::NGINX_CONFIG_PATH} \
      /etc/nginx/conf.d/#{Deploy::NAME}.conf
    }

    print "\nln nginx config: #{cmd}"
    `#{cmd}`
  end


  def write_localhosts_config
    path = "/etc/hosts"

    hosts = File.read(path)

    record = "127.0.0.1 #{Deploy::HOST} \#Nginx localhost"

    unless hosts[record]
      cmd = %Q{\
        sudo sh -c "\
        echo '' >> #{path} &&\
        echo '#{record}' >> #{path} &&\
        echo '' >> #{path}"\
      }

      `#{cmd}`
    end
  end
end

start时,先生成nginx配置,然后再将配置文件ln到/etc/nginx/conf.d/文件夹中, 这里需要root权限,复制好了,再写入/etc/hosts这样我们就可以直接用Deploy中配置的域名进行访问了, 写入/etc/hosts时先看下以前是否已经写入,如果已经写入了就不再写了。 再就是启动unicorn了,然后重启nginx

至于stop,呃我直接暴力解决了。。。 restart就不用说了

上面这些主要就是Deploy,通过它来让nginx与unicorn的配置统一,能对上号, 接下来就是项目位置,权限了,说实话,linux权限我真的不了解,也没怎么去了解过。 有时间还是要细看一下唉,不然太郁闷了,下面说下我遇到的问题:

开始nginx的配置网复制了个,报错了,呃,查了下,发现外面的nginx.conf中include conf.d/*.conf了 所以不能再包个http {xxx}了,我错了。。。

期间又几次配置对不上号,然后对上了页面还是打不开,去/var/log/nginx/error.log看了下, 连接Unicorn的sock文件时,No Permission, 我放在/home/my/rails/project里, 把sock移出去,权限是有了,又报sock连接不上,折腾好久,没办法了, 于是把项目git clone到/srv/railsproject/project,然后把railsproject给nginx用户, 呃好了,瞎折腾了半天。。。

Comments: