I – Nginx
I.1 – Nginx là gì
Nginx (pronounced “Engine-X”) : Là sản phẩm mã nguồn mở cho web server . Là một reverse proxy cho các giao thức HTTP, SMTP, POP3 and IMAP . Nhằn nâng cao hiệu xuất xử lý khi sử dung lượng RAM thấp . Được cấp phép bởi BSD chạy trên nên tảng Unix , Linux và các biến thểBSD, Mac OS X, Solaris, AIX, HP-UX và Microsoft Windows.
I.2 – Tổng quan
Nginx có thể triển khai nội dung của các trang web động bằng cách sử lý FastCGI, SCGI cho các scripts . Và có thể sử dụng như là một server cân bằng tải . Sau đó vấn đề C10K xuất hiện nói cách khác để cho phép mỗi máy chủ web phải có khả năng xử lý 10.000 khách hàng cùng một lúc. Cần phải phát triển một mạng lưới I / O tốt hơn và công nghệ quản lý chủ đề đã được xuất hiện. Sự xuất hiện của NGinx không phải là kết quả của một nỗ lực để giải quyết vấn đề C10K (như là một vấn đề phổ biến) nhưng “vấn đề C10K” đã thành công trong việc đưa ra các nỗ lực để nâng cao hiệu suất phát triển mạng máy chủ
Igor Sysoev phát triển nginx từ cách đây hơn 9 năm. Vào tháng 10/2004, phiên bản 0.1.0 được phát hành rộng rãi theo giấy phép BSD. Công dụng của nginx ngoài máy chủ web, còn có thể làm proxy nghịch cho Web và làm proxy email (SMTP/POP3/IMAP). Theo thống kê của Netcraft, trong số 1 triệu website lớn nhất thế giới, có 6,52% sử dụng nginx. Tại Nga, quê hương của nginx, có đến 46,9% sử dụng máy chủ này. Nginx chỉ đứng sau Apache và IIS (của Microsoft).
Nginx cung cấp gần như tất cả các chức năng máy chủ web:
I.3 – Cài đặt Nginx
#aptitude -y update
#aptitude -y install mongodb redis-server git-core libpcre3 libpcre3-dev libssl-dev mysql-server mysql-client libmysqlclient16-dev libxml2-dev libxslt-dev imagemagick libmagick9-dev libcurl3-dev libreadline-dev memcached build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion openjdk-6-jre moni
#mkdir -p /var/tmp/nginx/{proxy,client,fcgi}
#cd /usr/local/src
#wget http://nginx.org/download/nginx-1.0.15.tar.gz
#tar zxvf nginx-1.0.15.tar.gz
#cd nginx-1.0.15
CFLAGS='-march=nocona' ./configure \ --prefix=/opt/nginx \ --conf-path=/opt/nginx/conf/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx.pid \ --lock-path=/var/lock/nginx.lock \ --user=nobody \ --group=nogroup \ --with-http_stub_status_module \ --with-http_ssl_module \ --with-http_gzip_static_module \ --with-http_realip_module \ --http-log-path=/var/log/nginx/access.log \ --http-client-body-temp-path=/var/tmp/nginx/client/ \ --http-proxy-temp-path=/var/tmp/nginx/proxy/ \ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi
#make
#make install #ln -s /opt/nginx/sbin/nginx /usr/sbin
I.4 – Cấu hình Nginx
#vim /opt/nginx/conf/nginx.conf
#user nobody;
worker_processes 8;
events
{ worker_connections 1024; accept_mutex on; # "on" if nginx worker_processes > 1 use epoll; # enable for Linux 2.6+ }
http { include 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"';
upstream backend-unicorn-pc {
server localhost:5000;
}
upstream backend-unicorn-sp {
server localhost:5100;
sendfile on;
keepalive_timeout 30;
server {
listen 80;
server_name snap.framgia.com;
access_log /var/log/nginx/unicorn.access.log main;
client_max_body_size 5M;
set $is_sphone 0;
if ($http_user_agent ~ Android) {
set $is_sphone 1;
}
if ($http_user_agent ~ iPhone) {
set $is_sphone 1;
}
if ($http_user_agent ~ iPod) {
set $is_sphone 1;
}
if ($http_user_agent ~ iPad) {
set $is_sphone 1;
}
location /uploads/ {
root /usr/local/rails_apps/snap/current/public;
break;
}
location / {
root /usr/local/rails_apps/snap/current/public;
if (-f $request_filename) { break; }
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_read_timeout 60;
proxy_redirect off;
if ($is_sphone = 1) {
proxy_pass http://backend-unicorn-sp;
}
if ($is_sphone != 1) {
proxy_pass http://backend-unicorn-pc;
}
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
I.5 – Các thông số của NGinx
I.5.1 worker_processes
Với cấu hình mặc định, Nginx sẽ sử dụng một CPU để xử lý các tác vụ của mình. Tùy theo mức độ hoạt động của web server mà chúng ta có thể thay đổi lại thiết lập này. Ví dụ với các web server hay sử dụng về SSL, gzip thì ta nên đặt chỉ số của worker_processes này lên cao hơn. Nếu website của bạn có số lượng các tệp tin tĩnh nhiều, và dung lượng của chúng lớn hơn bộ nhớ RAM thì việc tăng worker_processes sẽ tối ưu băng thông đĩa của hệ thống.
Để xác định số cores của CPU của hệ thống ta có thể thực hiện lệnh
# cat /proc/cpuinfo | grep processor
processor : 0
processor : 1
processor : 2
processor : 3
Như ở trên, CPU của chúng ta có 4 cores. Để thay đổi mức sử dụng CPU của nginx ta sửa tệp tin cấu hình chính
vim /opt/nginx/conf/nginx.conf
worker_processes 4;
I.5.2 worker_connections
Worker_connections sẽ cho biết số lượng connection mà CPU sẽ xử lý. Mặc định, số lượng connection này được thiết lập là 1024. Để xem về mức giới hạn sử dụng của hệ thống bạn có thể dụng lệnh ulimit. Con số thiết lập của worker_connections nên nhỏ hơn hoặc bằng giới hạn này! Nếu bạn đã điều chỉnh lại giá trị worker_processes giúp Nginx sử dụng nhiều cores để xử lý các tác vụ hơn thì có thể thêm dòng cấu hình sau để tăng số lượng clients lên cao nhất.
max_clients = worker_processes * worker_connections
I.5.3 Buffers
Một trong những cấu hình quan trọng để tối ưu Nginx là thiết đặt các giá trị buffer. Nếu bạn thiết lập bộ nhớ buffer quá nhỏ thì sẽ dễ dẫn tới tình trạng “thắt cỗ chai” khi web server của chúng ta tiếp nhận một lượng traffic lớn. Để thay đổi các giá trị buffer này, chúng ta có thể thêm vào các dòng cấu hình ở thẻ http của file cấu hình chính nginx.conf
client_body_buffer_size 8K;
client_header_buffer_size 1k;
client_max_body_size 2m;
large_client_header_buffers 2 1k;
Trong đó:
client_body_buffer_size: Thiết đặt giá trị kích thước của body mà client yêu cầu. Nếu kích thước được yêu cầu lớn hơn giá trị buffer thì sẽ được lưu vào temporary file.
client_header_buffer_size: Thiết đặt giá trị kích thước của header mà client yêu cầu. Thông thường thì kích thước này 1K là đủ.
client_max_body_size: Thiết đặt giá trị kích thước tối đa của body mà client có thể yêu cầu được, xác định bởi dòng Conent-Length trong header. Nếu kích thước body yêu cầu vượt giới hạn nãy thì client sẽ nhận được thông báo lỗi “Request Entity Too Large” (413).
large_client_header_buffers: Thiết đặt giá trị kích về số lượng và kích thước lớn nhất của buffer dùng để đọc các headers có kích thước lớn từ các request của client. Nếu client gửi một header quá lớn Nginx sẽ trả về lỗi “Request URL too large” (414)hoặc “Bad request” (400)nếu header của request quá dài.
Ngoài ra chúng ta cũng cần thiết đặt lại các giá trị timeout để tối ưu hiệu suất hoạt động của web server với các client :
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 15;
send_timeout 10;
Trong đó:
client_body_timeout: Thiết đặt thời gian tải body của webpage từ client. Nếu quá thời gian này, client sẽ nhận thông báo trả về “Request time out” (408).
client_header_timeout: Thiết đặt thời gian tải title của webpage từ client. Nếu quá thời gian này, client sẽ nhận thông báo trả về “Request time out” (408).
keepalive_timeout: Thiết đặt thời gian sống của kết nối từ client, nếu quá thời gian này thì kết nối sẽ bị đóng.
send_timeout: Thiết đặt thời gian phản hồi dữ liệu giữa client và server, nếu quá thời gian này thì nginx sẽ tắt kết nối.
I.5.4 Nén các gói dữ liệu gửi đi bằng Gzip
Gzip sẽ giúp nén các dữ liệu trước khi chuyển chúng tới Client. Đây là một cách để tăng tốc độ tuy cập website của cúng ta. Trong thẻ http của file cấu hình chính nginx.conf ta có thể thêm
gzip on;
gzip_comp_level 2;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain application/xml;
gzip_disable "MSIE [1-6]\.";
V.5.5 Cache nội dung các tệp tin tĩnh
Hầu hết các request từ client tới website của chúng ta để load các nôi dung như: hình ảnh, java script, css, flash,… Chúng ta nên thực hiện việc lưu cache lại các tệp tin có nội dung tĩnh này trên Nginx
location ~* "\.(js|ico|gif|jpg|png|css|html|htm|swf|htc|xml|bm p|cur)$" {
root /home/site/public_html;
add_header Pragma "public";
add_header Cache-Control "public";
expires 3M;
access_log off;
log_not_found off;
I.5.6 Ẩn phiên bản của Nginx
Việc ẩn đi phiên bản của Nginx từ Server Header sẽ giúp hệ thống webserver của chúng ta được bảo mật tốt hơn. Để thực hiện điều này, trong thẻ http của của tệp tin cấu hình chính nginx.conf ta thêm vào dòng sau
server_tokens off;
I.5.7 Cấm các truy cập tới các tệp tin ẩn trên Nginx
Đôi khi trên các thư mục web chúng ta có lưu những tệp tin ẩn (bắt đầu với dấu chấm “.”) như .svn, .htaccess. Đây là các tệp tin không mang tính public đối với người dùng. Để ngăn chặn các truy xuất tới các tệp tin ẩn này ta có thể thêm vào đoạn cấu hình sau
location ~ /\. {
access_log off;
log_not_found off;
deny all;
}
I.5.8Các thông số cơ bản của caching
http {
proxy_cache_path /var/www/cache levels=2 keys_zone=my-cache:8m max_size=1000m inactive=600m;
proxy_temp_path /var/www/cache/tmp;
server {
location / {
proxy_pass http://example.net;
proxy_cache my-cache;
proxy_cache_valid 200 302 60m;
proxy_cache_valid 404 1m;
}
}
}
-
/vr/www/cache là thư mục chứa cache
-
level=2 phân cấp thư mục lưu cache
-
key_zone=cache_one:8m tên của cache zone là cache_one
-
inactive=1d: thành phần cache nào không được truy cập trong vòng 1 ngày sẽ bị xóa.
-
proxy_cache cache_one : chỉ định cache vào zone cache_one
-
proxy_cache_valid 200 15m : các request có file tồn tại (200) sẽ có giá trị cache trong vòng 15m.
-
proxy_cache_key $host$request_uri : chỉ định key cache, giúp nginx lần được cache của một URL ở đâu trong thư mục cache.
-
proxy_set_header X-Forwarded-For $remote_addr: dùng để thêm một dòng trong header trả về cho client.
-
proxy_ignore_headers : cái này để thông báo cho nginx bỏ qua những header nào của client. Trong các header của người dùng có thể có những trường để điều khiển cache (cache control) nên chúng ta cần báo cho nginx biết bỏ qua những thông số này để việc cache được kiểm soát tối đa.
-
proxy_read_timeout Default: 60s
II- Unicorn
II.1 – Unicorn là gì ?
Unicorn là server HTTP cho Ruby
Nginx gửi các request tới worker Unicorn thông qua Unix Domain Socket or TCP . Mỗi server đều có một con số worker nhất định mà trong giờ “cao điểm ” có thể đáp ứng được đồng thời nhiều yêu cầu bằng cách sắp xếp hàng đợi . Unicorn biết rõ được các worker đang xử lý tiến trình nào , hay bao bao lâu mỗi worker sử lý một yêu cầu. Thay vì xếp chồng hàng đợi chồng chất Unicorn sẽ hủy bỏ worker và ngay lập tức tạo 1 worker mới để phục vụ yêu cầu
II.2 – Cài đặt – Cấu hình
#gem install rails
#gem install unicorn
#cd /var/www/
#rails new unicorn
#cd unicorn
#unicorn_rails
I, [2012-09-07T23:57:09.486924 #2523] INFO -- : listening on addr=0.0.0.0:8080 fd=5
I, [2012-09-07T23:57:09.487888 #2523] INFO -- : worker=0 spawning...
I, [2012-09-07T23:57:09.489175 #2523] INFO -- : master process ready
I, [2012-09-07T23:57:09.490946 #2525] INFO -- : worker=0 spawned pid=2525
I, [2012-09-07T23:57:09.491675 #2525] INFO -- : Refreshing Gem list
I, [2012-09-07T23:57:15.208629 #2525] INFO -- : worker=0 ready
#mkdir /etc/unicorn
#cd /etc/unicorn/
#vim /etc/unicorn/unicorn2.conf
RAILS_ROOT=/var/www/unicorn
RAILS_ENV=production
#vim /etc/init.d/unicorn_2
#!/bin/sh
set -e
sig () {
test -s "$PID" && kill -$1 `cat "$PID"`
}
oldsig () {
test -s "$OLD_PID" && kill -$1 `cat "$OLD_PID"`
}
cmd () {
case $1 in
start)
sig 0 && echo >&2 "Already running" && exit 0
echo "Starting"
$CMD
;;
stop)
sig QUIT && echo "Stopping" && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && echo "Forcing a stop" && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig USR2 && sleep 5 && oldsig QUIT && echo "Killing old master" `cat $OLD_PID` && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
$CMD
;;
upgrade)
sig USR2 && echo Upgraded && exit 0
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
$CMD
;;
rotate)
sig USR1 && echo rotated logs OK && exit 0
echo >&2 "Couldn't rotate logs" && exit 1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
exit 1
;;
esac
}
setup () {
echo -n "$RAILS_ROOT: "
cd $RAILS_ROOT || exit 1
export PID=$RAILS_ROOT/tmp/pids/unicorn.pid
export OLD_PID="$PID.oldbin"
CMD="unicorn_rails -c config/unicorn.rb -E $RAILS_ENV -D"
}
start_stop () {
# either run the start/stop/reload/etc command for every config under /etc/unicorn
# or just do it for a specific one
# $1 contains the start/stop/etc command
# $2 if it exists, should be the specific config we want to act on
if [ $2 ]; then
. $2
setup
cmd $1
else
for CONFIG in /etc/unicorn/unicorn2.conf; do
# import the variables
. $CONFIG
setup
# run the start/stop/etc command
cmd $1
done
fi
}
ARGS="$1 $2"
start_stop $ARGS
#chmod +x /etc/init.d/unicorn_2
#cd /var/www/unicorn/config
#vim unicorn.rb
app_path = "/var/www/unicorn"
listen 2008 # by default Unicorn listens on port 8080
worker_processes 2 # this should be >= nr_cpus
pid "#{app_path}/tmp/pids/unicorn2.pid"
stderr_path "#{app_path}/log/unicorn2.log"
stdout_path "#{app_path}/log/unicorn2.log"
before_exec do |server|
ENV['BUNDLE_GEMFILE'] = "/var/www/unicorn/Gemfile"
end
before_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
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
end
###################require "redis"
after_fork do |server, worker|
if defined?(ActiveRecord::Base)
ActiveRecord::Base.establish_connection
end
if defined?(MultiDb::ConnectionProxy)
begin
# MultiDb::ConnectionProxy.sticky_slave = true
MultiDb::ConnectionProxy.setup!
rescue
end
end
if Rails.cache.respond_to?(:reset)
Rails.cache.reset
end
port = 5000 + worker.nr
child_pid = server.config[:pid].sub('.pid', ".#{port}.pid")
system("echo #{Process.pid} > #{child_pid}")
end
Kiểm tra các kết nối
#netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:27017 0.0.0.0:* LISTEN 864/mongod
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 880/mysqld
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 989/redis-server
tcp 0 0 127.0.0.1:11211 0.0.0.0:* LISTEN 924/memcached
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 840/boa
tcp 0 0 0.0.0.0:2001 0.0.0.0:* LISTEN 939/monkey
tcp 0 0 127.0.0.1:28017 0.0.0.0:* LISTEN 864/mongod
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1748/sshd
tcp 0 0 192.168.4.106:22 192.168.4.100:49216 ESTABLISHED 2001/sshd: kiloccnp
tcp 0 52 192.168.4.106:22 192.168.4.100:49396 ESTABLISHED 2533/sshd: kiloccnp
tcp6 0 0 :::22 :::* LISTEN 1748/sshd
tcp6 0 0 :::8000 :::* LISTEN 999/webfsd
#/etc/init.d/unicorn_2 start
#netstat -natp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:27017 0.0.0.0:* LISTEN 864/mongod
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN 880/mysqld
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 989/redis-server
tcp 0 0 127.0.0.1:11211 0.0.0.0:* LISTEN 924/memcached
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 840/boa
tcp 0 0 0.0.0.0:2001 0.0.0.0:* LISTEN 939/monkey
tcp 0 0 127.0.0.1:28017 0.0.0.0:* LISTEN 864/mongod
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1748/sshd
tcp 0 0 0.0.0.0:2008 0.0.0.0:* LISTEN 3069/unicorn.rb -E
tcp 0 0 192.168.4.106:22 192.168.4.100:49216 ESTABLISHED 2001/sshd: kiloccnp
tcp 0 52 192.168.4.106:22 192.168.4.100:49396 ESTABLISHED 2533/sshd: kiloccnp
tcp6 0 0 :::22 :::* LISTEN 1748/sshd
tcp6 0 0 :::8000 :::* LISTEN 999/webfsd
cd /var/www/unicorn/log
cat unicorn2.log
I, [2012-09-08T00:13:37.402200 #3069] INFO -- : listening on addr=0.0.0.0:2008 fd=5
I, [2012-09-08T00:13:37.402872 #3069] INFO -- : worker=0 spawning...
I, [2012-09-08T00:13:37.403439 #3069] INFO -- : worker=1 spawning...
I, [2012-09-08T00:13:37.403980 #3069] INFO -- : master process ready
I, [2012-09-08T00:13:37.409890 #3072] INFO -- : worker=0 spawned pid=3072
I, [2012-09-08T00:13:37.410073 #3072] INFO -- : Refreshing Gem list
I, [2012-09-08T00:13:37.412841 #3074] INFO -- : worker=1 spawned pid=3074
I, [2012-09-08T00:13:37.413020 #3074] INFO -- : Refreshing Gem list
I, [2012-09-08T00:13:42.680852 #3074] INFO -- : worker=1 ready
I, [2012-09-08T00:13:42.684405 #3072] INFO -- : worker=0 ready