HomeThủ Thuật ChungDatabaseHướng dẫn Nginx – Phần 1: Các khái niệm cơ bản
Last updated
Last updated
Sofsog 10/05/2018 Database, Server, Wordpress Không có phản hồiĐánh giá của người xem[Phiếu bầu: 1 Xếp hạng: 4/5]
Bài này hoặc loạt bài này (nếu mình có thời gian) mình thực hiện bằng cách vừa học vừa viết lại, nguồn từ internet, chủ yếu mình lược dịch lại từ các trang web nước ngoài. Mục đích vừa để chia sẻ vừa để lại lưu trữ (sau này mình quên có thể vào để xem lại). Ok, dài dòng đủ rồi, vào bài thôi:
Xem thêm bài liên quan:
Hướng dẫn Nginx – Phần 2: Performance
Nếu bạn tìm tới bài viết này chắc cũng đã hiểu sơ qua về Nginx. Nginx ban đầu được tạo ra như một máy chủ web để giải quyết vấn đề C10k . Và như một máy chủ web (web server) , nó có thể phục vụ dữ liệu của bạn với tốc độ gần bằng ánh sáng. Nhưng Nginx không chỉ là một máy chủ web. Bạn có thể sử dụng nó như một reverse proxy, làm cho việc tích hợp dễ dàng với các máy chủ ngược dòng chậm hơn (slower upstream servers) (như Unicorn, hoặc Puma). Bạn có thể phân phối lưu lượng truy cập của bạn một cách hoàn hảo (load balancer: cân bằng tải), stream media, thay đổi kích thước ảnh khi đang di chuyển, cache nội dung, và nhiều thứ nữa.
Huong-dan-nginx-sofsog.com
Kiến trúc cơ bản của nginx bao gồm một quy trình tổng (Master process) và các công nhân (workers) của nó. Master process phải đọc file cấu hình và duy trì những “process worker”, trong khi các workers thực hiện xử lý các yêu cầu thực tế.
Để bắt đầu nginx, bạn chỉ cần gõ:
1 | sudo nginx |
Trong lúc nginx đang chạy, bạn có thể quản lý nó bằng cách gửi các lệnh thích hợp:
1 | sudo nginx -s signal_thich-hop |
MỘT SỐ SIGNAL CÓ SẴN:
stop
: shutdown nhanh nginx
quit
: shutdown một cách cẩn thận, duyên dáng (^_^) (Chờ đợi các workers hoàn thành các tiến trình của chúng rồi mới tắt)
reload
: Tải lại file cấu hình
reopen
: Mở lại các file nhật ký (file log)
Theo mặc định, file cấu hình của nginx có thể được tìm thấy trong các đường dẫn sau:
/etc/nginx/nginx.conf
,
/usr/local/etc/nginx/nginx.conf
, hoặc
/usr/local/nginx/conf/nginx.conf
TRONG FILE CẤU HÌNH NÀY BAO GỒM:
directive: tùy chọn chứa tên và thông số (name và parameters), phải được kết thúc bằng dấu chấm phẩy.
1 | gzip on; |
context: Đây là nơi để bạn có thể khai báo các directive (tương tự như phạm vi “scope” trong các ngôn ngữ lập trình)
123456789 | worker_processes 2; # directive trong global context (chỉ thị trong ngữ cảnh chung) http { # http context (ngữ cảnh http) gzip on; # directive trong http context server { # server context (ngữ cảnh server) listen 80; # directive trong server context }} |
Bạn phải cẩn thận khi sử dụng một directive trong nhiều context, vì mô hình kế thừa (inheritance model) là khác nhau đối với các directive khác nhau. Có 3 loại directive, mỗi loại lại có mô hình kế thừa (inheritance model) riêng.
NORMAL:
Directive này chỉ có một giá trị cho mỗi context. Ngoài ra, nó chỉ có thể được định nghĩa một lần trong context. Các subcontext (ngữ cảnh con) có thể ghi đè directive cha (parent directive), nhưng việc ghi đè này chỉ hợp lệ trong đã subcontext nêu.
123456789101112 | gzip on;gzip off; # Không hợp lệ, vì chỉ có một directive trong cùng một context. server { location /downloads { gzip off; } location /assets { # gzip được bật ở đây }} |
ARRAY:
Việc thêm nhiều directive trong cùng một context sẽ thêm vào các giá trị, thay vì ghi đè chúng hoàn toàn. Việc định nghĩa một directive trong một subcontext sẽ ghi đè tất cả các giá trị cha trong subcontext đã cho.
12345678910 | error_log /var/log/nginx/error.log;error_log /var/log/nginx/error_notive.log notice;error_log /var/log/nginx/error_debug.log debug; server { location /downloads { # Dòng dưới này sẽ ghi đè toàn bộ các directive đã cho bên trên error_log /var/log/nginx/error_downloads.log; }} |
ACTION DIRECTIVE (CHỈ THỊ HÀNH ĐỘNG)
Hành động (action) là loại directive thay đổi mọi thứ. Hành vi kế thưa chúng sẽ phụ thuộc vào mô-đun.
Ví dụ, trong trường hợp rewrite directive, mỗi directive phù hợp sẽ được thực hiện:
12345678 | server { rewrite ^ /foobar; location /foobar { rewrite ^ /foo; rewrite ^ /bar; }} |
Nếu user nạp: /sample
:
server rewrite được thực hiện , rewriting từ /sample
, thành /foobar
location /foobar
khớp (subcontext)
location rewrite ở dòng đầu tiền sẽ được thực hiện, rewriting từ /foobar
, thành /foo
location rewrite ở dòng thứ hai được thực hiện tiếp sau đó, rewriting từ /foo
, thành /bar
Một ví dụ khác với return directive:
123456 | server { location / { return 200; return 404; }} |
Trong trường hợp bên trên, trạng thái 404 được trả về ngay lập tức.
Bên Trong nginx, ban có thể chỉ định nhiều máy chủ ảo, mỗi máy chủ được mô tả bằng server { } context (ngữ cảnh server)Inside nginx, you can specify multiple virtual servers, each described by a server { } context.
1234567891011121314151617181920 | server { listen *:80 default_server; server_name sofsog.com; return 200 "Hello from sofsog.com";} server { listen *:80; server_name server2.com; return 200 "Hello from server2.com";} server { listen *:81; server_name server3.com; return 200 "Hello from server3.com";} |
Đoạn cấu hình trên cung cấp cho nginx hiểu cách xử lý các yêu cầu gửi đến. Nginx trước tiên sẽ kiểm tra directive listen để kiểm tra xem máy chủ ảo (virtual server) nào đang lắng nghe trên sự kết hợp giữa IP:port đã cho. Sau đó, giá trị từ directive server_name được kiểm tra dựa trên “Host header” lưu trữ tên miền của máy chủ.Nginx sẽ chọn máy chủ ảo theo thứ tự sau:
Danh sách server trên IP:port, với một server_name
directive phù hợp (match)
Danh sách server trên IP:port, với default_server
được đánh dấu
Danh sách server trên IP:port, đầu tiên được định nghĩa
Nếu không có kết quả phù hợp, từ chối kết nối.
Trong kết ví dụ cấu hình bên trên, kết quả sẽ là:
12345 | Gửi yêu cầu đến: server2.com:80 => "Hello from server2.com"Gửi yêu cầu đến www.server2.com:80 => "Hello from sofsog.com"Gửi yêu cầu đến server3.com:80 => "Hello from sofsog.com"Gửi yêu cầu đến server2.com:81 => "Hello from server3.com"Gửi yêu cầu đến server3.com:81 => "Hello from server3.com" |
SERVER_NAME
DIRECTIVEserver_name directive chấp nhận nhiều giá trị. Nó cũng xử lý các ký tự đại diện và biểu thức thông dụng. (wildcard và regular expressions)
1234 | server_name sofsog.com www.sofsog.com; # exact matchserver_name *.sofsog.com; # wildcard matchingserver_name sofsog.*; # wildcard matchingserver_name ~^[0-9]*\.sofsog\.com$; # regular expressions matching |
Khi có sự không rõ ràng, nginx sử dụng thứ tự sau:
Tên chính xác
Tên ký tự đại diện dài nhất bắt đầu bằng dấu sao, vd: “*.example.org”
Tên ký tự đại diện dài nhất kết thúc bằng dấu sao, vd: “mail.*”
Biểu thức thông dụng (regular expressions) khớp đầu tiên (Theo thứ tự xuất hiện trong file cấu hình)
Nginx sẽ lưu trữ 02 hash tables (bảng băm) là: tên chính xác, tên ký tự đại diện dài nhất bắt đầu bằng dấu sao, tên ký tự đại diện dài nhất kết thúc bằng dấu sao. Nếu kết quả không nằm trong bất kỳ bảng nào, các biểu thức thông đụng (regular expressions) sẽ được kiểm tra tuần tự.Gần ghi nhớ rằng:
1 | server_name .sofsog.com; |
Là viết tắt của:
1 | server_name sofsog.com www.sofsog.com *.sofsog.com; |
Nhưng giữa 2 cách viết trên vẫn có sự khác biệt là: .sofsog.com được lưu trữ trong bảng thứ hai, có nghĩa là nó chậm hơn một chút so với khai báo rõ ràng (sofsog.com www.sofsog.com *.sofsog.com)
LISTEN
DIRECTIVETrong hầu hết các trường hợp, bạn sẽ thấy rằng listen
directive chấp nhận các giá trị IP:port
12345678 | listen 127.0.0.1:80;listen 127.0.0.1; # Sẽ mặc định cổng 80 listen *:81;listen 81; # mặc định tất cả các IP (tên miền) được sử dụng listen [::]:80; # IPv6 addresseslisten [::1]; # IPv6 addresses |
Tuy nhiên, bạn cũng có thể chỉ định UNIX-domain sockets
1 | listen unix:/var/run/nginx.sock; |
Bạn thậm chí có thể sử dụng hostname
12 | listen localhost:80;listen sofsog.com:80; |
Điều này nên được sử dụng thận trọng, vì tên máy chủ (hostname) có thể không được giải quyết khi khởi chạy nginx, khiến nginx không thể liên kết trên TCP socket đã cho.
Với tất cả kiến thức bên trên, chúng ta có thể tạo và hiểu cấu hình tối thiểu cần thiết để chạy nginx
123456789101112 | # /etc/nginx/nginx.conf events {} # Bối cảnh events cần được xác định để xem xét cấu hình hợp lệ http { server { listen 80; server_name sofsog.com www.sofsog.com *.sofsog.com; return 200 "Hello"; }} |
ROOT
, LOCATION
, VÀ TRY_FILES
DIRECTIVEROOT
DIRECTIVEroot
directive khai báo thư mục gốc cho các yêu cầu, cho phép nginx ánh xạ yêu cầu gửi đến (incoming request) vào file hệ hống.
12345 | server { listen 80; server_name sofsog.com; root /var/www/sofsog.com;} |
Điều này cho phép nginx trả lại nội dung máy chủ theo yêu cầu
12 | sofsog.com:80/index.html # returns /var/www/sofsog.com/index.htmlsofsog.com:80/foo/index.html # returns /var/www/sofsog.com/foo/index.html |
LOCATION
DIRECTIVElocation
directive đặt cấu hình tùy thuộc vào URI được yêu cầu.
location [modifier] path
123 | location /foo { # ...} |
Khi không có modifier nào được chỉ định, path được coi là tiền tố (prefix), và mọi thứ phía sau nó đều hợp lệ. Như ví dụ bên trên, kết quả bên dưới này đều hợp lệ (match):
12345 | /foo/fooo/foo123/foo/bar/index.html... |
Ngoài ra, nhiều location
directives có thể được sử dụng trong một context nhất định.
12345678910111213 | server { listen 80; server_name sofsog.com; root /var/www/sofsog.com; location / { return 200 "root"; } location /foo { return 200 "foo"; }} |
1234 | sofsog.com:80 / # => "root"sofsog.com:80 /foo # => "foo"sofsog.com:80 /foo123 # => "foo"sofsog.com:80 /bar # => "root" |
Nginx cũng cung cấp vài modifiers, có thể được sử dụng kết hợp với location. Những modifiers đó tác động đến khối location nào mà được sử dụng, vì mỗi modifier, đã được chỉ định ưu tiên.
1234 | = - Exact match^~ - Preferential match~ && ~* - Regex matchno modifier - Prefix match |
Nginx đầu tiên sẽ kiểm tra bất kỳ “exact match” nào. Nếu nó không tìm thấy, nó sẽ tìm kiếm những preferential. Nếu match này cũng thất bại, các regex match sẽ được kiểm tra theo thứ tự xuất hiện của chúng. Nếu mọi thứ không thành công, thì giá trị tiền tố cuối cùng (prefix match) sẽ được sử dụng.
12345678910111213141516171819 | location /match { return 200 'Prefix match: matches everything that starting with /match';} location ~* /match[0-9] { return 200 'Case insensitive regex match';} location ~ /MATCH[0-9] { return 200 'Case sensitive regex match';} location ^~ /match0 { return 200 'Preferential match';} location = /match { return 200 'Exact match';} |
12345 | /match # => 'Exact match'/match0 # => 'Preferential match'/match1 # => 'Case insensitive regex match'/MATCH1 # => 'Case sensitive regex match'/match-abc # => 'Prefix match: matches everything that starting with /match' |
TRY_FILES
DIRECTIVEDirective này sẽ thử các đường dẫn (path) khác nhau, trả về bất cứ gì được tìm thấy
1 | try_files $uri index.html =404; |
Ví dụ bên trên: đối với yêu cầu /foo.html, nó sẽ cố gắng trả về các file theo thứ tự sau:
$uri ( /foo.html )
index.html
Nếu không tìm thấy: 404.
Điều thú vị là, nếu chúng ta định nghĩa try_files trong server context và sau đó xác định location khớp với tất cả các yêu cầu, try_files của chúng ta sẽ không được thực hiện. Điều này xảy ra vì try_files trong server context định nghĩa vị trí giả (pseudo-location) của riêng nó, đó là location kém cụ thể nhất có thể. Do đó, định nghĩa location / sẽ cụ thể hơn vị trí giả (pseudo-location) của chúng ta.
123456 | server { try_files $uri /index.html =404; location / { }} |
Do đó, bạn nên tránh try_files trong server context :
12345 | server { location / { try_files $uri /index.html =404; }} |
Cảm ơn các bạn đã đọc đến đây. Loạt bài này sẽ không thể có được nếu không có số lượng lớn tài nguyên được tìm thấy trên internet. Dưới đây là một số trang web tuyệt vời mà mình thấy đặc biệt hữu ích khi viết loạt bài này:
Ilya Grigorik blog, và cuốn sánh của anh ta: High Performance Browser Networking
Mình sẽ rất vui khi thấy một số phản hồi hoặc thảo luận, vì vậy, vui lòng để lại nhận xét hoặc liên lạc lại bằng bất kỳ cách nào thuận tiện! Bạn có thích hướng dẫn này không? Bạn có gợi ý gì về chủ để tiếp theo không? Hoặc có thể bạn phát hiện ra một số lỗi? Hãy cho mình biết nhé và hẹn gặp lại bạn lần sau!
Như đã hứa trong phần 1, trong phần này, tôi sẽ tập trung viết về cấu hình vhost cho nginx và cấu hình php-fpm để vhost xử lý được file php.
Vhost viết tắt của virtual host là kỹ thuật cho phép nhiều website có thể chia sẻ chung một IP. Thuật ngữ này bắt nguồn từ apache. Tác giả nginx thì lại thích gọi nó là Server block. Hơi dị nhỉ. Cá nhân tôi vẫn thích cách gọi truyền thống hơn. Trước khi có dịch vụ VPS thì share hosting phát triển rất mạnh. Các share hosting tận dụng các kỹ thuật này để cung cấp chỗ đặt của nhiều website trên webserver.
Nhưng làm cách nào webserver hiểu được request cần đến đâu để đẩy vào website phù hợp ?
Bản thân webserver tất nhiên biết được các host mà nó có. Phía client, khi người dùng nhập một địa chỉ ví dụ www.vhost.example.com domain này được resolve thành IP đến webserver chứa site đó. Bản thân trong http request bắn đến webserver có một trường Host. Ở đây trường này có giá trị www.vhost.example.com (Các bạn capture http request bằng wireshark sẽ thấy) Webserver sẽ đọc giá trị trường này và map nó với server_name của các host nó có. Nếu khớp nó sẽ đẩy request đến vhost đó.
Các file sẽ cấu hình /usr/local/nginx/conf/nginx.conf - Đây là file cấu hình chung của nginx /usr/local/nginx/conf.d/vhost.example.com.conf - Đây chính là file cấu hình cho vhost
vi /usr/local/nginx/conf/nginx.conf
Hầu hết config trong file này giống như file măc định của nginx. Tôi chỉ sửa đôi chút:
Chuyển log vào /var/log/nginx không để trong thư mục cài đặt
Chuyển pid file vào /var/run không để trong thư mục cài đặt
Bỏ comment dòng log_format
Sử dụng đường dẫn tuyệt đối hoàn toàn
Thêm dòng include /usr/local/nginx/conf.d/*.conf;
Thêm dòng user nginx;
Tạo sẵn một directory để chứa log nginx
Tạo directory để chứa config vhost
mkdir -p /usr/local/nginx/conf.d/
vi /usr/local/nginx/conf.d/vhost.example.com.conf
Tạo document root cho vhost vhost.example.com
Chuẩn bị một file html:
Trên client, tôi cấu hình trỏ host đơn giản như sau.
Trong thực tế, để website có thể được truy cập bởi người dùng trên internet, tôi cần đăng ký IP wan cho webserver, một domain name và cấu hình domain name server để trỏ domain name đã đăng ký về IP wan của webserver.
restart nginx sau đó trên browser tôi gõ: http://vhost.example.com/test.html trình duyệt bèn trả lại:
Bản thân nginx không hỗ trợ xử lý php. Việc xử lý php sẽ do một service khác đảm nhận. Nginx sẽ forward request đến service này và nhận kết quả về. Service này là php-fpm.
Trước hết tôi cần cài php-fpm
yum instal php-fpm
Các file sẽ cấu hình: /etc/php-fpm.conf /etc/php-fpm.d/www.conf /usr/local/nginx/conf.d/vhost.example.com.conf
vim /etc/php-fpm.conf
vim /etc/php-fpm.d/www.conf
Tôi chỉ sửa ba chỗ so với config mặc định của www.conf
listen = /tmp/php_fpm.sock
listen.owner = nginx
listen.group = nginx
Cuối cùng bổ sung vào cuối file /usr/local/nginx/conf.d/vhost.example.com.conf
Mục đích của đoạn cấu hình trên là: Toàn bộ các request đến các file có tận cùng là php sẽ được đẩy đến unix socket mà php-fpm lắng nghe để biên dịch. Chính vì thế listen.owner và listen.group của unix socket tôi để là nginx:nginx
Chuẩn bị một file php để test
Kết thúc phần 2, trong phần tiếp, tôi sẽ viết về cách phân quyền thư mục chứa web, basic authentication và giới hạn truy cập đến vhost theo IP.
restart nginx và php-fpm service sau đó trên browser tôi gõ: http://vhost.example.com/test.php trình duyệt bèn trả lại: