Sunday, August 30, 2009

Sử dụng Nginx và memcached để tăng tốc Apache trên Debian Lenny

Tác giả: Nguyễn Việt Cường (a.k.a. mrcuongnv)

Trong bài viết này, tôi giới thiệu cách thức cài đặt Nginx kết hợp với memcached trên Debian "lenny" 5.0 nhằm tăng hiệu năng phục vụ của web server mà ở đây là Apache 2.2. Bài viết này là tổng hợp từ nhiều nguồn khác nhau trên Internet và là bản ghi chép của tôi trong quá trình nâng cấp một web server thực tế có lượng truy cập lớn.

1. Giới thiệu


Gần đây, web server mà tôi quản lí (phi vụ lợi) có lượng truy cập tăng đột biến mà không phải vì bị tấn công. Tải (load) của server những lúc cao điểm thường lên ~40 trong khi chỉ có 8 cores phục vụ với 8GB RAM. Công việc tối ưu bắt đầu.

Chúng tôi chỉ có một server duy nhất nên web server, db server được đặt chung với nhau. Điều này là một bất lợi lớn. Nhưng không sao, chúng ta phải chấp nhận vì có một server tốt như thế để hoạt động phi vụ lợi là rất tốt rồi.

Ban đầu, tôi tưởng rằng vấn đề nằm ở DB server (MySQL) vì theo dõi thấy có rất nhiều lệnh thực hiện rất lâu, đặc biệt là các lệnh thực hiện phép nối bảng lớn. Chúng tôi đã tiến hành tối ưu MySQL rồi đặt cron để tối ưu toàn bộ CSDL 2 ngày một lần. Vấn đề có vẻ như được giải quyết khi server chạy liên tục 2 tháng liền mà không gặp bất cứ trục trặc gì. Tuy nhiên, gần đây thì tình trạng tải tăng cao lại tiếp tục diễn ra, đặc biệt là quãng 20-24h (giờ Việt Nam). Tôi đã xem xét lại chi tiết và phát hiện ra rằng, những lúc tải tăng cao thì DB server không hoạt động mấy, rất ít câu lệnh được thực hiện (lúc tải < 20). Tuy nhiên khi tải ~30-40 thì một số câu lệnh SQL đột nhiên thực hiện rất lâu, điều này có thể giải thích là các tiến trình của web server đã chiếm hết tài nguyên rồi thì làm sao db server chạy nhanh được nữa. Như vậy thì ta phải tối ưu web server thôi.

Sau một hồi hỏi anh Google và chị Bing, tôi quyết định sẽ sử dụng Nginx để làm proxy ngược (reverse proxy) cho Apache và kết hợp sử dụng memcached để lưu những dữ liệu tĩnh (static file) vào trong bộ nhớ.

Theo định nghĩa trên Wikipedia thì:
  1. Nginx (phát âm giống "engine x") là một máy chủ web (web server), proxy ngược (reserve proxy) và e-mail proxy (IMAP/POP3) nhẹ, hiệu năng cao, sử dụng giấy phép kiểu BSD. Nó có thể chạy trên UNIX, Linux, các dòng BSD, Mac OS X, Solaris và Microsoft Windows.
  2. memcached (phát âm là mem-cash-dee) là một hệ thống lưu trữ bản sao các đối tượng (objects) và dữ liệu được truy cập nhiều lần để tăng tốc độc truy xuất. Nó thường được sử dụng để tối ưu hóa việc tải dữ liệu từ cơ sở dữ liệu cho các ứng dụng trên nền web. Vào lúc đầu, hệ thống memcached được phát triển bởi Danga Interactive và dùng cho LiveJournal. Sau đó memcached trở nên phổ biến và được dùng trên các trang web khác.

Vì sao tôi không sử dụng thẳng Nginx để làm web server? Lí do ở đây là vì Apache tỏ ra tốt hơn Nginx trong việc phục vụ các trang web động (dynamic page) (điều này tôi tham khảo trên web, sẽ kiểm tra trong một bài khác). Một trong những nguyên nhân là do Nginx sử dụng FastCGI, mà cái này thì chậm. Vậy là mô hình đã hình thành: Apache sẽ chạy ở dưới, phục vụ trên một cổng khác (8080); còn Nginx chạy ở trên, phục vụ trên cổng (80). Các request tới web server sẽ đi qua Nginx trước, rồi sau đó sẽ được chuyển qua Apache để xử lí và trả kết quả ngược lại cho Nginx; tiếp đó Nginx sẽ trả về cho client. Tuy nhiên nếu request đó không phải là một trang web động mà lại là một nội dung tĩnh thì hành động này có vẻ như thừa thãi. Giải pháp đưa ra là (1) Nginx sẽ phục vụ trực tiếp các file tĩnh hoặc (2) Nginx sử dụng memcached để ghi nhớ các file đó trong bộ nhớ và chỉ việc lấy ra khi cần thiết.

Trên server của tôi có chứa nhiều website (shared server) và để chống hack local thì chúng tôi sử dụng ACL kết hợp mpm-itk để tăng cường bảo mật. Khi sử dụng mpm-itk thì mỗi website sẽ được chạy dưới một uid và gid riêng. Điều này không tương thích với Nginx vì nó chỉ chạy được với một uid và gid duy nhất., do vậy không có đủ quyền để truy cập trực tiếp vào thư mục của các website Do vậy chúng tôi phải sử dụng giải pháp memcached để giúp Nginx có thể phục vụ trực tiếp client với các file tĩnh.

Các bạn cũng nên lưu ý là khi cài đặt memcached rồi thì có rất nhiều phần mềm mã nguồn mở sử dụng được cái này để tăng hiệu năng như Drupal, vBulletin, phpBB,... và không kể hết...

2. Cài đặt


Trong bài viết này, tôi chỉ hướng dẫn cài đặt trên Debian "lenny" 5.0. Cài đặt trên Ubuntu "chắc" cũng tương tự, nếu có thì chỉ là thay đổi chút ít. Ở đây tôi không hướng dẫn cài ACL và mpm-itk mà sẽ giới thiệu trong một dịp khác.

Trước hết, chúng ta phải cài đặt Apache, PHP, Nginx và memcached.

# aptitude install apache2 libapache2-mod-php5 libapache2-mod-rpaf nginx memcached


Chúng ta cần cài module rpaf của Apache vì request gửi đến Apache sẽ xuất pháp từ Nginx và do vậy mang IP của máy chạy Nginx. Module rpaf sẽ giúp Apache nhận ra IP thực của client trong request mà Nginx gửi sang.

Bây giờ chúng ta sẽ phải sửa file cấu hình của apache2 để cho nó chạy trên cổng 8080. Mở file /etc/apache2/ports.conf, thay 80 bằng 8080 ở hai dòng sau:

NameVirtualHost *:80
Listen 80

Ta được:

NameVirtualHost *:8080
Listen 8080

Bật module php5 và rpaf:

# a2enmod php5 rpaf

Ta tạo một website làm ví dụ, ở đây tôi sử dụng tên miền blog.cuongnv.com. Ta tạo một file cấu hình cho website này như sau:

# vi /etc/apache2/sites-available/blog.cuongnv.com

Và điền nội dung như sau (thư mục gốc cho web của domain này là /home/www/blog.cuongnv.com/public_html).


<VirtualHost *:8080>
    ServerName blog.cuongnv.com
    ServerAdmin blog@cuongnv.com

    DocumentRoot "/home/www/blog.cuongnv.com/public_html"

    <Directory "/home/www/blog.cuongnv.com/public_html">
        Options -Indexes FollowSymLinks MultiViews
        AllowOverride All
    </Directory>

    ErrorLog /var/log/apache2/blog.cuongnv.com_error_log
    LogFormat "%h %l %>s %b" common
    CustomLog /var/log/apache2/blog.cuongnv.com_access_log common
</VirtualHost>

Kích hoạt domain này (bản chất là tạo một soft link trong thư mục /etc/apache2/sites-enabled tới file tương ứng trong thư mục /etc/apache2/sites-available):

# a2ensite blog.cuongnv.com

Khởi động lại dịch vụ Apache:

# /etc/init.d/apache2 restart

Khởi động lại dịch vụ memcached nếu chưa tự khởi động khi cài xong:

# /etc/init.d/memcached restart

Như vậy là xong phần cấu hình cho Apache. Bây giờ chúng ta sẽ cấu hình cho Nginx. Cấu trúc thư mục trong /etc của Nginx cũng hoàn toàn tương tự như của Apache.

Trước hết, ta tạo file cấu hình cho module proxy của Nginx. Các thông tin này lấy từ web site của Nginx.

# vi /etc/nginx/proxy.conf

Điền nội dung sau:

proxy_redirect          off;

proxy_set_header        Host            $host;
proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

client_max_body_size    10m;
client_body_buffer_size 128k;

proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;

proxy_buffer_size               4k;
proxy_buffers                   32 4k;
proxy_busy_buffers_size         64k;
proxy_temp_file_write_size      64k;

Bây giờ là công việc cấu hình một virtual host cho domain blog.cuongnv.com để tương ứng với Apache. Tạo file:

# vi /etc/nginx/sites-avaiable/blog.cuongnv.com

Với nội dung như sau:

server {
        listen   80;
        server_name  blog.cuongnv.com;

        access_log  /var/log/nginx/blog.cuongnv.com.access.log;

        location / {
                proxy_pass      http://blog.cuongnv.com:8080;
                include         /etc/nginx/proxy.conf;
        }

        location ~ \.php$ {
                proxy_pass      http://blog.cuongnv.com:8080;
                include         /etc/nginx/proxy.conf;
        }

        # Danh sach file tinh vi dụ
        location ~* ^.+.(jpg|jpeg|gif|png|ico|css|tar|mid|midi|wav|js)$ {
                expires                 max;
                set $memcached_key      "$scheme://$host$request_uri";
                memcached_pass          127.0.0.1:11211;
                error_page              404 = /fallback;
        }

        location /fallback {
                internal;
                expires         max;
                proxy_pass      http://blog.cuongnv.com:8080;
                include         /etc/nginx/proxy.conf;
                break;
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
                root   /var/www/nginx-default;
        }
}

Kích hoạt domain trên:

# ln -s /etc/nginx/sites-available/blog.cuongnv.com /etc/nginx/sites-enabled/

Khởi động lại Nginx:

# /etc/init.d/nginx restart

Bây giờ bạn đã có thể truy cập vào website vừa cấu hình một cách bình thường do Nginx phục vụ, ở đây là: http://blog.cuongnv.com. Ngoài ra, bạn có thể truy cập trực tiếp dịch vụ của Apache tại http://blog.cuongnv.com:8080. Tất nhiên đây là các link giả định, thực tế phải là đường link của bạn.

Trong file cấu hình ở trên, chúng ta đã cấu hình để những yêu cầu tới file có đuôi là .php sẽ được chuyển cho Apache phục vụ. Các file tĩnh (với đuôi được liệt kê ở trước đó) thì được tìm kiếm trong memcached, nếu memcached không có thì sẽ chuyển cho Apache phục vụ (lỗi 404 chuyển qua /fallback). Việc tìm kiếm này sử dụng key là URL đầy đủ của file tương ứng, bao gồm scheme (http, https), host (blog.cuongnv.com), uri đầy đủ (uri và query string).

Tuy nhiên, cần phải đặc biệt chú ý, Nginx không hề giúp ta cache các file vào trong memcached một cách tự động mà ta phải tự tay làm việc đó. Trong hệ thống của tôi thì tôi sử dụng một script để cache tất cả các file tĩnh cần thiết vào trong bộ nhớ. Việc này được thực hiện định kì một lần mỗi ngày.

Script này tôi tham khảo từ blog của Levent Serinol viết bằng PHP. Hiện tại tôi đang viết một script khác có thể duyệt qua tất cả các virtual host hiện tại và cache cho toàn bộ các host đó với các file có  đuôi và độ lớn nhỏ hơn một ngưỡng cho trước. Tôi sẽ public script này trong một vài ngày tới.

Cài đặt php5-cli để chạy được các file php trên dòng lệnh:

# aptitude install php5-cli

Tạo file tương ứng trong /etc/cron.daily:

# vi /etc/cron.daily/cache_static_file.php

Điền nội dung sau:

#!/usr/bin/php5

<?php
function rscandir($base = '', &$data = array())
{
        $array = array_diff(scandir($base), array('.', '..'));

        foreach ($array as $value)
        {
                if (is_dir($base.$value))
                {
                        $data = rscandir($base . $value . '/', $data);
                }
                elseif (is_file($base.$value))
                {
                        $rest = substr($value, -4);
                        if ((!strcmp($rest,'.jpg')) ||
                            (!strcmp($rest,'.png')) ||
                            (!strcmp(substr($value, -3),'.js'))  ||
                            (!strcmp($rest,'.css')) ||
                            (!strcmp($rest,'.gif')) )
                        {
                            $data[] = $base.$value;
                        }
                }
        }
        return $data;
}
$mylist=rscandir("/home/www/blog.cuongnv.com/public_html");

$srch = array('/home/www/blog.cuongnv.com/public_html');
$newval = array('http://blog.cuongnv.com');

$memcache_obj = memcache_connect('127.0.0.1', 11211);

while (list($key, $val) = each($mylist))
{
        $url = str_replace($srch, $newval, $val);
        echo "$key => $url -> " . filesize($val) . "\n";
        $value = file_get_contents($val);
        memcache_add($memcache_obj, $url, $value, false, 0);
}
?>

