Skip to content

Instantly share code, notes, and snippets.

@AndorChen
Created Nov 5, 2011
Embed
What would you like to do?
Howto Setting Up Rails Production Environment on VPS
---
:verbose: true
:bulk_threshold: 1000
install: --no-ri --no-rdoc --env-shebang
:source:
- http://rubygems.org
- http://gemcutter.org
- http://gems.rubyforge.org/
- http://gems.github.com
:benchmark: false
:backtrace: false
update: --no-ri --no-rdoc --env-shebang
:update_sources: true

#如何在 VPS 上架设 Rails 生产环境

VPS OS: Ubuntu 10.10
Local OS: Mac OS X 10.6.8
HTTP Server: nginx + unicore
Ruby: 1.9.2-p290
Rails: 3.1.x
Update: 2011-11-05

注意:区分本地操作和 VPS 上的操作

# VPS 的 root 用户
root@vps:~$

# VPS 的 deployer 用户
deployer@vps:~$

# 本地操作
mac: ~$

###1. 新建专用用户

不用系统提供的 root 用户,新建一个专门用来部署的用户:

# 添加 deployer 用户,设置用户组为 staff
root@vps:~$ useradd -m -g staff -s /bin/bash deployer

# 为 deployer 设置密码
root@vps:~$ passwd deployer

# 根据提示输入两次密码

设置用户权限:

root@vps:~$ visudo -f /etc/sudoers

添加以下内容:

%staff ALL=(ALL) ALL

###2. SSH 设置

生成 dsa 密匙:

mac: ~$ ssh-keygen -t dsa

会在 ~/.ssh/ 中生成两个文件:

  • id_dsa:私有密匙
  • id_dsa.pub:公共密匙

打开 ~/.ssh/config(不存在则新建):

mac: ~$ sudo mvim ~/.ssh/config

编辑:

# VPS
Host vps
  HostName 100.100.100.100
  User deployer
  IdentityFile ~/.ssh/id_dsa

其中:

  • Host 名称随意,便于记忆就好,因为以后连接 SSH 时要用到
  • HostName 是 VPS 的 IP 地址

使用 deployer 账户连接 VPS,新建 ~/.ssh 目录:

deployer@vps:~$ mkdir .ssh

退出,回到本地。将刚刚生成的公共密匙拷贝到 VPS 上:

mac: ~$ scp ~/.ssh/id_dsa.pub deployer@vps:.ssh/authorized_keys2

以后就可以不用密码直接连接到 VPS 了:

mac: ~$ ssh vps

如果觉得有必要,可以同样对 root 用户进行相应的设置。

###2. 校正时区

deployer@vps:~$: sudo dpkg-reconfigure tzdata

选择 Asia-> Chongqing,测试设定时候正确

deployer@vps:~$ date

###3. 更新源

deployer@vps:~$ sudo apt-get update
deployer@vps:~$ sudo apt-get upgrade

###4. 安装通用包

编译器等:

deployer@vps:~$ sudo apt-get install gcc
deployer@vps:~$ sudo apt-get install build-essential

需要的库:

deployer@vps:~$ sudo apt-get install build-essential bison openssl libreadline6 libreadline6-dev
deployer@vps:~$ sudo apt-get install curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev
deployer@vps:~$ sudo apt-get install libsqlite3-0 libsqlite3-dev sqlite3 libxml2-dev
deployer@vps:~$ sudo apt-get install libxslt-dev autoconf libc6-dev nodejs

Vim 编辑器:

deployer@vps:~$ sudo apt-get install vim-nox

###5. 安装 nginx

deployer@vps:~$ sudo bash -c 'echo "deb http://ppa.launchpad.net/nginx/stable/ubuntu $(lsb_release -cs) main" > /etc/apt/sources.list.d/nginx-stable-$(lsb_release -cs).list'
deployer@vps:~$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C && sudo aptitude update
deployer@vps:~$ sudo apt-get install nginx

# 查看是否安装成功
deployer@vps:~$ nginx -v

###6. 编译、安装 Ruby

Ruby 版本:1.9.2-p290

# 创建源文件存放目录
deployer@vps:~$ mkdir src
deployer@vps:~$ cd src

# 解压
deployer@vps:~/src$ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p290.tar.gz
deployer@vps:~/src$ tar -xzvf ruby-1.9.2-p290.tar.gz
deployer@vps:~/src$ cd ruby-1.9.2-p290

# 编译
deployer@vps:~/src/ruby-1.9.2-p290$ ./configure
deployer@vps:~/src/ruby-1.9.2-p290$ make
deployer@vps:~/src/ruby-1.9.2-p290$ make test
deployer@vps:~/src/ruby-1.9.2-p290$ sudo make install

# 查看是否安装成功,安装的目录
deployer@vps:~$ ruby -v
deployer@vps:~$ which ruby

###7. 升级 Rubygems

Ruby 1.9.2 内建了对 Rubygems 的支持,但是版本过低,升级到最新版本

deployer@vps:~$ sudo gem update --system

###8. 设置 .gemrc

主要目的是设置 gem 安装时不安装 ri 和 rdoc 文件。

deployer@vps:~$ sudo bash -c 'curl https://raw.github.com/gist/1341422/.gemrc > ~/.gemrc'

###9. 安装 Rails

deployer@vps:~$ sudo gem install rails

###10. 安装 unicorn

deployer@vps:~$ sudo gem install unicorn

###11. 生成测试 app

deployer@vps:~$ mkdir sites
deployer@vps:~$ cd sites

deployer@vps:~/sites$ rails new test_app

###12. 设置 nginx

deployer@vps:~$ sudo bash -c 'curl https://raw.github.com/gist/1341422/nginx.conf > /etc/nginx/nginx.conf'

###13. 设置 unicorn

需要修改文件中的 App 路径。

deployer@vps:~/sites/test_app$ sudo bash -c 'curl -L https://raw.github.com/gist/1341422/unicorn.rb > config/unicorn.rb'

###14. 测试 unicorn

deployer@vps:~/sites/test_app$ unicorn -c config/unicorn.rb -D

打开站点地址,应该就可以看到 Rails 的欢迎界面了。

###15. 设置 unicorn 随系统自运行

deployer@vps:~$ sudo bash -c 'curl -L https://raw.github.com/gist/1341422/unicorn.sh > /etc/init.d/unicorn'

deployer@vps:~$ sudo chmod +x /etc/init.d/unicorn
deployer@vps:~$ sudo update-rc.d unicorn defaults

###参考资源

# This conf sample is fetched from http://unicorn.bogomips.org/examples/nginx.conf
worker_processes 1;
user deployer staff;
pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;
events {
worker_connections 1024;
accept_mutex off;
}
http {
include mime.types;
default_type application/octet-stream;
access_log /tmp/nginx.access.log combined;
sendfile on;
tcp_nopush on;
tcp_nodelay off;
# no text/html, see http://forum.slicehost.com/comments.php?DiscussionID=3763
gzip on;
gzip_http_version 1.0;
gzip_proxied any;
gzip_min_length 500;
gzip_disable "MSIE [1-6]\.";
gzip_types text/plain text/xml text/css
text/comma-separated-values
text/javascript application/x-javascript
application/atom+xml;
upstream app_server {
server unix:/tmp/.sock fail_timeout=0;
}
server {
listen 80 default deferred;
client_max_body_size 4G;
server_name _;
keepalive_timeout 5;
# MODIFY: path for static files
root /home/deployer/sites/test_app/public;
try_files $uri/index.html $uri.html $uri @app;
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app_server;
}
error_page 500 502 503 504 /500.html;
location = /500.html {
root /home/deployer/sites/test_app/public;
}
}
}
# Sample verbose configuration file for Unicorn (not Rack)
# Took from https://raw.github.com/defunkt/unicorn/master/examples/unicorn.conf.rb
#
# This configuration file documents many features of Unicorn
# that may not be needed for some applications. See
# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
# for a much simpler configuration file.
#
# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
# documentation.
# Use at least one worker per core if you're on a dedicated server,
# more will usually help for _short_ waits on databases/caches.
worker_processes 4
# Since Unicorn is never exposed to outside clients, it does not need to
# run on the standard HTTP port (80), there is no reason to start Unicorn
# as root unless it's from system init scripts.
# If running the master process as root and the workers as an unprivileged
# user, do this to switch euid/egid in the workers (also chowns logs):
# user "unprivileged_user", "unprivileged_group"
# Help ensure your application will always spawn in the symlinked
# "current" directory that Capistrano sets up.
APP_PATH = "/home/deployer/sites/test_app" # MODIFY
working_directory APP_PATH
# listen on both a Unix domain socket and a TCP port,
# we use a shorter backlog for quicker failover when busy
listen "/tmp/.sock", :backlog => 64
listen 8080, :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
timeout 30
# feel free to point this anywhere accessible on the filesystem
pid APP_PATH+"/tmp/pids/unicorn.pid"
# By default, the Unicorn logger will write to stderr.
# Additionally, ome applications/frameworks log to stderr or stdout,
# so prevent them from going to /dev/null when daemonized here:
stderr_path APP_PATH+"/log/unicorn.stderr.log"
stdout_path APP_PATH+"/log/unicorn.stdout.log"
# combine REE with "preload_app true" for memory savings
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
preload_app true
GC.respond_to?(:copy_on_write_friendly=) and
GC.copy_on_write_friendly = true
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
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
# The following is only recommended for memory/DB-constrained
# installations. It is not needed if your system can house
# twice as many worker_processes as you have configured.
#
# # This allows a new master process to incrementally
# # phase out the old master process with SIGTTOU to avoid a
# # thundering herd (especially in the "preload_app false" case)
# # when doing a transparent upgrade. The last worker spawned
# # will then kill off the old master process with a SIGQUIT.
# old_pid = "#{server.config[:pid]}.oldbin"
# if old_pid != server.pid
# begin
# sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
# Process.kill(sig, File.read(old_pid).to_i)
# rescue Errno::ENOENT, Errno::ESRCH
# end
# end
#
# Throttle the master from forking too quickly by sleeping. Due
# to the implementation of standard Unix signal handlers, this
# helps (but does not completely) prevent identical, repeated signals
# from being lost when the receiving process is busy.
# sleep 1
end
after_fork do |server, worker|
# per-process listener ports for debugging/admin/migrations
# addr = "127.0.0.1:#{9293 + worker.nr}"
# server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
# the following is *required* for Rails + "preload_app true",
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
# 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
#! /bin/sh
###BEGIN INIT INFO
# Provides: unicorn
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the unicorn web server
# Description: starts unicorn
###END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/bin/unicorn_rails
DAEMON_OPTS="-c /home/deployer/sites/test_app/config/unicorn.rb -E production -D"
NAME=unicorn_rails
DESC=unicorn_rails
PID=/home/deployer/sites/test_app/tmp/pids/unicorn.pid
case "$1" in
start)
echo -n "Starting $DESC: "
$DAEMON $DAEMON_OPTS
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
kill -QUIT `cat $PID`
echo "$NAME."
;;
restart)
echo -n "Restarting $DESC: "
kill -QUIT `cat $PID`
sleep 1
$DAEMON $DAEMON_OPTS
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configurations: "
kill -HUP `cat $PID`
echo "$NAME."
;;
*)
echo "Usage: $NAME {start|stop|restart|reload}" >&2
exit 1
;;
esac
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment