วิธี config และใช้ nginx + php-fpm + fast-cgi เบื้องต้นให้ใช้งานกับ Codeigniter by

31
Dec
2

เนื่องด้วยทั่วๆ ไปแล้ว web server มาตรฐานทั่วๆ ไปที่ใช้งานกันคือ Apache แต่ว่า Apache นั้นบริโภค Memory อย่างไม่ไยดี และถ้าเว็บเรามีการใช้งาน static file จำพวกรูปภาพหรือ css, js มากๆ ก็จะกิน Memory หนักขึ้นไปอีก หลักการของ nginx จึงเปรียบเสมือนการเป็นป้ายบอกทางว่าถ้าเป็น static file ให้เรียกไฟล์นั้นๆ ขึ้นมาตรงๆ เลยไม่ต้องผ่าน apache หรือตัวกลางอื่นๆ แต่ถ้าเป็น Dynamic file เช่น PHP ก็ให้ส่งไปยัง Fast-cgi แทน ซึ่ง Fast-cgi จะทำหน้าที่อ่าน PHP เพียงอย่างเดียว ไม่ต้องเผื่อรองรับ static file จึงกิน Memory น้อยกว่านั่นเอง สุดท้ายจึงทำให้สามารถรองรับการใช้งานเว็บไซต์ที่มี traffic หนักๆ ได้อย่างสบายๆ กว่า apache มากหลายเท่าตัว

อันที่จริงแล้ว Apache ก็ไม่ได้มีแต่ข้อเสียขนาดนั้น ข้อดีของ Apache คือเหมือนเป็น All-in-one machine ติดตั้งตัวเดียว ทำงานได้ทุกอย่าง ถ้ามีคนใช้งานไม่มาก เราจะแทบไม่ต้องไปยุ่งกับการปรับ config เลยด้วยซ้ำไป เพียงแต่ว่าเมื่อเราต้องการให้รองรับการใช้งานหนักๆ ได้ เราจึงจำเป็นต้องใช้ “ผู้เชี่ยวชาญเฉพาะด้าน” ซึ่งในที่นี้คือ fast-cgi นั่นเอง และจะมี php-fpm คอยควบคุมสั่งการอีกทอดหนึ่งซึ่งแน่นอนว่าการ config เพื่อใช้งานจึงยุ่งยากกว่า แต่ก็แลกมากับประสิทธิภาพที่น่าพอใจ

ตัวอย่าง file config ของ nginx จาก server เกมของผม (ก่อนอื่นออกตัวก่อนครับว่าผมเองก็พึ่งหัดใช้ nginx เหมือนกัน อาจยังไม่ใช่ config ที่ดีที่สุดในตอนนี้ แต่เป็นเพียง config เบื้องต้นให้ใช้งานได้ครับ) ซึ่งอยู่ที่ /opt/local/etc/nginx/nginx.conf (เครื่องอื่นอาจเก็บอยู่ที่อื่นนะครับ ลองหาดู)

user www www;
worker_processes 1;

events {
# After increasing this value You probably should increase limit
# of file descriptors (for example in start_precmd in startup script)
worker_connections 1024;
}

http {
include /opt/local/etc/nginx/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"';

#access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

#gzip on;

server {
listen 80;
server_name kdq-web;

#charset koi8-r;

#access_log /var/log/nginx/host.access.log main;

location / {
root /home/jill/web/public;
index index.php index.html index.htm;
# file doesn't exist, let CI handle it
if (!-f $request_filename) {
rewrite ^(.*)/maingame/(.*)$ /maingame/index.php?/$2 last;
}
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root share/examples/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
root /home/jill/web/public;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/jill/web/public$fastcgi_script_name;
include /opt/local/etc/nginx/fastcgi_params;
}
}

server {
listen 443;
server_name kdq-web;

ssl on;
ssl_certificate /opt/local/etc/openssl/levelup/self.crt;
ssl_certificate_key /opt/local/etc/openssl/levelup/self.key;

ssl_session_timeout 5m;

ssl_protocols SSLv2 SSLv3 TLSv1;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;

location / {
root /home/jill/web/public;
index index.php index.html index.htm;
# file doesn't exist, let CI handle it
if (!-f $request_filename) {
rewrite ^(.*)/maingame/(.*)$ /maingame/index.php?/$2 last;
}
}

location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/jill/web/public$fastcgi_script_name;
fastcgi_param HTTPS on;
include /opt/local/etc/nginx/fastcgi_params;
}
}
}

จุดที่น่าสนใจคือตรงนี้ครับ ขออธิบายเป็นจุดๆ นะครับ

1. listen ใช้บอกว่าจะให้ดัก port อะไร ถ้าเว็บทั่วไป (http) ก็ 80 ถ้า secure (https) ก็ 443 ครับ

location / {
root /home/jill/web/public;
index index.php index.html index.htm;
# file doesn't exist, let CI handle it
if (!-f $request_filename) {
rewrite ^(.*)/maingame/(.*)$ /maingame/index.php?/$2 last;
}
}

2. ตรงนี้คือ directory root ตั้งต้นของเว็บผมอยู่ที่ /home/jill/web/public และมีการใช้งานระบบ URI Routing ของ Codeigniter ดังนั้น ผมต้องเซ็ต if ไว้ด้วยว่าถ้าหาไฟล์ไม่เจอให้ rewrite ใหม่ไปยัง maingame/index.php แทน (นับจาก root ที่ตั้งเมื่อกี้) ซึ่งเป็นไฟล์เริ่มต้อนของ Codeigniter และย้าย parameter ที่อยู่ต่อท้าย /maingame/ ไปอยู่ด้านหลัง index.php?/ แทน (ต้องมี ? ไม่อย่างนั้น nginx ก็จะยังหา file ไปเจออยู่อย่างนั้น และ rewrite ใหม่เรื่อยๆ จนเจอ infinite loop) ซึ่งตรงนี้ใช้ syntax แบบ regular expression ครับ

location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/jill/web/public$fastcgi_script_name;
fastcgi_param HTTPS on;
include /opt/local/etc/nginx/fastcgi_params;
}

3. ตรงนี้คือ config สำหรับส่งค่าไปยัง fast-cgi ครับ (port มาตรฐานคือ 9000 อาจแก้ได้ตามต้องการ) ที่น่าสนใจคือ fastcgi_param ใช้ส่งค่าเข้า $_SERVER ครับ ตามชื่อที่ต้องการเลย ถ้าเป็น https ผมบังคับส่ง $_SERVER['HTTPS'] ด้วยเพราะ Apache เคยมีตัวแปรนี้ที่ผมใช้งานอยู่ แต่ใน nginx ต้องกำหนดเพิ่มเองครับ และสุดท้ายคือ include parameter สำคัญอื่นๆ เข้ามาจากไฟล์ fastcgi_params ครับ ในไฟล์ก็มีเนื้อหาด้านล่าง (copy ไปใช้ได้เลย ถ้าเครื่องของคุณยังไม่มี)

fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;

# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;

4. จะมี ssl on ให้เปิดใช้ https และ ssl_certificate, ssl_certificate_key เอาไว้กำหนด path ไปยัง cer และ key ตามลำดับครับ

เป็นอันเสร็จสิ้นสำหรับ nginx ส่วน php-fpm ซึ่งเอาไว้ใช้สร้าง process ของ fast-cgi ขึ้นโดยอัตโนมัติและสามารถปรับเพิ่ม-ลดได้ตามปริมาณคนเข้าจริงๆ ของ server (ดีกว่า spawn-fcgi มากครับ ทั้งเรื่องความเสถียร และการ config ให้ใช้งานได้ ซึ่งตัว spawn-fcgi จะไม่สามารถเพิ่ม-ลดจำนวน process ที่ใช้งานตามปริมาณคนเข้าจริงได้) ถ้าไม่มี php-fpm แล้วละก็ หาก process fast-cgi(ที่เราสร้างเองจากการรันใน shell) ตายลงไปละก็ ก็จะไม่มี process มาคอยรับงานจาก nginx ไปประมวลผล php จริงๆ ทำให้ server พังนั่นเองครับ php-fpm นั้นจะติดมากับ php5.3 อยู่แล้ว (หรือถ้าไม่มีก็สามารถ install ตามทีหลังได้ง่าย) หากเป็น php5.2 ก็ยังใช้ php-fpm ได้อยู่เพียงแต่ว่าต้อง build, compile จาก source รวมกับ source php ตัวหลักเอาเองครับ ใครอยากอ่านรายละเอียดข้อดี php-fpm เพิ่มตามอ่านได้ที่ php-fpm.org

config ของ php-fpm จะอยู่ที่ /opt/local/etc/php-fpm.conf (เฉพาะของเครื่องผมอีกเช่นกัน เครื่องคุณอาจอยู่ที่อื่น) config ที่น่าสนใจมีดังนี้

  1. listen = 127.0.0.1:9000 อันนี้คือใส่ให้เหมือน nginx นั่นแหละครับ
  2. pm – ให้เลือกว่าเป็น static หรือ dynamic ซึ่ง static คือตายตัว dynamic คือปรับตาม idle process ที่มี (ควรเลือก dynamic ครับ)
  3. pm.max_children – ให้แตก child ได้สูงสุดไม่เกินกี่ตัวในเวลาเดียวกัน
  4. pm.start_servers – เริ่มต้นมามี child กี่ตัว
  5. pm.min_spare_servers – จำนวน child ที่อยู่ใน idle state ขั้นต่ำ
  6. pm.max_spare_servers – จำนวน child ที่อยู่ใน idle state ไม่เกินกี่ตัว (ถ้าเกินจะ auto kill ทิ้ง)
  7. pm.max_requests – ใช้จำกัดไม่ให้ process นึงกิน memory เกินเท่าไหร่ ใช้กรณีเกิดปัญหา memory leak จากตัว php แต่ยังหาสาเหตุไม่เจอ ให้ auto kill ถ้า request เยอะเกินไปก่อน

สุดท้ายคือ code ที่ต้องแก้ไขใน config.php ของ codeigniter เพื่อให้สามารถอ่าน URI ที่ส่งต่อมาจาก nginx ได้ครับ ดังนี้

$config['uri_protocol'] = "REQUEST_URI";
$index_page = $config['index_page'];
if (!empty($index_page) && strpos($_SERVER['QUERY_STRING'], $index_page) === FALSE)
$script_filename = str_replace($index_page, '', $_SERVER['SCRIPT_NAME']);
else
$script_filename = $_SERVER['SCRIPT_NAME'];
$request_uri = $_SERVER['QUERY_STRING'];
if ($request_uri[strlen($request_uri)-1] != '/')
$request_uri .= '/';

$request_uri = str_replace($script_filename, '', $request_uri);
list($_SERVER['REQUEST_URI']) = explode("&",$request_uri,2);

เรียบร้อยครับสำหรับการ config nginx + php-fpm สำหรับ Codeigniter วันนี้เท่านี้ละครับ :)

Enjoy this article?

Consider subscribing to our RSS feed!

2 ความเห็น

  1. dekdar
    13:36 on May 31st, 2012

    ติดตามอยู่ครับ เกี่ยวกัย nginx ข้อมูลดีมากๆ ครับ

  2. ลงประกาศฟรี
    12:09 on March 17th, 2014

    เอาไปใช้ยังไงคับ

ใส่ความเห็น

RSS feed for comments on this post