Sửa quyền của file để là file chạy và khởi động lại dịch vụ cron:

# chmod a+rx /etc/cron.daily/cache_static_file.php 
# /etc/init.d/cron restart

27 comments:

Phan Trọng Khanh said...

Cảm ơn bài viết của anh nha :D. Mà cho em hỏi, cái lệnh đọc nội dung file của php có tốn nhiều tài nguyên hoặc thời gian không ạ? Với những file lớn ý :D.

Unknown said...

Lượng tài nguyên tốn tương đương với kích thước file. Thời gian thì đa số phụ thuộc vào tốc độ đọc/ghi của ổ cứng.

Phan Trọng Khanh said...

$srch = array('/home/www/blog.cuongnv.com/public_html');
Ở mấy cái biến đường dẫn kiểu này, nếu em không thêm '/' ở cuối thì sẽ bị sai đường dẫn file. Nếu như của anh thì nó sẽ thành dạng: /home/www/blog.cuongnv.com/public_htmlfile.mp3

Unknown said...

Em nhầm thế nào ý chứ. Anh làm thế là để những đường dẫn như kiểu:

/home/www/blog.cuongnv.com/public_html/file.mp3

nhận được từ quá trình search ở trên sẽ được replace thành:

http://blog.cuongnv.com/file.mp3

để làm key cho memcache mà.

Phan Trọng Khanh said...

À em nhầm, do khi test em để cái $srch là /home/www/blog.cuongnv.com/public_html/ nên cái $newval cũng phải có '/' ở cuối :D. Sorry.
Em thử rồi, chạy ngon :D. Nhưng khi thằng nginx được cấu hình để lấy static file trong memcached, nếu memcached bị lỗi thì sẽ không tìm thấy file. Có thể xử lý để khi memcached lỗi, nó lấy file gốc được không anh?

Phan Trọng Khanh said...

À, em thấy rồi, nó ở mấy cái link anh post ở dưới. Mình sẽ bắt lỗi 404 của nginx và chuyển request tới apache server :D.

Unknown said...

Ờ, thằng này không chịu nhìn kĩ gì cả :D.

Anonymous said...

Mình cấu hính giống bạn nói mà khi chạy file image không phải là PHP sao nó vẫn chạy về apache nhỉ? mình sai ở đâu hả bạn.

Unknown said...

cái cấu hình nginx của bạn ko sử dụng HTTP cache rồi. mọi http request đều ko đc xử lý cache

svbacgiang said...

Theo mình đc biết thì MemCache được lưu trữ trên RAM.Nếu server có 1000 site sử dung MemCache. RAM sẽ full~> cho mình hỏi dữ liệu sẽ đc lưu trữ ở đâu?các tác vụ khác sẽ xử lý ntn khi RAM bị hết??

Unknown said...

@svbacgiang: Chào bạn,

Swap space sẽ được sử dụng khi RAM bị đầy. Khi cả swap bị đầy thì hệ thống sẽ không cấp phát bộ nhớ thêm cho các tiến trình cần bộ nhớ nữa. Khi đó các tiến trình sẽ bị crash hoặc dừng để chờ, tuỳ thuộc vào quá trình xử lí của nó.

Về memcache thì khi bạn khởi động memcached thì bạn đã phải định trước lượng bộ nhớ sử dụng rồi (mặc định là 64MB ở hầu hết các distro). Khi memcached sử dụng hết bộ nhớ thì nó sẽ tự động loại bỏ các items "ít hoạt động" ra khỏi bộ nhớ. Bạn cũng có thể disable tính năng này. Khi đó memcache sẽ không thể lưu thêm được gì.

Anonymous said...

Hiện nay Nginx đã tối ưu hơn Apache rất nhiều cho nên việc sử dụng Nginx làm reverse proxy và Apache làm webserver là không cần thiết (mà đã không cần thiết thì sẽ là lãng phí, CPU, RAM, HDD, ... vv), thay vào đó nên sử dụng Nginx làm webserver và fastcgi_cache để cache lại. Mô hình này hiện nay là hiệu suất nhất, hơn cả việc sử dụng Varnish làm proxy.

Unknown said...

Cảm ơn bạn, mình đã cài và cấu hình thành công để chạy Diễn đàn http://cungthangtien.com & trang bán túi xách http://tuixachstyle.com
Giờ chạy khá mượt không ì ạch như trước nữa

Unknown said...

Hi Mr Cườngnv:

Mình đang dùng Centos, apache làm webserver, db mysql có dùng memcached.

Tuy nhiên, website trung bình real time khoảng 3000 user active (GA), httpd gọi đến rất nhiều.

Mình làm server quảng cáo, nên chỉ gọi ảnh ra, và update click và impression và db.

Vậy việc sử dung nghinx có hỗ trợ tốt cho việc giảm tải server, caching image không??


Tks bạn

Unknown said...

Hi Tuấn,

Bài này mình viết cách đây lâu quá rồi nên tình hình đã thay đổi rất nhiều kể từ thời điểm đấy. Đến thời điểm hiện tại thì cách hoạt động của nginx đã được thực tế chứng minh là hiệu quả hơn Apache rất nhiều (bạn sẽ thấy rất nhiều httpd process hoặc thread).

Vì vậy nếu bạn phân vân giữa Nginx và Apache thì bạn nên chuyển qua Nginx. Nginx có khả năng phục vụ tương đương Apache với lượng tài nguyên thấp hơn nhiều.

Về chuyện caching thì nếu RAM của bạn đủ lớn thì về cơ bản là tất cả sẽ được cache bởi Linux ngay sau lần đọc đầu tiên. Nếu RAM ít thì bạn có thể xem xét lắp thêm một ổ SSD và sử dụng làm cache cho ổ HDD chứa dữ liệu thông qua Flashcache (do Facebook phát triển).

Thân mến!
Cường

AMBNCORP said...

Eo ơi, có Bác nào xem giải pháp giúp em, tình hình là trang em dạo này càng ngày càng ì ạch (Do CSDL tăng, do xây dựng web chưa tối ưu). Đặc biệt là học sinh vào thi và Upload kho câu hỏi lên thì ..vô cùng là cực!Lại gặp được em.. dốt đặc cắn mai về máy chủ và CSDL.. Các bác xem có ai giúp em không, chứ sắp Die rùi. Trang em chuyên về mảng giáo dục http://ambn.vn Thank các Bác

traininginstitute said...

This is a fantastic website , thanks for sharing.
data science course in malaysia

Deniel Alex said...

Great article this is really informative and innovative: with new updates.with new updates. It was really valuable. Thanks a lot. Some countries allow automatic visa extension India. Travelers can easily online apply for Indian visa extension. Thus the Government of India has provided an easy-to-use method to apply for the Indian Visa Extension (FRRO Visa Extension) through a simple online application form.

traininginstitute said...

Very awesome!!! When I seek for this I found this website at the top of all blogs in search engine.
full stack development course

jacobalexander said...


After checking through a few posts, I realized this was a new site for me. I'm definitely glad I found it and I will be bookmarking and checking back often. Travelers intending to visit India for tourism are permitted entry into India on an e-tourist visa Inde/Indian tourist visa through an online Indian visa website. Apply online for an India travel visa via the Indian visa website.


data science said...

Through this post, I realize that your great information in playing with all the pieces was exceptionally useful. I advise this is the primary spot where I discover issues I've been scanning for. You have a smart yet alluring method of composing.

PMP Training in Malaysia said...

Pleasant data, important and incredible structure, as offer great stuff with smart thoughts and ideas, loads of extraordinary data and motivation, the two of which I need, because of offer such an accommodating data here.

PMP Training in Malaysia said...

360DigiTMG, the top-rated organisation among the most prestigious industries around the world, is an educational destination for those looking to pursue their dreams around the globe. The company is changing careers of many people through constant improvement, 360DigiTMG provides an outstanding learning experience and distinguishes itself from the pack. 360DigiTMG is a prominent global presence by offering world-class training. Its main office is in India and subsidiaries across Malaysia, USA, East Asia, Australia, Uk, Netherlands, and the Middle East.

Albert said...

Good morning everybody.. Your article is more useful for all the people. Thank you sir.. Many people ask What is Indian visa fee for United States of America? You can read all the info about Indian visa fee on our India eVisa page.

Isabella Ava said...


Hello everyone, Foreign citizens can enter India for business purposes. Indian government allows foreign country citizens they can can be apply for e business visa for India. e Business Visa India, Non-Indian visitors who are looking for business business or meetings, setting up industrial or business enterprises in India or other similar business activities etc.

Edison hope said...

Nice blog this is very inspiring and informative material thanks for sharing us.. If you want to plan a trip to Turkey, you will need a Turkey evisa. Turkish electronic visa or Turkish e visa is an official travel permit granting entry into Turkey, issued by the Turkish government to foreign travelers from around the world. Foreign nationals can apply for eVisa Turkey online.

Bralin Allison said...

The best Article via pictures that I have ever seen before with useful content and very informative. Thanks for sharing information. Turkey e visa is an online way to get on your given email. You don’t need to visit the embassy for it.