Thứ Hai, 23 tháng 6, 2014

Nginx


Link tham khảo:







I -  Giới thiệu tổng quát NGINX
Theo wiki thì NGINX nó đảm nhiệm nhiều chức năng lắm: "Nginx (pronounced "engine-x") is an open source reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer, HTTP cache, and a web server (origin server)"
Tuy nhiên, do mình tìm hiểu NGINX để áp dụng vào dự án mình đang làm, cho nên theo mức độ hiểu biết của mình thì NGINX đóng vai trò như một vị trí "tiền tiêu". Tức là, nó đứng ra nhận các request trước, sau đó xử lý, xử lý ở đây cụ thể là, những request nào yêu cầu nội dung tĩnh (static) thì NGINX sẽ lấy trực tiếp những file cache đã lưu sẵn để trả về cho client, những request nào yêu cầu nội dung động (dynamic) thì sẽ forward request đó đến proxied server, sau đó nhận kết quả trả về từ proxied server rồi trả về cho client.
Kiến trúc NGINX (link tham khảo tại đây)
Đi sơ qua khái niệm NGINX để dễ hình dung. Dưới đây sẽ là phần cấu hình cụ thể NGINX.

II - Web Server
Khái niệm:
Trước tiên, cần tìm hiểu các khái niệm Directive Context. Trong NGINX, directive là khái niệm chỉ những từ khóa xác định các chỉ thị / lệnh cần thực hiện. Một số directives chính được gọi là Context: events, http, server, location. Những directives nào nằm ngoài các contexts trên được gọi là directives nằm trong main context.
Ví dụ về một full configuration trong nginx:
user nobody; # directive thuộc main context
events {
 #config cho events
}
http {
 #config cho http và cho tất cả các virtual servers (xem thêm bên dưới mục virtual server) bên trong nó
 server {
   #config cho virtual server 1
   location /one {
      #config xử lý URIs dạng '/one'
   }
   location /two {
      #config xử lý URIs dạng '/two'
   }
  }
  server {
    #config cho virtual server 2
  }
}
Virtual server:
- Để xử lý http request, cần ít nhất 1 virtual server
- Trong 1 http context có thể có nhiều virtual server.
- Trong 1 virtual server cần 1 directive listen để xác định IP address và Port cần lắng nghe.
- Ví dụ: virtual server sau sẽ lắng nghe địa chỉ 127.0.0.1 tại port 8080
server {
  listen 127.0.0.1:8080;
}
- Nếu thiếu port -> lắng nghe port standard (do mình cấu hình)
- Nếu thiếu address -> lắng nghe tất cả các địa chỉ address.
- Nếu thiếu listen directive:
 + Standard port: 80/tcp.
 + Default port: 8000/tcp.
- Nếu nhiều virtual server sử dụng chung IP và Port thì dựa vào directive server_name để xác định cần vào virtual server nào. (Ở đây mình chỉ đưa ra mức khái niệm, không nói sâu về mức server_name này).

Location: xác định bằng directive location
- Bên trong mỗi virtual server, có thể cấu hình nhiều dạng URIs có thể được điều hướng đến nhiều proxy hoặc lấy file từ file system.
- Tham số của location có thể là prefix string (mình tạm gọi tắt PS) hoặc regular expression (mình tạm gọi tắt RE) (đánh dấu bằng dấu ngã (~)).
- Để xử lý URI sẽ match theo PS hay RE thì luồng xử lý chỗ location này như sau:
  1. Dựa trên URI truyền vào, kiểm tra qua tất cả PS.
  2. Ký tự "=" dùng để mô tả cho PS. Nếu tìm thấy PS nào chính xác với URI và có ký tự "=" đứng trước thì quá trình tìm kiếm sẽ kết thúc.
  3. Ký tự "^~" cũng dùng để mô tả cho PS. Nếu tìm thấy PS nào có độ trùng dài nhất so với URI thì sẽ lấy PS đó và kết thúc tìm kiếm (không kiểm tra qua RE).
  4. Lưu lại PS nào có độ trùng dài nhất.
  5. Kiểm tra qua RE.
  6. Dừng lại ở RE nào đúng nhất được tìm thấy đầu tiên. Kết thúc tìm kiếm.
  7. Nếu không có RE nào thỏa mãn, lấy PS được lưu lại tại bước 4.
Ví dụ: Nếu URI "/" được truy suất nhiều (vào trang chủ), có thể cấu hình như sau để quá trình xử lý location nhanh nhất.
location = / {
 # config
}
Một ví dụ khác về cấu hình nhiều location trong 1 virtual server. Trong ví dụ bên dưới, tất cả URI không bắt đầu bằng /images/ sẽ được chuyển qua 1 proxy server (http://www.example.com.vn) nhớ directive proxy_pass. Những URI nào có dạng như "/images/example.png" sẽ được NGINX trả lại file "/data/images/example.png" nằm trên file system.
server {
 location / {
   proxy_pass http://www.example.com.vn;
 }
 location /images/ {
  root /data;
 }
}

III - Reverse Proxy
Chuyển 1 Request đến 1 Proxied Server
- NGINX chuyển request đến 1 proxied server, nhận lại response và gửi trả về cho client.
- NGINX hỗ trợ HTTP server và non-HTTP server.
- NGINX hỗ trợ các loại protocols sau: FastCGI, uwsgi, SCGI và memcached.
Ví dụ về việc chuyển 1 request đến 1 HTTP proxied server bằng proxy_pass directive:
localtion /some/path/ {
  proxy_pass http://www.example.com/link/;
}
Trong ví dụ trên, địa chỉ proxied server http://www.example.com theo sau bởi URI /link/. Điều này có nghĩa là nếu URI của proxeid server (ở ví dụ này là /link/) sẽ thay thế cho tham số URI của location (ở ví dụ này là /some/path/). Cho nên, nếu 1 request có dạng /some/path/page.html sẽ được chuyển thành http://www.example.com/link/page.html.
- Để chuyển đến 1 non-HTTP proxied server, NGINX sử dụng các directive tương ứng:
  • fastcgi_pass cho FastCGI server.
  • uwsgi_pass cho uwsgi server.
  • scgi_pass cho SCGI server.
  • memcached_pass cho memcached server.
III - NGINX Content Caching
Phần này hướng dẫn cách cấu hình caching (cache lại những responses từ những proxied servers) trong NGINX. Khi bật tính năng caching, NGINX sẽ lưu lại kết quả responses trên disk và sử dụng chúng để respond lại cho clients vào những lần sau mà không cần phải gửi request đến proxied server.
Bật tính năng cache
- Khai báo proxy_cache_path directive ở cấp http context. Sau đó, khai báo proxy_cache directive bên trong context muốn thực hiện cache. Ví dụ:
http {
  ...
  proxy_cache_path /data/nginx/cache keys_zone=one:10m;
  server {
    proxy_cache one;
    location / {
       proxy_pass http://localhost:8000;
    }
  }
}
- proxy_cache_path có 2 tham số bắt buộc:
  1. Đường dẫn file system nơi sẽ lưu responses. Trên ví dụ trên là "/data/nginx/cache".
  2. Tên và kích thước của vùng shared memory zone được xác định bởi từ khóa keys_zone. Trong ví dụ trên, tên vùng shared memory zone là "one", kích thước là 10m (One megabyte zone can store about 8 thousand keys - theo giải thích của NGINX). Tên vùng shared memory zone này cũng phải được chỉ định tại proxy_cache directive
- Kích thước của vùng lưu trữ cache response có thể được giới hạn bởi tham số max_size. Tuy nhiên, thông thường, kích thước của vùng lưu trữ này có thể vượt quá giới hạn tạm thời. Sau đó sẽ có một tiến trình gọi là cache manager process sẽ đi kiểm tra và loại bỏ những vùng cached responses ít dùng đến nhất.

Caching Processes
Có 2 loại NGINX process: cache loader và cache manager.
  1. Cache manager sẽ chạy định kì để xóa bớt những cached responses ít dùng nhất khi dung lượt vùng lưu trữ vượt quá max_size cho phép.
  2.  Cache loader chỉ active 1 lần duy nhất, ngay khi NGINX start. Tiến trình này sẽ load những thông tin metadata từ những lần cache dữ liệu trước đó vào vùng shared memory zone. Đặt điểm của process cache loader:
  • Phải load tất cả metadata 1 lần nên sẽ làm chậm hệ thống.
  • Do vậy, process sẽ chia làm nhiều lần (NGINX gọi là iteration) thực hiện và được cấu hình bởi tham số loader_threshold (đơn vị miliseconds), loader_files và loader_sleeps.
  • Loader_threshold là khoảng thời gian thực hiện tối đa của 1 iteration.
  • Loader_files là số lượng items tối đa (default là 100) cần load trong 1 iteration.
  • Loader_sleeps là thời gian dừng giữa 2 iteration.
Ví dụ về cấu hình proxy_cache_path:
proxy_cache_path /data/nginx/cache keys_zone=one:10m loader_threshold=300 loader_files=200;
Specifying Which Requests to Cache: cấu hình những request nào sẽ được cache
- Mặc định, NGINX sẽ cache tất cả các responses có phương thước GET và HEAD trong lần đầu tiên nhận được từ proxied server.
- NGINX phân biệt các request dựa vào các key (có thể cấu hình thông qua proxy_cache_key directive). Khi 2 request có cùng key, NGINX sẽ dùng cùng 1 cached response để gửi lại client.
- Có thể cấu hình số lượng request có key trùng nhau để bắt đầu cache (mặc định là 1), thông qua proxy_cache_min_uses directive.

Limiting or Bypassing Caching: giới hạn hoặc bỏ qua caching
- Mặc định, thời gian lưu trữ response là không giới hạn. Trừ khi tiến trình cache manager chạy và loại bỏ những response ít dùng nhất trong trường hợp vượt ngưỡng lưu trữ (cấu hình tham số max_size đã đề cập ở trên)
- Tuy nhiên, có thể cấu hình thời gian (tính bằng phút) lưu trữ response bằng proxy_cache_valid directive.
- Ngoài ra còn có cơ chế proxy_cache_bypassproxy_no_cache (có thể tự tìm hiểu thêm).

Restricting Access
Nginx cung cấp quản lý truy cập bằng 2 cách:
  • IP address
  • HTTP authentication
IP address
Sử dụng directive allow hoặc deny. Ví dụ:
location / {
  allow 192.168.1.1/24;
  deny all;
}

Authentication
Sử dụng directive auth_basic auth_basic_user_file. Ví dụ:
location / {
  auth_basic "closed website";
  auth_basic_user_file /etc/nginx/authfile;
}
Trong đó /etc/nginx/authfile là file chứa username/password chứng thực. Tạo authfile thông qua lệnh htpasswd.
cd /etc/nginx
htpasswd -c -m authfile <username>
Chú ý, nhớ set lại quyền cho file authfile để thực hiện được.

Sau khi cài đặt xong, muốn truy xuất file của nginx, cần phải nhập username / password.
Ví dụ, mình có file test.txt nằm trong thư mục nginx (địa chỉ 192.168.1.69). Username/password = usertest/123456, dùng để chứng thực khi truy xuất file. Có  3 cách truy xuất:
1. Trên trình duyệt, nhập địa chỉ: http://192.168.1.69/test.txt
Lúc này browser sẽ hiện ra khung nhập username/password.
2. Truyền tham số trực tiếp trên trình duyệt như sau: http://usertest:123456@192.168.1.69/test.txt
3. Sử dụng code java (ưu điểm là encode được username/password khi truyền).
public static void main(String[] args) throws ClientProtocolException,
            IOException {
        String USER_AGENT = "Mozilla/5.0";
        String url = "http://10.30.174.211:9069/sabeco/donatest/donatestnginx.txt";

        HttpClient client = HttpClientBuilder.create().build();
        HttpGet request = new HttpGet(url);

        // add request header
        String encoding = new String(
                org.apache.commons.codec.binary.Base64
                        .encodeBase64(org.apache.commons.codec.binary.StringUtils
                                .getBytesUtf8("nginxtestuser:123456")));
        request.addHeader("Authorization", "Basic " + encoding);
        HttpResponse response = client.execute(request);

        System.out.println("Response Code : "
                + response.getStatusLine().getStatusCode());

        BufferedReader rd = new BufferedReader(new InputStreamReader(response
                .getEntity().getContent()));

        StringBuffer result = new StringBuffer();
        String line = "";
        while ((line = rd.readLine()) != null) {
            result.append(line);
        }
        System.out.println(result.toString());
    }

Không có nhận xét nào:

Đăng nhận xét